diff options
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/include/llvm/Bitcode/LLVMBitCodes.h | 22 | ||||
-rw-r--r-- | llvm/include/llvm/IR/ModuleSummaryIndex.h | 106 | ||||
-rw-r--r-- | llvm/include/llvm/IR/ModuleSummaryIndexYAML.h | 6 | ||||
-rw-r--r-- | llvm/lib/Analysis/ModuleSummaryAnalysis.cpp | 125 | ||||
-rw-r--r-- | llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 44 | ||||
-rw-r--r-- | llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 49 | ||||
-rw-r--r-- | llvm/test/Bitcode/thinlto-type-vcalls.ll | 105 | ||||
-rw-r--r-- | llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp | 4 |
8 files changed, 426 insertions, 35 deletions
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index c996c38261c..8d84fef9742 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -213,8 +213,28 @@ enum GlobalValueSummarySymtabCodes { FS_COMBINED_ORIGINAL_NAME = 9, // VERSION of the summary, bumped when adding flags for instance. FS_VERSION = 10, - // The list of llvm.type.test type identifiers used by the following function. + // The list of llvm.type.test type identifiers used by the following function + // that are used other than by an llvm.assume. + // [n x typeid] FS_TYPE_TESTS = 11, + // The list of virtual calls made by this function using + // llvm.assume(llvm.type.test) intrinsics that do not have all constant + // integer arguments. + // [n x (typeid, offset)] + FS_TYPE_TEST_ASSUME_VCALLS = 12, + // The list of virtual calls made by this function using + // llvm.type.checked.load intrinsics that do not have all constant integer + // arguments. + // [n x (typeid, offset)] + FS_TYPE_CHECKED_LOAD_VCALLS = 13, + // Identifies a virtual call made by this function using an + // llvm.assume(llvm.type.test) intrinsic with all constant integer arguments. + // [typeid, offset, n x arg] + FS_TYPE_TEST_ASSUME_CONST_VCALL = 14, + // Identifies a virtual call made by this function using an + // llvm.type.checked.load intrinsic with all constant integer arguments. + // [typeid, offset, n x arg] + FS_TYPE_CHECKED_LOAD_CONST_VCALL = 15, }; enum MetadataCodes { diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h b/llvm/include/llvm/IR/ModuleSummaryIndex.h index c710c41cccd..12802e232a5 100644 --- a/llvm/include/llvm/IR/ModuleSummaryIndex.h +++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -249,6 +249,22 @@ public: /// <CalleeValueInfo, CalleeInfo> call edge pair. typedef std::pair<ValueInfo, CalleeInfo> EdgeTy; + /// An "identifier" for a virtual function. This contains the type identifier + /// represented as a GUID and the offset from the address point to the virtual + /// function pointer. + struct VFuncId { + GlobalValue::GUID GUID; + uint64_t Offset; + }; + + /// A specification for a virtual function call with all constant integer + /// arguments. This is used to perform virtual constant propagation on the + /// summary. + struct ConstVCall { + VFuncId VFunc; + std::vector<uint64_t> Args; + }; + private: /// Number of instructions (ignoring debug instructions, e.g.) computed /// during the initial compile step when the summary index is first built. @@ -257,17 +273,36 @@ private: /// List of <CalleeValueInfo, CalleeInfo> call edge pairs from this function. std::vector<EdgeTy> CallGraphEdgeList; - /// List of type identifiers used by this function, represented as GUIDs. - std::vector<GlobalValue::GUID> TypeIdList; + /// List of type identifiers used by this function in llvm.type.test + /// intrinsics other than by an llvm.assume intrinsic, represented as GUIDs. + std::vector<GlobalValue::GUID> TypeTests; + + /// List of virtual calls made by this function using (respectively) + /// llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics that do + /// not have all constant integer arguments. + std::vector<VFuncId> TypeTestAssumeVCalls, TypeCheckedLoadVCalls; + + /// List of virtual calls made by this function using (respectively) + /// llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics with + /// all constant integer arguments. + std::vector<ConstVCall> TypeTestAssumeConstVCalls, TypeCheckedLoadConstVCalls; public: /// Summary constructors. FunctionSummary(GVFlags Flags, unsigned NumInsts, std::vector<ValueInfo> Refs, std::vector<EdgeTy> CGEdges, - std::vector<GlobalValue::GUID> TypeIds) + std::vector<GlobalValue::GUID> TypeTests, + std::vector<VFuncId> TypeTestAssumeVCalls, + std::vector<VFuncId> TypeCheckedLoadVCalls, + std::vector<ConstVCall> TypeTestAssumeConstVCalls, + std::vector<ConstVCall> TypeCheckedLoadConstVCalls) : GlobalValueSummary(FunctionKind, Flags, std::move(Refs)), InstCount(NumInsts), CallGraphEdgeList(std::move(CGEdges)), - TypeIdList(std::move(TypeIds)) {} + TypeTests(std::move(TypeTests)), + TypeTestAssumeVCalls(std::move(TypeTestAssumeVCalls)), + TypeCheckedLoadVCalls(std::move(TypeCheckedLoadVCalls)), + TypeTestAssumeConstVCalls(std::move(TypeTestAssumeConstVCalls)), + TypeCheckedLoadConstVCalls(std::move(TypeCheckedLoadConstVCalls)) {} /// Check if this is a function summary. static bool classof(const GlobalValueSummary *GVS) { @@ -280,8 +315,67 @@ public: /// Return the list of <CalleeValueInfo, CalleeInfo> pairs. ArrayRef<EdgeTy> calls() const { return CallGraphEdgeList; } - /// Returns the list of type identifiers used by this function. - ArrayRef<GlobalValue::GUID> type_tests() const { return TypeIdList; } + /// Returns the list of type identifiers used by this function in + /// llvm.type.test intrinsics other than by an llvm.assume intrinsic, + /// represented as GUIDs. + ArrayRef<GlobalValue::GUID> type_tests() const { return TypeTests; } + + /// Returns the list of virtual calls made by this function using + /// llvm.assume(llvm.type.test) intrinsics that do not have all constant + /// integer arguments. + ArrayRef<VFuncId> type_test_assume_vcalls() const { + return TypeTestAssumeVCalls; + } + + /// Returns the list of virtual calls made by this function using + /// llvm.type.checked.load intrinsics that do not have all constant integer + /// arguments. + ArrayRef<VFuncId> type_checked_load_vcalls() const { + return TypeCheckedLoadVCalls; + } + + /// Returns the list of virtual calls made by this function using + /// llvm.assume(llvm.type.test) intrinsics with all constant integer + /// arguments. + ArrayRef<ConstVCall> type_test_assume_const_vcalls() const { + return TypeTestAssumeConstVCalls; + } + + /// Returns the list of virtual calls made by this function using + /// llvm.type.checked.load intrinsics with all constant integer arguments. + ArrayRef<ConstVCall> type_checked_load_const_vcalls() const { + return TypeCheckedLoadConstVCalls; + } +}; + +template <> struct DenseMapInfo<FunctionSummary::VFuncId> { + static inline FunctionSummary::VFuncId getEmptyKey() { + return {0, uint64_t(-1)}; + } + static inline FunctionSummary::VFuncId getTombstoneKey() { + return {0, uint64_t(-2)}; + } + static bool isEqual(FunctionSummary::VFuncId L, FunctionSummary::VFuncId R) { + return L.GUID == R.GUID && L.Offset == R.Offset; + } + static unsigned getHashValue(FunctionSummary::VFuncId I) { return I.GUID; } +}; + +template <> struct DenseMapInfo<FunctionSummary::ConstVCall> { + static inline FunctionSummary::ConstVCall getEmptyKey() { + return {{0, uint64_t(-1)}, {}}; + } + static inline FunctionSummary::ConstVCall getTombstoneKey() { + return {{0, uint64_t(-2)}, {}}; + } + static bool isEqual(FunctionSummary::ConstVCall L, + FunctionSummary::ConstVCall R) { + return DenseMapInfo<FunctionSummary::VFuncId>::isEqual(L.VFunc, R.VFunc) && + L.Args == R.Args; + } + static unsigned getHashValue(FunctionSummary::ConstVCall I) { + return I.VFunc.GUID; + } }; /// \brief Global variable summary information to aid decisions and diff --git a/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h b/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h index e2880ec6fec..a5942eb2b0f 100644 --- a/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h +++ b/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h @@ -82,7 +82,11 @@ template <> struct CustomMappingTraits<GlobalValueSummaryMapTy> { false); Elem.push_back(llvm::make_unique<FunctionSummary>( GVFlags, 0, ArrayRef<ValueInfo>{}, - ArrayRef<FunctionSummary::EdgeTy>{}, std::move(FSum.TypeTests))); + ArrayRef<FunctionSummary::EdgeTy>{}, std::move(FSum.TypeTests), + ArrayRef<FunctionSummary::VFuncId>{}, + ArrayRef<FunctionSummary::VFuncId>{}, + ArrayRef<FunctionSummary::ConstVCall>{}, + ArrayRef<FunctionSummary::ConstVCall>{})); } } static void output(IO &io, GlobalValueSummaryMapTy &V) { diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp index f5ba637e58e..70b55674c07 100644 --- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -84,6 +84,92 @@ static bool isNonRenamableLocal(const GlobalValue &GV) { return GV.hasSection() && GV.hasLocalLinkage(); } +/// Determine whether this call has all constant integer arguments (excluding +/// "this") and summarize it to VCalls or ConstVCalls as appropriate. +static void addVCallToSet(DevirtCallSite Call, GlobalValue::GUID Guid, + SetVector<FunctionSummary::VFuncId> &VCalls, + SetVector<FunctionSummary::ConstVCall> &ConstVCalls) { + std::vector<uint64_t> Args; + // Start from the second argument to skip the "this" pointer. + for (auto &Arg : make_range(Call.CS.arg_begin() + 1, Call.CS.arg_end())) { + auto *CI = dyn_cast<ConstantInt>(Arg); + if (!CI || CI->getBitWidth() > 64) { + VCalls.insert({Guid, Call.Offset}); + return; + } + Args.push_back(CI->getZExtValue()); + } + ConstVCalls.insert({{Guid, Call.Offset}, std::move(Args)}); +} + +/// If this intrinsic call requires that we add information to the function +/// summary, do so via the non-constant reference arguments. +static void addIntrinsicToSummary( + const CallInst *CI, SetVector<GlobalValue::GUID> &TypeTests, + SetVector<FunctionSummary::VFuncId> &TypeTestAssumeVCalls, + SetVector<FunctionSummary::VFuncId> &TypeCheckedLoadVCalls, + SetVector<FunctionSummary::ConstVCall> &TypeTestAssumeConstVCalls, + SetVector<FunctionSummary::ConstVCall> &TypeCheckedLoadConstVCalls) { + switch (CI->getCalledFunction()->getIntrinsicID()) { + case Intrinsic::type_test: { + auto *TypeMDVal = cast<MetadataAsValue>(CI->getArgOperand(1)); + auto *TypeId = dyn_cast<MDString>(TypeMDVal->getMetadata()); + if (!TypeId) + break; + GlobalValue::GUID Guid = GlobalValue::getGUID(TypeId->getString()); + + // Produce a summary from type.test intrinsics. We only summarize type.test + // intrinsics that are used other than by an llvm.assume intrinsic. + // Intrinsics that are assumed are relevant only to the devirtualization + // pass, not the type test lowering pass. + bool HasNonAssumeUses = llvm::any_of(CI->uses(), [](const Use &CIU) { + auto *AssumeCI = dyn_cast<CallInst>(CIU.getUser()); + if (!AssumeCI) + return true; + Function *F = AssumeCI->getCalledFunction(); + return !F || F->getIntrinsicID() != Intrinsic::assume; + }); + if (HasNonAssumeUses) + TypeTests.insert(Guid); + + SmallVector<DevirtCallSite, 4> DevirtCalls; + SmallVector<CallInst *, 4> Assumes; + findDevirtualizableCallsForTypeTest(DevirtCalls, Assumes, CI); + for (auto &Call : DevirtCalls) + addVCallToSet(Call, Guid, TypeTestAssumeVCalls, + TypeTestAssumeConstVCalls); + + break; + } + + case Intrinsic::type_checked_load: { + auto *TypeMDVal = cast<MetadataAsValue>(CI->getArgOperand(2)); + auto *TypeId = dyn_cast<MDString>(TypeMDVal->getMetadata()); + if (!TypeId) + break; + GlobalValue::GUID Guid = GlobalValue::getGUID(TypeId->getString()); + + SmallVector<DevirtCallSite, 4> DevirtCalls; + SmallVector<Instruction *, 4> LoadedPtrs; + SmallVector<Instruction *, 4> Preds; + bool HasNonCallUses = false; + findDevirtualizableCallsForTypeCheckedLoad(DevirtCalls, LoadedPtrs, Preds, + HasNonCallUses, CI); + // Any non-call uses of the result of llvm.type.checked.load will + // prevent us from optimizing away the llvm.type.test. + if (HasNonCallUses) + TypeTests.insert(Guid); + for (auto &Call : DevirtCalls) + addVCallToSet(Call, Guid, TypeCheckedLoadVCalls, + TypeCheckedLoadConstVCalls); + + break; + } + default: + break; + } +} + static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, const Function &F, BlockFrequencyInfo *BFI, @@ -99,6 +185,10 @@ computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, MapVector<ValueInfo, CalleeInfo> CallGraphEdges; SetVector<ValueInfo> RefEdges; SetVector<GlobalValue::GUID> TypeTests; + SetVector<FunctionSummary::VFuncId> TypeTestAssumeVCalls, + TypeCheckedLoadVCalls; + SetVector<FunctionSummary::ConstVCall> TypeTestAssumeConstVCalls, + TypeCheckedLoadConstVCalls; ICallPromotionAnalysis ICallAnalysis; bool HasInlineAsmMaybeReferencingInternal = false; @@ -133,25 +223,11 @@ computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, // Check if this is a direct call to a known function or a known // intrinsic, or an indirect call with profile data. if (CalledFunction) { - if (CalledFunction->isIntrinsic()) { - if (CalledFunction->getIntrinsicID() != Intrinsic::type_test) - continue; - // Produce a summary from type.test intrinsics. We only summarize - // type.test intrinsics that are used other than by an llvm.assume - // intrinsic. Intrinsics that are assumed are relevant only to the - // devirtualization pass, not the type test lowering pass. - bool HasNonAssumeUses = llvm::any_of(CI->uses(), [](const Use &CIU) { - auto *AssumeCI = dyn_cast<CallInst>(CIU.getUser()); - if (!AssumeCI) - return true; - Function *F = AssumeCI->getCalledFunction(); - return !F || F->getIntrinsicID() != Intrinsic::assume; - }); - if (HasNonAssumeUses) { - auto *TypeMDVal = cast<MetadataAsValue>(CI->getArgOperand(1)); - if (auto *TypeId = dyn_cast<MDString>(TypeMDVal->getMetadata())) - TypeTests.insert(GlobalValue::getGUID(TypeId->getString())); - } + if (CI && CalledFunction->isIntrinsic()) { + addIntrinsicToSummary( + CI, TypeTests, TypeTestAssumeVCalls, TypeCheckedLoadVCalls, + TypeTestAssumeConstVCalls, TypeCheckedLoadConstVCalls); + continue; } // We should have named any anonymous globals assert(CalledFunction->hasName()); @@ -193,7 +269,10 @@ computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, /* LiveRoot = */ false); auto FuncSummary = llvm::make_unique<FunctionSummary>( Flags, NumInsts, RefEdges.takeVector(), CallGraphEdges.takeVector(), - TypeTests.takeVector()); + TypeTests.takeVector(), TypeTestAssumeVCalls.takeVector(), + TypeCheckedLoadVCalls.takeVector(), + TypeTestAssumeConstVCalls.takeVector(), + TypeCheckedLoadConstVCalls.takeVector()); if (NonRenamableLocal) CantBePromoted.insert(F.getGUID()); Index.addGlobalValueSummary(F.getName(), std::move(FuncSummary)); @@ -347,7 +426,11 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex( llvm::make_unique<FunctionSummary>( GVFlags, 0, ArrayRef<ValueInfo>{}, ArrayRef<FunctionSummary::EdgeTy>{}, - ArrayRef<GlobalValue::GUID>{}); + ArrayRef<GlobalValue::GUID>{}, + ArrayRef<FunctionSummary::VFuncId>{}, + ArrayRef<FunctionSummary::VFuncId>{}, + ArrayRef<FunctionSummary::ConstVCall>{}, + ArrayRef<FunctionSummary::ConstVCall>{}); Index.addGlobalValueSummary(Name, std::move(Summary)); } else { std::unique_ptr<GlobalVarSummary> Summary = diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index a46e49ccde8..fe73efef5a5 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -4848,6 +4848,10 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary( GlobalValueSummary *LastSeenSummary = nullptr; bool Combined = false; std::vector<GlobalValue::GUID> PendingTypeTests; + std::vector<FunctionSummary::VFuncId> PendingTypeTestAssumeVCalls, + PendingTypeCheckedLoadVCalls; + std::vector<FunctionSummary::ConstVCall> PendingTypeTestAssumeConstVCalls, + PendingTypeCheckedLoadConstVCalls; while (true) { BitstreamEntry Entry = Stream.advanceSkippingSubblocks(); @@ -4914,8 +4918,15 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary( IsOldProfileFormat, HasProfile); auto FS = llvm::make_unique<FunctionSummary>( Flags, InstCount, std::move(Refs), std::move(Calls), - std::move(PendingTypeTests)); + std::move(PendingTypeTests), std::move(PendingTypeTestAssumeVCalls), + std::move(PendingTypeCheckedLoadVCalls), + std::move(PendingTypeTestAssumeConstVCalls), + std::move(PendingTypeCheckedLoadConstVCalls)); PendingTypeTests.clear(); + PendingTypeTestAssumeVCalls.clear(); + PendingTypeCheckedLoadVCalls.clear(); + PendingTypeTestAssumeConstVCalls.clear(); + PendingTypeCheckedLoadConstVCalls.clear(); auto GUID = getGUIDFromValueId(ValueID); FS->setModulePath(TheIndex.addModulePath(ModulePath, 0)->first()); FS->setOriginalName(GUID.second); @@ -4989,8 +5000,15 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary( GlobalValue::GUID GUID = getGUIDFromValueId(ValueID).first; auto FS = llvm::make_unique<FunctionSummary>( Flags, InstCount, std::move(Refs), std::move(Edges), - std::move(PendingTypeTests)); + std::move(PendingTypeTests), std::move(PendingTypeTestAssumeVCalls), + std::move(PendingTypeCheckedLoadVCalls), + std::move(PendingTypeTestAssumeConstVCalls), + std::move(PendingTypeCheckedLoadConstVCalls)); PendingTypeTests.clear(); + PendingTypeTestAssumeVCalls.clear(); + PendingTypeCheckedLoadVCalls.clear(); + PendingTypeTestAssumeConstVCalls.clear(); + PendingTypeCheckedLoadConstVCalls.clear(); LastSeenSummary = FS.get(); FS->setModulePath(ModuleIdMap[ModuleId]); TheIndex.addGlobalValueSummary(GUID, std::move(FS)); @@ -5054,6 +5072,28 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary( Record.end()); break; } + case bitc::FS_TYPE_TEST_ASSUME_VCALLS: { + assert(PendingTypeTestAssumeVCalls.empty()); + for (unsigned I = 0; I != Record.size(); I += 2) + PendingTypeTestAssumeVCalls.push_back({Record[I], Record[I+1]}); + break; + } + case bitc::FS_TYPE_CHECKED_LOAD_VCALLS: { + assert(PendingTypeCheckedLoadVCalls.empty()); + for (unsigned I = 0; I != Record.size(); I += 2) + PendingTypeCheckedLoadVCalls.push_back({Record[I], Record[I+1]}); + break; + } + case bitc::FS_TYPE_TEST_ASSUME_CONST_VCALL: { + PendingTypeTestAssumeConstVCalls.push_back( + {{Record[0], Record[1]}, {Record.begin() + 2, Record.end()}}); + break; + } + case bitc::FS_TYPE_CHECKED_LOAD_CONST_VCALL: { + PendingTypeCheckedLoadConstVCalls.push_back( + {{Record[0], Record[1]}, {Record.begin() + 2, Record.end()}}); + break; + } } } llvm_unreachable("Exit infinite loop"); diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index 4eac89c37a5..1281fca5f69 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -3368,6 +3368,49 @@ void IndexBitcodeWriter::writeModStrings() { Stream.ExitBlock(); } +/// Write the function type metadata related records that need to appear before +/// a function summary entry (whether per-module or combined). +static void writeFunctionTypeMetadataRecords(BitstreamWriter &Stream, + FunctionSummary *FS) { + if (!FS->type_tests().empty()) + Stream.EmitRecord(bitc::FS_TYPE_TESTS, FS->type_tests()); + + SmallVector<uint64_t, 64> Record; + + auto WriteVFuncIdVec = [&](uint64_t Ty, + ArrayRef<FunctionSummary::VFuncId> VFs) { + if (VFs.empty()) + return; + Record.clear(); + for (auto &VF : VFs) { + Record.push_back(VF.GUID); + Record.push_back(VF.Offset); + } + Stream.EmitRecord(Ty, Record); + }; + + WriteVFuncIdVec(bitc::FS_TYPE_TEST_ASSUME_VCALLS, + FS->type_test_assume_vcalls()); + WriteVFuncIdVec(bitc::FS_TYPE_CHECKED_LOAD_VCALLS, + FS->type_checked_load_vcalls()); + + auto WriteConstVCallVec = [&](uint64_t Ty, + ArrayRef<FunctionSummary::ConstVCall> VCs) { + for (auto &VC : VCs) { + Record.clear(); + Record.push_back(VC.VFunc.GUID); + Record.push_back(VC.VFunc.Offset); + Record.insert(Record.end(), VC.Args.begin(), VC.Args.end()); + Stream.EmitRecord(Ty, Record); + } + }; + + WriteConstVCallVec(bitc::FS_TYPE_TEST_ASSUME_CONST_VCALL, + FS->type_test_assume_const_vcalls()); + WriteConstVCallVec(bitc::FS_TYPE_CHECKED_LOAD_CONST_VCALL, + FS->type_checked_load_const_vcalls()); +} + // Helper to emit a single function summary record. void ModuleBitcodeWriter::writePerModuleFunctionSummaryRecord( SmallVector<uint64_t, 64> &NameVals, GlobalValueSummary *Summary, @@ -3376,8 +3419,7 @@ void ModuleBitcodeWriter::writePerModuleFunctionSummaryRecord( NameVals.push_back(ValueID); FunctionSummary *FS = cast<FunctionSummary>(Summary); - if (!FS->type_tests().empty()) - Stream.EmitRecord(bitc::FS_TYPE_TESTS, FS->type_tests()); + writeFunctionTypeMetadataRecords(Stream, FS); NameVals.push_back(getEncodedGVSummaryFlags(FS->flags())); NameVals.push_back(FS->instCount()); @@ -3637,8 +3679,7 @@ void IndexBitcodeWriter::writeCombinedGlobalValueSummary() { } auto *FS = cast<FunctionSummary>(S); - if (!FS->type_tests().empty()) - Stream.EmitRecord(bitc::FS_TYPE_TESTS, FS->type_tests()); + writeFunctionTypeMetadataRecords(Stream, FS); NameVals.push_back(ValueId); NameVals.push_back(Index.getModuleId(FS->modulePath())); diff --git a/llvm/test/Bitcode/thinlto-type-vcalls.ll b/llvm/test/Bitcode/thinlto-type-vcalls.ll new file mode 100644 index 00000000000..40d229d1214 --- /dev/null +++ b/llvm/test/Bitcode/thinlto-type-vcalls.ll @@ -0,0 +1,105 @@ +; RUN: opt -module-summary %s -o %t.o +; RUN: llvm-bcanalyzer -dump %t.o | FileCheck %s +; RUN: llvm-lto -thinlto -o %t2 %t.o +; RUN: llvm-bcanalyzer -dump %t2.thinlto.bc | FileCheck --check-prefix=COMBINED %s + +target datalayout = "e-p:64:64" +target triple = "x86_64-unknown-linux-gnu" + +; COMBINED: <TYPE_TEST_ASSUME_VCALLS op0=6699318081062747564 op1=16/> +; COMBINED-NEXT: <COMBINED +; COMBINED-NEXT: <TYPE_CHECKED_LOAD_VCALLS op0=6699318081062747564 op1=16/> +; COMBINED-NEXT: <COMBINED +; COMBINED-NEXT: <TYPE_TEST_ASSUME_VCALLS op0=6699318081062747564 op1=24 op2=-2012135647395072713 op3=32/> +; COMBINED-NEXT: <COMBINED +; COMBINED-NEXT: <TYPE_TEST_ASSUME_CONST_VCALL op0=6699318081062747564 op1=16 op2=42/> +; COMBINED-NEXT: <TYPE_TEST_ASSUME_CONST_VCALL op0=6699318081062747564 op1=24 op2=43/> +; COMBINED-NEXT: <COMBINED +; COMBINED-NEXT: <TYPE_CHECKED_LOAD_CONST_VCALL op0=6699318081062747564 op1=16 op2=42/> +; COMBINED-NEXT: <COMBINED +; COMBINED-NEXT: <TYPE_TESTS op0=7546896869197086323/> +; COMBINED-NEXT: <COMBINED + +; CHECK: <TYPE_TEST_ASSUME_VCALLS op0=6699318081062747564 op1=16/> +define void @f1([3 x i8*]* %vtable) { + %vtablei8 = bitcast [3 x i8*]* %vtable to i8* + %p = call i1 @llvm.type.test(i8* %vtablei8, metadata !"foo") + call void @llvm.assume(i1 %p) + %fptrptr = getelementptr [3 x i8*], [3 x i8*]* %vtable, i32 0, i32 2 + %fptr = load i8*, i8** %fptrptr + %fptr_casted = bitcast i8* %fptr to void (i8*, i32)* + call void %fptr_casted(i8* null, i32 undef) + ret void +} + +; CHECK: <TYPE_TEST_ASSUME_VCALLS op0=6699318081062747564 op1=24 op2=-2012135647395072713 op3=32/> +define void @f2([3 x i8*]* %vtable, [3 x i8*]* %vtable2) { + %vtablei8 = bitcast [3 x i8*]* %vtable to i8* + %p = call i1 @llvm.type.test(i8* %vtablei8, metadata !"foo") + call void @llvm.assume(i1 %p) + %fptrptr = getelementptr [3 x i8*], [3 x i8*]* %vtable, i32 0, i32 3 + %fptr = load i8*, i8** %fptrptr + %fptr_casted = bitcast i8* %fptr to void (i8*, i32)* + call void %fptr_casted(i8* null, i32 undef) + + %vtablei82 = bitcast [3 x i8*]* %vtable2 to i8* + %p2 = call i1 @llvm.type.test(i8* %vtablei82, metadata !"bar") + call void @llvm.assume(i1 %p2) + %fptrptr2 = getelementptr [3 x i8*], [3 x i8*]* %vtable2, i32 0, i32 4 + %fptr2 = load i8*, i8** %fptrptr2 + %fptr_casted2 = bitcast i8* %fptr2 to void (i8*, i128)* + call void %fptr_casted2(i8* null, i128 0) + + ret void +} + +; CHECK: <TYPE_CHECKED_LOAD_VCALLS op0=6699318081062747564 op1=16/> +define void @f3(i8* %vtable) { + %pair = call {i8*, i1} @llvm.type.checked.load(i8* %vtable, i32 16, metadata !"foo") + %fptr = extractvalue {i8*, i1} %pair, 0 + %fptr_casted = bitcast i8* %fptr to void (i8*, i32)* + call void %fptr_casted(i8* null, i32 undef) + ret void +} + +; CHECK: <TYPE_TEST_ASSUME_CONST_VCALL op0=6699318081062747564 op1=16 op2=42/> +; CHECK-NEXT: <TYPE_TEST_ASSUME_CONST_VCALL op0=6699318081062747564 op1=24 op2=43/> +define void @f4([3 x i8*]* %vtable, [3 x i8*]* %vtable2) { + %vtablei8 = bitcast [3 x i8*]* %vtable to i8* + %p = call i1 @llvm.type.test(i8* %vtablei8, metadata !"foo") + call void @llvm.assume(i1 %p) + %fptrptr = getelementptr [3 x i8*], [3 x i8*]* %vtable, i32 0, i32 2 + %fptr = load i8*, i8** %fptrptr + %fptr_casted = bitcast i8* %fptr to void (i8*, i32)* + call void %fptr_casted(i8* null, i32 42) + + %vtablei82 = bitcast [3 x i8*]* %vtable2 to i8* + %p2 = call i1 @llvm.type.test(i8* %vtablei82, metadata !"foo") + call void @llvm.assume(i1 %p2) + %fptrptr2 = getelementptr [3 x i8*], [3 x i8*]* %vtable2, i32 0, i32 3 + %fptr2 = load i8*, i8** %fptrptr2 + %fptr_casted2 = bitcast i8* %fptr2 to void (i8*, i32)* + call void %fptr_casted2(i8* null, i32 43) + ret void +} + +; CHECK: <TYPE_CHECKED_LOAD_CONST_VCALL op0=6699318081062747564 op1=16 op2=42/> +define void @f5(i8* %vtable) { + %pair = call {i8*, i1} @llvm.type.checked.load(i8* %vtable, i32 16, metadata !"foo") + %fptr = extractvalue {i8*, i1} %pair, 0 + %fptr_casted = bitcast i8* %fptr to void (i8*, i32)* + call void %fptr_casted(i8* null, i32 42) + ret void +} + +; CHECK-NOT: <TYPE_CHECKED_LOAD_CONST_VCALL op0=7546896869197086323 +; CHECK: <TYPE_TESTS op0=7546896869197086323/> +; CHECK-NOT: <TYPE_CHECKED_LOAD_CONST_VCALL op0=7546896869197086323 +define {i8*, i1} @f6(i8* %vtable) { + %pair = call {i8*, i1} @llvm.type.checked.load(i8* %vtable, i32 16, metadata !"baz") + ret {i8*, i1} %pair +} + +declare i1 @llvm.type.test(i8*, metadata) nounwind readnone +declare void @llvm.assume(i1) +declare {i8*, i1} @llvm.type.checked.load(i8*, i32, metadata) diff --git a/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index b84c4a83dee..6f144213521 100644 --- a/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -312,6 +312,10 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, STRINGIFY_CODE(FS, COMBINED_ORIGINAL_NAME) STRINGIFY_CODE(FS, VERSION) STRINGIFY_CODE(FS, TYPE_TESTS) + STRINGIFY_CODE(FS, TYPE_TEST_ASSUME_VCALLS) + STRINGIFY_CODE(FS, TYPE_CHECKED_LOAD_VCALLS) + STRINGIFY_CODE(FS, TYPE_TEST_ASSUME_CONST_VCALL) + STRINGIFY_CODE(FS, TYPE_CHECKED_LOAD_CONST_VCALL) } case bitc::METADATA_ATTACHMENT_ID: switch(CodeID) { |