Skip to content

[clang][CodeGen] Global constructors/destructors are globals #93914

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

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

AlexVlx
Copy link
Contributor

@AlexVlx AlexVlx commented May 31, 2024

Currently the payload for global ctors/dtors is passed as an unqualified pointer, which relies on the target being reasonable regarding what AS 0 means, and also is overly conservative. This change correctly places the payload in the target's global AS, and does the same thing for the global_ctors/global_dtors arrays.

@AlexVlx AlexVlx added clang:codegen IR generation bugs: mangling, exceptions, etc. llvm-reduce labels May 31, 2024
@llvmbot
Copy link
Member

llvmbot commented May 31, 2024

@llvm/pr-subscribers-clang-codegen

Author: Alex Voicu (AlexVlx)

Changes

Currently the payload for global ctors/dtors is passed as an unqualified pointer, which relies on the target being reasonable regarding what AS 0 means, and also is overly conservative. This change correctly places the payload in the target's global AS, and does the same thing for the global_ctors/global_dtors arrays.


Full diff: https://github.com/llvm/llvm-project/pull/93914.diff

9 Files Affected:

  • (modified) clang/lib/CodeGen/CodeGenModule.cpp (+6-5)
  • (modified) clang/test/CodeGen/asan-constructor.c (+2)
  • (modified) clang/test/CodeGen/attr-retain.c (-1)
  • (modified) clang/test/CodeGenCUDA/llvm-used.cu (+1-1)
  • (modified) clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp (+2-1)
  • (modified) clang/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp (+5-1)
  • (modified) clang/test/OpenMP/amdgcn_target_global_constructor.cpp (+2-2)
  • (modified) llvm/lib/Transforms/Utils/ModuleUtils.cpp (+6-7)
  • (modified) llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll (+2-2)
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index c2314c3a57d33..b59c5c52783e9 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2047,9 +2047,9 @@ void CodeGenModule::EmitCtorList(CtorList &Fns, const char *GlobalName) {
   llvm::Type *CtorPFTy = llvm::PointerType::get(CtorFTy,
       TheModule.getDataLayout().getProgramAddressSpace());
 
-  // Get the type of a ctor entry, { i32, void ()*, i8* }.
-  llvm::StructType *CtorStructTy = llvm::StructType::get(
-      Int32Ty, CtorPFTy, VoidPtrTy);
+  // Get the type of a ctor entry, { i32, program void ()*, global i8* }.
+  llvm::StructType *CtorStructTy =
+      llvm::StructType::get(Int32Ty, CtorPFTy, GlobalsInt8PtrTy);
 
   // Construct the constructor and destructor arrays.
   ConstantInitBuilder builder(*this);
@@ -2061,14 +2061,15 @@ void CodeGenModule::EmitCtorList(CtorList &Fns, const char *GlobalName) {
     if (I.AssociatedData)
       ctor.add(I.AssociatedData);
     else
-      ctor.addNullPointer(VoidPtrTy);
+      ctor.addNullPointer(GlobalsInt8PtrTy);
     ctor.finishAndAddTo(ctors);
   }
 
   auto list =
     ctors.finishAndCreateGlobal(GlobalName, getPointerAlign(),
                                 /*constant*/ false,
-                                llvm::GlobalValue::AppendingLinkage);
+                                llvm::GlobalValue::AppendingLinkage,
+                                GlobalsInt8PtrTy->getAddressSpace());
 
   // The LTO linker doesn't seem to like it when we set an alignment
   // on appending variables.  Take it off as a workaround.
diff --git a/clang/test/CodeGen/asan-constructor.c b/clang/test/CodeGen/asan-constructor.c
index 762bf0778565d..41212d45a5c63 100644
--- a/clang/test/CodeGen/asan-constructor.c
+++ b/clang/test/CodeGen/asan-constructor.c
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -fsanitize=address -emit-llvm -o - %s | FileCheck %s
 // RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fsanitize=address -emit-llvm -o - %s | FileCheck %s --check-prefix=EMSCRIPTEN
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -fsanitize=address -emit-llvm -o - %s | FileCheck %s --check-prefix=GLOBALAS
 
 // CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @asan.module_ctor, ptr null }]
 // EMSCRIPTEN: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 50, ptr @asan.module_ctor, ptr null }]
+// GLOBALAS: @llvm.global_ctors = appending addrspace(1) global [1 x { i32, ptr, ptr addrspace(1) }] [{ i32, ptr, ptr addrspace(1) } { i32 1, ptr @asan.module_ctor, ptr addrspace(1) null }]
diff --git a/clang/test/CodeGen/attr-retain.c b/clang/test/CodeGen/attr-retain.c
index fc41be76f9d0a..6a9869c00392d 100644
--- a/clang/test/CodeGen/attr-retain.c
+++ b/clang/test/CodeGen/attr-retain.c
@@ -11,7 +11,6 @@
 
 // CHECK:      @llvm.used = appending global [8 x ptr] [ptr @c0, ptr @foo.l0, ptr @f0, ptr @f2, ptr @g0, ptr @g1, ptr @g3, ptr @g4], section "llvm.metadata"
 // CHECK:      @llvm.compiler.used = appending global [3 x ptr] [ptr @f2, ptr @g3, ptr @g4], section "llvm.metadata"
-
 const int c0 __attribute__((retain)) = 42;
 
 void foo(void) {
diff --git a/clang/test/CodeGenCUDA/llvm-used.cu b/clang/test/CodeGenCUDA/llvm-used.cu
index c39111dd48036..9f22dabde3c2d 100644
--- a/clang/test/CodeGenCUDA/llvm-used.cu
+++ b/clang/test/CodeGenCUDA/llvm-used.cu
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -emit-llvm %s -o - -fcuda-is-device -triple nvptx64-unknown-unknown | FileCheck %s
 
 
-// Make sure we emit the proper addrspacecast for llvm.used.  PR22383 exposed an
+// Make sure we emit the proper addrspacecast for llvm.used. PR22383 exposed an
 // issue where we were generating a bitcast instead of an addrspacecast.
 
 // CHECK: @llvm.compiler.used = appending global [1 x ptr] [ptr addrspacecast (ptr addrspace(1) @a to ptr)], section "llvm.metadata"
diff --git a/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp b/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp
index aa2f078a5fb0c..2e22192e8b388 100644
--- a/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp
+++ b/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp
@@ -90,7 +90,8 @@ std::initializer_list<int> thread_local x = {1, 2, 3, 4};
 // AMDGCN: @[[REFTMP1:.*]] = private addrspace(4) constant [2 x i32] [i32 42, i32 43], align 4
 // AMDGCN: @[[REFTMP2:.*]] = private addrspace(4) constant [3 x %{{.*}}] [%{{.*}} { i32 1 }, %{{.*}} { i32 2 }, %{{.*}} { i32 3 }], align 4
 
-// CHECK: appending global
+// X86: appending global
+// AMDGCN: appending addrspace(1) global
 
 // thread_local initializer:
 // X86-LABEL: define internal void @__cxx_global_var_init
diff --git a/clang/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp b/clang/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp
index 46c4c4d391769..58055130cd708 100644
--- a/clang/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp
+++ b/clang/test/CodeGenCXX/static-member-variable-explicit-specialization.cpp
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 %s -std=c++1y -triple=x86_64-pc-linux -emit-llvm -o - | FileCheck --check-prefix=ELF --check-prefix=ALL %s
 // RUN: %clang_cc1 %s -std=c++1y -triple=x86_64-apple-darwin -emit-llvm -o - | FileCheck --check-prefix=MACHO --check-prefix=ALL %s
 // RUN: %clang_cc1 %s -std=c++1y -triple=x86_64-pc-linux -emit-llvm -fdeclspec -DSELECTANY -o - | FileCheck --check-prefix=ELF-SELECTANY %s
+// RUN: %clang_cc1 %s -std=c++1y -triple=amdgcn-amd-amdhsa -emit-llvm -o - | FileCheck --check-prefix=GLOBALAS-ELF %s
+// RUN: %clang_cc1 %s -std=c++1y -triple=amdgcn-amd-amdhsa -emit-llvm -fdeclspec -DSELECTANY -o - | FileCheck --check-prefix=GLOBALAS-ELF-SELECTANY %s
 
 #ifdef SELECTANY
 struct S {
@@ -12,6 +14,7 @@ int f();
 
 // ELF-SELECTANY: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @__cxx_global_var_init, ptr @selectany }]
 // ELF-SELECTANY: @llvm.used = appending global [1 x ptr] [ptr @selectany]
+// GLOBALAS-ELF-SELECTANY: @llvm.global_ctors = appending addrspace(1) global [1 x { i32, ptr, ptr addrspace(1) }] [{ i32, ptr, ptr addrspace(1) } { i32 65535, ptr @__cxx_global_var_init, ptr addrspace(1) @selectany }]
 int __declspec(selectany) selectany = f();
 
 #else
@@ -30,6 +33,7 @@ template<> int A<char>::a;
 template<> int A<bool>::a = 10;
 
 // ALL: @llvm.global_ctors = appending global [16 x { i32, ptr, ptr }]
+// GLOBALAS-ELF: @llvm.global_ctors = appending addrspace(1) global [16 x { i32, ptr, ptr addrspace(1) }]
 
 // ELF:  [{ i32, ptr, ptr } { i32 65535, ptr @[[unordered:[^,]*]], ptr @_ZN1AIsE1aE },
 // MACHO: [{ i32, ptr, ptr } { i32 65535, ptr @[[unordered:[^,]*]], ptr null },
@@ -73,7 +77,7 @@ template<> int A<bool>::a = 10;
 // MACHO: { i32, ptr, ptr } { i32 65535, ptr @[[unordered22:[^,]*]], ptr null },
 
 // ELF:  { i32, ptr, ptr } { i32 65535, ptr @[[unordered21:[^,]*]], ptr @_ZN4Fib2ILi5EE1aE },
-// MACHO: { i32, ptr, ptr } { i32 65535, ptr @[[unordered21:[^,]*]], ptr null }, 
+// MACHO: { i32, ptr, ptr } { i32 65535, ptr @[[unordered21:[^,]*]], ptr null },
 
 // ALL:  { i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_static_member_variable_explicit_specialization.cpp, ptr null }]
 
diff --git a/clang/test/OpenMP/amdgcn_target_global_constructor.cpp b/clang/test/OpenMP/amdgcn_target_global_constructor.cpp
index 80a5067b357a2..fe414b59588c0 100644
--- a/clang/test/OpenMP/amdgcn_target_global_constructor.cpp
+++ b/clang/test/OpenMP/amdgcn_target_global_constructor.cpp
@@ -21,8 +21,8 @@ S A;
 
 #endif
 //.
-// CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_amdgcn_target_global_constructor.cpp, ptr null }]
-// CHECK: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @__dtor_A, ptr null }]
+// CHECK: @llvm.global_ctors = appending addrspace(1) global [1 x { i32, ptr, ptr addrspace(1) }] [{ i32, ptr, ptr addrspace(1) } { i32 65535, ptr @_GLOBAL__sub_I_amdgcn_target_global_constructor.cpp, ptr addrspace(1) null }]
+// CHECK: @llvm.global_dtors = appending addrspace(1) global [1 x { i32, ptr, ptr addrspace(1) }] [{ i32, ptr, ptr addrspace(1) } { i32 65535, ptr @__dtor_A, ptr addrspace(1) null }]
 //.
 // CHECK-LABEL: define {{[^@]+}}@__cxx_global_var_init
 // CHECK-SAME: () #[[ATTR0:[0-9]+]] {
diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
index 122279160cc7e..7151cc869bc95 100644
--- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp
+++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
@@ -30,6 +30,8 @@ static void appendToGlobalArray(StringRef ArrayName, Module &M, Function *F,
                                 int Priority, Constant *Data) {
   IRBuilder<> IRB(M.getContext());
   FunctionType *FnTy = FunctionType::get(IRB.getVoidTy(), false);
+  PointerType *GlobalsInt8PtrTy = PointerType::get(
+      M.getContext(), M.getDataLayout().getDefaultGlobalsAddressSpace());
 
   // Get the current set of static global constructors and add the new ctor
   // to the list.
@@ -47,15 +49,15 @@ static void appendToGlobalArray(StringRef ArrayName, Module &M, Function *F,
   } else {
     EltTy = StructType::get(IRB.getInt32Ty(),
                             PointerType::get(FnTy, F->getAddressSpace()),
-                            IRB.getPtrTy());
+                            GlobalsInt8PtrTy);
   }
 
   // Build a 3 field global_ctor entry.  We don't take a comdat key.
   Constant *CSVals[3];
   CSVals[0] = IRB.getInt32(Priority);
   CSVals[1] = F;
-  CSVals[2] = Data ? ConstantExpr::getPointerCast(Data, IRB.getPtrTy())
-                   : Constant::getNullValue(IRB.getPtrTy());
+  CSVals[2] = Data ? ConstantExpr::getPointerCast(Data, GlobalsInt8PtrTy)
+                   : Constant::getNullValue(GlobalsInt8PtrTy);
   Constant *RuntimeCtorInit =
       ConstantStruct::get(EltTy, ArrayRef(CSVals, EltTy->getNumElements()));
 
@@ -437,12 +439,9 @@ bool llvm::lowerGlobalIFuncUsersAsGlobalCtor(
 
   InitBuilder.CreateRetVoid();
 
-  PointerType *ConstantDataTy = PointerType::get(Ctx, 0);
-
   // TODO: Is this the right priority? Probably should be before any other
   // constructors?
   const int Priority = 10;
-  appendToGlobalCtors(M, NewCtor, Priority,
-                      ConstantPointerNull::get(ConstantDataTy));
+  appendToGlobalCtors(M, NewCtor, Priority, nullptr);
   return UnhandledUsers;
 }
diff --git a/llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll b/llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll
index e275d61764b21..7ddab0b6a815e 100644
--- a/llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll
+++ b/llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll
@@ -15,8 +15,8 @@ define void @existing_ctor() addrspace(1) {
 
 ; CHECK-FINAL: [[TABLE:@[0-9]+]] = internal addrspace(2) global [6 x ptr addrspace(1)] poison, align 8
 
-; CHECK-FINAL: @llvm.global_ctors = appending addrspace(2) global [2 x { i32, ptr addrspace(1), ptr }] [{ i32, ptr addrspace(1), ptr } { i32 0, ptr addrspace(1) @existing_ctor, ptr null }, { i32, ptr addrspace(1), ptr } { i32 10, ptr addrspace(1) [[TABLE_CTOR:@[0-9]+]], ptr null }]
-@llvm.global_ctors = appending global [1 x { i32, ptr addrspace(1), ptr }] [{ i32, ptr addrspace(1), ptr } { i32 0, ptr addrspace(1) @existing_ctor, ptr null }]
+; CHECK-FINAL: @llvm.global_ctors = appending addrspace(2) global [2 x { i32, ptr addrspace(1), ptr addrspace(2) }] [{ i32, ptr addrspace(1), ptr addrspace(2) } { i32 0, ptr addrspace(1) @existing_ctor, ptr addrspace(2) null }, { i32, ptr addrspace(1), ptr addrspace(2) } { i32 10, ptr addrspace(1) [[TABLE_CTOR:@[0-9]+]], ptr addrspace(2) null }]
+@llvm.global_ctors = appending global [1 x { i32, ptr addrspace(1), ptr addrspace(2) }] [{ i32, ptr addrspace(1), ptr addrspace(2) } { i32 0, ptr addrspace(1) @existing_ctor, ptr addrspace(2) null }]
 
 
 

@AlexVlx AlexVlx changed the title Global constructors/destructors are globals [clang][CodeGen] Global constructors/destructors are globals May 31, 2024
Copy link

github-actions bot commented May 31, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@yxsamliu
Copy link
Collaborator

llvm datalayout defines

P - program addr space for functions
G - global addr space for global variables

https://llvm.org/docs/LangRef.html#langref-datalayout

should we use P for llvm.global_ctors instead of G?

@jrtc27
Copy link
Collaborator

jrtc27 commented May 31, 2024

The third argument here is like for llvm.used, it's a way to associate the entry with a global or function. If the corresponding global or function is omitted from the output then the entry will be removed. It isn't used for anything at run time. So I think there should be a consistent story between llvm.used and llvm.global_[cd]tors.

@arsenm
Copy link
Contributor

arsenm commented May 31, 2024

The third argument here is like for llvm.used, it's a way to associate the entry with a global or function. If the corresponding global or function is omitted from the output then the entry will be removed. It isn't used for anything at run time. So I think there should be a consistent story between llvm.used and llvm.global_[cd]tors.

I briefly skimmed the codegen and as far as I could tell an addrspacecast constant expression in global_ctors won't do the right thing, we probably should fix that too

@AlexVlx
Copy link
Contributor Author

AlexVlx commented May 31, 2024

llvm datalayout defines

P - program addr space for functions G - global addr space for global variables

https://llvm.org/docs/LangRef.html#langref-datalayout

should we use P for llvm.global_ctors instead of G?

llvm datalayout defines

P - program addr space for functions G - global addr space for global variables

https://llvm.org/docs/LangRef.html#langref-datalayout

should we use P for llvm.global_ctors instead of G?

llvm datalayout defines

P - program addr space for functions G - global addr space for global variables

https://llvm.org/docs/LangRef.html#langref-datalayout

should we use P for llvm.global_ctors instead of G?

I'm not sure we can do that for anything but the ctor function (where it's already used), since, per langref, the third element can be either a global or a function, and whilst all functions are globals not all globals are functions (the distinction is arbitrary for many targets and, as @jrtc27 says, it's just lifetime ensuring hook, but we should probably stick with doing what we claim to do in the public documents).

@AlexVlx
Copy link
Contributor Author

AlexVlx commented May 31, 2024

The third argument here is like for llvm.used, it's a way to associate the entry with a global or function. If the corresponding global or function is omitted from the output then the entry will be removed. It isn't used for anything at run time. So I think there should be a consistent story between llvm.used and llvm.global_[cd]tors.

IMHO, and I could be wrong, the key challenge that we have both here and re used, is that for these we'd really want a truly generic/flat/everywhere-usable pointer type - it can be casted to / from any other AS etc., essentially a DONTCARE in IR. An unqualified ptr should probably have been that, rather than an implicitly AS0 ptr. It's impossible for Clang to query that from a target today (AFAICT), the only close equivalent being asking for the target's AS corresponding to LangAS::Default, and hoping that it's a sane mapping.

Emitting these unconditionally as unqualified ptrs as is bothers me because we're calling the elements out as globals but then not acting accordingly, and because unqualified actually means AS0, which then relies on the target not being funky with AS0 (some targets are). Perhaps an alternative is to tweak LangRef wording to say that that these are always emitted as unqualified ptrs, and that their ephemeral nature implies that their AS is meaningless?

@arsenm
Copy link
Contributor

arsenm commented May 31, 2024

Perhaps an alternative is to tweak LangRef wording to say that that these are always emitted as unqualified ptrs, and that their ephemeral nature implies that their AS is meaningless?

I think this is the correct way to handle it. Also we'll need a few stripPointerCasts added somewhere

@yxsamliu
Copy link
Collaborator

Perhaps an alternative is to tweak LangRef wording to say that that these are always emitted as unqualified ptrs, and that their ephemeral nature implies that their AS is meaningless?

I think this is the correct way to handle it. Also we'll need a few stripPointerCasts added somewhere

+1

Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this redundant with #93601?

@AlexVlx
Copy link
Contributor Author

AlexVlx commented Jun 6, 2024

Is this redundant with #93601?

I don't think so, they're paired, this is an offshoot of that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. llvm-reduce
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants