diff --git a/llvm/include/llvm/IR/GlobalIFunc.h b/llvm/include/llvm/IR/GlobalIFunc.h --- a/llvm/include/llvm/IR/GlobalIFunc.h +++ b/llvm/include/llvm/IR/GlobalIFunc.h @@ -93,6 +93,12 @@ static bool classof(const Value *V) { return V->getValueID() == Value::GlobalIFuncVal; } + + // Apply specific operation to all resolver-related values. If resolver target + // is already a global object, then apply the operation to it directly. If + // target is a GlobalExpr or a GlobalAlias, evaluate it to its base object and + // apply the operation for the base object and all aliases along the path. + void applyAlongResolverPath(function_ref Op) const; }; template <> diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp --- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -646,15 +646,18 @@ Index.addGlobalValueSummary(V, std::move(GVarSummary)); } -static void -computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A, - DenseSet &CantBePromoted) { +static void computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A, + DenseSet &CantBePromoted) { + // Skip summary for indirect function aliases as summary for aliasee will not + // be emitted. + const GlobalObject *Aliasee = A.getAliaseeObject(); + if (isa(Aliasee)) + return; bool NonRenamableLocal = isNonRenamableLocal(A); GlobalValueSummary::GVFlags Flags( A.getLinkage(), A.getVisibility(), NonRenamableLocal, /* Live = */ false, A.isDSOLocal(), A.canBeOmittedFromSymbolTable()); auto AS = std::make_unique(Flags); - auto *Aliasee = A.getAliaseeObject(); auto AliaseeVI = Index.getValueInfo(Aliasee->getGUID()); assert(AliaseeVI && "Alias expects aliasee summary to be available"); assert(AliaseeVI.getSummaryList().size() == 1 && @@ -811,6 +814,13 @@ for (const GlobalAlias &A : M.aliases()) computeAliasSummary(Index, A, CantBePromoted); + // Iterate through ifuncs, set their resolvers all alive. + for (const GlobalIFunc &I : M.ifuncs()) { + I.applyAlongResolverPath([&Index](const GlobalValue &GV) { + Index.getGlobalValueSummary(GV)->setLive(true); + }); + } + for (auto *V : LocalsUsed) { auto *Summary = Index.getGlobalValueSummary(*V); assert(Summary && "Missing summary for global value"); diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -4104,8 +4104,9 @@ for (const GlobalAlias &A : M.aliases()) { auto *Aliasee = A.getAliaseeObject(); - if (!Aliasee->hasName()) - // Nameless function don't have an entry in the summary, skip it. + // Skip ifunc and nameless functions which don't have an entry in the + // summary. + if (!Aliasee->hasName() || isa(Aliasee)) continue; auto AliasId = VE.getValueID(&A); auto AliaseeId = VE.getValueID(Aliasee); diff --git a/llvm/lib/IR/Globals.cpp b/llvm/lib/IR/Globals.cpp --- a/llvm/lib/IR/Globals.cpp +++ b/llvm/lib/IR/Globals.cpp @@ -316,32 +316,38 @@ return true; } +template static const GlobalObject * -findBaseObject(const Constant *C, DenseSet &Aliases) { - if (auto *GO = dyn_cast(C)) +findBaseObject(const Constant *C, DenseSet &Aliases, + const Operation &Op) { + if (auto *GO = dyn_cast(C)) { + Op(*GO); return GO; - if (auto *GA = dyn_cast(C)) + } + if (auto *GA = dyn_cast(C)) { + Op(*GA); if (Aliases.insert(GA).second) - return findBaseObject(GA->getOperand(0), Aliases); + return findBaseObject(GA->getOperand(0), Aliases, Op); + } if (auto *CE = dyn_cast(C)) { switch (CE->getOpcode()) { case Instruction::Add: { - auto *LHS = findBaseObject(CE->getOperand(0), Aliases); - auto *RHS = findBaseObject(CE->getOperand(1), Aliases); + auto *LHS = findBaseObject(CE->getOperand(0), Aliases, Op); + auto *RHS = findBaseObject(CE->getOperand(1), Aliases, Op); if (LHS && RHS) return nullptr; return LHS ? LHS : RHS; } case Instruction::Sub: { - if (findBaseObject(CE->getOperand(1), Aliases)) + if (findBaseObject(CE->getOperand(1), Aliases, Op)) return nullptr; - return findBaseObject(CE->getOperand(0), Aliases); + return findBaseObject(CE->getOperand(0), Aliases, Op); } case Instruction::IntToPtr: case Instruction::PtrToInt: case Instruction::BitCast: case Instruction::GetElementPtr: - return findBaseObject(CE->getOperand(0), Aliases); + return findBaseObject(CE->getOperand(0), Aliases, Op); default: break; } @@ -351,7 +357,7 @@ const GlobalObject *GlobalValue::getAliaseeObject() const { DenseSet Aliases; - return findBaseObject(this, Aliases); + return findBaseObject(this, Aliases, [](const GlobalValue &) {}); } bool GlobalValue::isAbsoluteSymbolRef() const { @@ -544,7 +550,7 @@ const GlobalObject *GlobalAlias::getAliaseeObject() const { DenseSet Aliases; - return findBaseObject(getOperand(0), Aliases); + return findBaseObject(getOperand(0), Aliases, [](const GlobalValue &) {}); } //===----------------------------------------------------------------------===// @@ -577,5 +583,12 @@ const Function *GlobalIFunc::getResolverFunction() const { DenseSet Aliases; - return dyn_cast(findBaseObject(getResolver(), Aliases)); + return dyn_cast( + findBaseObject(getResolver(), Aliases, [](const GlobalValue &) {})); +} + +void GlobalIFunc::applyAlongResolverPath( + function_ref Op) const { + DenseSet Aliases; + findBaseObject(getResolver(), Aliases, Op); } diff --git a/llvm/lib/Transforms/IPO/FunctionImport.cpp b/llvm/lib/Transforms/IPO/FunctionImport.cpp --- a/llvm/lib/Transforms/IPO/FunctionImport.cpp +++ b/llvm/lib/Transforms/IPO/FunctionImport.cpp @@ -1147,6 +1147,14 @@ // Declare a callback for the internalize pass that will ask for every // candidate GlobalValue if it can be internalized or not. auto MustPreserveGV = [&](const GlobalValue &GV) -> bool { + // It may be the case that GV is on a chain of an ifunc, its alias and + // subsequent aliases. In this case, the summary for the value is not + // available. + if (isa(&GV) || + (isa(&GV) && + isa(cast(&GV)->getAliaseeObject()))) + return true; + // Lookup the linkage recorded in the summaries during global analysis. auto GS = DefinedGlobals.find(GV.getGUID()); if (GS == DefinedGlobals.end()) { @@ -1277,7 +1285,7 @@ } } for (GlobalAlias &GA : SrcModule->aliases()) { - if (!GA.hasName()) + if (!GA.hasName() || isa(GA.getAliaseeObject())) continue; auto GUID = GA.getGUID(); auto Import = ImportGUIDs.count(GUID); diff --git a/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp b/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp --- a/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp +++ b/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp @@ -35,6 +35,13 @@ bool FunctionImportGlobalProcessing::shouldPromoteLocalToGlobal( const GlobalValue *SGV, ValueInfo VI) { assert(SGV->hasLocalLinkage()); + + // Ifuncs and ifunc alias does not have summary. + if (isa(SGV) || + (isa(SGV) && + isa(cast(SGV)->getAliaseeObject()))) + return false; + // Both the imported references and the original local variable must // be promoted. if (!isPerformingImport() && !isModuleExporting()) diff --git a/llvm/test/ThinLTO/X86/alias-ifunc.ll b/llvm/test/ThinLTO/X86/alias-ifunc.ll new file mode 100644 --- /dev/null +++ b/llvm/test/ThinLTO/X86/alias-ifunc.ll @@ -0,0 +1,57 @@ +; RUN: opt -module-summary -o %t.bc %s +; RUN: llvm-dis < %t.bc | FileCheck %s --check-prefix=CHECK-BAR +; RUN: llvm-dis < %t.bc | FileCheck %s --check-prefix=CHECK-BAZ +; RUN: llvm-dis < %t.bc | FileCheck %s --check-prefix=CHECK-QUX +; RUN: llvm-dis < %t.bc | FileCheck %s --check-prefix=CHECK-RESOLVER +; RUN: llvm-dis < %t.bc | FileCheck %s --check-prefix=CHECK-QUUX +; RUN: llvm-dis < %t.bc | FileCheck %s --check-prefix=CHECK-CORGE +; RUN: llvm-dis < %t.bc | FileCheck %s --check-prefix=CHECK-GRAULT +; RUN: llvm-lto2 run %t.bc -r %t.bc,foo,px -r %t.bc,bar,px -r %t.bc,baz,px -r %t.bc,qux,px -r %t.bc,grault,px -o %t2 +; RUN: llvm-nm %t2.1 | FileCheck %s --check-prefix=CHECK-SYMBOL + +; CHECK-SYMBOL: i bar +; CHECK-SYMBOL: i baz +; CHECK-SYMBOL: i foo +; CHECK-SYMBOL: t foo_resolver +; CHECK-SYMBOL: i grault +; CHECK-SYMBOL: i quuz +; CHECK-SYMBOL: i qux + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" +@foo = ifunc i32 (i32), ptr @foo_resolver +; CHECK-RESOLVER: (name: "foo_resolver" +; CHECK-RESOLVER-SAME: live: 1 +define internal i32 (i32)* @foo_resolver() { +entry: + ret i32 (i32)* null +} +; CHECK-BAR: (name: "bar" +; CHECK-BAR-NOT: summaries: ( +; CHECK-BAR-SAME: ; guid = {{[0-9]+}} +@bar = alias i32 (i32), ptr @foo + +; CHECK-BAZ: (name: "baz" +; CHECK-BAZ-NOT: summaries: ( +; CHECK-BAZ-SAME: ; guid = {{[0-9]+}} +@baz = weak alias i32 (i32), ptr @foo + +; CHECK-QUX: (name: "qux" +; CHECK-QUX-NOT: summaries: ( +; CHECK-QUX-SAME: ; guid = {{[0-9]+}} +@qux = alias i32 (i32), ptr @bar + +; CHECK-QUUX: (name: "quux" +; CHECK-QUUX-SAME: live: 1 +@quux = internal alias i32 (i32)* (), ptr @foo_resolver +@quuz = internal ifunc i32 (i32), ptr @quux + +; CHECK-CORGE: (name: "corge" +; CHECK-CORGE-NOT: summaries: ( +; CHECK-CORGE-SAME: ; guid = {{[0-9]+}} +@corge = internal alias i32 (i32), ptr @quuz + +; CHECK-GRAULT: (name: "grault" +; CHECK-GRAULT-NOT: summaries: ( +; CHECK-GRAULT-SAME: ; guid = {{[0-9]+}} +@grault = alias i32 (i32), ptr @corge