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,17 @@ /// List of call edge pairs from this function. std::vector CallGraphEdgeList; + /// List of type identifiers used by this function, represented as GUIDs. + std::vector TypeIdList; + public: /// Summary constructors. FunctionSummary(GVFlags Flags, unsigned NumInsts, std::vector Refs, - std::vector CGEdges) + std::vector CGEdges, + std::vector TypeIds) : GlobalValueSummary(FunctionKind, Flags, std::move(Refs)), - InstCount(NumInsts), CallGraphEdgeList(std::move(CGEdges)) {} + InstCount(NumInsts), CallGraphEdgeList(std::move(CGEdges)), + TypeIdList(std::move(TypeIds)) {} /// Check if this is a function summary. static bool classof(const GlobalValueSummary *GVS) { @@ -301,6 +306,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; @@ -123,11 +125,29 @@ assert(!CalledFunction && "Expected null called function in callsite for alias"); CalledFunction = dyn_cast(GA->getBaseObject()); } - // Check if this is a direct call to a known function. + // Check if this is a direct call to a known function or a known + // intrinsic, or an indirect call with profile data. if (CalledFunction) { - // Skip intrinsics. - if (CalledFunction->isIntrinsic()) - continue; + 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(CIU.getUser()); + if (!AssumeCI) + return true; + Function *F = AssumeCI->getCalledFunction(); + return !F || F->getIntrinsicID() != Intrinsic::assume; + }); + if (HasNonAssumeUses) { + auto *TypeMDVal = cast(CI->getArgOperand(1)); + if (auto *TypeId = dyn_cast(TypeMDVal->getMetadata())) + TypeTests.insert(GlobalValue::getGUID(TypeId->getString())); + } + } // We should have named any anonymous globals assert(CalledFunction->hasName()); auto ScaledCount = BFI ? BFI->getBlockProfileCount(&BB) : None; @@ -160,7 +180,8 @@ GlobalValueSummary::GVFlags Flags(F); auto FuncSummary = llvm::make_unique( - Flags, NumInsts, RefEdges.takeVector(), CallGraphEdges.takeVector()); + Flags, NumInsts, RefEdges.takeVector(), CallGraphEdges.takeVector(), + TypeTests.takeVector()); if (HasInlineAsmMaybeReferencingInternal) FuncSummary->setHasInlineAsmMaybeReferencingInternal(); Index.addGlobalValueSummary(F.getName(), std::move(FuncSummary)); @@ -280,7 +301,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/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -4847,6 +4847,7 @@ // "OriginalName" attachement. GlobalValueSummary *LastSeenSummary = nullptr; bool Combined = false; + std::vector PendingTypeTests; while (true) { BitstreamEntry Entry = Stream.advanceSkippingSubblocks(); @@ -4912,7 +4913,8 @@ ArrayRef(Record).slice(CallGraphEdgeStartIndex), IsOldProfileFormat, HasProfile); auto FS = llvm::make_unique( - Flags, InstCount, std::move(Refs), std::move(Calls)); + Flags, InstCount, std::move(Refs), std::move(Calls), + std::move(PendingTypeTests)); auto GUID = getGUIDFromValueId(ValueID); FS->setModulePath(TheIndex.addModulePath(ModulePath, 0)->first()); FS->setOriginalName(GUID.second); @@ -4985,7 +4987,8 @@ IsOldProfileFormat, HasProfile); GlobalValue::GUID GUID = getGUIDFromValueId(ValueID).first; auto FS = llvm::make_unique( - Flags, InstCount, std::move(Refs), std::move(Edges)); + Flags, InstCount, std::move(Refs), std::move(Edges), + std::move(PendingTypeTests)); LastSeenSummary = FS.get(); FS->setModulePath(ModuleIdMap[ModuleId]); TheIndex.addGlobalValueSummary(GUID, std::move(FS)); @@ -5041,6 +5044,13 @@ LastSeenSummary->setOriginalName(OriginalName); // Reset the LastSeenSummary LastSeenSummary = nullptr; + break; + } + case bitc::FS_TYPE_TESTS: { + assert(PendingTypeTests.empty()); + 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 @@ -3286,6 +3286,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()); @@ -3544,6 +3547,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/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/test/Bitcode/thinlto-unused-type-tests.ll =================================================================== --- /dev/null +++ llvm/test/Bitcode/thinlto-unused-type-tests.ll @@ -0,0 +1,13 @@ +; RUN: opt -module-summary %s -o %t.o +; RUN: llvm-bcanalyzer -dump %t.o | FileCheck %s + +; CHECK-NOT: