Skip to content

Commit 61e87c3

Browse files
authored
Lower global variables (#4228)
1 parent 2d3842f commit 61e87c3

File tree

11 files changed

+205
-5
lines changed

11 files changed

+205
-5
lines changed

toolchain/check/global_init.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ auto GlobalInit::Finalize() -> void {
3434
context_->inst_block_stack().Pop();
3535

3636
auto name_id = context_->sem_ir().identifiers().Add("__global_init");
37-
context_->sem_ir().functions().Add(
37+
context_->sem_ir().set_global_ctor_id(context_->sem_ir().functions().Add(
3838
{{.name_id = SemIR::NameId::ForIdentifier(name_id),
3939
.parent_scope_id = SemIR::NameScopeId::Package,
4040
.generic_id = SemIR::GenericId::Invalid,
@@ -47,7 +47,7 @@ auto GlobalInit::Finalize() -> void {
4747
.non_owning_decl_id = SemIR::InstId::Invalid,
4848
.first_owning_decl_id = SemIR::InstId::Invalid},
4949
{.return_storage_id = SemIR::InstId::Invalid,
50-
.body_block_ids = {SemIR::InstBlockId::GlobalInit}}});
50+
.body_block_ids = {SemIR::InstBlockId::GlobalInit}}}));
5151
}
5252

5353
} // namespace Carbon::Check

toolchain/lower/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,6 @@ cc_library(
5353
"//toolchain/sem_ir:inst_namer",
5454
"@llvm-project//llvm:Core",
5555
"@llvm-project//llvm:Support",
56+
"@llvm-project//llvm:TransformUtils",
5657
],
5758
)

toolchain/lower/file_context.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "common/vlog.h"
88
#include "llvm/ADT/STLExtras.h"
99
#include "llvm/ADT/Sequence.h"
10+
#include "llvm/Transforms/Utils/ModuleUtils.h"
1011
#include "toolchain/base/kind_switch.h"
1112
#include "toolchain/lower/constant.h"
1213
#include "toolchain/lower/function_context.h"
@@ -50,7 +51,15 @@ auto FileContext::Run() -> std::unique_ptr<llvm::Module> {
5051
functions_[i] = BuildFunctionDecl(SemIR::FunctionId(i));
5152
}
5253

53-
// TODO: Lower global variable declarations.
54+
// Lower global variable declarations.
55+
for (auto inst_id :
56+
sem_ir().inst_blocks().Get(sem_ir().top_inst_block_id())) {
57+
// Only `VarStorage` indicates a global variable declaration in the
58+
// top instruction block.
59+
if (auto var = sem_ir().insts().TryGetAs<SemIR::VarStorage>(inst_id)) {
60+
global_variables_.Insert(inst_id, BuildGlobalVariableDecl(*var));
61+
}
62+
}
5463

5564
// Lower constants.
5665
constants_.resize(sem_ir_->insts().size());
@@ -60,8 +69,13 @@ auto FileContext::Run() -> std::unique_ptr<llvm::Module> {
6069
for (auto i : llvm::seq(sem_ir_->functions().size())) {
6170
BuildFunctionDefinition(SemIR::FunctionId(i));
6271
}
63-
64-
// TODO: Lower global variable initializers.
72+
// Append `__global_init` to `llvm::global_ctors` to initialize global
73+
// variables.
74+
if (sem_ir().global_ctor_id().is_valid()) {
75+
llvm::appendToGlobalCtors(llvm_module(),
76+
GetFunction(sem_ir().global_ctor_id()),
77+
/*Priority=*/0);
78+
}
6579

6680
return std::move(llvm_module_);
6781
}
@@ -463,4 +477,17 @@ auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* {
463477
}
464478
}
465479

480+
auto FileContext::BuildGlobalVariableDecl(SemIR::VarStorage var_storage)
481+
-> llvm::GlobalVariable* {
482+
// TODO: Mangle name.
483+
auto mangled_name =
484+
*sem_ir().names().GetAsStringIfIdentifier(var_storage.name_id);
485+
auto* type =
486+
var_storage.type_id.is_valid() ? GetType(var_storage.type_id) : nullptr;
487+
return new llvm::GlobalVariable(llvm_module(), type,
488+
/*isConstant=*/false,
489+
llvm::GlobalVariable::InternalLinkage,
490+
/*Initializer=*/nullptr, mangled_name);
491+
}
492+
466493
} // namespace Carbon::Lower

toolchain/lower/file_context.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class FileContext {
5959
auto llvm_module() -> llvm::Module& { return *llvm_module_; }
6060
auto sem_ir() -> const SemIR::File& { return *sem_ir_; }
6161
auto inst_namer() -> const SemIR::InstNamer* { return inst_namer_; }
62+
auto global_variables() -> const Map<SemIR::InstId, llvm::GlobalVariable*>& {
63+
return global_variables_;
64+
}
6265

6366
private:
6467
// Builds the declaration for the given function, which should then be cached
@@ -73,6 +76,11 @@ class FileContext {
7376
// the caller.
7477
auto BuildType(SemIR::InstId inst_id) -> llvm::Type*;
7578

79+
// Builds the global for the given instruction, which should then be cached by
80+
// the caller.
81+
auto BuildGlobalVariableDecl(SemIR::VarStorage var_storage)
82+
-> llvm::GlobalVariable*;
83+
7684
// State for building the LLVM IR.
7785
llvm::LLVMContext* llvm_context_;
7886
std::unique_ptr<llvm::Module> llvm_module_;
@@ -101,6 +109,9 @@ class FileContext {
101109
// Maps constants to their lowered values.
102110
// We resize this directly to the (often large) correct size.
103111
llvm::SmallVector<llvm::Constant*, 0> constants_;
112+
113+
// Maps global variables to their lowered variant.
114+
Map<SemIR::InstId, llvm::GlobalVariable*> global_variables_;
104115
};
105116

106117
} // namespace Carbon::Lower

toolchain/lower/function_context.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ class FunctionContext {
5252
if (auto result = locals_.Lookup(inst_id)) {
5353
return result.value();
5454
}
55+
56+
if (auto result = file_context_->global_variables().Lookup(inst_id)) {
57+
return result.value();
58+
}
5559
return file_context_->GetGlobal(inst_id);
5660
}
5761

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
//
5+
// AUTOUPDATE
6+
// TIP: To test this file alone, run:
7+
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/global/class_obj.carbon
8+
// TIP: To dump output, run:
9+
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/global/class_obj.carbon
10+
class A {}
11+
12+
var a: A = {};
13+
14+
// CHECK:STDOUT: ; ModuleID = 'class_obj.carbon'
15+
// CHECK:STDOUT: source_filename = "class_obj.carbon"
16+
// CHECK:STDOUT:
17+
// CHECK:STDOUT: @a = internal global {}
18+
// CHECK:STDOUT: @struct.loc12_14 = internal constant {} zeroinitializer
19+
// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @__global_init, ptr null }]
20+
// CHECK:STDOUT:
21+
// CHECK:STDOUT: define void @__global_init() {
22+
// CHECK:STDOUT: entry:
23+
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 @a, ptr align 1 @struct.loc12_14, i64 0, i1 false)
24+
// CHECK:STDOUT: ret void
25+
// CHECK:STDOUT: }
26+
// CHECK:STDOUT:
27+
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
28+
// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
29+
// CHECK:STDOUT:
30+
// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
//
5+
// AUTOUPDATE
6+
// TIP: To test this file alone, run:
7+
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/global/class_with_fun.carbon
8+
// TIP: To dump output, run:
9+
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/global/class_with_fun.carbon
10+
class A {}
11+
12+
fn ret_a() -> A {
13+
return {};
14+
}
15+
16+
var a: A = {};
17+
18+
// CHECK:STDOUT: ; ModuleID = 'class_with_fun.carbon'
19+
// CHECK:STDOUT: source_filename = "class_with_fun.carbon"
20+
// CHECK:STDOUT:
21+
// CHECK:STDOUT: @a = internal global {}
22+
// CHECK:STDOUT: @struct.loc13_12 = internal constant {} zeroinitializer
23+
// CHECK:STDOUT: @struct.loc16_14 = internal constant {} zeroinitializer
24+
// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @__global_init, ptr null }]
25+
// CHECK:STDOUT:
26+
// CHECK:STDOUT: define void @ret_a(ptr sret({}) %return) {
27+
// CHECK:STDOUT: entry:
28+
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %return, ptr align 1 @struct.loc13_12, i64 0, i1 false)
29+
// CHECK:STDOUT: ret void
30+
// CHECK:STDOUT: }
31+
// CHECK:STDOUT:
32+
// CHECK:STDOUT: define void @__global_init() {
33+
// CHECK:STDOUT: entry:
34+
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 @a, ptr align 1 @struct.loc16_14, i64 0, i1 false)
35+
// CHECK:STDOUT: ret void
36+
// CHECK:STDOUT: }
37+
// CHECK:STDOUT:
38+
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
39+
// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
40+
// CHECK:STDOUT:
41+
// CHECK:STDOUT: ; uselistorder directives
42+
// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
43+
// CHECK:STDOUT:
44+
// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
//
5+
// AUTOUPDATE
6+
// TIP: To test this file alone, run:
7+
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/global/decl.carbon
8+
// TIP: To dump output, run:
9+
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/global/decl.carbon
10+
var a: i32;
11+
12+
// CHECK:STDOUT: ; ModuleID = 'decl.carbon'
13+
// CHECK:STDOUT: source_filename = "decl.carbon"
14+
// CHECK:STDOUT:
15+
// CHECK:STDOUT: @a = internal global i32
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
//
5+
// AUTOUPDATE
6+
// TIP: To test this file alone, run:
7+
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/global/simple_init.carbon
8+
// TIP: To dump output, run:
9+
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/global/simple_init.carbon
10+
var a: i32 = 0;
11+
12+
// CHECK:STDOUT: ; ModuleID = 'simple_init.carbon'
13+
// CHECK:STDOUT: source_filename = "simple_init.carbon"
14+
// CHECK:STDOUT:
15+
// CHECK:STDOUT: @a = internal global i32
16+
// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @__global_init, ptr null }]
17+
// CHECK:STDOUT:
18+
// CHECK:STDOUT: define void @__global_init() {
19+
// CHECK:STDOUT: entry:
20+
// CHECK:STDOUT: store i32 0, ptr @a, align 4
21+
// CHECK:STDOUT: ret void
22+
// CHECK:STDOUT: }
23+
// CHECK:STDOUT:
24+
// CHECK:STDOUT: ; uselistorder directives
25+
// CHECK:STDOUT: uselistorder i32 0, { 1, 0 }
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
//
5+
// AUTOUPDATE
6+
// TIP: To test this file alone, run:
7+
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/global/simple_with_fun.carbon
8+
// TIP: To dump output, run:
9+
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/global/simple_with_fun.carbon
10+
11+
fn test_a() -> i32 {
12+
return 0;
13+
}
14+
15+
var a: i32 = test_a();
16+
17+
// CHECK:STDOUT: ; ModuleID = 'simple_with_fun.carbon'
18+
// CHECK:STDOUT: source_filename = "simple_with_fun.carbon"
19+
// CHECK:STDOUT:
20+
// CHECK:STDOUT: @a = internal global i32
21+
// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @__global_init, ptr null }]
22+
// CHECK:STDOUT:
23+
// CHECK:STDOUT: define i32 @test_a() {
24+
// CHECK:STDOUT: entry:
25+
// CHECK:STDOUT: ret i32 0
26+
// CHECK:STDOUT: }
27+
// CHECK:STDOUT:
28+
// CHECK:STDOUT: define void @__global_init() {
29+
// CHECK:STDOUT: entry:
30+
// CHECK:STDOUT: %test_a.call = call i32 @test_a()
31+
// CHECK:STDOUT: store i32 %test_a.call, ptr @a, align 4
32+
// CHECK:STDOUT: ret void
33+
// CHECK:STDOUT: }
34+
// CHECK:STDOUT:
35+
// CHECK:STDOUT: ; uselistorder directives
36+
// CHECK:STDOUT: uselistorder i32 0, { 1, 0 }

toolchain/sem_ir/file.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ class File : public Printable<File> {
163163
auto set_top_inst_block_id(InstBlockId block_id) -> void {
164164
top_inst_block_id_ = block_id;
165165
}
166+
auto global_ctor_id() const -> FunctionId { return global_ctor_id_; }
167+
auto set_global_ctor_id(FunctionId function_id) -> void {
168+
global_ctor_id_ = function_id;
169+
}
166170

167171
// Returns true if there were errors creating the semantics IR.
168172
auto has_errors() const -> bool { return has_errors_; }
@@ -242,6 +246,9 @@ class File : public Printable<File> {
242246
// The top instruction block ID.
243247
InstBlockId top_inst_block_id_ = InstBlockId::Invalid;
244248

249+
// The global constructor function id.
250+
FunctionId global_ctor_id_ = FunctionId::Invalid;
251+
245252
// Storage for instructions that represent computed global constants, such as
246253
// types.
247254
ConstantStore constants_;

0 commit comments

Comments
 (0)