diff options
author | Fangrui Song <i@maskray.me> | 2023-08-29 12:26:40 -0700 |
---|---|---|
committer | Fangrui Song <i@maskray.me> | 2023-08-29 12:26:40 -0700 |
commit | 6276927bf3f6ce4a9ef0b9941b2c6450ae4cd1eb (patch) | |
tree | 0412e5fad4769ad12aedfa8d7eb30dba40890b8f /llvm/lib/Analysis/ModuleSummaryAnalysis.cpp | |
parent | 39191c45771564b8a0930d71b4c229147cf839db (diff) | |
download | llvm-6276927bf3f6ce4a9ef0b9941b2c6450ae4cd1eb.zip llvm-6276927bf3f6ce4a9ef0b9941b2c6450ae4cd1eb.tar.gz llvm-6276927bf3f6ce4a9ef0b9941b2c6450ae4cd1eb.tar.bz2 |
[ThinLTO] Mark callers of local ifunc not eligible for import
Fix https://github.com/llvm/llvm-project/issues/58740
The `target_clones` attribute results in ifunc on eligible targets
(Linux glibc/Android or FreeBSD). If the function has internal linkage,
we will get an internal linkage ifunc.
```
__attribute__((target_clones("popcnt", "default")))
static int foo(int n) { return __builtin_popcount(n); }
int use(int n) { return foo(n); }
@foo.ifunc = internal ifunc i32 (i32), ptr @foo.resolver
define internal nonnull ptr @foo.resolver() comdat {
; local linkage comdat is another issue that should be fixed
...
select i1 %.not, ptr @foo.default.1, ptr @foo.popcnt.0
...
}
define internal i32 @foo.default.1(i32 noundef %n)
```
ifuncs are not included in module summaries, so LTO doesn't know the
local linkage `foo.default.1` referenced by `foo.resolver`
should be promoted. If a caller of `foo` (e.g. `use`) is imported,
the local linkage `foo.resolver` will be cloned as a definition
(IRLinker::shouldLink), leading to linker errors.
```
ld.lld: error: undefined hidden symbol: foo.default.1.llvm.8017227050314953235
>>> referenced by bar.c
>>> lto.tmp:(foo.ifunc)
```
As a simple fix, just mark `use` as not eligible for import. Non-local
linkage ifuncs do not have the problem, because they are not imported,
and not cloned when a caller is imported.
---
https://reviews.llvm.org/D82745 contains a more involved fix, though the
original bug it intended to fix
(https://github.com/llvm/llvm-project/issues/45833) now works.
Note: importing ifunc is tricky.
If we import an ifunc, we need to make sure the resolver and the
implementation are in the translation unit, as required by
https://sourceware.org/glibc/wiki/GNU_IFUNC
> Requirement (a): Resolver must be defined in the same translation unit as the implementations.
This is infeasible if the implementation is changed to
available_externally.
In addition, the imported ifunc may be referenced by two translation
units. This doesn't work with PowerPC32 -msecure-plt
(https://maskray.me/blog/2021-01-18-gnu-indirect-function).
At the very least, every referencing translation unit needs one extra
IRELATIVE dynamic relocation.
At least for the local linkage ifunc case, it doesn't have much use
outside of `target_clones`, as a global pointer is usually a better
replacement.
I think ifuncs just have too many pitfalls to design more IR features
around it to optimize them.
Reviewed By: tejohnson
Differential Revision: https://reviews.llvm.org/D158961
Diffstat (limited to 'llvm/lib/Analysis/ModuleSummaryAnalysis.cpp')
-rw-r--r-- | llvm/lib/Analysis/ModuleSummaryAnalysis.cpp | 13 |
1 files changed, 12 insertions, 1 deletions
diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp index 775bb95..a88622e 100644 --- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -305,6 +305,7 @@ static void computeFunctionSummary( bool HasInlineAsmMaybeReferencingInternal = false; bool HasIndirBranchToBlockAddress = false; + bool HasIFuncCall = false; bool HasUnknownCall = false; bool MayThrow = false; for (const BasicBlock &BB : F) { @@ -417,6 +418,16 @@ static void computeFunctionSummary( } } else { HasUnknownCall = true; + // If F is imported, a local linkage ifunc (e.g. target_clones on a + // static function) called by F will be cloned. Since summaries don't + // track ifunc, we do not know implementation functions referenced by + // the ifunc resolver need to be promoted in the exporter, and we will + // get linker errors due to cloned declarations for implementation + // functions. As a simple fix, just mark F as not eligible for import. + // Non-local ifunc is not cloned and does not have the issue. + if (auto *GI = dyn_cast_if_present<GlobalIFunc>(CalledValue)) + if (GI->hasLocalLinkage()) + HasIFuncCall = true; // Skip inline assembly calls. if (CI && CI->isInlineAsm()) continue; @@ -599,7 +610,7 @@ static void computeFunctionSummary( bool NonRenamableLocal = isNonRenamableLocal(F); bool NotEligibleForImport = NonRenamableLocal || HasInlineAsmMaybeReferencingInternal || - HasIndirBranchToBlockAddress; + HasIndirBranchToBlockAddress || HasIFuncCall; GlobalValueSummary::GVFlags Flags( F.getLinkage(), F.getVisibility(), NotEligibleForImport, /* Live = */ false, F.isDSOLocal(), F.canBeOmittedFromSymbolTable()); |