Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -296,6 +296,10 @@ // Range information for accessed offsets for every argument. // [n x (paramno, range, numcalls, numcalls x (callee_guid, paramno, range))] FS_PARAM_ACCESS = 25, + // IFUNC: [valueid, flags, valueid] + FS_IFUNC = 26, + // COMBINED_IFUNC: [valueid, modid, flags, valueid] + FS_COMBINED_IFUNC = 27, }; enum MetadataCodes { Index: llvm/include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- llvm/include/llvm/IR/ModuleSummaryIndex.h +++ llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -288,7 +288,12 @@ class GlobalValueSummary { public: /// Sububclass discriminator (for dyn_cast<> et al.) - enum SummaryKind : unsigned { AliasKind, FunctionKind, GlobalVarKind }; + enum SummaryKind : unsigned { + AliasKind, + IfuncKind, + FunctionKind, + GlobalVarKind + }; /// Group flags (Linkage, NotEligibleToImport, etc.) as a bitfield. struct GVFlags { @@ -369,8 +374,9 @@ protected: GlobalValueSummary(SummaryKind K, GVFlags Flags, std::vector Refs) : Kind(K), Flags(Flags), RefEdgeList(std::move(Refs)) { - assert((K != AliasKind || Refs.empty()) && - "Expect no references for AliasSummary"); + + assert(((K != AliasKind && K != IfuncKind) || (Refs.empty())) && + "Expect no references for Alias and Ifunc summary"); } public: @@ -435,74 +441,99 @@ /// Return the list of values referenced by this global value definition. ArrayRef refs() const { return RefEdgeList; } - /// If this is an alias summary, returns the summary of the aliased object (a - /// global variable or function), otherwise returns itself. + /// If this is an global indirect value summary, returns the summary of the + /// indirect object (a global variable or function), otherwise returns itself. GlobalValueSummary *getBaseObject(); const GlobalValueSummary *getBaseObject() const; friend class ModuleSummaryIndex; }; -/// Alias summary information. -class AliasSummary : public GlobalValueSummary { - ValueInfo AliaseeValueInfo; - - /// This is the Aliasee in the same module as alias (could get from VI, trades - /// memory for time). Note that this pointer may be null (and the value info - /// empty) when we have a distributed index where the alias is being imported - /// (as a copy of the aliasee), but the aliasee is not. - GlobalValueSummary *AliaseeSummary; +class GlobalIndirectValueSummary : public GlobalValueSummary { + ValueInfo IndirectValueInfo; + /// This is the Aliasee in the same module as alias or Resolver in the same + /// module as ifunc (could get from VI, trades memory for time). Note that + /// this pointer may be null (and the value info empty) when we have a + /// distributed index where the alias is being imported (as a copy of the + /// aliasee), but the aliasee is not. + GlobalValueSummary *IndirectValueSummary; public: - AliasSummary(GVFlags Flags) - : GlobalValueSummary(AliasKind, Flags, ArrayRef{}), - AliaseeSummary(nullptr) {} + GlobalIndirectValueSummary(enum SummaryKind kind, GVFlags Flags) + : GlobalValueSummary(kind, Flags, ArrayRef{}), + IndirectValueSummary(nullptr) {} - /// Check if this is an alias summary. static bool classof(const GlobalValueSummary *GVS) { - return GVS->getSummaryKind() == AliasKind; + auto kind = GVS->getSummaryKind(); + return kind == AliasKind || kind == IfuncKind; } - void setAliasee(ValueInfo &AliaseeVI, GlobalValueSummary *Aliasee) { - AliaseeValueInfo = AliaseeVI; - AliaseeSummary = Aliasee; + void setIndirectSymbol(ValueInfo &ValueInfo, GlobalValueSummary *Summary) { + IndirectValueInfo = ValueInfo; + IndirectValueSummary = Summary; } - bool hasAliasee() const { - assert(!!AliaseeSummary == (AliaseeValueInfo && - !AliaseeValueInfo.getSummaryList().empty()) && - "Expect to have both aliasee summary and summary list or neither"); - return !!AliaseeSummary; + bool hasIndirectSymbol() const { + assert(!!IndirectValueSummary == + (IndirectValueInfo && + !IndirectValueInfo.getSummaryList().empty()) && + "Expect to have both value summary and summary list or neither"); + return !!IndirectValueSummary; } - const GlobalValueSummary &getAliasee() const { - assert(AliaseeSummary && "Unexpected missing aliasee summary"); - return *AliaseeSummary; + const GlobalValueSummary &getIndirectSymbol() const { + assert(IndirectValueSummary && "Unexpected missing resolver summary"); + return *IndirectValueSummary; } - GlobalValueSummary &getAliasee() { + GlobalValueSummary &getIndirectSymbol() { return const_cast( - static_cast(this)->getAliasee()); + static_cast(this) + ->getIndirectSymbol()); } - ValueInfo getAliaseeVI() const { - assert(AliaseeValueInfo && "Unexpected missing aliasee"); - return AliaseeValueInfo; + + ValueInfo getIndirectSymbolVI() const { + assert(IndirectValueInfo && "Unexpected missing indirect value info"); + return IndirectValueInfo; + } + + GlobalValue::GUID getIndirectSymbolGUID() const { + assert(IndirectValueInfo && "Unexpected missing indirect value info"); + return IndirectValueInfo.getGUID(); } - GlobalValue::GUID getAliaseeGUID() const { - assert(AliaseeValueInfo && "Unexpected missing aliasee"); - return AliaseeValueInfo.getGUID(); +}; + +/// Alias summary information. +class AliasSummary : public GlobalIndirectValueSummary { +public: + AliasSummary(GVFlags Flags) : GlobalIndirectValueSummary(AliasKind, Flags) {} + + /// Check if this is an alias summary. + static bool classof(const GlobalValueSummary *GVS) { + return GVS->getSummaryKind() == AliasKind; + } +}; + +/// Ifunc summary information. +class IfuncSummary : public GlobalIndirectValueSummary { +public: + IfuncSummary(GVFlags Flags) : GlobalIndirectValueSummary(IfuncKind, Flags) {} + + /// Check if this is an ifunc summary. + static bool classof(const GlobalValueSummary *GVS) { + return GVS->getSummaryKind() == IfuncKind; } }; const inline GlobalValueSummary *GlobalValueSummary::getBaseObject() const { - if (auto *AS = dyn_cast(this)) - return &AS->getAliasee(); + if (auto *GIV = dyn_cast(this)) + return &GIV->getIndirectSymbol(); return this; } inline GlobalValueSummary *GlobalValueSummary::getBaseObject() { - if (auto *AS = dyn_cast(this)) - return &AS->getAliasee(); + if (auto *GIV = dyn_cast(this)) + return &GIV->getIndirectSymbol(); return this; } @@ -1119,7 +1150,7 @@ // in the way some record are interpreted, like flags for instance. // Note that incrementing this may require changes in both BitcodeReader.cpp // and BitcodeWriter.cpp. - static constexpr uint64_t BitcodeSummaryVersion = 9; + static constexpr uint64_t BitcodeSummaryVersion = 10; // Regular LTO module name for ASM writer static constexpr const char *getRegularLTOModuleName() { Index: llvm/lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -322,12 +322,13 @@ // Stripping pointer casts can reveal a called function. CalledFunction = dyn_cast(CalledValue); } - // Check if this is an alias to a function. If so, get the - // called aliasee for the checks below. - if (auto *GA = dyn_cast(CalledValue)) { + // Check if this is an alias to a function or ifunc. + // If so, get the called aliasee or resolver for the checks below. + if (auto *GIS = dyn_cast(CalledValue)) { assert(!CalledFunction && "Expected null called function in callsite for alias"); - CalledFunction = dyn_cast(GA->getBaseObject()); + CalledFunction = dyn_cast(GIS->getBaseObject()); } + // Check if this is a direct call to a known function or a known // intrinsic, or an indirect call with profile data. if (CalledFunction) { @@ -345,10 +346,10 @@ if (ForceSummaryEdgesCold != FunctionSummary::FSHT_None) Hotness = CalleeInfo::HotnessType::Cold; - // Use the original CalledValue, in case it was an alias. We want - // to record the call edge to the alias in that case. Eventually - // an alias summary will be created to associate the alias and - // aliasee. + // Use the original CalledValue, in case it was an alias or ifunc. + // We want to record the call edge to the aliasee or resolver in that + // case. Eventually an indirect value summary will be created to + // associate the alias and aliasee or ifunc and its resolver. auto &ValueInfo = CallGraphEdges[Index.getOrInsertValueInfo( cast(CalledValue))]; ValueInfo.updateHotness(Hotness); @@ -617,23 +618,28 @@ } static void -computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A, - DenseSet &CantBePromoted) { - bool NonRenamableLocal = isNonRenamableLocal(A); +computeGlobalIndirectSummary(ModuleSummaryIndex &Index, + GlobalValueSummary::SummaryKind Kind, + const GlobalIndirectSymbol &GIS, + DenseSet &CantBePromoted) { + bool NonRenamableLocal = isNonRenamableLocal(GIS); GlobalValueSummary::GVFlags Flags( - A.getLinkage(), A.getVisibility(), NonRenamableLocal, - /* Live = */ false, A.isDSOLocal(), - A.hasLinkOnceODRLinkage() && A.hasGlobalUnnamedAddr()); - auto AS = std::make_unique(Flags); - auto *Aliasee = A.getBaseObject(); - auto AliaseeVI = Index.getValueInfo(Aliasee->getGUID()); - assert(AliaseeVI && "Alias expects aliasee summary to be available"); - assert(AliaseeVI.getSummaryList().size() == 1 && - "Expected a single entry per aliasee in per-module index"); - AS->setAliasee(AliaseeVI, AliaseeVI.getSummaryList()[0].get()); + GIS.getLinkage(), GIS.getVisibility(), NonRenamableLocal, + /* Live = */ false, GIS.isDSOLocal(), + GIS.hasLinkOnceODRLinkage() && GIS.hasGlobalUnnamedAddr()); + + auto GIV = std::make_unique(Kind, Flags); + auto *IndirectSymbol = GIS.getBaseObject(); + auto IndirectSymbolVI = Index.getValueInfo(IndirectSymbol->getGUID()); + assert(IndirectSymbolVI && + "GlobalIndirectValue expects indirect summary to be available"); + assert(IndirectSymbolVI.getSummaryList().size() == 1 && + "Expected a single entry per indirect symbol in per-module index"); + GIV->setIndirectSymbol(IndirectSymbolVI, + IndirectSymbolVI.getSummaryList()[0].get()); if (NonRenamableLocal) - CantBePromoted.insert(A.getGUID()); - Index.addGlobalValueSummary(A, std::move(AS)); + CantBePromoted.insert(GIS.getGUID()); + Index.addGlobalValueSummary(GIS, std::move(GIV)); } // Set LiveRoot flag on entries matching the given value name. @@ -774,10 +780,15 @@ computeVariableSummary(Index, G, CantBePromoted, M, Types); } - // Compute summaries for all aliases defined in module, and save in the - // index. + // Compute summaries for all aliases and ifuncs defined in module, + // and save in the index. + for (const GlobalIFunc &I : M.ifuncs()) + computeGlobalIndirectSummary(Index, GlobalValueSummary::IfuncKind, I, + CantBePromoted); + for (const GlobalAlias &A : M.aliases()) - computeAliasSummary(Index, A, CantBePromoted); + computeGlobalIndirectSummary(Index, GlobalValueSummary::AliasKind, A, + CantBePromoted); for (auto *V : LocalsUsed) { auto *Summary = Index.getGlobalValueSummary(*V); Index: llvm/lib/Analysis/StackSafetyAnalysis.cpp =================================================================== --- llvm/lib/Analysis/StackSafetyAnalysis.cpp +++ llvm/lib/Analysis/StackSafetyAnalysis.cpp @@ -618,7 +618,7 @@ if (!GVS->isLive()) continue; if (const AliasSummary *AS = dyn_cast(GVS.get())) - if (!AS->hasAliasee()) + if (!AS->hasIndirectSymbol()) continue; if (!isa(GVS->getBaseObject())) continue; @@ -654,7 +654,7 @@ if (FunctionSummary *FS = dyn_cast(S)) return FS; AliasSummary *AS = dyn_cast(S); - if (!AS || !AS->hasAliasee()) + if (!AS || !AS->hasIndirectSymbol()) return nullptr; S = AS->getBaseObject(); if (S == AS) Index: llvm/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/lib/AsmParser/LLLexer.cpp +++ llvm/lib/AsmParser/LLLexer.cpp @@ -779,6 +779,7 @@ KEYWORD(vTableFuncs); KEYWORD(virtFunc); KEYWORD(aliasee); + KEYWORD(resolver); KEYWORD(refs); KEYWORD(typeIdInfo); KEYWORD(typeTests); Index: llvm/lib/AsmParser/LLParser.h =================================================================== --- llvm/lib/AsmParser/LLParser.h +++ llvm/lib/AsmParser/LLParser.h @@ -144,6 +144,8 @@ ForwardRefValueInfos; std::map>> ForwardRefAliasees; + std::map>> + ForwardRefResolvers; std::vector NumberedValueInfos; // Summary type id reference information. @@ -345,6 +347,7 @@ bool parseFunctionSummary(std::string Name, GlobalValue::GUID, unsigned ID); bool parseVariableSummary(std::string Name, GlobalValue::GUID, unsigned ID); bool parseAliasSummary(std::string Name, GlobalValue::GUID, unsigned ID); + bool parseIfuncSummary(std::string Name, GlobalValue::GUID, unsigned ID); bool parseGVFlags(GlobalValueSummary::GVFlags &GVFlags); bool parseGVarFlags(GlobalVarSummary::GVarFlags &GVarFlags); bool parseOptionalFFlags(FunctionSummary::FFlags &FFlags); Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -289,6 +289,11 @@ "use of undefined summary '^" + Twine(ForwardRefAliasees.begin()->first) + "'"); + if (!ForwardRefResolvers.empty()) + return error(ForwardRefResolvers.begin()->second.front().second, + "use of undefined summary '^" + + Twine(ForwardRefResolvers.begin()->first) + "'"); + if (!ForwardRefTypeIds.empty()) return error(ForwardRefTypeIds.begin()->second.front().second, "use of undefined type id summary '^" + @@ -8455,14 +8460,26 @@ auto FwdRefAliasees = ForwardRefAliasees.find(ID); if (FwdRefAliasees != ForwardRefAliasees.end()) { for (auto AliaseeRef : FwdRefAliasees->second) { - assert(!AliaseeRef.first->hasAliasee() && + assert(!AliaseeRef.first->hasIndirectSymbol() && "Forward referencing alias already has aliasee"); assert(Summary && "Aliasee must be a definition"); - AliaseeRef.first->setAliasee(VI, Summary.get()); + AliaseeRef.first->setIndirectSymbol(VI, Summary.get()); } ForwardRefAliasees.erase(FwdRefAliasees); } + // Resolve forward references from ifuncs + auto FwdRefResolvers = ForwardRefResolvers.find(ID); + if (FwdRefResolvers != ForwardRefResolvers.end()) { + for (auto ResolverRef : FwdRefResolvers->second) { + assert(!ResolverRef.first->hasIndirectSymbol() && + "Forward referencing ifunc already has resolver"); + assert(Summary && "Resolver must be a definition"); + ResolverRef.first->setIndirectSymbol(VI, Summary.get()); + } + ForwardRefResolvers.erase(FwdRefResolvers); + } + // Add the summary if one was provided. if (Summary) Index->addGlobalValueSummary(VI, std::move(Summary)); @@ -8575,6 +8592,10 @@ if (parseAliasSummary(Name, GUID, ID)) return true; break; + case lltok::kw_ifunc: + if (parseIfuncSummary(Name, GUID, ID)) + return true; + break; default: return error(Lex.getLoc(), "expected summary type"); } @@ -8765,7 +8786,7 @@ } else { auto Summary = Index->findSummaryInModule(AliaseeVI, ModulePath); assert(Summary && "Aliasee must be a definition"); - AS->setAliasee(AliaseeVI, Summary); + AS->setIndirectSymbol(AliaseeVI, Summary); } addGlobalValueToIndex(Name, GUID, (GlobalValue::LinkageTypes)GVFlags.Linkage, @@ -8774,6 +8795,56 @@ return false; } +/// IfuncSummary +/// ::= 'ifunc' ':' '(' 'module' ':' ModuleReference ',' GVFlags ',' +/// 'resolver' ':' GVReference ')' +bool LLParser::parseIfuncSummary(std::string Name, GlobalValue::GUID GUID, + unsigned ID) { + assert(Lex.getKind() == lltok::kw_ifunc); + LocTy Loc = Lex.getLoc(); + Lex.Lex(); + + StringRef ModulePath; + GlobalValueSummary::GVFlags GVFlags = GlobalValueSummary::GVFlags( + GlobalValue::ExternalLinkage, GlobalValue::DefaultVisibility, + /*NotEligibleToImport=*/false, + /*Live=*/false, /*IsLocal=*/false, /*CanAutoHide=*/false); + if (parseToken(lltok::colon, "expected ':' here") || + parseToken(lltok::lparen, "expected '(' here") || + parseModuleReference(ModulePath) || + parseToken(lltok::comma, "expected ',' here") || parseGVFlags(GVFlags) || + parseToken(lltok::comma, "expected ',' here") || + parseToken(lltok::kw_resolver, "expected 'resolver' here") || + parseToken(lltok::colon, "expected ':' here")) + return true; + + ValueInfo ResolverVI; + unsigned GVId; + if (parseGVReference(ResolverVI, GVId)) + return true; + + if (parseToken(lltok::rparen, "expected ')' here")) + return true; + + auto IF = std::make_unique(GVFlags); + + IF->setModulePath(ModulePath); + + // Record forward reference if the resolver is not parsed yet. + if (ResolverVI.getRef() == FwdVIRef) { + ForwardRefResolvers[GVId].emplace_back(IF.get(), Loc); + } else { + auto Summary = Index->findSummaryInModule(ResolverVI, ModulePath); + assert(Summary && "Resolver must be a definition"); + IF->setIndirectSymbol(ResolverVI, Summary); + } + + addGlobalValueToIndex(Name, GUID, (GlobalValue::LinkageTypes)GVFlags.Linkage, + ID, std::move(IF)); + + return false; +} + /// Flag /// ::= [0|1] bool LLParser::parseFlag(unsigned &Val) { Index: llvm/lib/AsmParser/LLToken.h =================================================================== --- llvm/lib/AsmParser/LLToken.h +++ llvm/lib/AsmParser/LLToken.h @@ -412,6 +412,7 @@ kw_vTableFuncs, kw_virtFunc, kw_aliasee, + kw_resolver, kw_refs, kw_typeIdInfo, kw_typeTests, Index: llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp +++ llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp @@ -140,6 +140,7 @@ STRINGIFY_CODE(MODULE_CODE, METADATA_VALUES_UNUSED) STRINGIFY_CODE(MODULE_CODE, SOURCE_FILENAME) STRINGIFY_CODE(MODULE_CODE, HASH) + STRINGIFY_CODE(MODULE_CODE, IFUNC) } case bitc::IDENTIFICATION_BLOCK_ID: switch (CodeID) { @@ -313,6 +314,7 @@ STRINGIFY_CODE(FS, TYPE_ID_METADATA) STRINGIFY_CODE(FS, BLOCK_COUNT) STRINGIFY_CODE(FS, PARAM_ACCESS) + STRINGIFY_CODE(FS, IFUNC) } case bitc::METADATA_ATTACHMENT_ID: switch (CodeID) { Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -5884,12 +5884,14 @@ // was historically always the start of the regular bitcode header. VSTOffset = Record[0] - 1; break; - // v1 GLOBALVAR: [pointer type, isconst, initid, linkage, ...] - // v1 FUNCTION: [type, callingconv, isproto, linkage, ...] - // v1 ALIAS: [alias type, addrspace, aliasee val#, linkage, ...] + // v1 GLOBALVAR: [pointer type, isconst, initid, linkage, ...] + // v1 FUNCTION: [type, callingconv, isproto, linkage, ...] + // v1 IFUNC: [ifunc type, addrspace, resolver val#, linkage, ...] + // v1 ALIAS: [alias type, addrspace, aliasee val#, linkage, ...] // v2: [strtab offset, strtab size, v1] case bitc::MODULE_CODE_GLOBALVAR: case bitc::MODULE_CODE_FUNCTION: + case bitc::MODULE_CODE_IFUNC: case bitc::MODULE_CODE_ALIAS: { StringRef Name; ArrayRef GVRecord; @@ -6234,13 +6236,42 @@ auto AliaseeInModule = TheIndex.findSummaryInModule(AliaseeVI, ModulePath); if (!AliaseeInModule) return error("Alias expects aliasee summary to be parsed"); - AS->setAliasee(AliaseeVI, AliaseeInModule); + AS->setIndirectSymbol(AliaseeVI, AliaseeInModule); auto GUID = getValueInfoFromValueId(ValueID); AS->setOriginalName(GUID.second); TheIndex.addGlobalValueSummary(GUID.first, std::move(AS)); break; } + // FS_IFUNC: [valueid, flags, valueid] + // Ifuncs must be emitted (and parsed) after all FS_PERMODULE entries, as + // they expect all resolver summaries to be available. + case bitc::FS_IFUNC: { + unsigned ValueID = Record[0]; + uint64_t RawFlags = Record[1]; + unsigned ResolverID = Record[2]; + auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); + auto IF = std::make_unique(Flags); + // The module path string ref set in the summary must be owned by the + // index's module string table. Since we don't have a module path + // string table section in the per-module index, we create a single + // module path string table entry with an empty (0) ID to take + // ownership. + IF->setModulePath(getThisModule()->first()); + + auto ResolverVI = getValueInfoFromValueId(ResolverID).first; + auto ResolverInModule = + TheIndex.findSummaryInModule(ResolverVI, ModulePath); + if (!ResolverInModule) + return error("Ifunc expects resolver summary to be parsed"); + IF->setIndirectSymbol(ResolverVI, ResolverInModule); + + auto GUID = getValueInfoFromValueId(ValueID); + IF->setOriginalName(GUID.second); + TheIndex.addGlobalValueSummary(GUID.first, std::move(IF)); + break; + } + // FS_PERMODULE_GLOBALVAR_INIT_REFS: [valueid, flags, varflags, n x valueid] case bitc::FS_PERMODULE_GLOBALVAR_INIT_REFS: { unsigned ValueID = Record[0]; @@ -6372,13 +6403,36 @@ auto AliaseeVI = getValueInfoFromValueId(AliaseeValueId).first; auto AliaseeInModule = TheIndex.findSummaryInModule(AliaseeVI, AS->modulePath()); - AS->setAliasee(AliaseeVI, AliaseeInModule); + AS->setIndirectSymbol(AliaseeVI, AliaseeInModule); ValueInfo VI = getValueInfoFromValueId(ValueID).first; LastSeenGUID = VI.getGUID(); TheIndex.addGlobalValueSummary(VI, std::move(AS)); break; } + // FS_COMBINED_IFUNC: [valueid, modid, flags, valueid] + // Ifuncs must be emitted (and parsed) after all FS_COMBINED entries, as + // they expect all resolver summaries to be available. + case bitc::FS_COMBINED_IFUNC: { + unsigned ValueID = Record[0]; + uint64_t ModuleId = Record[1]; + uint64_t RawFlags = Record[2]; + unsigned ResolverValueId = Record[3]; + auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); + auto IF = std::make_unique(Flags); + LastSeenSummary = IF.get(); + IF->setModulePath(ModuleIdMap[ModuleId]); + + auto ResolverVI = getValueInfoFromValueId(ResolverValueId).first; + auto ResolverInModule = + TheIndex.findSummaryInModule(ResolverVI, IF->modulePath()); + IF->setIndirectSymbol(ResolverVI, ResolverInModule); + + ValueInfo VI = getValueInfoFromValueId(ValueID).first; + LastSeenGUID = VI.getGUID(); + TheIndex.addGlobalValueSummary(VI, std::move(IF)); + break; + } // FS_COMBINED_GLOBALVAR_INIT_REFS: [valueid, modid, flags, n x valueid] case bitc::FS_COMBINED_GLOBALVAR_INIT_REFS: { unsigned ValueID = Record[0]; Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -456,11 +456,14 @@ for (auto &M : *ModuleToSummariesForIndex) for (auto &Summary : M.second) { Callback(Summary, false); - // Ensure aliasee is handled, e.g. for assigning a valueId, + // Ensure aliasee and ifunc resolver are handled, + // e.g. for assigning a valueId, // even if we are not importing the aliasee directly (the // imported alias will contain a copy of aliasee). - if (auto *AS = dyn_cast(Summary.getSecond())) - Callback({AS->getAliaseeGUID(), &AS->getAliasee()}, true); + if (auto *GIV = + dyn_cast(Summary.getSecond())) + Callback({GIV->getIndirectSymbolGUID(), &GIV->getIndirectSymbol()}, + true); } } else { for (auto &Summaries : Index) @@ -3969,6 +3972,14 @@ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid unsigned FSAliasAbbrev = Stream.EmitAbbrev(std::move(Abbv)); + // Abbrev for FS_IFUNC. + Abbv = std::make_shared(); + Abbv->Add(BitCodeAbbrevOp(bitc::FS_IFUNC)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // flags + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid + unsigned FSIfuncAbbrev = Stream.EmitAbbrev(std::move(Abbv)); + // Abbrev for FS_TYPE_ID_METADATA Abbv = std::make_shared(); Abbv->Add(BitCodeAbbrevOp(bitc::FS_TYPE_ID_METADATA)); @@ -4022,6 +4033,20 @@ NameVals.clear(); } + for (const GlobalIFunc &I : M.ifuncs()) { + auto *Resolver = I.getBaseObject(); + auto IfuncId = VE.getValueID(&I); + auto ResolverId = VE.getValueID(Resolver); + + auto *Summary = Index->getGlobalValueSummary(I); + IfuncSummary *IF = cast(Summary); + + NameVals.push_back(IfuncId); + NameVals.push_back(getEncodedGVSummaryFlags(IF->flags())); + NameVals.push_back(ResolverId); + Stream.EmitRecord(bitc::FS_IFUNC, NameVals, FSIfuncAbbrev); + NameVals.clear(); + } for (auto &S : Index->typeIdCompatibleVtableMap()) { writeTypeIdCompatibleVtableSummaryRecord(NameVals, StrtabBuilder, S.first, S.second, VE); @@ -4038,7 +4063,7 @@ /// Emit the combined summary section into the combined index file. void IndexBitcodeWriter::writeCombinedGlobalValueSummary() { - Stream.EnterSubblock(bitc::GLOBALVAL_SUMMARY_BLOCK_ID, 3); + Stream.EnterSubblock(bitc::GLOBALVAL_SUMMARY_BLOCK_ID, 4); Stream.EmitRecord( bitc::FS_VERSION, ArrayRef{ModuleSummaryIndex::BitcodeSummaryVersion}); @@ -4103,12 +4128,24 @@ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // flags Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid unsigned FSAliasAbbrev = Stream.EmitAbbrev(std::move(Abbv)); + // Abbrev for FS_COMBINED_IFUNC. + Abbv = std::make_shared(); + Abbv->Add(BitCodeAbbrevOp(bitc::FS_COMBINED_IFUNC)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // modid + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // flags + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid + unsigned FSIfuncAbbrev = Stream.EmitAbbrev(std::move(Abbv)); // The aliases are emitted as a post-pass, and will point to the value // id of the aliasee. Save them in a vector for post-processing. SmallVector Aliases; - // Save the value id for each summary for alias emission. + // The ifuncs are emitted as a post-pass, and will point to the value + // id of the resolver. Save them in a vector for post-processing. + SmallVector Ifuncs; + + // Save the value id for each summary for alias and ifunc emission. DenseMap SummaryToValueIdMap; SmallVector NameVals; @@ -4128,7 +4165,7 @@ }; std::set DefOrUseGUIDs; - forEachSummary([&](GVInfo I, bool IsAliasee) { + forEachSummary([&](GVInfo I, bool IsAliaseeOrIRes) { GlobalValueSummary *S = I.second; assert(S); DefOrUseGUIDs.insert(I.first); @@ -4139,10 +4176,11 @@ assert(ValueId); SummaryToValueIdMap[S] = *ValueId; - // If this is invoked for an aliasee, we want to record the above - // mapping, but then not emit a summary entry (if the aliasee is - // to be imported, we will invoke this separately with IsAliasee=false). - if (IsAliasee) + // If this is invoked for an aliasee or ifunc resolver, we want to record + // the above mapping, but then not emit a summary entry (if the aliasee is + // to be imported, we will invoke this separately with + // IsAliaseeOrIRes=false). + if (IsAliaseeOrIRes) return; if (auto *AS = dyn_cast(S)) { @@ -4152,6 +4190,13 @@ return; } + if (auto *IF = dyn_cast(S)) { + // Will process ifuncs as a post-pass because the reader wants all + // global to be loaded first. + Ifuncs.push_back(IF); + return; + } + if (auto *VS = dyn_cast(S)) { NameVals.push_back(*ValueId); NameVals.push_back(Index.getModuleId(VS->modulePath())); @@ -4269,7 +4314,7 @@ NameVals.push_back(AliasValueId); NameVals.push_back(Index.getModuleId(AS->modulePath())); NameVals.push_back(getEncodedGVSummaryFlags(AS->flags())); - auto AliaseeValueId = SummaryToValueIdMap[&AS->getAliasee()]; + auto AliaseeValueId = SummaryToValueIdMap[&AS->getIndirectSymbol()]; assert(AliaseeValueId); NameVals.push_back(AliaseeValueId); @@ -4278,10 +4323,28 @@ NameVals.clear(); MaybeEmitOriginalName(*AS); - if (auto *FS = dyn_cast(&AS->getAliasee())) + if (auto *FS = dyn_cast(&AS->getIndirectSymbol())) getReferencedTypeIds(FS, ReferencedTypeIds); } + for (auto *IF : Ifuncs) { + auto IfuncValueId = SummaryToValueIdMap[IF]; + assert(IfuncValueId); + NameVals.push_back(IfuncValueId); + NameVals.push_back(Index.getModuleId(IF->modulePath())); + NameVals.push_back(getEncodedGVSummaryFlags(IF->flags())); + auto ResolverValueId = SummaryToValueIdMap[&IF->getIndirectSymbol()]; + assert(ResolverValueId); + NameVals.push_back(ResolverValueId); + + // Emit the finished record. + Stream.EmitRecord(bitc::FS_COMBINED_IFUNC, NameVals, FSIfuncAbbrev); + NameVals.clear(); + MaybeEmitOriginalName(*IF); + auto *FS = dyn_cast(&IF->getIndirectSymbol()); + assert(FS); + getReferencedTypeIds(FS, ReferencedTypeIds); + } if (!Index.cfiFunctionDefs().empty()) { for (auto &S : Index.cfiFunctionDefs()) { if (DefOrUseGUIDs.count( Index: llvm/lib/IR/AsmWriter.cpp =================================================================== --- llvm/lib/IR/AsmWriter.cpp +++ llvm/lib/IR/AsmWriter.cpp @@ -2629,6 +2629,7 @@ void printSummaryInfo(unsigned Slot, const ValueInfo &VI); void printSummary(const GlobalValueSummary &Summary); void printAliasSummary(const AliasSummary *AS); + void printIfuncSummary(const IfuncSummary *IF); void printGlobalVarSummary(const GlobalVarSummary *GS); void printFunctionSummary(const FunctionSummary *FS); void printTypeIdSummary(const TypeIdSummary &TIS); @@ -3102,6 +3103,8 @@ switch (SK) { case GlobalValueSummary::AliasKind: return "alias"; + case GlobalValueSummary::IfuncKind: + return "ifunc"; case GlobalValueSummary::FunctionKind: return "function"; case GlobalValueSummary::GlobalVarKind: @@ -3115,8 +3118,21 @@ // The indexes emitted for distributed backends may not include the // aliasee summary (only if it is being imported directly). Handle // that case by just emitting "null" as the aliasee. - if (AS->hasAliasee()) - Out << "^" << Machine.getGUIDSlot(SummaryToGUIDMap[&AS->getAliasee()]); + if (AS->hasIndirectSymbol()) + Out << "^" + << Machine.getGUIDSlot(SummaryToGUIDMap[&AS->getIndirectSymbol()]); + else + Out << "null"; +} + +void AssemblyWriter::printIfuncSummary(const IfuncSummary *IF) { + Out << ", resolver: "; + // The indexes emitted for distributed backends may not include the + // resolver summary (only if it is being imported directly). Handle + // that case by just emitting "null" as the resolver. + if (IF->hasIndirectSymbol()) + Out << "^" + << Machine.getGUIDSlot(SummaryToGUIDMap[&IF->getIndirectSymbol()]); else Out << "null"; } @@ -3371,6 +3387,8 @@ if (Summary.getSummaryKind() == GlobalValueSummary::AliasKind) printAliasSummary(cast(&Summary)); + else if (Summary.getSummaryKind() == GlobalValueSummary::IfuncKind) + printIfuncSummary(cast(&Summary)); else if (Summary.getSummaryKind() == GlobalValueSummary::FunctionKind) printFunctionSummary(cast(&Summary)); else Index: llvm/lib/IR/Globals.cpp =================================================================== --- llvm/lib/IR/Globals.cpp +++ llvm/lib/IR/Globals.cpp @@ -18,6 +18,7 @@ #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalAlias.h" +#include "llvm/IR/GlobalIFunc.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Module.h" @@ -160,9 +161,9 @@ } StringRef GlobalValue::getSection() const { - if (auto *GA = dyn_cast(this)) { + if (auto *GIS = dyn_cast(this)) { // In general we cannot compute this at the IR level, but we try. - if (const GlobalObject *GO = GA->getBaseObject()) + if (const GlobalObject *GO = GIS->getBaseObject()) return GO->getSection(); return ""; } Index: llvm/lib/IR/ModuleSummaryIndex.cpp =================================================================== --- llvm/lib/IR/ModuleSummaryIndex.cpp +++ llvm/lib/IR/ModuleSummaryIndex.cpp @@ -472,7 +472,7 @@ } static std::string getNodeLabel(const ValueInfo &VI, GlobalValueSummary *GVS) { - if (isa(GVS)) + if (isa(GVS)) return getNodeVisualName(VI); std::string Attrs = getSummaryAttributes(GVS); @@ -626,8 +626,9 @@ Draw(SummaryIt.first, R.getGUID(), R.isWriteOnly() ? -1 : (R.isReadOnly() ? -2 : -3)); - if (auto *AS = dyn_cast_or_null(SummaryIt.second)) { - Draw(SummaryIt.first, AS->getAliaseeGUID(), -4); + if (auto *GIV = + dyn_cast_or_null(SummaryIt.second)) { + Draw(SummaryIt.first, GIV->getIndirectSymbolGUID(), -4); continue; } Index: llvm/lib/LTO/LTO.cpp =================================================================== --- llvm/lib/LTO/LTO.cpp +++ llvm/lib/LTO/LTO.cpp @@ -250,10 +250,10 @@ for (auto &ImpF : ImpM.second) { GlobalValueSummary *S = Index.findSummaryInModule(ImpF, ImpM.first()); AddUsedThings(S); - // If this is an alias, we also care about any types/etc. that the aliasee - // may reference. - if (auto *AS = dyn_cast_or_null(S)) - AddUsedThings(AS->getBaseObject()); + // If this is an alias/ifunc, we also care about any types/etc. + // that the aliasee/resolver may reference. + if (auto *GIV = dyn_cast_or_null(S)) + AddUsedThings(GIV->getBaseObject()); } auto AddTypeIdSummary = [&](StringRef TId, const TypeIdSummary &S) { @@ -319,7 +319,7 @@ static void thinLTOResolvePrevailingGUID( const Config &C, ValueInfo VI, - DenseSet &GlobalInvolvedWithAlias, + DenseSet &GlobalInvolvedWithIndirectValue, function_ref isPrevailing, function_ref @@ -362,9 +362,10 @@ if (C.VisibilityScheme == Config::FromPrevailing) Visibility = S->getVisibility(); } - // Alias and aliasee can't be turned into available_externally. - else if (!isa(S.get()) && - !GlobalInvolvedWithAlias.count(S.get())) + // Alias and aliasee or ifunc and resolver can't be turned into + // available_externally. + else if (!isa(S.get()) && + !GlobalInvolvedWithIndirectValue.count(S.get())) S->setLinkage(GlobalValue::AvailableExternallyLinkage); // For ELF, set visibility to the computed visibility from summaries. We @@ -404,15 +405,15 @@ // We won't optimize the globals that are referenced by an alias for now // Ideally we should turn the alias into a global and duplicate the definition // when needed. - DenseSet GlobalInvolvedWithAlias; + DenseSet GlobalInvolvedWithIndirectValue; for (auto &I : Index) for (auto &S : I.second.SummaryList) - if (auto AS = dyn_cast(S.get())) - GlobalInvolvedWithAlias.insert(&AS->getAliasee()); + if (auto GIV = dyn_cast(S.get())) + GlobalInvolvedWithIndirectValue.insert(&GIV->getIndirectSymbol()); for (auto &I : Index) thinLTOResolvePrevailingGUID(C, Index.getValueInfo(I), - GlobalInvolvedWithAlias, isPrevailing, + GlobalInvolvedWithIndirectValue, isPrevailing, recordNewLinkage, GUIDPreservedSymbols); } Index: llvm/lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- llvm/lib/Transforms/IPO/FunctionImport.cpp +++ llvm/lib/Transforms/IPO/FunctionImport.cpp @@ -24,6 +24,7 @@ #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalAlias.h" +#include "llvm/IR/GlobalIFunc.h" #include "llvm/IR/GlobalObject.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" @@ -206,6 +207,10 @@ return false; } + if (GVSummary->getSummaryKind() == GlobalValueSummary::IfuncKind) { + return false; + } + auto *Summary = cast(GVSummary->getBaseObject()); // If this is a local function, make sure we import the copy @@ -858,7 +863,7 @@ } // Make value live and add it to the worklist if it was not live before. - auto visit = [&](ValueInfo VI, bool IsAliasee) { + auto visit = [&](ValueInfo VI, bool IsAliaseeOrResolver) { // FIXME: If we knew which edges were created for indirect call profiles, // we could skip them here. Any that are live should be reached via // other edges, e.g. reference edges. Otherwise, using a profile collected @@ -894,7 +899,7 @@ Interposable = true; } - if (!IsAliasee) { + if (!IsAliaseeOrResolver) { if (!KeepAliveLinkage) return; @@ -914,13 +919,14 @@ while (!Worklist.empty()) { auto VI = Worklist.pop_back_val(); for (auto &Summary : VI.getSummaryList()) { - if (auto *AS = dyn_cast(Summary.get())) { - // If this is an alias, visit the aliasee VI to ensure that all copies - // are marked live and it is added to the worklist for further - // processing of its references. - visit(AS->getAliaseeVI(), true); + if (auto *GIV = dyn_cast(Summary.get())) { + // If this is an alias or ifunc, visit the aliasee / resolver VI to + // ensure that all copies are marked live and it is added to the + // worklist for further processing of its references. + visit(GIV->getIndirectSymbolVI(), true); continue; } + for (auto Ref : Summary->refs()) visit(Ref, false); if (auto *FS = dyn_cast(Summary.get())) Index: llvm/test/Assembler/thinlto-summary.ll =================================================================== --- llvm/test/Assembler/thinlto-summary.ll +++ llvm/test/Assembler/thinlto-summary.ll @@ -45,26 +45,32 @@ ; Alias summary with backwards reference to aliasee. ^18 = gv: (guid: 17, summaries: (alias: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1), aliasee: ^14))) +; Test Ifunc summaries with forward reference to resolver: +^19 = gv: (guid: 18, summaries: (ifunc: (module: ^0, flags: (linkage: private, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1), resolver: ^20))) +^20 = gv: (guid: 19, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1), insts: 1, refs: (^21)))) +^21 = gv: (guid: 20, summaries: (function: (module: ^0, flags: (linkage: internal, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1), insts: 1))) +; Test Ifunc summaries with backwards reference to resolver: +^22 = gv: (guid: 21, summaries: (ifunc: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1), resolver: ^20))) + ; Test all types of TypeIdInfo on function summaries. -^19 = gv: (guid: 18, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 4, typeIdInfo: (typeTests: (^24, ^26))))) -^20 = gv: (guid: 19, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 8, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (^27, offset: 16)))))) -^21 = gv: (guid: 20, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 5, typeIdInfo: (typeCheckedLoadVCalls: (vFuncId: (^25, offset: 16)))))) -^22 = gv: (guid: 21, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 15, typeIdInfo: (typeTestAssumeConstVCalls: ((vFuncId: (^27, offset: 16), args: (42)), (vFuncId: (^27, offset: 24))))))) -^23 = gv: (guid: 22, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 5, typeIdInfo: (typeCheckedLoadConstVCalls: ((vFuncId: (^28, offset: 16), args: (42))))))) +^23 = gv: (guid: 22, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 4, typeIdInfo: (typeTests: (^28, ^30))))) +^24 = gv: (guid: 23, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 8, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (^31, offset: 16)))))) +^25 = gv: (guid: 24, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 5, typeIdInfo: (typeCheckedLoadVCalls: (vFuncId: (^29, offset: 16)))))) +^26 = gv: (guid: 25, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 15, typeIdInfo: (typeTestAssumeConstVCalls: ((vFuncId: (^31, offset: 16), args: (42)), (vFuncId: (^31, offset: 24))))))) +^27 = gv: (guid: 26, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 5, typeIdInfo: (typeCheckedLoadConstVCalls: ((vFuncId: (^32, offset: 16), args: (42))))))) ; Test TypeId summaries: - -^24 = typeid: (name: "_ZTS1C", summary: (typeTestRes: (kind: single, sizeM1BitWidth: 0))) +^28 = typeid: (name: "_ZTS1C", summary: (typeTestRes: (kind: single, sizeM1BitWidth: 0))) ; Test TypeId with other optional fields (alignLog2/sizeM1/bitMask/inlineBits) -^25 = typeid: (name: "_ZTS1B", summary: (typeTestRes: (kind: inline, sizeM1BitWidth: 0, alignLog2: 1, sizeM1: 2, bitMask: 3, inlineBits: 4))) +^29 = typeid: (name: "_ZTS1B", summary: (typeTestRes: (kind: inline, sizeM1BitWidth: 0, alignLog2: 1, sizeM1: 2, bitMask: 3, inlineBits: 4))) ; Test the AllOnes resolution, and all kinds of WholeProgramDevirtResolution ; types, including all optional resolution by argument kinds. -^26 = typeid: (name: "_ZTS1A", summary: (typeTestRes: (kind: allOnes, sizeM1BitWidth: 7), wpdResolutions: ((offset: 0, wpdRes: (kind: branchFunnel)), (offset: 8, wpdRes: (kind: singleImpl, singleImplName: "_ZN1A1nEi")), (offset: 16, wpdRes: (kind: indir, resByArg: (args: (1, 2), byArg: (kind: indir, byte: 2, bit: 3), args: (3), byArg: (kind: uniformRetVal, info: 1), args: (4), byArg: (kind: uniqueRetVal, info: 1), args: (5), byArg: (kind: virtualConstProp))))))) +^30 = typeid: (name: "_ZTS1A", summary: (typeTestRes: (kind: allOnes, sizeM1BitWidth: 7), wpdResolutions: ((offset: 0, wpdRes: (kind: branchFunnel)), (offset: 8, wpdRes: (kind: singleImpl, singleImplName: "_ZN1A1nEi")), (offset: 16, wpdRes: (kind: indir, resByArg: (args: (1, 2), byArg: (kind: indir, byte: 2, bit: 3), args: (3), byArg: (kind: uniformRetVal, info: 1), args: (4), byArg: (kind: uniqueRetVal, info: 1), args: (5), byArg: (kind: virtualConstProp))))))) ; Test the other kinds of type test resoultions -^27 = typeid: (name: "_ZTS1D", summary: (typeTestRes: (kind: byteArray, sizeM1BitWidth: 0))) -^28 = typeid: (name: "_ZTS1E", summary: (typeTestRes: (kind: unsat, sizeM1BitWidth: 0))) -^29 = flags: 8 -^30 = blockcount: 1888 +^31 = typeid: (name: "_ZTS1D", summary: (typeTestRes: (kind: byteArray, sizeM1BitWidth: 0))) +^32 = typeid: (name: "_ZTS1E", summary: (typeTestRes: (kind: unsat, sizeM1BitWidth: 0))) +^33 = flags: 8 +^34 = blockcount: 1888 ; Make sure we get back from llvm-dis essentially what we put in via llvm-as. ; CHECK: ^0 = module: (path: "thinlto-summary1.o", hash: (1369602428, 2747878711, 259090915, 2507395659, 1141468049)) @@ -86,18 +92,22 @@ ; CHECK: ^16 = gv: (guid: 15, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 1, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 1)))) ; CHECK: ^17 = gv: (guid: 16, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 1, noRecurse: 0, returnDoesNotAlias: 1, noInline: 0, alwaysInline: 0), calls: ((callee: ^15))))) ; CHECK: ^18 = gv: (guid: 17, summaries: (alias: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), aliasee: ^14))) -; CHECK: ^19 = gv: (guid: 18, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 4, typeIdInfo: (typeTests: (^24, ^26))))) -; CHECK: ^20 = gv: (guid: 19, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 8, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (^27, offset: 16)))))) -; CHECK: ^21 = gv: (guid: 20, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, typeIdInfo: (typeCheckedLoadVCalls: (vFuncId: (^25, offset: 16)))))) -; CHECK: ^22 = gv: (guid: 21, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 15, typeIdInfo: (typeTestAssumeConstVCalls: ((vFuncId: (^27, offset: 16), args: (42)), (vFuncId: (^27, offset: 24))))))) -; CHECK: ^23 = gv: (guid: 22, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, typeIdInfo: (typeCheckedLoadConstVCalls: ((vFuncId: (^28, offset: 16), args: (42))))))) -; CHECK: ^24 = typeid: (name: "_ZTS1C", summary: (typeTestRes: (kind: single, sizeM1BitWidth: 0))) ; guid = 1884921850105019584 -; CHECK: ^25 = typeid: (name: "_ZTS1B", summary: (typeTestRes: (kind: inline, sizeM1BitWidth: 0, alignLog2: 1, sizeM1: 2, bitMask: 3, inlineBits: 4))) ; guid = 6203814149063363976 -; CHECK: ^26 = typeid: (name: "_ZTS1A", summary: (typeTestRes: (kind: allOnes, sizeM1BitWidth: 7), wpdResolutions: ((offset: 0, wpdRes: (kind: branchFunnel)), (offset: 8, wpdRes: (kind: singleImpl, singleImplName: "_ZN1A1nEi")), (offset: 16, wpdRes: (kind: indir, resByArg: (args: (1, 2), byArg: (kind: indir, byte: 2, bit: 3), args: (3), byArg: (kind: uniformRetVal, info: 1), args: (4), byArg: (kind: uniqueRetVal, info: 1), args: (5), byArg: (kind: virtualConstProp))))))) ; guid = 7004155349499253778 -; CHECK: ^27 = typeid: (name: "_ZTS1D", summary: (typeTestRes: (kind: byteArray, sizeM1BitWidth: 0))) ; guid = 9614786172484273522 -; CHECK: ^28 = typeid: (name: "_ZTS1E", summary: (typeTestRes: (kind: unsat, sizeM1BitWidth: 0))) ; guid = 17437243864166745132 -; CHECK: ^29 = flags: 8 -; CHECK: ^30 = blockcount: 1888 +; CHECK: ^19 = gv: (guid: 18, summaries: (ifunc: (module: ^0, flags: (linkage: private, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), resolver: ^20))) +; CHECK: ^20 = gv: (guid: 19, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 1, refs: (^21)))) +; CHECK: ^21 = gv: (guid: 20, summaries: (function: (module: ^0, flags: (linkage: internal, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 1))) +; CHECK: ^22 = gv: (guid: 21, summaries: (ifunc: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), resolver: ^20))) +; CHECK: ^23 = gv: (guid: 22, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 4, typeIdInfo: (typeTests: (^28, ^30))))) +; CHECK: ^24 = gv: (guid: 23, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 8, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (^31, offset: 16)))))) +; CHECK: ^25 = gv: (guid: 24, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, typeIdInfo: (typeCheckedLoadVCalls: (vFuncId: (^29, offset: 16)))))) +; CHECK: ^26 = gv: (guid: 25, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 15, typeIdInfo: (typeTestAssumeConstVCalls: ((vFuncId: (^31, offset: 16), args: (42)), (vFuncId: (^31, offset: 24))))))) +; CHECK: ^27 = gv: (guid: 26, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, typeIdInfo: (typeCheckedLoadConstVCalls: ((vFuncId: (^32, offset: 16), args: (42))))))) +; CHECK: ^28 = typeid: (name: "_ZTS1C", summary: (typeTestRes: (kind: single, sizeM1BitWidth: 0))) ; guid = 1884921850105019584 +; CHECK: ^29 = typeid: (name: "_ZTS1B", summary: (typeTestRes: (kind: inline, sizeM1BitWidth: 0, alignLog2: 1, sizeM1: 2, bitMask: 3, inlineBits: 4))) ; guid = 6203814149063363976 +; CHECK: ^30 = typeid: (name: "_ZTS1A", summary: (typeTestRes: (kind: allOnes, sizeM1BitWidth: 7), wpdResolutions: ((offset: 0, wpdRes: (kind: branchFunnel)), (offset: 8, wpdRes: (kind: singleImpl, singleImplName: "_ZN1A1nEi")), (offset: 16, wpdRes: (kind: indir, resByArg: (args: (1, 2), byArg: (kind: indir, byte: 2, bit: 3), args: (3), byArg: (kind: uniformRetVal, info: 1), args: (4), byArg: (kind: uniqueRetVal, info: 1), args: (5), byArg: (kind: virtualConstProp))))))) ; guid = 7004155349499253778 +; CHECK: ^31 = typeid: (name: "_ZTS1D", summary: (typeTestRes: (kind: byteArray, sizeM1BitWidth: 0))) ; guid = 9614786172484273522 +; CHECK: ^32 = typeid: (name: "_ZTS1E", summary: (typeTestRes: (kind: unsat, sizeM1BitWidth: 0))) ; guid = 17437243864166745132 +; CHECK: ^33 = flags: 8 +; CHECK: ^34 = blockcount: 1888 ; Make sure parsing of a non-summary entry containing a ":" does not fail ; after summary parsing, which handles colons differently. Index: llvm/test/Bitcode/summary_version.ll =================================================================== --- llvm/test/Bitcode/summary_version.ll +++ llvm/test/Bitcode/summary_version.ll @@ -2,7 +2,7 @@ ; RUN: opt -module-summary %s -o - | llvm-bcanalyzer -dump | FileCheck %s ; CHECK: +; CHECK: Index: llvm/test/Bitcode/thinlto-ifunc.ll =================================================================== --- /dev/null +++ llvm/test/Bitcode/thinlto-ifunc.ll @@ -0,0 +1,38 @@ +; Test to check the callgraph in summary +; RUN: opt -module-summary %s -o %t.o +; RUN: llvm-bcanalyzer -dump %t.o | FileCheck %s +; RUN: llvm-dis -o - %t.o | FileCheck %s --check-prefix=DIS + +@ifunc = dso_local ifunc i32 (), bitcast (i32 ()* ()* @resolver to i32 ()*) + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 ()* @resolver() #0 { + ret i32 ()* @called +} + +; Function Attrs: noinline nounwind optnone uwtable +define internal i32 @called() #0 { + ret i32 1 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { + %1 = call i32 @ifunc() + ret i32 0 +} + +; CHECK: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-NEXT: + +; DIS: ^0 = module: (path: "{{.*}}", hash: (0, 0, 0, 0, 0)) +; DIS: ^1 = gv: (name: "ifunc", summaries: (ifunc: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), resolver: ^4))) ; guid = 1234216394087659437 +; DIS: ^2 = gv: (name: "called", summaries: (function: (module: ^0, flags: (linkage: internal, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 1))) ; guid = 14789455179670652464 +; DIS: ^3 = gv: (name: "main", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 2, calls: ((callee: ^1))))) ; guid = 15822663052811949562 +; DIS: ^4 = gv: (name: "resolver", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 1, refs: (^2)))) ; guid = 18291748799076262136 Index: llvm/test/ThinLTO/X86/Inputs/ifunc_import.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/Inputs/ifunc_import.ll @@ -0,0 +1,65 @@ +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" + +define dso_local void @called() { + ret void +} + +@globalIfunc = ifunc void (), bitcast (void ()* ()* @globalResolver to void ()*) +@globalWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @globalResolver to void ()*) +@globalLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @globalResolver to void ()*) +@globalWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @globalResolver to void ()*) +@globalLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @globalResolver to void ()*) +define hidden void ()* @globalResolver() { +entry: + ret void ()* @called +} + +@internalIfunc = ifunc void (), bitcast (void ()* ()* @internalResolver to void ()*) +@internalWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @internalResolver to void ()*) +@internalLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @internalResolver to void ()*) +@internalWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @internalResolver to void ()*) +@internalLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @internalResolver to void ()*) +define internal void ()* @internalResolver() { +entry: + ret void ()* @called +} + +@linkonceODRIfunc = ifunc void (), bitcast (void ()* ()* @linkonceODRResolver to void ()*) +@linkonceODRWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @linkonceODRResolver to void ()*) +@linkonceODRLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @linkonceODRResolver to void ()*) +@linkonceODRWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @linkonceODRResolver to void ()*) +@linkonceODRLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @linkonceODRResolver to void ()*) +define linkonce_odr void ()* @linkonceODRResolver() { +entry: + ret void ()* @called +} + +@weakODRIfunc = ifunc void (), bitcast (void ()* ()* @weakODRResolver to void ()*) +@weakODRWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @weakODRResolver to void ()*) +@weakODRLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @weakODRResolver to void ()*) +@weakODRWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @weakODRResolver to void ()*) +@weakODRLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @weakODRResolver to void ()*) +define weak_odr void ()* @weakODRResolver() { +entry: + ret void ()* @called +} + +@linkonceIfunc = ifunc void (), bitcast (void ()* ()* @linkonceResolver to void ()*) +@linkonceWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @linkonceResolver to void ()*) +@linkonceLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @linkonceResolver to void ()*) +@linkonceWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @linkonceResolver to void ()*) +@linkonceLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @linkonceResolver to void ()*) +define linkonce void ()* @linkonceResolver() { +entry: + ret void ()* @called +} + +@weakIfunc = ifunc void (), bitcast (void ()* ()* @weakResolver to void ()*) +@weakWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @weakResolver to void ()*) +@weakLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @weakResolver to void ()*) +@weakWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @weakResolver to void ()*) +@weakLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @weakResolver to void ()*) +define weak void ()* @weakResolver() { +entry: + ret void ()* @called +} Index: llvm/test/ThinLTO/X86/ifunc_import.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/ifunc_import.ll @@ -0,0 +1,154 @@ +; RUN: opt -module-summary %s -o %t1.bc +; RUN: opt -module-summary %p/Inputs/ifunc_import.ll -o %t2.bc +; RUN: llvm-lto -thinlto-action=thinlink -o %t.index.bc %t1.bc %t2.bc +; RUN: llvm-lto -thinlto-action=promote -thinlto-index %t.index.bc %t2.bc -o - | llvm-dis -o - | FileCheck -allow-deprecated-dag-overlap %s --check-prefix=PROMOTE +; RUN: llvm-lto -thinlto-action=import -thinlto-index %t.index.bc %t1.bc -o - | llvm-dis -o - | FileCheck -allow-deprecated-dag-overlap %s --check-prefix=IMPORT + +; PROMOTE-DAG: @globalIfunc = ifunc void (), bitcast (void ()* ()* @globalResolver to void ()*) +; PROMOTE-DAG: @globalWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @globalResolver to void ()*) +; PROMOTE-DAG: @globalLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @globalResolver to void ()*) +; PROMOTE-DAG: @globalWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @globalResolver to void ()*) +; PROMOTE-DAG: @globalLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @globalResolver to void ()*) +; PROMOTE-DAG: @internalIfunc = ifunc void (), bitcast (void ()* ()* @internalResolver to void ()*) +; PROMOTE-DAG: @internalWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @internalResolver to void ()*) +; PROMOTE-DAG: @internalLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @internalResolver to void ()*) +; PROMOTE-DAG: @internalWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @internalResolver to void ()*) +; PROMOTE-DAG: @internalLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @internalResolver to void ()*) +; PROMOTE-DAG: @linkonceODRIfunc = ifunc void (), bitcast (void ()* ()* @linkonceODRResolver to void ()*) +; PROMOTE-DAG: @linkonceODRWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @linkonceODRResolver to void ()*) +; PROMOTE-DAG: @linkonceODRLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @linkonceODRResolver to void ()*) +; PROMOTE-DAG: @linkonceODRWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @linkonceODRResolver to void ()*) +; PROMOTE-DAG: @linkonceODRLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @linkonceODRResolver to void ()*) +; PROMOTE-DAG: @weakODRIfunc = ifunc void (), bitcast (void ()* ()* @weakODRResolver to void ()*) +; PROMOTE-DAG: @weakODRWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @weakODRResolver to void ()*) +; PROMOTE-DAG: @weakODRLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @weakODRResolver to void ()*) +; PROMOTE-DAG: @weakODRWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @weakODRResolver to void ()*) +; PROMOTE-DAG: @weakODRLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @weakODRResolver to void ()*) +; PROMOTE-DAG: @linkonceIfunc = ifunc void (), bitcast (void ()* ()* @linkonceResolver to void ()*) +; PROMOTE-DAG: @linkonceWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @linkonceResolver to void ()*) +; PROMOTE-DAG: @linkonceLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @linkonceResolver to void ()*) +; PROMOTE-DAG: @linkonceWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @linkonceResolver to void ()*) +; PROMOTE-DAG: @linkonceLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @linkonceResolver to void ()*) +; PROMOTE-DAG: @weakIfunc = ifunc void (), bitcast (void ()* ()* @weakResolver to void ()*) +; PROMOTE-DAG: @weakWeakIfunc = weak ifunc void (), bitcast (void ()* ()* @weakResolver to void ()*) +; PROMOTE-DAG: @weakLinkonceIfunc = linkonce ifunc void (), bitcast (void ()* ()* @weakResolver to void ()*) +; PROMOTE-DAG: @weakWeakODRIfunc = weak_odr ifunc void (), bitcast (void ()* ()* @weakResolver to void ()*) +; PROMOTE-DAG: @weakLinkonceODRIfunc = linkonce_odr ifunc void (), bitcast (void ()* ()* @weakResolver to void ()*) + +; PROMOTE-DAG: define hidden void ()* @globalResolver() +; PROMOTE-DAG: define internal void ()* @internalResolver() +; PROMOTE-DAG: define weak_odr void ()* @linkonceODRResolver() +; PROMOTE-DAG: define weak_odr void ()* @weakODRResolver() +; PROMOTE-DAG: define weak void ()* @linkonceResolver() +; PROMOTE-DAG: define weak void ()* @weakResolver() + +; IMPORT-DAG: declare void @globalIfunc() +; IMPORT-DAG: declare void @globalWeakIfunc() +; IMPORT-DAG: declare void @globalLinkonceIfunc() +; IMPORT-DAG: declare void @globalWeakODRIfunc() +; IMPORT-DAG: declare void @globalLinkonceODRIfunc() +; IMPORT-DAG: declare void @internalIfunc() +; IMPORT-DAG: declare void @internalWeakIfunc() +; IMPORT-DAG: declare void @internalLinkonceIfunc() +; IMPORT-DAG: declare void @internalWeakODRIfunc() +; IMPORT-DAG: declare void @internalLinkonceODRIfunc() +; IMPORT-DAG: declare void @linkonceODRIfunc() +; IMPORT-DAG: declare void @linkonceODRWeakIfunc() +; IMPORT-DAG: declare void @linkonceODRLinkonceIfunc() +; IMPORT-DAG: declare void @linkonceODRWeakODRIfunc() +; IMPORT-DAG: declare void @linkonceODRLinkonceODRIfunc() +; IMPORT-DAG: declare void @weakODRIfunc() +; IMPORT-DAG: declare void @weakODRWeakIfunc() +; IMPORT-DAG: declare void @weakODRLinkonceIfunc() +; IMPORT-DAG: declare void @weakODRWeakODRIfunc() +; IMPORT-DAG: declare void @weakODRLinkonceODRIfunc() +; IMPORT-DAG: declare void @linkonceIfunc() +; IMPORT-DAG: declare void @linkonceWeakIfunc() +; IMPORT-DAG: declare void @linkonceLinkonceIfunc() +; IMPORT-DAG: declare void @linkonceWeakODRIfunc() +; IMPORT-DAG: declare void @linkonceLinkonceODRIfunc() +; IMPORT-DAG: declare void @weakIfunc() +; IMPORT-DAG: declare void @weakWeakIfunc() +; IMPORT-DAG: declare void @weakLinkonceIfunc() +; IMPORT-DAG: declare void @weakWeakODRIfunc() +; IMPORT-DAG: declare void @weakLinkonceODRIfunc() + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" + +define i32 @main() { +entry: + call void @globalIfunc() + call void @globalWeakIfunc() + call void @globalLinkonceIfunc() + call void @globalWeakODRIfunc() + call void @globalLinkonceODRIfunc() + + call void @internalIfunc() + call void @internalWeakIfunc() + call void @internalLinkonceIfunc() + call void @internalWeakODRIfunc() + call void @internalLinkonceODRIfunc() + + call void @linkonceODRIfunc() + call void @linkonceODRWeakIfunc() + call void @linkonceODRLinkonceIfunc() + call void @linkonceODRWeakODRIfunc() + call void @linkonceODRLinkonceODRIfunc() + + call void @weakODRIfunc() + call void @weakODRWeakIfunc() + call void @weakODRLinkonceIfunc() + call void @weakODRWeakODRIfunc() + call void @weakODRLinkonceODRIfunc() + + call void @linkonceIfunc() + call void @linkonceWeakIfunc() + call void @linkonceLinkonceIfunc() + call void @linkonceWeakODRIfunc() + call void @linkonceLinkonceODRIfunc() + + call void @weakIfunc() + call void @weakWeakIfunc() + call void @weakLinkonceIfunc() + call void @weakWeakODRIfunc() + call void @weakLinkonceODRIfunc() + + ret i32 0 +} + + +declare void @globalIfunc() +declare void @globalWeakIfunc() +declare void @globalLinkonceIfunc() +declare void @globalWeakODRIfunc() +declare void @globalLinkonceODRIfunc() + +declare void @internalIfunc() +declare void @internalWeakIfunc() +declare void @internalLinkonceIfunc() +declare void @internalWeakODRIfunc() +declare void @internalLinkonceODRIfunc() + +declare void @linkonceODRIfunc() +declare void @linkonceODRWeakIfunc() +declare void @linkonceODRLinkonceIfunc() +declare void @linkonceODRWeakODRIfunc() +declare void @linkonceODRLinkonceODRIfunc() + +declare void @weakODRIfunc() +declare void @weakODRWeakIfunc() +declare void @weakODRLinkonceIfunc() +declare void @weakODRWeakODRIfunc() +declare void @weakODRLinkonceODRIfunc() + +declare void @linkonceIfunc() +declare void @linkonceWeakIfunc() +declare void @linkonceLinkonceIfunc() +declare void @linkonceWeakODRIfunc() +declare void @linkonceLinkonceODRIfunc() + +declare void @weakIfunc() +declare void @weakWeakIfunc() +declare void @weakLinkonceIfunc() +declare void @weakWeakODRIfunc() +declare void @weakLinkonceODRIfunc()