Explorar el Código

Give internal linkage to global init function. (#6591)

The mangled name of the global init function is the same for all files
in a package, so giving it external linkage results in link errors if
more than one file in a package has global initializers. We never need
to refer to it from outside the file, so give it internal linkage.

This also requires that we stop eagerly emitting a declaration of it --
if it's empty, we don't emit a definition, and LLVM doesn't allow us to
emit an undefined declaration of an internal linkage symbol.
Richard Smith hace 3 meses
padre
commit
7bfeae0fd5

+ 13 - 2
toolchain/lower/file_context.cpp

@@ -90,6 +90,11 @@ auto FileContext::PrepareToLower() -> void {
 
   // Lower function declarations.
   for (auto [id, _] : sem_ir_->functions().enumerate()) {
+    if (id == sem_ir().global_ctor_id()) {
+      // The global constructor is only lowered when we generate its definition.
+      // LLVM doesn't allow an internal linkage function to be undefined.
+      continue;
+    }
     functions_.Set(id, BuildFunctionDecl(id));
   }
 
@@ -151,11 +156,12 @@ auto FileContext::LowerDefinitions() -> void {
   // variables.
   if (auto global_ctor_id = sem_ir().global_ctor_id();
       global_ctor_id.has_value()) {
+    auto* llvm_function = BuildFunctionDecl(global_ctor_id);
+    functions_.Set(global_ctor_id, llvm_function);
     const auto& global_ctor = sem_ir().functions().Get(global_ctor_id);
     BuildFunctionBody(global_ctor_id, SemIR::SpecificId::None, global_ctor,
                       *this, global_ctor);
-    llvm::appendToGlobalCtors(llvm_module(),
-                              GetFunction(sem_ir().global_ctor_id()),
+    llvm::appendToGlobalCtors(llvm_module(), llvm_function,
                               /*Priority=*/0);
   }
 }
@@ -460,6 +466,11 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id,
   // `available_externally` definition.
   auto linkage = specific_id.has_value() ? llvm::Function::LinkOnceODRLinkage
                                          : llvm::Function::ExternalLinkage;
+  if (function_id == sem_ir().global_ctor_id()) {
+    // The global constructor name would collide with global constructors for
+    // other files in the same package, so use an internal linkage symbol.
+    linkage = llvm::Function::InternalLinkage;
+  }
 
   Mangler m(*this);
   std::string mangled_name = m.Mangle(function_id, specific_id);

+ 1 - 1
toolchain/lower/testdata/global/class_obj.carbon

@@ -21,7 +21,7 @@ var a: A = {};
 // CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @_C__global_init.Main, ptr null }]
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !4 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 @_Ca.Main, ptr align 1 @A.val.loc14_1, i64 0, i1 false), !dbg !7
 // CHECK:STDOUT:   ret void, !dbg !8

+ 4 - 7
toolchain/lower/testdata/global/class_with_fun.carbon

@@ -31,19 +31,16 @@ var a: A = {};
 // CHECK:STDOUT:   ret void, !dbg !8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
+// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !9 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !9 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 @_Ca.Main, ptr align 1 @A.val.loc15_12, i64 0, i1 false), !dbg !12
 // CHECK:STDOUT:   ret void, !dbg !13
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
-// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1
-// CHECK:STDOUT:
-// CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
-// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nounwind }
 // CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT:

+ 1 - 1
toolchain/lower/testdata/global/simple_init.carbon

@@ -18,7 +18,7 @@ var a: i32 = 0;
 // CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @_C__global_init.Main, ptr null }]
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !4 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   store i32 0, ptr @_Ca.Main, align 4, !dbg !7
 // CHECK:STDOUT:   ret void, !dbg !8

+ 1 - 1
toolchain/lower/testdata/global/simple_with_fun.carbon

@@ -29,7 +29,7 @@ var a: i32 = test_a();
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !9 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !9 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %test_a.call = call i32 @_Ctest_a.Main(), !dbg !12
 // CHECK:STDOUT:   store i32 %test_a.call, ptr @_Ca.Main, align 4, !dbg !13

+ 1 - 1
toolchain/lower/testdata/global/use.carbon

@@ -32,7 +32,7 @@ fn F() -> i32 {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !10 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !10 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   store i32 5, ptr @_Ca.Main, align 4, !dbg !13
 // CHECK:STDOUT:   %.loc14 = load i32, ptr @_Ca.Main, align 4, !dbg !14

+ 4 - 4
toolchain/lower/testdata/var/global.carbon

@@ -57,7 +57,7 @@ var (_: i32, _: i32) = (3, 4);
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !9 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !9 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   store i32 1, ptr @_Ca.Main, align 4, !dbg !12
 // CHECK:STDOUT:   ret void, !dbg !13
@@ -102,7 +102,7 @@ var (_: i32, _: i32) = (3, 4);
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !11 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !11 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   store i32 2, ptr @_Cb.Main, align 4, !dbg !14
 // CHECK:STDOUT:   store i32 3, ptr @_Cc.Main, align 4, !dbg !15
@@ -151,7 +151,7 @@ var (_: i32, _: i32) = (3, 4);
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !11 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !11 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 @_Cd.Main, ptr align 4 @tuple.0a0.loc4_1, i64 8, i1 false), !dbg !14
 // CHECK:STDOUT:   ret void, !dbg !15
@@ -195,7 +195,7 @@ var (_: i32, _: i32) = (3, 4);
 // CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @_C__global_init.Main, ptr null }]
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !4 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 @_Cx.Main, ptr align 4 @tuple.21c.loc4_1, i64 8, i1 false), !dbg !7
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 @var.anon.var_patt.loc5, ptr align 4 @tuple.ffd.loc5_1, i64 8, i1 false), !dbg !8

+ 2 - 2
toolchain/lower/testdata/var/import.carbon

@@ -55,7 +55,7 @@ fn Z() -> i32 { return z; }
 // CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @_C__global_init.Main, ptr null }]
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !4 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   store i32 42, ptr @_Cv.Main, align 4, !dbg !7
 // CHECK:STDOUT:   ret void, !dbg !8
@@ -113,7 +113,7 @@ fn Z() -> i32 { return z; }
 // CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @_C__global_init.Main, ptr null }]
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nounwind
-// CHECK:STDOUT: define void @_C__global_init.Main() #0 !dbg !4 {
+// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !4 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 @_Cv.Main, ptr align 4 @tuple.21c.loc4_1, i64 8, i1 false), !dbg !7
 // CHECK:STDOUT:   store i32 4, ptr @_Cy.Main, align 4, !dbg !8