-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[AsmPrinter] Always emit global equivalents if there is non-global uses #145648
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
Conversation
@llvm/pr-subscribers-backend-x86 Author: dianqk (dianqk) ChangesA case found from rust-lang/rust#142752: https://llvm.godbolt.org/z/T7ce9saWh. We should emit target triple = "x86_64-unknown-linux-gnu"
@<!-- -->rel_0 = private unnamed_addr constant [1 x i32] [
i32 trunc (i64 sub (i64 ptrtoint (ptr @<!-- -->bar_0 to i64), i64 ptrtoint (ptr @<!-- -->rel_0 to i64)) to i32)]
@<!-- -->bar_0 = internal unnamed_addr constant ptr @<!-- -->foo_0, align 8
@<!-- -->foo_0 = external global ptr, align 8
define void @<!-- -->foo(ptr %arg0) {
store ptr @<!-- -->bar_0, ptr %arg0, align 8
ret void
} Full diff: https://github.com/llvm/llvm-project/pull/145648.diff 3 Files Affected:
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 6ad54fcd6d0e5..22764409d0c54 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -151,8 +151,12 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
/// Map global GOT equivalent MCSymbols to GlobalVariables and keep track of
/// its number of uses by other globals.
- using GOTEquivUsePair = std::pair<const GlobalVariable *, unsigned>;
- MapVector<const MCSymbol *, GOTEquivUsePair> GlobalGOTEquivs;
+ struct GOTEquivUse {
+ const GlobalVariable *GV;
+ unsigned NumUses;
+ bool HasNonGlobalUsers;
+ };
+ MapVector<const MCSymbol *, GOTEquivUse> GlobalGOTEquivs;
// Flags representing which CFI section is required for a function/module.
enum class CFISection : unsigned {
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 3b96225236cd6..4ee05423aa96d 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -740,9 +740,11 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
if (emitSpecialLLVMGlobal(GV))
return;
- // Skip the emission of global equivalents. The symbol can be emitted later
- // on by emitGlobalGOTEquivs in case it turns out to be needed.
- if (GlobalGOTEquivs.count(getSymbol(GV)))
+ // Skip the emission of global equivalents if they are only used by global
+ // values. The symbol can be emitted later on by emitGlobalGOTEquivs in case
+ // it turns out to be needed.
+ if (GlobalGOTEquivs.contains(getSymbol(GV)) &&
+ !GlobalGOTEquivs[getSymbol(GV)].HasNonGlobalUsers)
return;
if (isVerbose()) {
@@ -2142,16 +2144,20 @@ void AsmPrinter::emitFunctionBody() {
}
/// Compute the number of Global Variables that uses a Constant.
-static unsigned getNumGlobalVariableUses(const Constant *C) {
- if (!C)
+static unsigned getNumGlobalVariableUses(const Constant *C,
+ bool &HasNonGlobalUsers) {
+ if (!C) {
+ HasNonGlobalUsers = true;
return 0;
+ }
if (isa<GlobalVariable>(C))
return 1;
unsigned NumUses = 0;
for (const auto *CU : C->users())
- NumUses += getNumGlobalVariableUses(dyn_cast<Constant>(CU));
+ NumUses +=
+ getNumGlobalVariableUses(dyn_cast<Constant>(CU), HasNonGlobalUsers);
return NumUses;
}
@@ -2162,7 +2168,8 @@ static unsigned getNumGlobalVariableUses(const Constant *C) {
/// candidates are skipped and are emitted later in case at least one cstexpr
/// isn't replaced by a PC relative GOT entry access.
static bool isGOTEquivalentCandidate(const GlobalVariable *GV,
- unsigned &NumGOTEquivUsers) {
+ unsigned &NumGOTEquivUsers,
+ bool &HasNonGlobalUsers) {
// Global GOT equivalents are unnamed private globals with a constant
// pointer initializer to another global symbol. They must point to a
// GlobalVariable or Function, i.e., as GlobalValue.
@@ -2174,7 +2181,8 @@ static bool isGOTEquivalentCandidate(const GlobalVariable *GV,
// To be a got equivalent, at least one of its users need to be a constant
// expression used by another global variable.
for (const auto *U : GV->users())
- NumGOTEquivUsers += getNumGlobalVariableUses(dyn_cast<Constant>(U));
+ NumGOTEquivUsers +=
+ getNumGlobalVariableUses(dyn_cast<Constant>(U), HasNonGlobalUsers);
return NumGOTEquivUsers > 0;
}
@@ -2192,11 +2200,13 @@ void AsmPrinter::computeGlobalGOTEquivs(Module &M) {
for (const auto &G : M.globals()) {
unsigned NumGOTEquivUsers = 0;
- if (!isGOTEquivalentCandidate(&G, NumGOTEquivUsers))
+ bool HasNonGlobalUsers = false;
+ if (!isGOTEquivalentCandidate(&G, NumGOTEquivUsers, HasNonGlobalUsers))
continue;
const MCSymbol *GOTEquivSym = getSymbol(&G);
- GlobalGOTEquivs[GOTEquivSym] = std::make_pair(&G, NumGOTEquivUsers);
+ GlobalGOTEquivs[GOTEquivSym] =
+ AsmPrinter::GOTEquivUse{&G, NumGOTEquivUsers, HasNonGlobalUsers};
}
}
@@ -2209,9 +2219,9 @@ void AsmPrinter::emitGlobalGOTEquivs() {
SmallVector<const GlobalVariable *, 8> FailedCandidates;
for (auto &I : GlobalGOTEquivs) {
- const GlobalVariable *GV = I.second.first;
- unsigned Cnt = I.second.second;
- if (Cnt)
+ const GlobalVariable *GV = I.second.GV;
+ unsigned Cnt = I.second.NumUses;
+ if (Cnt > 0 && !I.second.HasNonGlobalUsers)
FailedCandidates.push_back(GV);
}
GlobalGOTEquivs.clear();
@@ -3992,9 +4002,10 @@ static void handleIndirectSymViaGOTPCRel(AsmPrinter &AP, const MCExpr **ME,
// .long 42
// foo:
// .long bar@GOTPCREL+<gotpcrelcst>
- AsmPrinter::GOTEquivUsePair Result = AP.GlobalGOTEquivs[GOTEquivSym];
- const GlobalVariable *GV = Result.first;
- int NumUses = (int)Result.second;
+ AsmPrinter::GOTEquivUse Result = AP.GlobalGOTEquivs[GOTEquivSym];
+ const GlobalVariable *GV = Result.GV;
+ bool HasNonGlobalUsers = Result.HasNonGlobalUsers;
+ int NumUses = (int)Result.NumUses;
const GlobalValue *FinalGV = dyn_cast<GlobalValue>(GV->getOperand(0));
const MCSymbol *FinalSym = AP.getSymbol(FinalGV);
*ME = AP.getObjFileLowering().getIndirectSymViaGOTPCRel(
@@ -4003,7 +4014,8 @@ static void handleIndirectSymViaGOTPCRel(AsmPrinter &AP, const MCExpr **ME,
// Update GOT equivalent usage information
--NumUses;
if (NumUses >= 0)
- AP.GlobalGOTEquivs[GOTEquivSym] = std::make_pair(GV, NumUses);
+ AP.GlobalGOTEquivs[GOTEquivSym] =
+ AsmPrinter::GOTEquivUse{GV, (unsigned)NumUses, HasNonGlobalUsers};
}
static void emitGlobalConstantImpl(const DataLayout &DL, const Constant *CV,
diff --git a/llvm/test/MC/X86/gotpcrel-non-globals.ll b/llvm/test/MC/X86/gotpcrel-non-globals.ll
new file mode 100644
index 0000000000000..cbf22cd1e4ac0
--- /dev/null
+++ b/llvm/test/MC/X86/gotpcrel-non-globals.ll
@@ -0,0 +1,28 @@
+; RUN: llc < %s | FileCheck %s
+
+target triple = "x86_64-unknown-linux-gnu"
+
+; Check that we submit the `@bar_*` symbols, and that we don't submit multiple symbols.
+
+; CHECK-LABEL: .Lrel_0:
+; CHECK: .long foo_0@GOTPCREL+0
+; CHECK-LABEL: .Lrel_1_failed:
+; CHECK: .long bar_1-foo_0
+
+; CHECK: bar_0:
+; CHECK: bar_1:
+
+@rel_0 = private unnamed_addr constant [1 x i32] [
+ i32 trunc (i64 sub (i64 ptrtoint (ptr @bar_0 to i64), i64 ptrtoint (ptr @rel_0 to i64)) to i32)]
+@rel_1_failed = private unnamed_addr constant [1 x i32] [
+ i32 trunc (i64 sub (i64 ptrtoint (ptr @bar_1 to i64), i64 ptrtoint (ptr @foo_0 to i64)) to i32)]
+@bar_0 = internal unnamed_addr constant ptr @foo_0, align 8
+@bar_1 = internal unnamed_addr constant ptr @foo_1, align 8
+@foo_0 = external global ptr, align 8
+@foo_1 = external global ptr, align 8
+
+define void @foo(ptr %arg0, ptr %arg1) {
+ store ptr @bar_0, ptr %arg0, align 8
+ store ptr @bar_1, ptr %arg1, align 8
+ ret void
+}
|
@llvm/pr-subscribers-mc Author: dianqk (dianqk) ChangesA case found from rust-lang/rust#142752: https://llvm.godbolt.org/z/T7ce9saWh. We should emit target triple = "x86_64-unknown-linux-gnu"
@<!-- -->rel_0 = private unnamed_addr constant [1 x i32] [
i32 trunc (i64 sub (i64 ptrtoint (ptr @<!-- -->bar_0 to i64), i64 ptrtoint (ptr @<!-- -->rel_0 to i64)) to i32)]
@<!-- -->bar_0 = internal unnamed_addr constant ptr @<!-- -->foo_0, align 8
@<!-- -->foo_0 = external global ptr, align 8
define void @<!-- -->foo(ptr %arg0) {
store ptr @<!-- -->bar_0, ptr %arg0, align 8
ret void
} Full diff: https://github.com/llvm/llvm-project/pull/145648.diff 3 Files Affected:
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 6ad54fcd6d0e5..22764409d0c54 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -151,8 +151,12 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
/// Map global GOT equivalent MCSymbols to GlobalVariables and keep track of
/// its number of uses by other globals.
- using GOTEquivUsePair = std::pair<const GlobalVariable *, unsigned>;
- MapVector<const MCSymbol *, GOTEquivUsePair> GlobalGOTEquivs;
+ struct GOTEquivUse {
+ const GlobalVariable *GV;
+ unsigned NumUses;
+ bool HasNonGlobalUsers;
+ };
+ MapVector<const MCSymbol *, GOTEquivUse> GlobalGOTEquivs;
// Flags representing which CFI section is required for a function/module.
enum class CFISection : unsigned {
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 3b96225236cd6..4ee05423aa96d 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -740,9 +740,11 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
if (emitSpecialLLVMGlobal(GV))
return;
- // Skip the emission of global equivalents. The symbol can be emitted later
- // on by emitGlobalGOTEquivs in case it turns out to be needed.
- if (GlobalGOTEquivs.count(getSymbol(GV)))
+ // Skip the emission of global equivalents if they are only used by global
+ // values. The symbol can be emitted later on by emitGlobalGOTEquivs in case
+ // it turns out to be needed.
+ if (GlobalGOTEquivs.contains(getSymbol(GV)) &&
+ !GlobalGOTEquivs[getSymbol(GV)].HasNonGlobalUsers)
return;
if (isVerbose()) {
@@ -2142,16 +2144,20 @@ void AsmPrinter::emitFunctionBody() {
}
/// Compute the number of Global Variables that uses a Constant.
-static unsigned getNumGlobalVariableUses(const Constant *C) {
- if (!C)
+static unsigned getNumGlobalVariableUses(const Constant *C,
+ bool &HasNonGlobalUsers) {
+ if (!C) {
+ HasNonGlobalUsers = true;
return 0;
+ }
if (isa<GlobalVariable>(C))
return 1;
unsigned NumUses = 0;
for (const auto *CU : C->users())
- NumUses += getNumGlobalVariableUses(dyn_cast<Constant>(CU));
+ NumUses +=
+ getNumGlobalVariableUses(dyn_cast<Constant>(CU), HasNonGlobalUsers);
return NumUses;
}
@@ -2162,7 +2168,8 @@ static unsigned getNumGlobalVariableUses(const Constant *C) {
/// candidates are skipped and are emitted later in case at least one cstexpr
/// isn't replaced by a PC relative GOT entry access.
static bool isGOTEquivalentCandidate(const GlobalVariable *GV,
- unsigned &NumGOTEquivUsers) {
+ unsigned &NumGOTEquivUsers,
+ bool &HasNonGlobalUsers) {
// Global GOT equivalents are unnamed private globals with a constant
// pointer initializer to another global symbol. They must point to a
// GlobalVariable or Function, i.e., as GlobalValue.
@@ -2174,7 +2181,8 @@ static bool isGOTEquivalentCandidate(const GlobalVariable *GV,
// To be a got equivalent, at least one of its users need to be a constant
// expression used by another global variable.
for (const auto *U : GV->users())
- NumGOTEquivUsers += getNumGlobalVariableUses(dyn_cast<Constant>(U));
+ NumGOTEquivUsers +=
+ getNumGlobalVariableUses(dyn_cast<Constant>(U), HasNonGlobalUsers);
return NumGOTEquivUsers > 0;
}
@@ -2192,11 +2200,13 @@ void AsmPrinter::computeGlobalGOTEquivs(Module &M) {
for (const auto &G : M.globals()) {
unsigned NumGOTEquivUsers = 0;
- if (!isGOTEquivalentCandidate(&G, NumGOTEquivUsers))
+ bool HasNonGlobalUsers = false;
+ if (!isGOTEquivalentCandidate(&G, NumGOTEquivUsers, HasNonGlobalUsers))
continue;
const MCSymbol *GOTEquivSym = getSymbol(&G);
- GlobalGOTEquivs[GOTEquivSym] = std::make_pair(&G, NumGOTEquivUsers);
+ GlobalGOTEquivs[GOTEquivSym] =
+ AsmPrinter::GOTEquivUse{&G, NumGOTEquivUsers, HasNonGlobalUsers};
}
}
@@ -2209,9 +2219,9 @@ void AsmPrinter::emitGlobalGOTEquivs() {
SmallVector<const GlobalVariable *, 8> FailedCandidates;
for (auto &I : GlobalGOTEquivs) {
- const GlobalVariable *GV = I.second.first;
- unsigned Cnt = I.second.second;
- if (Cnt)
+ const GlobalVariable *GV = I.second.GV;
+ unsigned Cnt = I.second.NumUses;
+ if (Cnt > 0 && !I.second.HasNonGlobalUsers)
FailedCandidates.push_back(GV);
}
GlobalGOTEquivs.clear();
@@ -3992,9 +4002,10 @@ static void handleIndirectSymViaGOTPCRel(AsmPrinter &AP, const MCExpr **ME,
// .long 42
// foo:
// .long bar@GOTPCREL+<gotpcrelcst>
- AsmPrinter::GOTEquivUsePair Result = AP.GlobalGOTEquivs[GOTEquivSym];
- const GlobalVariable *GV = Result.first;
- int NumUses = (int)Result.second;
+ AsmPrinter::GOTEquivUse Result = AP.GlobalGOTEquivs[GOTEquivSym];
+ const GlobalVariable *GV = Result.GV;
+ bool HasNonGlobalUsers = Result.HasNonGlobalUsers;
+ int NumUses = (int)Result.NumUses;
const GlobalValue *FinalGV = dyn_cast<GlobalValue>(GV->getOperand(0));
const MCSymbol *FinalSym = AP.getSymbol(FinalGV);
*ME = AP.getObjFileLowering().getIndirectSymViaGOTPCRel(
@@ -4003,7 +4014,8 @@ static void handleIndirectSymViaGOTPCRel(AsmPrinter &AP, const MCExpr **ME,
// Update GOT equivalent usage information
--NumUses;
if (NumUses >= 0)
- AP.GlobalGOTEquivs[GOTEquivSym] = std::make_pair(GV, NumUses);
+ AP.GlobalGOTEquivs[GOTEquivSym] =
+ AsmPrinter::GOTEquivUse{GV, (unsigned)NumUses, HasNonGlobalUsers};
}
static void emitGlobalConstantImpl(const DataLayout &DL, const Constant *CV,
diff --git a/llvm/test/MC/X86/gotpcrel-non-globals.ll b/llvm/test/MC/X86/gotpcrel-non-globals.ll
new file mode 100644
index 0000000000000..cbf22cd1e4ac0
--- /dev/null
+++ b/llvm/test/MC/X86/gotpcrel-non-globals.ll
@@ -0,0 +1,28 @@
+; RUN: llc < %s | FileCheck %s
+
+target triple = "x86_64-unknown-linux-gnu"
+
+; Check that we submit the `@bar_*` symbols, and that we don't submit multiple symbols.
+
+; CHECK-LABEL: .Lrel_0:
+; CHECK: .long foo_0@GOTPCREL+0
+; CHECK-LABEL: .Lrel_1_failed:
+; CHECK: .long bar_1-foo_0
+
+; CHECK: bar_0:
+; CHECK: bar_1:
+
+@rel_0 = private unnamed_addr constant [1 x i32] [
+ i32 trunc (i64 sub (i64 ptrtoint (ptr @bar_0 to i64), i64 ptrtoint (ptr @rel_0 to i64)) to i32)]
+@rel_1_failed = private unnamed_addr constant [1 x i32] [
+ i32 trunc (i64 sub (i64 ptrtoint (ptr @bar_1 to i64), i64 ptrtoint (ptr @foo_0 to i64)) to i32)]
+@bar_0 = internal unnamed_addr constant ptr @foo_0, align 8
+@bar_1 = internal unnamed_addr constant ptr @foo_1, align 8
+@foo_0 = external global ptr, align 8
+@foo_1 = external global ptr, align 8
+
+define void @foo(ptr %arg0, ptr %arg1) {
+ store ptr @bar_0, ptr %arg0, align 8
+ store ptr @bar_1, ptr %arg1, align 8
+ ret void
+}
|
if (!C) { | ||
HasNonGlobalUsers = true; | ||
return 0; | ||
} |
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'd directly handle the case where the dyn_cast failed in the users loop instead of adding this out argument
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 you'd still need it for the recursive case. It would be good to add a test where the extra uses are in getelementptr constant expressions.
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.
added in cd85ee4.
if (Cnt) | ||
const GlobalVariable *GV = I.second.GV; | ||
unsigned Cnt = I.second.NumUses; | ||
if (Cnt > 0 && !I.second.HasNonGlobalUsers) |
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'm confused by this. I would have expected the check here to be this instead?
if (Cnt > 0 && !I.second.HasNonGlobalUsers) | |
if (Cnt > 0 || I.second.HasNonGlobalUsers) |
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.
Here is to prevent duplicate emissions.
For HasNonGlobalUsers
case, emitGlobalVariable
has emitted them before emitGlobalGOTEquivs
:
// Skip the emission of global equivalents if they are only used by global
// values. The symbol can be emitted later on by emitGlobalGOTEquivs in case
// it turns out to be needed.
if (GlobalGOTEquivs.contains(getSymbol(GV)) &&
!GlobalGOTEquivs[getSymbol(GV)].HasNonGlobalUsers)
return;
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 see, I didn't notice the other change. But why not just let them always be emitted here, instead of needing checks in both places?
struct GOTEquivUse { | ||
const GlobalVariable *GV; | ||
unsigned NumUses; | ||
bool HasNonGlobalUsers; |
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 breaks the ABI, so we will not be able to backport it in this form (though I'm not sure if there will be another LLVM 20 release). An alternative would be to make the number of uses the total number of uses (rather than global variable uses). Or even just adding 1 to it for non-global users would also work.
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 prefer the latter, because the former would return true for isGOTEquivalentCandidate
in most scenarios.
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.
LGTM
/cherry-pick 630d55c |
/pull-request #145690 |
A case found from rust-lang/rust#142752: https://llvm.godbolt.org/z/T7ce9saWh.
We should emit
@bar_0
for the following code: