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.
This commit is contained in:
Richard Smith
2026-01-13 22:02:12 +00:00
committed by GitHub
parent 050d1f0c30
commit 7bfeae0fd5
8 changed files with 27 additions and 19 deletions
+13 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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