forked from OSchip/llvm-project
[ThinLTO] Support aliased GlobalIFunc
Fixes https://github.com/llvm/llvm-project/issues/56290: when an ifunc is aliased in LTO, clang will attempt to create an alias summary; however, as ifunc is not included in the module summary, doing so will lead to crash. Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D129009
This commit is contained in:
parent
fe1678d1b2
commit
304027206c
|
@ -93,6 +93,12 @@ public:
|
|||
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<void(const GlobalValue &)> Op) const;
|
||||
};
|
||||
|
||||
template <>
|
||||
|
|
|
@ -646,15 +646,18 @@ static void computeVariableSummary(ModuleSummaryIndex &Index,
|
|||
Index.addGlobalValueSummary(V, std::move(GVarSummary));
|
||||
}
|
||||
|
||||
static void
|
||||
computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A,
|
||||
DenseSet<GlobalValue::GUID> &CantBePromoted) {
|
||||
static void computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A,
|
||||
DenseSet<GlobalValue::GUID> &CantBePromoted) {
|
||||
// Skip summary for indirect function aliases as summary for aliasee will not
|
||||
// be emitted.
|
||||
const GlobalObject *Aliasee = A.getAliaseeObject();
|
||||
if (isa<GlobalIFunc>(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<AliasSummary>(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 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex(
|
|||
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");
|
||||
|
|
|
@ -4104,8 +4104,9 @@ void ModuleBitcodeWriterBase::writePerModuleGlobalValueSummary() {
|
|||
|
||||
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<GlobalIFunc>(Aliasee))
|
||||
continue;
|
||||
auto AliasId = VE.getValueID(&A);
|
||||
auto AliaseeId = VE.getValueID(Aliasee);
|
||||
|
|
|
@ -316,32 +316,38 @@ bool GlobalObject::canIncreaseAlignment() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
template <typename Operation>
|
||||
static const GlobalObject *
|
||||
findBaseObject(const Constant *C, DenseSet<const GlobalAlias *> &Aliases) {
|
||||
if (auto *GO = dyn_cast<GlobalObject>(C))
|
||||
findBaseObject(const Constant *C, DenseSet<const GlobalAlias *> &Aliases,
|
||||
const Operation &Op) {
|
||||
if (auto *GO = dyn_cast<GlobalObject>(C)) {
|
||||
Op(*GO);
|
||||
return GO;
|
||||
if (auto *GA = dyn_cast<GlobalAlias>(C))
|
||||
}
|
||||
if (auto *GA = dyn_cast<GlobalAlias>(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<ConstantExpr>(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 @@ findBaseObject(const Constant *C, DenseSet<const GlobalAlias *> &Aliases) {
|
|||
|
||||
const GlobalObject *GlobalValue::getAliaseeObject() const {
|
||||
DenseSet<const GlobalAlias *> Aliases;
|
||||
return findBaseObject(this, Aliases);
|
||||
return findBaseObject(this, Aliases, [](const GlobalValue &) {});
|
||||
}
|
||||
|
||||
bool GlobalValue::isAbsoluteSymbolRef() const {
|
||||
|
@ -544,7 +550,7 @@ void GlobalAlias::setAliasee(Constant *Aliasee) {
|
|||
|
||||
const GlobalObject *GlobalAlias::getAliaseeObject() const {
|
||||
DenseSet<const GlobalAlias *> Aliases;
|
||||
return findBaseObject(getOperand(0), Aliases);
|
||||
return findBaseObject(getOperand(0), Aliases, [](const GlobalValue &) {});
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -577,5 +583,12 @@ void GlobalIFunc::eraseFromParent() {
|
|||
|
||||
const Function *GlobalIFunc::getResolverFunction() const {
|
||||
DenseSet<const GlobalAlias *> Aliases;
|
||||
return dyn_cast<Function>(findBaseObject(getResolver(), Aliases));
|
||||
return dyn_cast<Function>(
|
||||
findBaseObject(getResolver(), Aliases, [](const GlobalValue &) {}));
|
||||
}
|
||||
|
||||
void GlobalIFunc::applyAlongResolverPath(
|
||||
function_ref<void(const GlobalValue &)> Op) const {
|
||||
DenseSet<const GlobalAlias *> Aliases;
|
||||
findBaseObject(getResolver(), Aliases, Op);
|
||||
}
|
||||
|
|
|
@ -1147,6 +1147,14 @@ void llvm::thinLTOInternalizeModule(Module &TheModule,
|
|||
// 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<GlobalIFunc>(&GV) ||
|
||||
(isa<GlobalAlias>(&GV) &&
|
||||
isa<GlobalIFunc>(cast<GlobalAlias>(&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 @@ Expected<bool> FunctionImporter::importFunctions(
|
|||
}
|
||||
}
|
||||
for (GlobalAlias &GA : SrcModule->aliases()) {
|
||||
if (!GA.hasName())
|
||||
if (!GA.hasName() || isa<GlobalIFunc>(GA.getAliaseeObject()))
|
||||
continue;
|
||||
auto GUID = GA.getGUID();
|
||||
auto Import = ImportGUIDs.count(GUID);
|
||||
|
|
|
@ -35,6 +35,13 @@ bool FunctionImportGlobalProcessing::doImportAsDefinition(
|
|||
bool FunctionImportGlobalProcessing::shouldPromoteLocalToGlobal(
|
||||
const GlobalValue *SGV, ValueInfo VI) {
|
||||
assert(SGV->hasLocalLinkage());
|
||||
|
||||
// Ifuncs and ifunc alias does not have summary.
|
||||
if (isa<GlobalIFunc>(SGV) ||
|
||||
(isa<GlobalAlias>(SGV) &&
|
||||
isa<GlobalIFunc>(cast<GlobalAlias>(SGV)->getAliaseeObject())))
|
||||
return false;
|
||||
|
||||
// Both the imported references and the original local variable must
|
||||
// be promoted.
|
||||
if (!isPerformingImport() && !isModuleExporting())
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue