-
Notifications
You must be signed in to change notification settings - Fork 13.8k
[ElimAvailExtern] Add an option to allow to convert global variables in a specified address space to local #144287
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
base: main
Are you sure you want to change the base?
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
@llvm/pr-subscribers-llvm-transforms Author: Shilei Tian (shiltian) ChangesCurrently, the This PR adds an option to allow the pass to convert global variables in a A It's safe to privatize such global variables, because they're local to their Full diff: https://github.com/llvm/llvm-project/pull/144287.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/IPO/ElimAvailExtern.cpp b/llvm/lib/Transforms/IPO/ElimAvailExtern.cpp
index 718452fc02764..a015ba8ccfd4a 100644
--- a/llvm/lib/Transforms/IPO/ElimAvailExtern.cpp
+++ b/llvm/lib/Transforms/IPO/ElimAvailExtern.cpp
@@ -35,8 +35,14 @@ static cl::opt<bool> ConvertToLocal(
cl::desc("Convert available_externally into locals, renaming them "
"to avoid link-time clashes."));
+static cl::opt<unsigned> ConvertGlobalVariableInAddrSpace(
+ "avail-extern-gv-in-addrspace-to-local", cl::Hidden,
+ cl::desc(
+ "Convert available_externally global variables into locals if they are "
+ "in specificed addrspace, renaming them to avoid link-time clashes."));
+
STATISTIC(NumRemovals, "Number of functions removed");
-STATISTIC(NumConversions, "Number of functions converted");
+STATISTIC(NumConversions, "Number of functions and globalbs converted");
STATISTIC(NumVariables, "Number of global variables removed");
void deleteFunction(Function &F) {
@@ -88,9 +94,32 @@ static void convertToLocalCopy(Module &M, Function &F) {
++NumConversions;
}
+static void convertToLocalCopy(Module &M, GlobalValue &GV) {
+ assert(GV.hasAvailableExternallyLinkage());
+ std::string OrigName = GV.getName().str();
+ std::string NewName = OrigName + ".__uniq" + getUniqueModuleId(&M);
+ GV.setName(NewName);
+ GV.setLinkage(GlobalValue::InternalLinkage);
+ ++NumConversions;
+}
+
static bool eliminateAvailableExternally(Module &M, bool Convert) {
bool Changed = false;
+ // Convert global variables in specified address space before changing it to
+ // external linkage below.
+ if (ConvertGlobalVariableInAddrSpace.getNumOccurrences()) {
+ for (GlobalVariable &GV : M.globals()) {
+ if (!GV.hasAvailableExternallyLinkage() || GV.use_empty())
+ continue;
+
+ if (GV.getAddressSpace() == ConvertGlobalVariableInAddrSpace)
+ convertToLocalCopy(M, GV);
+
+ Changed = true;
+ }
+ }
+
// Drop initializers of available externally global variables.
for (GlobalVariable &GV : M.globals()) {
if (!GV.hasAvailableExternallyLinkage())
diff --git a/llvm/test/Transforms/EliminateAvailableExternally/convert-global-variables-to-local.ll b/llvm/test/Transforms/EliminateAvailableExternally/convert-global-variables-to-local.ll
new file mode 100644
index 0000000000000..6995b97e79887
--- /dev/null
+++ b/llvm/test/Transforms/EliminateAvailableExternally/convert-global-variables-to-local.ll
@@ -0,0 +1,21 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 5
+; RUN: opt -S -passes=elim-avail-extern -avail-extern-gv-in-addrspace-to-local=3 %s -o - | FileCheck %s
+
+@shared = internal addrspace(3) global i32 undef, align 4
+@shared.imported = available_externally hidden unnamed_addr addrspace(3) global i32 undef, align 4
+
+;.
+; CHECK: @shared = internal addrspace(3) global i32 undef, align 4
+; CHECK: @shared.imported.__uniq.[[UUID:.*]] = internal unnamed_addr addrspace(3) global i32 undef, align 4
+;.
+define void @foo(i32 %v) {
+; CHECK-LABEL: define void @foo(
+; CHECK-SAME: i32 [[V:%.*]]) {
+; CHECK-NEXT: store i32 [[V]], ptr addrspace(3) @shared, align 4
+; CHECK-NEXT: store i32 [[V]], ptr addrspace(3) @shared.imported.__uniq.[[UUID]], align 4
+; CHECK-NEXT: ret void
+;
+ store i32 %v, ptr addrspace(3) @shared, align 4
+ store i32 %v, ptr addrspace(3) @shared.imported, align 4
+ ret void
+}
|
3127751
to
b633297
Compare
You can test this locally with the following command:git diff -U0 --pickaxe-regex -S '([^a-zA-Z0-9#_-]undef[^a-zA-Z0-9_-]|UndefValue::get)' 'HEAD~1' HEAD llvm/test/Transforms/EliminateAvailableExternally/convert-global-variables-to-local.ll llvm/lib/Transforms/IPO/ElimAvailExtern.cpp llvm/test/Transforms/EliminateAvailableExternally/transform-to-local.ll The following files introduce new uses of undef:
Undef is now deprecated and should only be used in the rare cases where no replacement is possible. For example, a load of uninitialized memory yields In tests, avoid using For example, this is considered a bad practice: define void @fn() {
...
br i1 undef, ...
} Please use the following instead: define void @fn(i1 %cond) {
...
br i1 %cond, ...
} Please refer to the Undefined Behavior Manual for more information. |
b633297
to
1cfc903
Compare
…in a specified address space to local Currently, the `EliminateAvailableExternallyPass` only converts certain available externally functions to local. For global variables, it only drops their initializers. This PR adds an option to allow the pass to convert global variables in a specified address space to local. The motivation for this change is to correctly support lowering of LDS variables (`__shared__` variables, in more generic terminology) when ThinLTO is enabled for AMDGPU. A `__shared__` variable is lowered to a hidden global variable in a particular address space by the frontend, which is roughly same as a `static` local variable. To properly lower it in the backend, the compiler needs to check all its uses. Enabling ThinLTO currently breaks this when a function containing a `__shared__` variable is imported from another module. Even though the global variable is imported along with its associated function, and the function is privatized by the `EliminateAvailableExternallyPass`, the global variable itself is not. It's safe to privatize such global variables, because they're _local_ to their associated functions. If the function itself is privatized, its associated global variables should also be privatized accordingly.
1cfc903
to
9c643a2
Compare
STATISTIC(NumRemovals, "Number of functions removed"); | ||
STATISTIC(NumConversions, "Number of functions converted"); | ||
STATISTIC(NumConversions, "Number of functions and globals converted"); |
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.
would it help if we split this statistic in NumFunctionsConverted
and NumGlobalVariablesConverted
?
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.
Sure, will do
static bool eliminateAvailableExternally(Module &M, bool Convert) { | ||
bool Changed = false; | ||
|
||
// Convert global variables in specified address space before changing it to | ||
// external linkage below. | ||
if (ConvertGlobalVariableInAddrSpace.getNumOccurrences()) { |
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.
could this rather be done as part of the existing loop - for readability (the policies applied to GlobalVariables are in one place)
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.
Sure, although that means we will have to have the if-statement in each iteration.
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.
Can't see why that would be a problem.
@@ -88,9 +94,32 @@ static void convertToLocalCopy(Module &M, Function &F) { | |||
++NumConversions; | |||
} | |||
|
|||
static void convertToLocalCopy(Module &M, GlobalValue &GV) { |
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.
maybe factor out the calculation and setting of the new name and linkage and reuse it at line 77
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.
Currently, the EliminateAvailableExternallyPass only converts certain available externally functions to local.
This is not the default behavior. This pass mostly exists to drop definitions, not to convert to local. The conversion was added for contextual profiling. So the reference to this behavior in the summary is a bit odd. Do you also want to convert functions to locals? If so, should this be under the same option(s)?
Currently, the
EliminateAvailableExternallyPass
only converts certainavailable externally functions to local. For global variables, it only drops
their initializers.
This PR adds an option to allow the pass to convert global variables in a
specified address space to local. The motivation for this change is to correctly
support lowering of LDS variables (
__shared__
variables, in more genericterminology) when ThinLTO is enabled for AMDGPU.
A
__shared__
variable is lowered to a hidden global variable in a particularaddress space by the frontend, which is roughly same as a
static
localvariable. To properly lower it in the backend, the compiler needs to check all
its uses. Enabling ThinLTO currently breaks this when a function containing a
__shared__
variable is imported from another module. Even though the globalvariable is imported along with its associated function, and the function is
privatized by the
EliminateAvailableExternallyPass
, the global variable itselfis not.
It's safe to privatize such global variables, because they're local to their
associated functions. If the function itself is privatized, its associated
global variables should also be privatized accordingly.