Index: llvm/include/llvm/Analysis/TypeMetadataUtils.h =================================================================== --- llvm/include/llvm/Analysis/TypeMetadataUtils.h +++ llvm/include/llvm/Analysis/TypeMetadataUtils.h @@ -32,14 +32,16 @@ /// call sites based on the call and return them in DevirtCalls. void findDevirtualizableCallsForTypeTest( SmallVectorImpl &DevirtCalls, - SmallVectorImpl &Assumes, CallInst *CI); + SmallVectorImpl &Assumes, bool &HasNonAssumeUses, + const CallInst *CI); /// Given a call to the intrinsic @llvm.type.checked.load, find all /// devirtualizable call sites based on the call and return them in DevirtCalls. void findDevirtualizableCallsForTypeCheckedLoad( SmallVectorImpl &DevirtCalls, SmallVectorImpl &LoadedPtrs, - SmallVectorImpl &Preds, bool &HasNonCallUses, CallInst *CI); + SmallVectorImpl &Preds, bool &HasNonCallUses, + const CallInst *CI); } #endif Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -213,6 +213,8 @@ 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. + FS_TYPE_TESTS = 11, }; enum MetadataCodes { Index: llvm/include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- llvm/include/llvm/IR/ModuleSummaryIndex.h +++ llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -284,12 +284,15 @@ /// List of call edge pairs from this function. OwningArrayRef CallGraphEdgeList; + /// List of type identifiers used by this function, represented as GUIDs. + OwningArrayRef TypeIdList; + public: /// Summary constructors. FunctionSummary(GVFlags Flags, unsigned NumInsts, ArrayRef Refs, - ArrayRef CGEdges) + ArrayRef CGEdges, ArrayRef TypeIds) : GlobalValueSummary(FunctionKind, Flags, Refs), InstCount(NumInsts), - CallGraphEdgeList(CGEdges) {} + CallGraphEdgeList(CGEdges), TypeIdList(TypeIds) {} /// Check if this is a function summary. static bool classof(const GlobalValueSummary *GVS) { @@ -301,6 +304,9 @@ /// Return the list of pairs. ArrayRef calls() const { return CallGraphEdgeList; } + + /// Returns the list of type identifiers used by this function. + ArrayRef type_tests() const { return TypeIdList; } }; /// \brief Global variable summary information to aid decisions and Index: llvm/lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -22,6 +22,7 @@ #include "llvm/Analysis/IndirectCallPromotionAnalysis.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ProfileSummaryInfo.h" +#include "llvm/Analysis/TypeMetadataUtils.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/InstIterator.h" @@ -92,6 +93,7 @@ // counts for all static calls to a given callee. MapVector CallGraphEdges; SetVector RefEdges; + SetVector TypeTests; ICallPromotionAnalysis ICallAnalysis; bool HasInlineAsmMaybeReferencingInternal = false; @@ -125,9 +127,23 @@ } // Check if this is a direct call to a known function. if (CalledFunction) { - // Skip intrinsics. - if (CalledFunction->isIntrinsic()) + if (CalledFunction->isIntrinsic()) { + // Produce a summary from type.test intrinsics. + if (CalledFunction->getIntrinsicID() == Intrinsic::type_test) { + SmallVector DevirtCalls; + SmallVector Assumes; + bool HasNonAssumeUses; + findDevirtualizableCallsForTypeTest(DevirtCalls, Assumes, + HasNonAssumeUses, CI); + if (HasNonAssumeUses) { + auto TypeMDVal = cast(CI->getArgOperand(1)); + if (auto *TypeId = dyn_cast(TypeMDVal->getMetadata())) + TypeTests.insert(GlobalValue::getGUID(TypeId->getString())); + } + } + // Skip all other intrinsics. continue; + } // We should have named any anonymous globals assert(CalledFunction->hasName()); auto ScaledCount = BFI ? BFI->getBlockProfileCount(&BB) : None; @@ -161,7 +177,8 @@ GlobalValueSummary::GVFlags Flags(F); auto FuncSummary = llvm::make_unique( - Flags, NumInsts, RefEdges.getArrayRef(), CallGraphEdges.getArrayRef()); + Flags, NumInsts, RefEdges.getArrayRef(), CallGraphEdges.getArrayRef(), + TypeTests.getArrayRef()); if (HasInlineAsmMaybeReferencingInternal) FuncSummary->setHasInlineAsmMaybeReferencingInternal(); Index.addGlobalValueSummary(F.getName(), std::move(FuncSummary)); @@ -281,7 +298,8 @@ std::unique_ptr Summary = llvm::make_unique( GVFlags, 0, ArrayRef{}, - ArrayRef{}); + ArrayRef{}, + ArrayRef{}); Summary->setNoRename(); Index.addGlobalValueSummary(Name, std::move(Summary)); } else { Index: llvm/lib/Analysis/TypeMetadataUtils.cpp =================================================================== --- llvm/lib/Analysis/TypeMetadataUtils.cpp +++ llvm/lib/Analysis/TypeMetadataUtils.cpp @@ -39,7 +39,7 @@ // Search for virtual calls that load from VPtr and add them to DevirtCalls. static void -findLoadCallsAtConstantOffset(Module *M, +findLoadCallsAtConstantOffset(const Module *M, SmallVectorImpl &DevirtCalls, Value *VPtr, int64_t Offset) { for (const Use &U : VPtr->uses()) { @@ -62,10 +62,12 @@ void llvm::findDevirtualizableCallsForTypeTest( SmallVectorImpl &DevirtCalls, - SmallVectorImpl &Assumes, CallInst *CI) { + SmallVectorImpl &Assumes, bool &HasNonAssumeUses, + const CallInst *CI) { assert(CI->getCalledFunction()->getIntrinsicID() == Intrinsic::type_test); + HasNonAssumeUses = false; - Module *M = CI->getParent()->getParent()->getParent(); + const Module *M = CI->getParent()->getParent()->getParent(); // Find llvm.assume intrinsics for this llvm.type.test call. for (const Use &CIU : CI->uses()) { @@ -74,6 +76,8 @@ Function *F = AssumeCI->getCalledFunction(); if (F && F->getIntrinsicID() == Intrinsic::assume) Assumes.push_back(AssumeCI); + } else { + HasNonAssumeUses = true; } } @@ -87,7 +91,8 @@ void llvm::findDevirtualizableCallsForTypeCheckedLoad( SmallVectorImpl &DevirtCalls, SmallVectorImpl &LoadedPtrs, - SmallVectorImpl &Preds, bool &HasNonCallUses, CallInst *CI) { + SmallVectorImpl &Preds, bool &HasNonCallUses, + const CallInst *CI) { assert(CI->getCalledFunction()->getIntrinsicID() == Intrinsic::type_checked_load); @@ -97,7 +102,7 @@ return; } - for (Use &U : CI->uses()) { + for (const Use &U : CI->uses()) { auto CIU = U.getUser(); if (auto EVI = dyn_cast(CIU)) { if (EVI->getNumIndices() == 1 && EVI->getIndices()[0] == 0) { Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -4845,6 +4845,7 @@ // "OriginalName" attachement. GlobalValueSummary *LastSeenSummary = nullptr; bool Combined = false; + std::vector PendingTypeTests; while (true) { BitstreamEntry Entry = Stream.advanceSkippingSubblocks(); @@ -4909,8 +4910,9 @@ std::vector Calls = makeCallList( ArrayRef(Record).slice(CallGraphEdgeStartIndex), IsOldProfileFormat, HasProfile); - auto FS = - llvm::make_unique(Flags, InstCount, Refs, Calls); + auto FS = llvm::make_unique(Flags, InstCount, Refs, + Calls, PendingTypeTests); + PendingTypeTests.clear(); auto GUID = getGUIDFromValueId(ValueID); FS->setModulePath(TheIndex.addModulePath(ModulePath, 0)->first()); FS->setOriginalName(GUID.second); @@ -4981,8 +4983,9 @@ ArrayRef(Record).slice(CallGraphEdgeStartIndex), IsOldProfileFormat, HasProfile); GlobalValue::GUID GUID = getGUIDFromValueId(ValueID).first; - auto FS = - llvm::make_unique(Flags, InstCount, Refs, Edges); + auto FS = llvm::make_unique( + Flags, InstCount, Refs, Edges, PendingTypeTests); + PendingTypeTests.clear(); LastSeenSummary = FS.get(); FS->setModulePath(ModuleIdMap[ModuleId]); TheIndex.addGlobalValueSummary(GUID, std::move(FS)); @@ -5038,6 +5041,12 @@ LastSeenSummary->setOriginalName(OriginalName); // Reset the LastSeenSummary LastSeenSummary = nullptr; + break; + } + case bitc::FS_TYPE_TESTS: { + PendingTypeTests.insert(PendingTypeTests.end(), Record.begin(), + Record.end()); + break; } } } Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -3271,6 +3271,9 @@ NameVals.push_back(ValueID); FunctionSummary *FS = cast(Summary); + if (!FS->type_tests().empty()) + Stream.EmitRecord(bitc::FS_TYPE_TESTS, FS->type_tests()); + NameVals.push_back(getEncodedGVSummaryFlags(FS->flags())); NameVals.push_back(FS->instCount()); NameVals.push_back(FS->refs().size()); @@ -3530,6 +3533,9 @@ } auto *FS = cast(S); + if (!FS->type_tests().empty()) + Stream.EmitRecord(bitc::FS_TYPE_TESTS, FS->type_tests()); + NameVals.push_back(ValueId); NameVals.push_back(Index.getModuleId(FS->modulePath())); NameVals.push_back(getEncodedGVSummaryFlags(FS->flags())); Index: llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp =================================================================== --- llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -755,7 +755,9 @@ // Search for virtual calls based on %p and add them to DevirtCalls. SmallVector DevirtCalls; SmallVector Assumes; - findDevirtualizableCallsForTypeTest(DevirtCalls, Assumes, CI); + bool HasNonAssumeUses; + findDevirtualizableCallsForTypeTest(DevirtCalls, Assumes, HasNonAssumeUses, + CI); // If we found any, add them to CallSlots. Only do this if we haven't seen // the vtable pointer before, as it may have been CSE'd with pointers from Index: llvm/test/Bitcode/thinlto-type-tests.ll =================================================================== --- /dev/null +++ llvm/test/Bitcode/thinlto-type-tests.ll @@ -0,0 +1,30 @@ +; 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 + +; COMBINED: +; COMBINED: +; COMBINED: + +; CHECK: +define i1 @f() { + %p = call i1 @llvm.type.test(i8* null, metadata !"foo") + ret i1 %p +} + +; CHECK: +define i1 @g() { + %p = call i1 @llvm.type.test(i8* null, metadata !"foo") + %q = call i1 @llvm.type.test(i8* null, metadata !"bar") + %pq = and i1 %p, %q + ret i1 %pq +} + +; CHECK: +define i1 @h() { + %p = call i1 @llvm.type.test(i8* null, metadata !"bar") + ret i1 %p +} + +declare i1 @llvm.type.test(i8*, metadata) nounwind readnone Index: llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp =================================================================== --- llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -311,6 +311,7 @@ STRINGIFY_CODE(FS, COMBINED_ALIAS) STRINGIFY_CODE(FS, COMBINED_ORIGINAL_NAME) STRINGIFY_CODE(FS, VERSION) + STRINGIFY_CODE(FS, TYPE_TESTS) } case bitc::METADATA_ATTACHMENT_ID: switch(CodeID) {