-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[Linker] Do not keep a private member of a non-prevailing comdat group #69143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Linker] Do not keep a private member of a non-prevailing comdat group #69143
Conversation
@llvm/pr-subscribers-pgo @llvm/pr-subscribers-lto Author: Igor Kudrin (igorkudrin) Changes
Motivation example:
Full diff: https://github.com/llvm/llvm-project/pull/69143.diff 2 Files Affected:
diff --git a/llvm/lib/Linker/LinkModules.cpp b/llvm/lib/Linker/LinkModules.cpp
index 2f5fac4951f29e0..4fe1f1a0f51833f 100644
--- a/llvm/lib/Linker/LinkModules.cpp
+++ b/llvm/lib/Linker/LinkModules.cpp
@@ -462,6 +462,7 @@ void ModuleLinker::dropReplacedComdat(
bool ModuleLinker::run() {
Module &DstM = Mover.getModule();
DenseSet<const Comdat *> ReplacedDstComdats;
+ DenseSet<const Comdat *> NonPrevailingComdats;
for (const auto &SMEC : SrcM->getComdatSymbolTable()) {
const Comdat &C = SMEC.getValue();
@@ -473,6 +474,9 @@ bool ModuleLinker::run() {
return true;
ComdatsChosen[&C] = std::make_pair(SK, From);
+ if (From == LinkFrom::Dst)
+ NonPrevailingComdats.insert(&C);
+
if (From != LinkFrom::Src)
continue;
@@ -497,6 +501,23 @@ bool ModuleLinker::run() {
for (Function &GV : llvm::make_early_inc_range(DstM))
dropReplacedComdat(GV, ReplacedDstComdats);
+ if (!NonPrevailingComdats.empty()) {
+ DenseSet<GlobalObject *> AliasedGlobals;
+ for (auto &GA : SrcM->aliases())
+ if (GlobalObject *GO = GA.getAliaseeObject(); GO && GO->getComdat())
+ AliasedGlobals.insert(GO);
+ for (const Comdat *C : NonPrevailingComdats) {
+ SmallVector<GlobalObject *> ToUpdate;
+ for (GlobalObject *GO : C->getUsers())
+ if (GO->hasPrivateLinkage() && !AliasedGlobals.contains(GO))
+ ToUpdate.push_back(GO);
+ for (GlobalObject *GO : ToUpdate) {
+ GO->setLinkage(GlobalValue::AvailableExternallyLinkage);
+ GO->setComdat(nullptr);
+ }
+ }
+ }
+
for (GlobalVariable &GV : SrcM->globals())
if (GV.hasLinkOnceLinkage())
if (const Comdat *SC = GV.getComdat())
diff --git a/llvm/test/Linker/comdat-nonprevailing-decl.ll b/llvm/test/Linker/comdat-nonprevailing-decl.ll
index d3dfec8fb2bc728..30dd0e0abf8c56c 100644
--- a/llvm/test/Linker/comdat-nonprevailing-decl.ll
+++ b/llvm/test/Linker/comdat-nonprevailing-decl.ll
@@ -1,5 +1,6 @@
; RUN: rm -rf %t && split-file %s %t
; RUN: llvm-link -S %t/1.ll %t/1-aux.ll -o - | FileCheck %s
+; RUN: llvm-link -S %t/2.ll %t/2-aux.ll -o - | FileCheck %s --check-prefix=CHECK2
;--- 1.ll
$c = comdat any
@@ -23,3 +24,31 @@ define ptr @f3() {
ret ptr @v3
}
+;--- 2.ll
+;; Check that a private global variable from a non-prevailing comdat group is
+;; converted into 'available_externally' and excluded from the comdat group.
+
+; CHECK2: $__profc_foo = comdat any
+; CHECK2: @llvm.compiler.used = appending global [2 x ptr] [ptr @__profd_foo.[[SUFFIX:[0-9]+]], ptr @__profd_foo]
+; CHECK2: @__profd_foo.[[SUFFIX]] = private global ptr @__profc_foo, comdat($__profc_foo)
+; CHECK2: @__profc_foo = linkonce_odr global i64 1, comdat
+; CHECK2: @__profd_foo = available_externally dso_local global ptr @__profc_foo{{$}}
+
+$__profc_foo = comdat any
+@__profc_foo = linkonce_odr global i64 1, comdat
+@__profd_foo = private global ptr @__profc_foo, comdat($__profc_foo)
+@llvm.compiler.used = appending global [1 x ptr] [ ptr @__profd_foo ]
+
+define ptr @bar() {
+ ret ptr @__profc_foo
+}
+
+;--- 2-aux.ll
+$__profc_foo = comdat any
+@__profc_foo = linkonce_odr global i64 1, comdat
+@__profd_foo = private global ptr @__profc_foo, comdat($__profc_foo)
+@llvm.compiler.used = appending global [1 x ptr] [ ptr @__profd_foo ]
+
+define ptr @baz() {
+ ret ptr @__profc_foo
+}
|
b8a12db
to
29eca76
Compare
Ping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The premerge test failure looks unrelated. I think this is good, with a small modification.
for (const Comdat *C : NonPrevailingComdats) { | ||
SmallVector<GlobalObject *> ToUpdate; | ||
for (GlobalObject *GO : C->getUsers()) | ||
if (GO->hasPrivateLinkage() && !AliasedGlobals.contains(GO)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's more correct to use hasLocalLinkage
instead of hasPrivateLinkage
. Looking at the IRMover call sites, that's what's used there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change would break the Linker/comdat-refer-to-discarded.ll
test. Originally, it was Linker/comdat13.ll
in baa3bf8, which stated that the duplication of internal
globals "matches the expectations of COFF linkers`. Since no high-level example was provided, it is now quite difficult, at least for me, to judge whether that behavior is correct or not, so I would prefer not to change it in my patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the test and the history, it is still valid. I think the bug here is that these comdats referring to COFF internal linkage entities are considered NonPrevailingComdats
. The proper fix for that is probably in some other part of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should go ahead and land this as is. I'll file an issue about the logic bug I think I see.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (auto &GA : SrcM->aliases())
is not tested. Add some tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (auto &GA : SrcM->aliases())
is not tested. Add some tests?
This is covered by Linker/comdat-any.ll
, case 3.
for (const Comdat *C : NonPrevailingComdats) { | ||
SmallVector<GlobalObject *> ToUpdate; | ||
for (GlobalObject *GO : C->getUsers()) | ||
if (GO->hasPrivateLinkage() && !AliasedGlobals.contains(GO)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the test and the history, it is still valid. I think the bug here is that these comdats referring to COFF internal linkage entities are considered NonPrevailingComdats
. The proper fix for that is probably in some other part of the code.
@@ -473,6 +474,9 @@ bool ModuleLinker::run() { | |||
return true; | |||
ComdatsChosen[&C] = std::make_pair(SK, From); | |||
|
|||
if (From == LinkFrom::Dst) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for COFF (not for ELF) in the comdat-refer-to-discarded.ll test case we should get a result of LinkFrom::Both
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remember it right, LinkFrom::Both
corresponds to comdat nodeduplicate
, while comdat-refer-to-discarded.ll
uses comdat any
. Do you think the interpretation of a comdat selection type should depend on the target platform (COFF vs ELF)?
for (const Comdat *C : NonPrevailingComdats) { | ||
SmallVector<GlobalObject *> ToUpdate; | ||
for (GlobalObject *GO : C->getUsers()) | ||
if (GO->hasPrivateLinkage() && !AliasedGlobals.contains(GO)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should go ahead and land this as is. I'll file an issue about the logic bug I think I see.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I was able to think of some practical cases this would break, let me try to construct them.
Won't your change do the wrong thing for cases like this? https://gcc.godbolt.org/z/qYfbEY1Ee
Compile with: Relevant IR output:
Alright, so I've convinced myself that this works today because ASan uses the |
Remove |
Why? |
edit: I think this is fine. |
`IRMover` links in referenced private global values unconditionally, see `IRLinker::shouldLink()`. If they are part of a non-prevailing comdat, this leads to duplication of the values. Full and Thin LTO avoid duplication by changing the linkage of members of non-prevailing comdat groups to `available_externally`, which was implemented in https://reviews.llvm.org/D34803 and https://reviews.llvm.org/D135427. This patch does the same for `Linker`, but limits the effect only to private members without aliases to minimize interference. Motivation example: ``` > cat foo.h inline int foo(int a) { return a + 1; } > cat bar.cpp #include "foo.h" int bar(int a) { return foo(a + 1); } > cat main.cpp #include "foo.h" int bar(int a); int main(int argc, const char* argv[]) { return bar(argc) + foo(argc); } > clang++ -c -flto -fprofile-instr-generate main.cpp -o main.o > clang++ -c -flto -fprofile-instr-generate bar.cpp -o bar.o > clang++ -fuse-ld=lld -fprofile-instr-generate main.o bar.o -o test1 > ./test1 > llvm-profdata merge --text default.profraw -o - _Z3fooi # Counter Values: 2 > llvm-link main.o bar.o -o combined.o > clang++ -fuse-ld=lld -fprofile-instr-generate combined.o -o test2 > ./test2 > llvm-profdata merge --text default.profraw -o - _Z3fooi # Counter Values: 4 ```
29eca76
to
8f51816
Compare
IRMover
links in referenced private global values unconditionally, seeIRLinker::shouldLink()
. If they are part of a non-prevailing comdat, this leads to duplication of the values. Full and Thin LTO avoid duplication by changing the linkage of members of non-prevailing comdat groups toavailable_externally
, which was implemented in https://reviews.llvm.org/D34803 and https://reviews.llvm.org/D135427. This patch does the same forLinker
, but limits the effect only to private members without aliases to minimize interference.Motivation example: