Index: include/llvm/Analysis/TypeMetadataUtils.h =================================================================== --- include/llvm/Analysis/TypeMetadataUtils.h +++ include/llvm/Analysis/TypeMetadataUtils.h @@ -20,6 +20,13 @@ namespace llvm { +/// The type of CFI jumptable needed for a function. +enum CfiFunctionLinkage { + CFL_Definition = 0, + CFL_Declaration = 1, + CFL_WeakDeclaration = 2 +}; + /// A call site that could be devirtualized. struct DevirtCallSite { /// The offset from the address point to the virtual function. Index: include/llvm/Bitcode/BitcodeWriter.h =================================================================== --- include/llvm/Bitcode/BitcodeWriter.h +++ include/llvm/Bitcode/BitcodeWriter.h @@ -67,6 +67,10 @@ void writeModule(const Module *M, bool ShouldPreserveUseListOrder = false, const ModuleSummaryIndex *Index = nullptr, bool GenerateHash = false, ModuleHash *ModHash = nullptr); + + void writeIndex( + const ModuleSummaryIndex *Index, + const std::map *ModuleToSummariesForIndex); }; /// \brief Write the specified module to the specified raw output stream. Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -240,6 +240,14 @@ // summaries, but it can also appear in per-module summaries for PGO data. // [valueid, guid] FS_VALUE_GUID = 16, + // The list of local functions with CFI jump tables. Function names are + // strings in strtab. + // [n * name] + FS_CFI_FUNCTION_DEFS = 17, + // The list of external functions with CFI jump tables. Function names are + // strings in strtab. + // [n * name] + FS_CFI_FUNCTION_DECLS = 18, }; enum MetadataCodes { Index: include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- include/llvm/IR/ModuleSummaryIndex.h +++ include/llvm/IR/ModuleSummaryIndex.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -542,6 +543,9 @@ /// considered live. bool WithGlobalValueDeadStripping = false; + std::set CfiFunctionDefs; + std::set CfiFunctionDecls; + // YAML I/O support. friend yaml::MappingTraits; @@ -592,6 +596,12 @@ return I == OidGuidMap.end() ? 0 : I->second; } + std::set &cfiFunctionDefs() { return CfiFunctionDefs; } + const std::set &cfiFunctionDefs() const { return CfiFunctionDefs; } + + std::set &cfiFunctionDecls() { return CfiFunctionDecls; } + const std::set &cfiFunctionDecls() const { return CfiFunctionDecls; } + /// Add a global value summary for a value of the given name. void addGlobalValueSummary(StringRef ValueName, std::unique_ptr Summary) { Index: include/llvm/IR/ModuleSummaryIndexYAML.h =================================================================== --- include/llvm/IR/ModuleSummaryIndexYAML.h +++ include/llvm/IR/ModuleSummaryIndexYAML.h @@ -188,6 +188,7 @@ LLVM_YAML_IS_STRING_MAP(TypeIdSummary) LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionSummaryYaml) +LLVM_YAML_IS_SEQUENCE_VECTOR(std::string) namespace llvm { namespace yaml { @@ -240,6 +241,23 @@ io.mapOptional("TypeIdMap", index.TypeIdMap); io.mapOptional("WithGlobalValueDeadStripping", index.WithGlobalValueDeadStripping); + + if (io.outputting()) { + std::vector CfiFunctionDefs(index.CfiFunctionDefs.begin(), + index.CfiFunctionDefs.end()); + io.mapOptional("CfiFunctionDefs", CfiFunctionDefs); + std::vector CfiFunctionDecls(index.CfiFunctionDecls.begin(), + index.CfiFunctionDecls.end()); + io.mapOptional("CfiFunctionDecls", CfiFunctionDecls); + } else { + std::vector CfiFunctionDefs; + io.mapOptional("CfiFunctionDefs", CfiFunctionDefs); + index.CfiFunctionDefs = {CfiFunctionDefs.begin(), CfiFunctionDefs.end()}; + std::vector CfiFunctionDecls; + io.mapOptional("CfiFunctionDecls", CfiFunctionDecls); + index.CfiFunctionDecls = {CfiFunctionDecls.begin(), + CfiFunctionDecls.end()}; + } } }; Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -5251,6 +5251,18 @@ {{Record[0], Record[1]}, {Record.begin() + 2, Record.end()}}); break; } + case bitc::FS_CFI_FUNCTION_DEFS: { + std::set &CfiFunctionDefs = TheIndex.cfiFunctionDefs(); + for (unsigned I = 0; I != Record.size(); I += 2) + CfiFunctionDefs.insert({Strtab.data() + Record[I], Record[I+1]}); + break; + } + case bitc::FS_CFI_FUNCTION_DECLS: { + std::set &CfiFunctionDecls = TheIndex.cfiFunctionDecls(); + for (unsigned I = 0; I != Record.size(); I += 2) + CfiFunctionDecls.insert({Strtab.data() + Record[I], Record[I+1]}); + break; + } } } llvm_unreachable("Exit infinite loop"); Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -77,10 +77,13 @@ /// The stream created and owned by the client. BitstreamWriter &Stream; + StringTableBuilder &StrtabBuilder; + public: /// Constructs a BitcodeWriterBase object that writes to the provided /// \p Stream. - BitcodeWriterBase(BitstreamWriter &Stream) : Stream(Stream) {} + BitcodeWriterBase(BitstreamWriter &Stream, StringTableBuilder &StrtabBuilder) + : Stream(Stream), StrtabBuilder(StrtabBuilder) {} protected: void writeBitcodeHeader(); @@ -97,8 +100,6 @@ /// Pointer to the buffer allocated by caller for bitcode writing. const SmallVectorImpl &Buffer; - StringTableBuilder &StrtabBuilder; - /// The Module to write to bitcode. const Module &M; @@ -142,8 +143,8 @@ BitstreamWriter &Stream, bool ShouldPreserveUseListOrder, const ModuleSummaryIndex *Index, bool GenerateHash, ModuleHash *ModHash = nullptr) - : BitcodeWriterBase(Stream), Buffer(Buffer), StrtabBuilder(StrtabBuilder), - M(*M), VE(*M, ShouldPreserveUseListOrder), Index(Index), + : BitcodeWriterBase(Stream, StrtabBuilder), Buffer(Buffer), M(*M), + VE(*M, ShouldPreserveUseListOrder), Index(Index), GenerateHash(GenerateHash), ModHash(ModHash), BitcodeStartBit(Stream.GetCurrentBitNo()) { // Assign ValueIds to any callee values in the index that came from @@ -331,10 +332,11 @@ /// Constructs a IndexBitcodeWriter object for the given combined index, /// writing to the provided \p Buffer. When writing a subset of the index /// for a distributed backend, provide a \p ModuleToSummariesForIndex map. - IndexBitcodeWriter(BitstreamWriter &Stream, const ModuleSummaryIndex &Index, + IndexBitcodeWriter(BitstreamWriter &Stream, StringTableBuilder &StrtabBuilder, + const ModuleSummaryIndex &Index, const std::map *ModuleToSummariesForIndex = nullptr) - : BitcodeWriterBase(Stream), Index(Index), + : BitcodeWriterBase(Stream, StrtabBuilder), Index(Index), ModuleToSummariesForIndex(ModuleToSummariesForIndex) { // Assign unique value ids to all summaries to be written, for use // in writing out the call graph edges. Save the mapping from GUID @@ -3595,6 +3597,24 @@ MaybeEmitOriginalName(*AS); } + if (!Index.cfiFunctionDefs().empty()) { + for (auto &S : Index.cfiFunctionDefs()) { + NameVals.push_back(StrtabBuilder.add(S)); + NameVals.push_back(S.size()); + } + Stream.EmitRecord(bitc::FS_CFI_FUNCTION_DEFS, NameVals); + NameVals.clear(); + } + + if (!Index.cfiFunctionDecls().empty()) { + for (auto &S : Index.cfiFunctionDecls()) { + NameVals.push_back(StrtabBuilder.add(S)); + NameVals.push_back(S.size()); + } + Stream.EmitRecord(bitc::FS_CFI_FUNCTION_DECLS, NameVals); + NameVals.clear(); + } + Stream.ExitBlock(); } @@ -3829,6 +3849,14 @@ ModuleWriter.write(); } +void BitcodeWriter::writeIndex( + const ModuleSummaryIndex *Index, + const std::map *ModuleToSummariesForIndex) { + IndexBitcodeWriter IndexWriter(*Stream, StrtabBuilder, *Index, + ModuleToSummariesForIndex); + IndexWriter.write(); +} + /// WriteBitcodeToFile - Write the specified module to the specified output /// stream. void llvm::WriteBitcodeToFile(const Module *M, raw_ostream &Out, @@ -3880,11 +3908,9 @@ SmallVector Buffer; Buffer.reserve(256 * 1024); - BitstreamWriter Stream(Buffer); - writeBitcodeHeader(Stream); - - IndexBitcodeWriter IndexWriter(Stream, Index, ModuleToSummariesForIndex); - IndexWriter.write(); + BitcodeWriter Writer(Buffer); + Writer.writeIndex(&Index, ModuleToSummariesForIndex); + Writer.writeStrtab(); Out.write((char *)&Buffer.front(), Buffer.size()); } Index: lib/Transforms/IPO/CrossDSOCFI.cpp =================================================================== --- lib/Transforms/IPO/CrossDSOCFI.cpp +++ lib/Transforms/IPO/CrossDSOCFI.cpp @@ -95,6 +95,17 @@ } } + NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions"); + if (CfiFunctionsMD) { + for (auto Func : CfiFunctionsMD->operands()) { + assert(Func->getNumOperands() >= 2); + for (unsigned I = 2; I < Func->getNumOperands(); ++I) + if (ConstantInt *TypeId = + extractNumericTypeId(cast(Func->getOperand(I).get()))) + TypeIds.insert(TypeId->getZExtValue()); + } + } + LLVMContext &Ctx = M.getContext(); Constant *C = M.getOrInsertFunction( "__cfi_check", Type::getVoidTy(Ctx), Type::getInt64Ty(Ctx), Index: lib/Transforms/IPO/LowerTypeTests.cpp =================================================================== --- lib/Transforms/IPO/LowerTypeTests.cpp +++ lib/Transforms/IPO/LowerTypeTests.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/Triple.h" +#include "llvm/Analysis/TypeMetadataUtils.h" #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" @@ -206,17 +207,26 @@ class GlobalTypeMember final : TrailingObjects { GlobalObject *GO; size_t NTypes; + // For functions: true if this is a definition (either in the merged module or + // in one of the thinlto modules). + bool IsDefinition; + // For functions: true if this function is either defined or used in a thinlto + // module and its jumptable entry needs to be exported to thinlto backends. + bool IsExported; friend TrailingObjects; size_t numTrailingObjects(OverloadToken) const { return NTypes; } public: static GlobalTypeMember *create(BumpPtrAllocator &Alloc, GlobalObject *GO, + bool IsDefinition, bool IsExported, ArrayRef Types) { auto *GTM = static_cast(Alloc.Allocate( totalSizeToAlloc(Types.size()), alignof(GlobalTypeMember))); GTM->GO = GO; GTM->NTypes = Types.size(); + GTM->IsDefinition = IsDefinition; + GTM->IsExported = IsExported; std::uninitialized_copy(Types.begin(), Types.end(), GTM->getTrailingObjects()); return GTM; @@ -224,6 +234,12 @@ GlobalObject *getGlobal() const { return GO; } + bool isDefinition() const { + return IsDefinition; + } + bool isExported() const { + return IsExported; + } ArrayRef types() const { return makeArrayRef(getTrailingObjects(), NTypes); } @@ -294,6 +310,7 @@ void exportTypeId(StringRef TypeId, const TypeIdLowering &TIL); TypeIdLowering importTypeId(StringRef TypeId); void importTypeTest(CallInst *CI); + void importFunction(Function *F, bool isDefinition); BitSetInfo buildBitSet(Metadata *TypeId, @@ -820,6 +837,41 @@ CI->eraseFromParent(); } +// ThinLTO backend: the function F has a jump table entry; update this module +// accordingly. isDefinition describes the type of the jump table entry. +void LowerTypeTestsModule::importFunction(Function *F, bool isDefinition) { + assert(F->getType()->getAddressSpace() == 0); + + // Declaration of a local function - nothing to do. + if (F->isDeclarationForLinker() && isDefinition) + return; + + GlobalValue::VisibilityTypes Visibility = F->getVisibility(); + std::string Name = F->getName(); + Function *FDecl; + + if (F->isDeclarationForLinker() && !isDefinition) { + // Declaration of an external function. + FDecl = Function::Create(F->getFunctionType(), GlobalValue::ExternalLinkage, + Name + ".cfi_jt", &M); + FDecl->setVisibility(GlobalValue::HiddenVisibility); + } else { + // Definition. + assert(isDefinition); + F->setName(Name + ".cfi"); + F->setLinkage(GlobalValue::ExternalLinkage); + F->setVisibility(GlobalValue::HiddenVisibility); + FDecl = Function::Create(F->getFunctionType(), GlobalValue::ExternalLinkage, + Name, &M); + FDecl->setVisibility(Visibility); + } + + if (F->isWeakForLinker()) + replaceWeakDeclarationWithJumpTablePtr(F, FDecl); + else + F->replaceAllUsesWith(FDecl); +} + void LowerTypeTestsModule::lowerTypeTestCalls( ArrayRef TypeIds, Constant *CombinedGlobalAddr, const DenseMap &GlobalLayout) { @@ -1143,7 +1195,6 @@ // arithmetic that we normally use for globals. // FIXME: find a better way to represent the jumptable in the IR. - assert(!Functions.empty()); // Build a simple layout based on the regular layout of jump tables. @@ -1167,6 +1218,7 @@ // references to the original functions with references to the aliases. for (unsigned I = 0; I != Functions.size(); ++I) { Function *F = cast(Functions[I]->getGlobal()); + bool IsDefinition = Functions[I]->isDefinition(); Constant *CombinedGlobalElemPtr = ConstantExpr::getBitCast( ConstantExpr::getInBoundsGetElementPtr( @@ -1174,7 +1226,18 @@ ArrayRef{ConstantInt::get(IntPtrTy, 0), ConstantInt::get(IntPtrTy, I)}), F->getType()); - if (F->isDeclarationForLinker()) { + if (Functions[I]->isExported()) { + if (IsDefinition) { + ExportSummary->cfiFunctionDefs().insert(F->getName()); + } else { + GlobalAlias *JtAlias = GlobalAlias::create( + F->getValueType(), 0, GlobalValue::ExternalLinkage, + F->getName() + ".cfi_jt", CombinedGlobalElemPtr, &M); + JtAlias->setVisibility(GlobalValue::HiddenVisibility); + ExportSummary->cfiFunctionDecls().insert(F->getName()); + } + } + if (!IsDefinition) { if (F->isWeakForLinker()) replaceWeakDeclarationWithJumpTablePtr(F, CombinedGlobalElemPtr); else @@ -1182,9 +1245,8 @@ } else { assert(F->getType()->getAddressSpace() == 0); - GlobalAlias *FAlias = GlobalAlias::create(F->getValueType(), 0, - F->getLinkage(), "", - CombinedGlobalElemPtr, &M); + GlobalAlias *FAlias = GlobalAlias::create( + F->getValueType(), 0, F->getLinkage(), "", CombinedGlobalElemPtr, &M); FAlias->setVisibility(F->getVisibility()); FAlias->takeName(F); if (FAlias->hasName()) @@ -1350,18 +1412,51 @@ return Changed; } +static bool IsFunctionLive(StringRef Name, const ModuleSummaryIndex *Summary) { + auto GUID = GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name)); + ValueInfo Info = Summary->getValueInfo(GUID); + if (!Info) + return true; + const auto &SummaryList = Info.getSummaryList(); + if (SummaryList.empty()) + return true; + return Summary->isGlobalValueLive(SummaryList[0].get()); +} + bool LowerTypeTestsModule::lower() { Function *TypeTestFunc = M.getFunction(Intrinsic::getName(Intrinsic::type_test)); - if ((!TypeTestFunc || TypeTestFunc->use_empty()) && !ExportSummary) + if ((!TypeTestFunc || TypeTestFunc->use_empty()) && !ExportSummary && + !ImportSummary) return false; if (ImportSummary) { - for (auto UI = TypeTestFunc->use_begin(), UE = TypeTestFunc->use_end(); - UI != UE;) { - auto *CI = cast((*UI++).getUser()); - importTypeTest(CI); + if (TypeTestFunc) { + for (auto UI = TypeTestFunc->use_begin(), UE = TypeTestFunc->use_end(); + UI != UE;) { + auto *CI = cast((*UI++).getUser()); + importTypeTest(CI); + } } + + SmallVector Defs; + SmallVector Decls; + for (auto &F : M) { + // CFI functions are either external, or promoted. A local function may + // have the same name, but it's not the one we are looking for. + if (F.hasLocalLinkage()) + continue; + if (ImportSummary->cfiFunctionDefs().count(F.getName())) + Defs.push_back(&F); + else if (ImportSummary->cfiFunctionDecls().count(F.getName())) + Decls.push_back(&F); + } + + for (auto F : Defs) + importFunction(F, /*isDefinition*/ true); + for (auto F : Decls) + importFunction(F, /*isDefinition*/ false); + return true; } @@ -1387,6 +1482,53 @@ llvm::DenseMap TypeIdInfo; unsigned I = 0; SmallVector Types; + + struct ExportedFunctionInfo { + CfiFunctionLinkage Linkage; + MDNode *FuncMD; // {name, linkage, type[, type...]} + }; + DenseMap ExportedFunctions; + if (ExportSummary) { + NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions"); + if (CfiFunctionsMD) { + for (auto FuncMD : CfiFunctionsMD->operands()) { + assert(FuncMD->getNumOperands() >= 2); + StringRef FunctionName = + cast(FuncMD->getOperand(0))->getString(); + if (!IsFunctionLive(FunctionName, ExportSummary)) { + continue; + } + CfiFunctionLinkage Linkage = static_cast( + cast(FuncMD->getOperand(1)) + ->getValue() + ->getUniqueInteger() + .getZExtValue()); + auto P = ExportedFunctions.insert({FunctionName, {Linkage, FuncMD}}); + if (!P.second && P.first->second.Linkage != CFL_Definition) + P.first->second = {Linkage, FuncMD}; + } + + for (const auto &P : ExportedFunctions) { + StringRef FunctionName = P.first; + CfiFunctionLinkage Linkage = P.second.Linkage; + MDNode *FuncMD = P.second.FuncMD; + Function *F = M.getFunction(FunctionName); + if (!F) + F = Function::Create( + FunctionType::get(Type::getVoidTy(M.getContext()), false), + GlobalVariable::ExternalLinkage, FunctionName, &M); + + if (Linkage == CFL_WeakDeclaration) + F->setLinkage(GlobalValue::ExternalWeakLinkage); + + SmallVector Types; + for (unsigned I = 2; I < FuncMD->getNumOperands(); ++I) + F->addMetadata(LLVMContext::MD_type, + *cast(FuncMD->getOperand(I).get())); + } + } + } + for (GlobalObject &GO : M.global_objects()) { if (isa(GO) && GO.isDeclarationForLinker()) continue; @@ -1396,7 +1538,15 @@ if (Types.empty()) continue; - auto *GTM = GlobalTypeMember::create(Alloc, &GO, Types); + bool IsDefinition = !GO.isDeclarationForLinker(); + bool IsExported = false; + if (isa(GO) && ExportedFunctions.count(GO.getName())) { + IsDefinition |= ExportedFunctions[GO.getName()].Linkage == CFL_Definition; + IsExported = true; + } + + auto *GTM = + GlobalTypeMember::create(Alloc, &GO, IsDefinition, IsExported, Types); for (MDNode *Type : Types) { verifyTypeMDNode(&GO, Type); auto &Info = TypeIdInfo[cast(Type)->getOperand(1)]; Index: lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp =================================================================== --- lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp +++ lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp @@ -32,7 +32,8 @@ // Promote each local-linkage entity defined by ExportM and used by ImportM by // changing visibility and appending the given ModuleId. -void promoteInternals(Module &ExportM, Module &ImportM, StringRef ModuleId) { +void promoteInternals(Module &ExportM, Module &ImportM, StringRef ModuleId, + SetVector &PromoteExtra) { DenseMap RenamedComdats; for (auto &ExportGV : ExportM.global_values()) { if (!ExportGV.hasLocalLinkage()) @@ -40,7 +41,7 @@ auto Name = ExportGV.getName(); GlobalValue *ImportGV = ImportM.getNamedValue(Name); - if (!ImportGV || ImportGV->use_empty()) + if ((!ImportGV || ImportGV->use_empty()) && !PromoteExtra.count(&ExportGV)) continue; std::string NewName = (Name + ModuleId).str(); @@ -53,8 +54,10 @@ ExportGV.setLinkage(GlobalValue::ExternalLinkage); ExportGV.setVisibility(GlobalValue::HiddenVisibility); - ImportGV->setName(NewName); - ImportGV->setVisibility(GlobalValue::HiddenVisibility); + if (ImportGV) { + ImportGV->setName(NewName); + ImportGV->setVisibility(GlobalValue::HiddenVisibility); + } } if (!RenamedComdats.empty()) @@ -296,6 +299,11 @@ F.setComdat(nullptr); } + SetVector CfiFunctions; + for (auto &F : M) + if ((!F.hasLocalLinkage() || F.hasAddressTaken()) && HasTypeMetadata(&F)) + CfiFunctions.insert(&F); + // Remove all globals with type metadata, globals with comdats that live in // MergedM, and aliases pointing to such globals from the thin LTO module. filterModule(&M, [&](const GlobalValue *GV) { @@ -308,11 +316,39 @@ return true; }); - promoteInternals(*MergedM, M, ModuleId); - promoteInternals(M, *MergedM, ModuleId); + promoteInternals(*MergedM, M, ModuleId, CfiFunctions); + promoteInternals(M, *MergedM, ModuleId, CfiFunctions); + + SmallVector CfiFunctionMDs; + for (auto V : CfiFunctions) { + Function &F = *cast(V); + SmallVector Types; + F.getMetadata(LLVMContext::MD_type, Types); + + auto &Ctx = MergedM->getContext(); + SmallVector Elts; + Elts.push_back(MDString::get(Ctx, F.getName())); + CfiFunctionLinkage Linkage; + if (!F.isDeclarationForLinker()) + Linkage = CFL_Definition; + else if (F.isWeakForLinker()) + Linkage = CFL_WeakDeclaration; + else + Linkage = CFL_Declaration; + Elts.push_back(ConstantAsMetadata::get( + llvm::ConstantInt::get(Type::getInt8Ty(Ctx), Linkage))); + for (auto Type : Types) + Elts.push_back(Type); + CfiFunctionMDs.push_back(MDTuple::get(Ctx, Elts)); + } - simplifyExternals(*MergedM); + if(!CfiFunctionMDs.empty()) { + NamedMDNode *NMD = MergedM->getOrInsertNamedMetadata("cfi.functions"); + for (auto MD : CfiFunctionMDs) + NMD->addOperand(MD); + } + simplifyExternals(*MergedM); // FIXME: Try to re-use BSI and PFI from the original module here. ProfileSummaryInfo PSI(M); Index: test/ThinLTO/X86/cfi-icall.ll =================================================================== --- /dev/null +++ test/ThinLTO/X86/cfi-icall.ll @@ -0,0 +1,29 @@ +; RUN: opt -thinlto-bc %s -o %t1.bc +; RUN: llvm-lto2 run -thinlto-distributed-indexes %t1.bc -o %t.out -save-temps \ +; RUN: -r %t1.bc,foo,plx \ +; RUN: -r %t1.bc,bar,x +; RUN: llvm-bcanalyzer -dump %t.out.index.bc | FileCheck %s --check-prefix=COMBINED + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i1 @foo(i8* %p) !type !0 { +entry: + %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1") + ret i1 %x +} + +declare !type !0 void @bar() + +declare i1 @llvm.type.test(i8* %ptr, metadata %type) nounwind readnone + +!0 = !{i64 0, !"typeid1"} + +; COMBINED: +; COMBINED: +; COMBINED: + +; COMBINED: blob data = 'foobar' +; COMBINED-NEXT: Index: test/Transforms/CrossDSOCFI/cfi_functions.ll =================================================================== --- /dev/null +++ test/Transforms/CrossDSOCFI/cfi_functions.ll @@ -0,0 +1,23 @@ +; Test that types referenced in ThinLTO-style !cfi.functions are known to __cfi_check. +; RUN: opt -S -cross-dso-cfi < %s | FileCheck %s +; RUN: opt -S -passes=cross-dso-cfi < %s | FileCheck %s + +; CHECK: define void @__cfi_check( +; CHECK: switch i64 +; CHECK-NEXT: i64 1234, label +; CHECK-NEXT: i64 5678, label +; CHECK-NEXT: ] + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +!cfi.functions = !{!0, !1} +!llvm.module.flags = !{!6} + +!0 = !{!"f", i8 0, !2, !4} +!1 = !{!"g", i8 1, !3, !5} +!2 = !{i64 0, !"typeid1"} +!3 = !{i64 0, !"typeid2"} +!4 = !{i64 0, i64 1234} +!5 = !{i64 0, i64 5678} +!6 = !{i32 4, !"Cross-DSO CFI", i32 1} Index: test/Transforms/LowerTypeTests/Inputs/import-icall.yaml =================================================================== --- /dev/null +++ test/Transforms/LowerTypeTests/Inputs/import-icall.yaml @@ -0,0 +1,19 @@ +--- +TypeIdMap: + typeid1: + TTRes: + Kind: AllOnes + SizeM1BitWidth: 7 + typeid2: + TTRes: + Kind: Single + SizeM1BitWidth: 0 +WithGlobalValueDeadStripping: false +CfiFunctionDefs: + - local_a + - local_b + - does_not_exist +CfiFunctionDecls: + - external + - external_weak +... Index: test/Transforms/LowerTypeTests/export-icall.ll =================================================================== --- /dev/null +++ test/Transforms/LowerTypeTests/export-icall.ll @@ -0,0 +1,62 @@ +; RUN: opt -S -lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t < %s | FileCheck %s +; RUN: FileCheck --check-prefix=SUMMARY %s < %t + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @h(i8 %x) !type !2 { + ret void +} + +!cfi.functions = !{!0, !1, !3, !4, !5, !6} + +!0 = !{!"h", i8 0, !2} +!1 = !{!"h", i8 1, !2} +!2 = !{i64 0, !"typeid1"} +!3 = !{!"f", i8 0, !2} +!4 = !{!"external", i8 1, !2} +!5 = !{!"external_weak", i8 2, !2} +!6 = !{!"g", i8 0, !7} +!7 = !{i64 0, !"typeid2"} + + +; CHECK: @__typeid_typeid1_global_addr = hidden alias i8, bitcast (void ()* [[JT1:.*]] to i8*) +; CHECK: @__typeid_typeid1_align = hidden alias i8, inttoptr (i8 3 to i8*) +; CHECK: @__typeid_typeid1_size_m1 = hidden alias i8, inttoptr (i64 3 to i8*) + +; CHECK: @h = alias void (i8), bitcast (void ()* [[JT1]] to void (i8)*) +; CHECK: @external.cfi_jt = hidden alias void (), {{.*}}getelementptr {{.*}}void ()* [[JT1]] +; CHECK: @f = alias void (), {{.*}}getelementptr {{.*}}void ()* [[JT1]] +; CHECK: @external_weak.cfi_jt = hidden alias void (), {{.*}}getelementptr {{.*}}void ()* [[JT1]] + +; CHECK: @__typeid_typeid2_global_addr = hidden alias i8, bitcast (void ()* [[JT2:.*]] to i8*) + +; CHECK: @g = alias void (), void ()* [[JT2]] + +; CHECK: define internal void @h.cfi(i8 {{.*}}) !type !{{.*}} +; CHECK: declare !type !{{.*}} void @external() +; CHECK: declare !type !{{.*}} void @f.cfi() +; CHECK: declare !type !{{.*}} void @external_weak() +; CHECK: declare !type !{{.*}} void @g.cfi() + + +; SUMMARY: TypeIdMap: +; SUMMARY-NEXT: typeid1: +; SUMMARY-NEXT: TTRes: +; SUMMARY-NEXT: Kind: AllOnes +; SUMMARY-NEXT: SizeM1BitWidth: 7 +; SUMMARY-NEXT: WPDRes: +; SUMMARY-NEXT: typeid2: +; SUMMARY-NEXT: TTRes: +; SUMMARY-NEXT: Kind: Single +; SUMMARY-NEXT: SizeM1BitWidth: 0 +; SUMMARY-NEXT: WPDRes: + +; SUMMARY: CfiFunctionDefs: +; SUMMARY-NEXT: - f +; SUMMARY-NEXT: - g +; SUMMARY-NEXT: - h +; SUMMARY-NEXT: CfiFunctionDecls: +; SUMMARY-NEXT: - external +; SUMMARY-NEXT: - external_weak +; SUMMARY-NEXT: ... Index: test/Transforms/LowerTypeTests/import-icall.ll =================================================================== --- /dev/null +++ test/Transforms/LowerTypeTests/import-icall.ll @@ -0,0 +1,40 @@ +; RUN: opt -S -lowertypetests -lowertypetests-summary-action=import -lowertypetests-read-summary=%S/Inputs/import-icall.yaml < %s | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i8 @local_a() { + call void @external() + call void @external_weak() + ret i8 1 +} + +define internal i8 @local_b() { + %x = call i8 @local_a() + ret i8 %x +} + +define i8 @use_b() { + %x = call i8 @local_b() + ret i8 %x +} + + +declare void @external() +declare extern_weak void @external_weak() + +; CHECK: define hidden i8 @local_a.cfi() { +; CHECK-NEXT: call void @external.cfi_jt() +; CHECK-NEXT: call void select (i1 icmp ne (void ()* @external_weak, void ()* null), void ()* @external_weak.cfi_jt, void ()* null)() +; CHECK-NEXT: ret i8 1 +; CHECK-NEXT: } + +; internal @local_b is not the same function as "local_b" in the summary. +; CHECK: define internal i8 @local_b() { +; CHECK-NEXT: call i8 @local_a() + +; CHECK: declare void @external() +; CHECK: declare extern_weak void @external_weak() +; CHECK: declare i8 @local_a() +; CHECK: declare hidden void @external.cfi_jt() +; CHECK: declare hidden void @external_weak.cfi_jt() Index: tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp =================================================================== --- tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -320,6 +320,8 @@ STRINGIFY_CODE(FS, TYPE_TEST_ASSUME_CONST_VCALL) STRINGIFY_CODE(FS, TYPE_CHECKED_LOAD_CONST_VCALL) STRINGIFY_CODE(FS, VALUE_GUID) + STRINGIFY_CODE(FS, CFI_FUNCTION_DEFS) + STRINGIFY_CODE(FS, CFI_FUNCTION_DECLS) } case bitc::METADATA_ATTACHMENT_ID: switch(CodeID) {