diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 10:51:19 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 10:51:19 +0000 |
commit | eb11fae6d08f479c0799db45860a98af528fa6e7 (patch) | |
tree | 44d492a50c8c1a7eb8e2d17ea3360ec4d066f042 /lib/Transforms/IPO/LowerTypeTests.cpp | |
parent | b8a2042aa938069e862750553db0e4d82d25822c (diff) | |
download | src-eb11fae6d08f479c0799db45860a98af528fa6e7.tar.gz src-eb11fae6d08f479c0799db45860a98af528fa6e7.zip |
Vendor import of llvm trunk r338150:vendor/llvm/llvm-trunk-r338150
Notes
Notes:
svn path=/vendor/llvm/dist/; revision=336809
svn path=/vendor/llvm/llvm-trunk-r338150/; revision=336814; tag=vendor/llvm/llvm-trunk-r338150
Diffstat (limited to 'lib/Transforms/IPO/LowerTypeTests.cpp')
-rw-r--r-- | lib/Transforms/IPO/LowerTypeTests.cpp | 433 |
1 files changed, 363 insertions, 70 deletions
diff --git a/lib/Transforms/IPO/LowerTypeTests.cpp b/lib/Transforms/IPO/LowerTypeTests.cpp index 8db7e1e142d2..4f7571884707 100644 --- a/lib/Transforms/IPO/LowerTypeTests.cpp +++ b/lib/Transforms/IPO/LowerTypeTests.cpp @@ -8,6 +8,8 @@ //===----------------------------------------------------------------------===// // // This pass lowers type metadata and calls to the llvm.type.test intrinsic. +// It also ensures that globals are properly laid out for the +// llvm.icall.branch.funnel intrinsic. // See http://llvm.org/docs/TypeMetadata.html for more information. // //===----------------------------------------------------------------------===// @@ -25,6 +27,7 @@ #include "llvm/ADT/TinyPtrVector.h" #include "llvm/ADT/Triple.h" #include "llvm/Analysis/TypeMetadataUtils.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constant.h" @@ -291,6 +294,33 @@ public: } }; +struct ICallBranchFunnel final + : TrailingObjects<ICallBranchFunnel, GlobalTypeMember *> { + static ICallBranchFunnel *create(BumpPtrAllocator &Alloc, CallInst *CI, + ArrayRef<GlobalTypeMember *> Targets, + unsigned UniqueId) { + auto *Call = static_cast<ICallBranchFunnel *>( + Alloc.Allocate(totalSizeToAlloc<GlobalTypeMember *>(Targets.size()), + alignof(ICallBranchFunnel))); + Call->CI = CI; + Call->UniqueId = UniqueId; + Call->NTargets = Targets.size(); + std::uninitialized_copy(Targets.begin(), Targets.end(), + Call->getTrailingObjects<GlobalTypeMember *>()); + return Call; + } + + CallInst *CI; + ArrayRef<GlobalTypeMember *> targets() const { + return makeArrayRef(getTrailingObjects<GlobalTypeMember *>(), NTargets); + } + + unsigned UniqueId; + +private: + size_t NTargets; +}; + class LowerTypeTestsModule { Module &M; @@ -372,6 +402,7 @@ class LowerTypeTestsModule { const DenseMap<GlobalTypeMember *, uint64_t> &GlobalLayout); Value *lowerTypeTestCall(Metadata *TypeId, CallInst *CI, const TypeIdLowering &TIL); + void buildBitSetsFromGlobalVariables(ArrayRef<Metadata *> TypeIds, ArrayRef<GlobalTypeMember *> Globals); unsigned getJumpTableEntrySize(); @@ -383,19 +414,32 @@ class LowerTypeTestsModule { void buildBitSetsFromFunctions(ArrayRef<Metadata *> TypeIds, ArrayRef<GlobalTypeMember *> Functions); void buildBitSetsFromFunctionsNative(ArrayRef<Metadata *> TypeIds, - ArrayRef<GlobalTypeMember *> Functions); + ArrayRef<GlobalTypeMember *> Functions); void buildBitSetsFromFunctionsWASM(ArrayRef<Metadata *> TypeIds, ArrayRef<GlobalTypeMember *> Functions); - void buildBitSetsFromDisjointSet(ArrayRef<Metadata *> TypeIds, - ArrayRef<GlobalTypeMember *> Globals); + void + buildBitSetsFromDisjointSet(ArrayRef<Metadata *> TypeIds, + ArrayRef<GlobalTypeMember *> Globals, + ArrayRef<ICallBranchFunnel *> ICallBranchFunnels); - void replaceWeakDeclarationWithJumpTablePtr(Function *F, Constant *JT); + void replaceWeakDeclarationWithJumpTablePtr(Function *F, Constant *JT, bool IsDefinition); void moveInitializerToModuleConstructor(GlobalVariable *GV); void findGlobalVariableUsersOf(Constant *C, SmallSetVector<GlobalVariable *, 8> &Out); void createJumpTable(Function *F, ArrayRef<GlobalTypeMember *> Functions); + /// replaceCfiUses - Go through the uses list for this definition + /// and make each use point to "V" instead of "this" when the use is outside + /// the block. 'This's use list is expected to have at least one element. + /// Unlike replaceAllUsesWith this function skips blockaddr and direct call + /// uses. + void replaceCfiUses(Function *Old, Value *New, bool IsDefinition); + + /// replaceDirectCalls - Go through the uses list for this definition and + /// replace each use, which is a direct function call. + void replaceDirectCalls(Value *Old, Value *New); + public: LowerTypeTestsModule(Module &M, ModuleSummaryIndex *ExportSummary, const ModuleSummaryIndex *ImportSummary); @@ -427,8 +471,6 @@ struct LowerTypeTests : public ModulePass { } bool runOnModule(Module &M) override { - if (skipModule(M)) - return false; if (UseCommandLine) return LowerTypeTestsModule::runForTesting(M); return LowerTypeTestsModule(M, ExportSummary, ImportSummary).lower(); @@ -729,10 +771,12 @@ void LowerTypeTestsModule::buildBitSetsFromGlobalVariables( // Compute the amount of padding required. uint64_t Padding = NextPowerOf2(InitSize - 1) - InitSize; - // Cap at 128 was found experimentally to have a good data/instruction - // overhead tradeoff. - if (Padding > 128) - Padding = alignTo(InitSize, 128) - InitSize; + // Experiments of different caps with Chromium on both x64 and ARM64 + // have shown that the 32-byte cap generates the smallest binary on + // both platforms while different caps yield similar performance. + // (see https://lists.llvm.org/pipermail/llvm-dev/2018-July/124694.html) + if (Padding > 32) + Padding = alignTo(InitSize, 32) - InitSize; GlobalInits.push_back( ConstantAggregateZero::get(ArrayType::get(Int8Ty, Padding))); @@ -936,14 +980,23 @@ void LowerTypeTestsModule::importTypeTest(CallInst *CI) { 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) { + // Non-dso_local functions may be overriden at run time, + // don't short curcuit them + if (F->isDSOLocal()) { + Function *RealF = Function::Create(F->getFunctionType(), + GlobalValue::ExternalLinkage, + Name + ".cfi", &M); + RealF->setVisibility(GlobalVariable::HiddenVisibility); + replaceDirectCalls(F, RealF); + } + return; + } + + Function *FDecl; if (F->isDeclarationForLinker() && !isDefinition) { // Declaration of an external function. FDecl = Function::Create(F->getFunctionType(), GlobalValue::ExternalLinkage, @@ -952,10 +1005,25 @@ void LowerTypeTestsModule::importFunction(Function *F, bool isDefinition) { } else if (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); + Visibility = GlobalValue::HiddenVisibility; + + // Delete aliases pointing to this function, they'll be re-created in the + // merged output + SmallVector<GlobalAlias*, 4> ToErase; + for (auto &U : F->uses()) { + if (auto *A = dyn_cast<GlobalAlias>(U.getUser())) { + Function *AliasDecl = Function::Create( + F->getFunctionType(), GlobalValue::ExternalLinkage, "", &M); + AliasDecl->takeName(A); + A->replaceAllUsesWith(AliasDecl); + ToErase.push_back(A); + } + } + for (auto *A : ToErase) + A->eraseFromParent(); } else { // Function definition without type metadata, where some other translation // unit contained a declaration with type metadata. This normally happens @@ -966,9 +1034,13 @@ void LowerTypeTestsModule::importFunction(Function *F, bool isDefinition) { } if (F->isWeakForLinker()) - replaceWeakDeclarationWithJumpTablePtr(F, FDecl); + replaceWeakDeclarationWithJumpTablePtr(F, FDecl, isDefinition); else - F->replaceAllUsesWith(FDecl); + replaceCfiUses(F, FDecl, isDefinition); + + // Set visibility late because it's used in replaceCfiUses() to determine + // whether uses need to to be replaced. + F->setVisibility(Visibility); } void LowerTypeTestsModule::lowerTypeTestCalls( @@ -980,7 +1052,7 @@ void LowerTypeTestsModule::lowerTypeTestCalls( for (Metadata *TypeId : TypeIds) { // Build the bitset. BitSetInfo BSI = buildBitSet(TypeId, GlobalLayout); - DEBUG({ + LLVM_DEBUG({ if (auto MDS = dyn_cast<MDString>(TypeId)) dbgs() << MDS->getString() << ": "; else @@ -1150,7 +1222,7 @@ void LowerTypeTestsModule::findGlobalVariableUsersOf( // Replace all uses of F with (F ? JT : 0). void LowerTypeTestsModule::replaceWeakDeclarationWithJumpTablePtr( - Function *F, Constant *JT) { + Function *F, Constant *JT, bool IsDefinition) { // The target expression can not appear in a constant initializer on most // (all?) targets. Switch to a runtime initializer. SmallSetVector<GlobalVariable *, 8> GlobalVarUsers; @@ -1163,7 +1235,7 @@ void LowerTypeTestsModule::replaceWeakDeclarationWithJumpTablePtr( Function *PlaceholderFn = Function::Create(cast<FunctionType>(F->getValueType()), GlobalValue::ExternalWeakLinkage, "", &M); - F->replaceAllUsesWith(PlaceholderFn); + replaceCfiUses(F, PlaceholderFn, IsDefinition); Constant *Target = ConstantExpr::getSelect( ConstantExpr::getICmp(CmpInst::ICMP_NE, F, @@ -1226,12 +1298,6 @@ void LowerTypeTestsModule::createJumpTable( createJumpTableEntry(AsmOS, ConstraintOS, JumpTableArch, AsmArgs, cast<Function>(Functions[I]->getGlobal())); - // Try to emit the jump table at the end of the text segment. - // Jump table must come after __cfi_check in the cross-dso mode. - // FIXME: this magic section name seems to do the trick. - F->setSection(ObjectFormat == Triple::MachO - ? "__TEXT,__text,regular,pure_instructions" - : ".text.cfi"); // Align the whole table by entry size. F->setAlignment(getJumpTableEntrySize()); // Skip prologue. @@ -1248,6 +1314,8 @@ void LowerTypeTestsModule::createJumpTable( // by Clang for -march=armv7. F->addFnAttr("target-cpu", "cortex-a8"); } + // Make sure we don't emit .eh_frame for this function. + F->addFnAttr(Attribute::NoUnwind); BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", F); IRBuilder<> IRB(BB); @@ -1389,9 +1457,9 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsNative( } if (!IsDefinition) { if (F->isWeakForLinker()) - replaceWeakDeclarationWithJumpTablePtr(F, CombinedGlobalElemPtr); + replaceWeakDeclarationWithJumpTablePtr(F, CombinedGlobalElemPtr, IsDefinition); else - F->replaceAllUsesWith(CombinedGlobalElemPtr); + replaceCfiUses(F, CombinedGlobalElemPtr, IsDefinition); } else { assert(F->getType()->getAddressSpace() == 0); @@ -1401,10 +1469,10 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsNative( FAlias->takeName(F); if (FAlias->hasName()) F->setName(FAlias->getName() + ".cfi"); - F->replaceUsesExceptBlockAddr(FAlias); + replaceCfiUses(F, FAlias, IsDefinition); + if (!F->hasLocalLinkage()) + F->setVisibility(GlobalVariable::HiddenVisibility); } - if (!F->isDeclarationForLinker()) - F->setLinkage(GlobalValue::InternalLinkage); } createJumpTable(JumpTableFn, Functions); @@ -1447,7 +1515,8 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsWASM( } void LowerTypeTestsModule::buildBitSetsFromDisjointSet( - ArrayRef<Metadata *> TypeIds, ArrayRef<GlobalTypeMember *> Globals) { + ArrayRef<Metadata *> TypeIds, ArrayRef<GlobalTypeMember *> Globals, + ArrayRef<ICallBranchFunnel *> ICallBranchFunnels) { DenseMap<Metadata *, uint64_t> TypeIdIndices; for (unsigned I = 0; I != TypeIds.size(); ++I) TypeIdIndices[TypeIds[I]] = I; @@ -1456,15 +1525,25 @@ void LowerTypeTestsModule::buildBitSetsFromDisjointSet( // the type identifier. std::vector<std::set<uint64_t>> TypeMembers(TypeIds.size()); unsigned GlobalIndex = 0; + DenseMap<GlobalTypeMember *, uint64_t> GlobalIndices; for (GlobalTypeMember *GTM : Globals) { for (MDNode *Type : GTM->types()) { // Type = { offset, type identifier } - unsigned TypeIdIndex = TypeIdIndices[Type->getOperand(1)]; - TypeMembers[TypeIdIndex].insert(GlobalIndex); + auto I = TypeIdIndices.find(Type->getOperand(1)); + if (I != TypeIdIndices.end()) + TypeMembers[I->second].insert(GlobalIndex); } + GlobalIndices[GTM] = GlobalIndex; GlobalIndex++; } + for (ICallBranchFunnel *JT : ICallBranchFunnels) { + TypeMembers.emplace_back(); + std::set<uint64_t> &TMSet = TypeMembers.back(); + for (GlobalTypeMember *T : JT->targets()) + TMSet.insert(GlobalIndices[T]); + } + // Order the sets of indices by size. The GlobalLayoutBuilder works best // when given small index sets first. std::stable_sort( @@ -1514,7 +1593,7 @@ LowerTypeTestsModule::LowerTypeTestsModule( } bool LowerTypeTestsModule::runForTesting(Module &M) { - ModuleSummaryIndex Summary; + ModuleSummaryIndex Summary(/*HaveGVs=*/false); // Handle the command-line summary arguments. This code is for testing // purposes only, so we handle errors directly. @@ -1549,11 +1628,71 @@ bool LowerTypeTestsModule::runForTesting(Module &M) { return Changed; } +static bool isDirectCall(Use& U) { + auto *Usr = dyn_cast<CallInst>(U.getUser()); + if (Usr) { + CallSite CS(Usr); + if (CS.isCallee(&U)) + return true; + } + return false; +} + +void LowerTypeTestsModule::replaceCfiUses(Function *Old, Value *New, bool IsDefinition) { + SmallSetVector<Constant *, 4> Constants; + auto UI = Old->use_begin(), E = Old->use_end(); + for (; UI != E;) { + Use &U = *UI; + ++UI; + + // Skip block addresses + if (isa<BlockAddress>(U.getUser())) + continue; + + // Skip direct calls to externally defined or non-dso_local functions + if (isDirectCall(U) && (Old->isDSOLocal() || !IsDefinition)) + continue; + + // Must handle Constants specially, we cannot call replaceUsesOfWith on a + // constant because they are uniqued. + if (auto *C = dyn_cast<Constant>(U.getUser())) { + if (!isa<GlobalValue>(C)) { + // Save unique users to avoid processing operand replacement + // more than once. + Constants.insert(C); + continue; + } + } + + U.set(New); + } + + // Process operand replacement of saved constants. + for (auto *C : Constants) + C->handleOperandChange(Old, New); +} + +void LowerTypeTestsModule::replaceDirectCalls(Value *Old, Value *New) { + auto UI = Old->use_begin(), E = Old->use_end(); + for (; UI != E;) { + Use &U = *UI; + ++UI; + + if (!isDirectCall(U)) + continue; + + U.set(New); + } +} + bool LowerTypeTestsModule::lower() { Function *TypeTestFunc = M.getFunction(Intrinsic::getName(Intrinsic::type_test)); - if ((!TypeTestFunc || TypeTestFunc->use_empty()) && !ExportSummary && - !ImportSummary) + Function *ICallBranchFunnelFunc = + M.getFunction(Intrinsic::getName(Intrinsic::icall_branch_funnel)); + if ((!TypeTestFunc || TypeTestFunc->use_empty()) && + (!ICallBranchFunnelFunc || ICallBranchFunnelFunc->use_empty()) && + !ExportSummary && !ImportSummary) return false; if (ImportSummary) { @@ -1565,6 +1704,10 @@ bool LowerTypeTestsModule::lower() { } } + if (ICallBranchFunnelFunc && !ICallBranchFunnelFunc->use_empty()) + report_fatal_error( + "unexpected call to llvm.icall.branch.funnel during import phase"); + SmallVector<Function *, 8> Defs; SmallVector<Function *, 8> Decls; for (auto &F : M) { @@ -1589,8 +1732,8 @@ bool LowerTypeTestsModule::lower() { // Equivalence class set containing type identifiers and the globals that // reference them. This is used to partition the set of type identifiers in // the module into disjoint sets. - using GlobalClassesTy = - EquivalenceClasses<PointerUnion<GlobalTypeMember *, Metadata *>>; + using GlobalClassesTy = EquivalenceClasses< + PointerUnion3<GlobalTypeMember *, Metadata *, ICallBranchFunnel *>>; GlobalClassesTy GlobalClasses; // Verify the type metadata and build a few data structures to let us @@ -1602,33 +1745,61 @@ bool LowerTypeTestsModule::lower() { // identifiers. BumpPtrAllocator Alloc; struct TIInfo { - unsigned Index; + unsigned UniqueId; std::vector<GlobalTypeMember *> RefGlobals; }; DenseMap<Metadata *, TIInfo> TypeIdInfo; - unsigned I = 0; + unsigned CurUniqueId = 0; SmallVector<MDNode *, 2> Types; + // Cross-DSO CFI emits jumptable entries for exported functions as well as + // address taken functions in case they are address taken in other modules. + const bool CrossDsoCfi = M.getModuleFlag("Cross-DSO CFI") != nullptr; + struct ExportedFunctionInfo { CfiFunctionLinkage Linkage; MDNode *FuncMD; // {name, linkage, type[, type...]} }; DenseMap<StringRef, ExportedFunctionInfo> ExportedFunctions; if (ExportSummary) { + // A set of all functions that are address taken by a live global object. + DenseSet<GlobalValue::GUID> AddressTaken; + for (auto &I : *ExportSummary) + for (auto &GVS : I.second.SummaryList) + if (GVS->isLive()) + for (auto &Ref : GVS->refs()) + AddressTaken.insert(Ref.getGUID()); + NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions"); if (CfiFunctionsMD) { for (auto FuncMD : CfiFunctionsMD->operands()) { assert(FuncMD->getNumOperands() >= 2); StringRef FunctionName = cast<MDString>(FuncMD->getOperand(0))->getString(); - if (!ExportSummary->isGUIDLive(GlobalValue::getGUID( - GlobalValue::dropLLVMManglingEscape(FunctionName)))) - continue; CfiFunctionLinkage Linkage = static_cast<CfiFunctionLinkage>( cast<ConstantAsMetadata>(FuncMD->getOperand(1)) ->getValue() ->getUniqueInteger() .getZExtValue()); + const GlobalValue::GUID GUID = GlobalValue::getGUID( + GlobalValue::dropLLVMManglingEscape(FunctionName)); + // Do not emit jumptable entries for functions that are not-live and + // have no live references (and are not exported with cross-DSO CFI.) + if (!ExportSummary->isGUIDLive(GUID)) + continue; + if (!AddressTaken.count(GUID)) { + if (!CrossDsoCfi || Linkage != CFL_Definition) + continue; + + bool Exported = false; + if (auto VI = ExportSummary->getValueInfo(GUID)) + for (auto &GVS : VI.getSummaryList()) + if (GVS->isLive() && !GlobalValue::isLocalLinkage(GVS->linkage())) + Exported = true; + + if (!Exported) + continue; + } auto P = ExportedFunctions.insert({FunctionName, {Linkage, FuncMD}}); if (!P.second && P.first->second.Linkage != CFL_Definition) P.first->second = {Linkage, FuncMD}; @@ -1656,6 +1827,11 @@ bool LowerTypeTestsModule::lower() { F->clearMetadata(); } + // Update the linkage for extern_weak declarations when a definition + // exists. + if (Linkage == CFL_Definition && F->hasExternalWeakLinkage()) + F->setLinkage(GlobalValue::ExternalLinkage); + // If the function in the full LTO module is a declaration, replace its // type metadata with the type metadata we found in cfi.functions. That // metadata is presumed to be more accurate than the metadata attached @@ -1673,28 +1849,37 @@ bool LowerTypeTestsModule::lower() { } } + DenseMap<GlobalObject *, GlobalTypeMember *> GlobalTypeMembers; for (GlobalObject &GO : M.global_objects()) { if (isa<GlobalVariable>(GO) && GO.isDeclarationForLinker()) continue; Types.clear(); GO.getMetadata(LLVMContext::MD_type, Types); - if (Types.empty()) - continue; bool IsDefinition = !GO.isDeclarationForLinker(); bool IsExported = false; - if (isa<Function>(GO) && ExportedFunctions.count(GO.getName())) { - IsDefinition |= ExportedFunctions[GO.getName()].Linkage == CFL_Definition; - IsExported = true; + if (Function *F = dyn_cast<Function>(&GO)) { + if (ExportedFunctions.count(F->getName())) { + IsDefinition |= ExportedFunctions[F->getName()].Linkage == CFL_Definition; + IsExported = true; + // TODO: The logic here checks only that the function is address taken, + // not that the address takers are live. This can be updated to check + // their liveness and emit fewer jumptable entries once monolithic LTO + // builds also emit summaries. + } else if (!F->hasAddressTaken()) { + if (!CrossDsoCfi || !IsDefinition || F->hasLocalLinkage()) + continue; + } } auto *GTM = GlobalTypeMember::create(Alloc, &GO, IsDefinition, IsExported, Types); + GlobalTypeMembers[&GO] = GTM; for (MDNode *Type : Types) { verifyTypeMDNode(&GO, Type); auto &Info = TypeIdInfo[Type->getOperand(1)]; - Info.Index = ++I; + Info.UniqueId = ++CurUniqueId; Info.RefGlobals.push_back(GTM); } } @@ -1731,6 +1916,44 @@ bool LowerTypeTestsModule::lower() { } } + if (ICallBranchFunnelFunc) { + for (const Use &U : ICallBranchFunnelFunc->uses()) { + if (Arch != Triple::x86_64) + report_fatal_error( + "llvm.icall.branch.funnel not supported on this target"); + + auto CI = cast<CallInst>(U.getUser()); + + std::vector<GlobalTypeMember *> Targets; + if (CI->getNumArgOperands() % 2 != 1) + report_fatal_error("number of arguments should be odd"); + + GlobalClassesTy::member_iterator CurSet; + for (unsigned I = 1; I != CI->getNumArgOperands(); I += 2) { + int64_t Offset; + auto *Base = dyn_cast<GlobalObject>(GetPointerBaseWithConstantOffset( + CI->getOperand(I), Offset, M.getDataLayout())); + if (!Base) + report_fatal_error( + "Expected branch funnel operand to be global value"); + + GlobalTypeMember *GTM = GlobalTypeMembers[Base]; + Targets.push_back(GTM); + GlobalClassesTy::member_iterator NewSet = + GlobalClasses.findLeader(GlobalClasses.insert(GTM)); + if (I == 1) + CurSet = NewSet; + else + CurSet = GlobalClasses.unionSets(CurSet, NewSet); + } + + GlobalClasses.unionSets( + CurSet, GlobalClasses.findLeader( + GlobalClasses.insert(ICallBranchFunnel::create( + Alloc, CI, Targets, ++CurUniqueId)))); + } + } + if (ExportSummary) { DenseMap<GlobalValue::GUID, TinyPtrVector<Metadata *>> MetadataByGUID; for (auto &P : TypeIdInfo) { @@ -1764,54 +1987,124 @@ bool LowerTypeTestsModule::lower() { continue; ++NumTypeIdDisjointSets; - unsigned MaxIndex = 0; + unsigned MaxUniqueId = 0; for (GlobalClassesTy::member_iterator MI = GlobalClasses.member_begin(I); MI != GlobalClasses.member_end(); ++MI) { - if ((*MI).is<Metadata *>()) - MaxIndex = std::max(MaxIndex, TypeIdInfo[MI->get<Metadata *>()].Index); + if (auto *MD = MI->dyn_cast<Metadata *>()) + MaxUniqueId = std::max(MaxUniqueId, TypeIdInfo[MD].UniqueId); + else if (auto *BF = MI->dyn_cast<ICallBranchFunnel *>()) + MaxUniqueId = std::max(MaxUniqueId, BF->UniqueId); } - Sets.emplace_back(I, MaxIndex); + Sets.emplace_back(I, MaxUniqueId); } - std::sort(Sets.begin(), Sets.end(), - [](const std::pair<GlobalClassesTy::iterator, unsigned> &S1, - const std::pair<GlobalClassesTy::iterator, unsigned> &S2) { - return S1.second < S2.second; - }); + llvm::sort(Sets.begin(), Sets.end(), + [](const std::pair<GlobalClassesTy::iterator, unsigned> &S1, + const std::pair<GlobalClassesTy::iterator, unsigned> &S2) { + return S1.second < S2.second; + }); // For each disjoint set we found... for (const auto &S : Sets) { // Build the list of type identifiers in this disjoint set. std::vector<Metadata *> TypeIds; std::vector<GlobalTypeMember *> Globals; + std::vector<ICallBranchFunnel *> ICallBranchFunnels; for (GlobalClassesTy::member_iterator MI = GlobalClasses.member_begin(S.first); MI != GlobalClasses.member_end(); ++MI) { - if ((*MI).is<Metadata *>()) + if (MI->is<Metadata *>()) TypeIds.push_back(MI->get<Metadata *>()); - else + else if (MI->is<GlobalTypeMember *>()) Globals.push_back(MI->get<GlobalTypeMember *>()); + else + ICallBranchFunnels.push_back(MI->get<ICallBranchFunnel *>()); } - // Order type identifiers by global index for determinism. This ordering is - // stable as there is a one-to-one mapping between metadata and indices. - std::sort(TypeIds.begin(), TypeIds.end(), [&](Metadata *M1, Metadata *M2) { - return TypeIdInfo[M1].Index < TypeIdInfo[M2].Index; + // Order type identifiers by unique ID for determinism. This ordering is + // stable as there is a one-to-one mapping between metadata and unique IDs. + llvm::sort(TypeIds.begin(), TypeIds.end(), [&](Metadata *M1, Metadata *M2) { + return TypeIdInfo[M1].UniqueId < TypeIdInfo[M2].UniqueId; }); + // Same for the branch funnels. + llvm::sort(ICallBranchFunnels.begin(), ICallBranchFunnels.end(), + [&](ICallBranchFunnel *F1, ICallBranchFunnel *F2) { + return F1->UniqueId < F2->UniqueId; + }); + // Build bitsets for this disjoint set. - buildBitSetsFromDisjointSet(TypeIds, Globals); + buildBitSetsFromDisjointSet(TypeIds, Globals, ICallBranchFunnels); } allocateByteArrays(); + // Parse alias data to replace stand-in function declarations for aliases + // with an alias to the intended target. + if (ExportSummary) { + if (NamedMDNode *AliasesMD = M.getNamedMetadata("aliases")) { + for (auto AliasMD : AliasesMD->operands()) { + assert(AliasMD->getNumOperands() >= 4); + StringRef AliasName = + cast<MDString>(AliasMD->getOperand(0))->getString(); + StringRef Aliasee = cast<MDString>(AliasMD->getOperand(1))->getString(); + + if (!ExportedFunctions.count(Aliasee) || + ExportedFunctions[Aliasee].Linkage != CFL_Definition || + !M.getNamedAlias(Aliasee)) + continue; + + GlobalValue::VisibilityTypes Visibility = + static_cast<GlobalValue::VisibilityTypes>( + cast<ConstantAsMetadata>(AliasMD->getOperand(2)) + ->getValue() + ->getUniqueInteger() + .getZExtValue()); + bool Weak = + static_cast<bool>(cast<ConstantAsMetadata>(AliasMD->getOperand(3)) + ->getValue() + ->getUniqueInteger() + .getZExtValue()); + + auto *Alias = GlobalAlias::create("", M.getNamedAlias(Aliasee)); + Alias->setVisibility(Visibility); + if (Weak) + Alias->setLinkage(GlobalValue::WeakAnyLinkage); + + if (auto *F = M.getFunction(AliasName)) { + Alias->takeName(F); + F->replaceAllUsesWith(Alias); + F->eraseFromParent(); + } else { + Alias->setName(AliasName); + } + } + } + } + + // Emit .symver directives for exported functions, if they exist. + if (ExportSummary) { + if (NamedMDNode *SymversMD = M.getNamedMetadata("symvers")) { + for (auto Symver : SymversMD->operands()) { + assert(Symver->getNumOperands() >= 2); + StringRef SymbolName = + cast<MDString>(Symver->getOperand(0))->getString(); + StringRef Alias = cast<MDString>(Symver->getOperand(1))->getString(); + + if (!ExportedFunctions.count(SymbolName)) + continue; + + M.appendModuleInlineAsm( + (llvm::Twine(".symver ") + SymbolName + ", " + Alias).str()); + } + } + } + return true; } PreservedAnalyses LowerTypeTestsPass::run(Module &M, ModuleAnalysisManager &AM) { - bool Changed = LowerTypeTestsModule(M, /*ExportSummary=*/nullptr, - /*ImportSummary=*/nullptr) - .lower(); + bool Changed = LowerTypeTestsModule(M, ExportSummary, ImportSummary).lower(); if (!Changed) return PreservedAnalyses::all(); return PreservedAnalyses::none(); |