From e9a8e35462764336e29f832714278d2fd544a812 Mon Sep 17 00:00:00 2001 From: hamphet Date: Sun, 18 Aug 2024 16:53:10 -0700 Subject: [PATCH 1/2] Lower global variables --- toolchain/check/global_init.cpp | 4 +- toolchain/lower/BUILD | 1 + toolchain/lower/file_context.cpp | 35 +++++++++++++-- toolchain/lower/file_context.h | 8 ++++ toolchain/lower/function_context.h | 4 ++ .../lower/testdata/global/class_obj.carbon | 30 +++++++++++++ .../testdata/global/class_with_fun.carbon | 44 +++++++++++++++++++ toolchain/lower/testdata/global/decl.carbon | 15 +++++++ .../lower/testdata/global/simple_init.carbon | 25 +++++++++++ .../testdata/global/simple_with_fun.carbon | 36 +++++++++++++++ toolchain/sem_ir/file.h | 7 +++ 11 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 toolchain/lower/testdata/global/class_obj.carbon create mode 100644 toolchain/lower/testdata/global/class_with_fun.carbon create mode 100644 toolchain/lower/testdata/global/decl.carbon create mode 100644 toolchain/lower/testdata/global/simple_init.carbon create mode 100644 toolchain/lower/testdata/global/simple_with_fun.carbon diff --git a/toolchain/check/global_init.cpp b/toolchain/check/global_init.cpp index e08a10f2499e0..a0f00812cb2c6 100644 --- a/toolchain/check/global_init.cpp +++ b/toolchain/check/global_init.cpp @@ -34,7 +34,7 @@ auto GlobalInit::Finalize() -> void { context_->inst_block_stack().Pop(); auto name_id = context_->sem_ir().identifiers().Add("__global_init"); - context_->sem_ir().functions().Add( + context_->sem_ir().set_global_ctor_id(context_->sem_ir().functions().Add( {{.name_id = SemIR::NameId::ForIdentifier(name_id), .parent_scope_id = SemIR::NameScopeId::Package, .generic_id = SemIR::GenericId::Invalid, @@ -47,7 +47,7 @@ auto GlobalInit::Finalize() -> void { .non_owning_decl_id = SemIR::InstId::Invalid, .first_owning_decl_id = SemIR::InstId::Invalid}, {.return_storage_id = SemIR::InstId::Invalid, - .body_block_ids = {SemIR::InstBlockId::GlobalInit}}}); + .body_block_ids = {SemIR::InstBlockId::GlobalInit}}})); } } // namespace Carbon::Check diff --git a/toolchain/lower/BUILD b/toolchain/lower/BUILD index 5f6469790e8bd..4081a5b24d835 100644 --- a/toolchain/lower/BUILD +++ b/toolchain/lower/BUILD @@ -53,5 +53,6 @@ cc_library( "//toolchain/sem_ir:inst_namer", "@llvm-project//llvm:Core", "@llvm-project//llvm:Support", + "@llvm-project//llvm:TransformUtils", ], ) diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index ae264c64677d3..d003c4055ff86 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -7,6 +7,7 @@ #include "common/vlog.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Sequence.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" #include "toolchain/base/kind_switch.h" #include "toolchain/lower/constant.h" #include "toolchain/lower/function_context.h" @@ -50,7 +51,17 @@ auto FileContext::Run() -> std::unique_ptr { functions_[i] = BuildFunctionDecl(SemIR::FunctionId(i)); } - // TODO: Lower global variable declarations. + // Lower global variable declarations. + for (auto inst_id : + sem_ir().inst_blocks().Get(sem_ir().top_inst_block_id())) { + // Only `VarStorage` indicates global variable declaration in the + // top instruction block. + if (auto inst = sem_ir().insts().Get(inst_id); + inst.kind() != SemIR::InstKind::VarStorage) { + continue; + } + global_variables_.Insert(inst_id, BuildGlobalVariableDecl(inst_id)); + } // Lower constants. constants_.resize(sem_ir_->insts().size()); @@ -60,8 +71,13 @@ auto FileContext::Run() -> std::unique_ptr { for (auto i : llvm::seq(sem_ir_->functions().size())) { BuildFunctionDefinition(SemIR::FunctionId(i)); } - - // TODO: Lower global variable initializers. + // Append `__global_init` to `llvm::global_ctors` to initialize global + // variables. + if (sem_ir().global_ctor_id().is_valid()) { + llvm::appendToGlobalCtors(llvm_module(), + GetFunction(sem_ir().global_ctor_id()), + /*Priority=*/0); + } return std::move(llvm_module_); } @@ -463,4 +479,17 @@ auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* { } } +auto FileContext::BuildGlobalVariableDecl(SemIR::InstId inst_id) + -> llvm::GlobalVariable* { + auto var_storage = sem_ir().insts().Get(inst_id).As(); + auto mangled_name = + *sem_ir().names().GetAsStringIfIdentifier(var_storage.name_id); + auto* type = + var_storage.type_id.is_valid() ? GetType(var_storage.type_id) : nullptr; + return new llvm::GlobalVariable(llvm_module(), type, + /*isConstant=*/false, + llvm::GlobalVariable::InternalLinkage, + /*Initializer=*/nullptr, mangled_name); +} + } // namespace Carbon::Lower diff --git a/toolchain/lower/file_context.h b/toolchain/lower/file_context.h index 62e37543908b7..76b81d5e9425a 100644 --- a/toolchain/lower/file_context.h +++ b/toolchain/lower/file_context.h @@ -59,6 +59,9 @@ class FileContext { auto llvm_module() -> llvm::Module& { return *llvm_module_; } auto sem_ir() -> const SemIR::File& { return *sem_ir_; } auto inst_namer() -> const SemIR::InstNamer* { return inst_namer_; } + auto global_variables() -> const Map& { + return global_variables_; + } private: // Builds the declaration for the given function, which should then be cached @@ -73,6 +76,8 @@ class FileContext { // the caller. auto BuildType(SemIR::InstId inst_id) -> llvm::Type*; + auto BuildGlobalVariableDecl(SemIR::InstId inst_id) -> llvm::GlobalVariable*; + // State for building the LLVM IR. llvm::LLVMContext* llvm_context_; std::unique_ptr llvm_module_; @@ -101,6 +106,9 @@ class FileContext { // Maps constants to their lowered values. // We resize this directly to the (often large) correct size. llvm::SmallVector constants_; + + // Maps global variables to their lowered variant. + Map global_variables_; }; } // namespace Carbon::Lower diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index d9e73c4542a8d..051b73ed67e5d 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -52,6 +52,10 @@ class FunctionContext { if (auto result = locals_.Lookup(inst_id)) { return result.value(); } + + if (auto result = file_context_->global_variables().Lookup(inst_id)) { + return result.value(); + } return file_context_->GetGlobal(inst_id); } diff --git a/toolchain/lower/testdata/global/class_obj.carbon b/toolchain/lower/testdata/global/class_obj.carbon new file mode 100644 index 0000000000000..f29ffdb413548 --- /dev/null +++ b/toolchain/lower/testdata/global/class_obj.carbon @@ -0,0 +1,30 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/global/class_obj.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/global/class_obj.carbon +class A {} + +var a: A = {}; + +// CHECK:STDOUT: ; ModuleID = 'class_obj.carbon' +// CHECK:STDOUT: source_filename = "class_obj.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @a = internal global {} +// CHECK:STDOUT: @struct.loc12_14 = internal constant {} zeroinitializer +// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @__global_init, ptr null }] +// CHECK:STDOUT: +// CHECK:STDOUT: define void @__global_init() { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 @a, ptr align 1 @struct.loc12_14, i64 0, i1 false) +// CHECK:STDOUT: ret void +// 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 nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/global/class_with_fun.carbon b/toolchain/lower/testdata/global/class_with_fun.carbon new file mode 100644 index 0000000000000..290324de43f2b --- /dev/null +++ b/toolchain/lower/testdata/global/class_with_fun.carbon @@ -0,0 +1,44 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/global/class_with_fun.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/global/class_with_fun.carbon +class A {} + +fn ret_a() -> A { + return {}; +} + +var a: A = {}; + +// CHECK:STDOUT: ; ModuleID = 'class_with_fun.carbon' +// CHECK:STDOUT: source_filename = "class_with_fun.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @a = internal global {} +// CHECK:STDOUT: @struct.loc13_12 = internal constant {} zeroinitializer +// CHECK:STDOUT: @struct.loc16_14 = internal constant {} zeroinitializer +// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @__global_init, ptr null }] +// CHECK:STDOUT: +// CHECK:STDOUT: define void @ret_a(ptr sret({}) %return) { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %return, ptr align 1 @struct.loc13_12, i64 0, i1 false) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @__global_init() { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 @a, ptr align 1 @struct.loc16_14, i64 0, i1 false) +// CHECK:STDOUT: ret void +// 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 nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/toolchain/lower/testdata/global/decl.carbon b/toolchain/lower/testdata/global/decl.carbon new file mode 100644 index 0000000000000..ba68e1b4973ed --- /dev/null +++ b/toolchain/lower/testdata/global/decl.carbon @@ -0,0 +1,15 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/global/decl.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/global/decl.carbon +var a: i32; + +// CHECK:STDOUT: ; ModuleID = 'decl.carbon' +// CHECK:STDOUT: source_filename = "decl.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @a = internal global i32 diff --git a/toolchain/lower/testdata/global/simple_init.carbon b/toolchain/lower/testdata/global/simple_init.carbon new file mode 100644 index 0000000000000..b1f376eb327ad --- /dev/null +++ b/toolchain/lower/testdata/global/simple_init.carbon @@ -0,0 +1,25 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/global/simple_init.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/global/simple_init.carbon +var a: i32 = 0; + +// CHECK:STDOUT: ; ModuleID = 'simple_init.carbon' +// CHECK:STDOUT: source_filename = "simple_init.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @a = internal global i32 +// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @__global_init, ptr null }] +// CHECK:STDOUT: +// CHECK:STDOUT: define void @__global_init() { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: store i32 0, ptr @a, align 4 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 0, { 1, 0 } diff --git a/toolchain/lower/testdata/global/simple_with_fun.carbon b/toolchain/lower/testdata/global/simple_with_fun.carbon new file mode 100644 index 0000000000000..164c5bbd27963 --- /dev/null +++ b/toolchain/lower/testdata/global/simple_with_fun.carbon @@ -0,0 +1,36 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/global/simple_with_fun.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/global/simple_with_fun.carbon + +fn test_a() -> i32 { + return 0; +} + +var a: i32 = test_a(); + +// CHECK:STDOUT: ; ModuleID = 'simple_with_fun.carbon' +// CHECK:STDOUT: source_filename = "simple_with_fun.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @a = internal global i32 +// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @__global_init, ptr null }] +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @test_a() { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i32 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @__global_init() { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %test_a.call = call i32 @test_a() +// CHECK:STDOUT: store i32 %test_a.call, ptr @a, align 4 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder i32 0, { 1, 0 } diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 89b76e3f6c24d..c7d707991577d 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -163,6 +163,10 @@ class File : public Printable { auto set_top_inst_block_id(InstBlockId block_id) -> void { top_inst_block_id_ = block_id; } + auto global_ctor_id() const -> FunctionId { return global_ctor_id_; } + auto set_global_ctor_id(FunctionId function_id) -> void { + global_ctor_id_ = function_id; + } // Returns true if there were errors creating the semantics IR. auto has_errors() const -> bool { return has_errors_; } @@ -242,6 +246,9 @@ class File : public Printable { // The top instruction block ID. InstBlockId top_inst_block_id_ = InstBlockId::Invalid; + // The global constructor function id. + FunctionId global_ctor_id_ = FunctionId::Invalid; + // Storage for instructions that represent computed global constants, such as // types. ConstantStore constants_; From 233882c97ee7afbf0bcfdcc72de2da55cbf27d24 Mon Sep 17 00:00:00 2001 From: hamphet Date: Mon, 19 Aug 2024 15:32:24 -0700 Subject: [PATCH 2/2] Fixed typos and changed `insts` calls. --- toolchain/lower/file_context.cpp | 12 +++++------- toolchain/lower/file_context.h | 5 ++++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index d003c4055ff86..d938ca81f8b3e 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -54,13 +54,11 @@ auto FileContext::Run() -> std::unique_ptr { // Lower global variable declarations. for (auto inst_id : sem_ir().inst_blocks().Get(sem_ir().top_inst_block_id())) { - // Only `VarStorage` indicates global variable declaration in the + // Only `VarStorage` indicates a global variable declaration in the // top instruction block. - if (auto inst = sem_ir().insts().Get(inst_id); - inst.kind() != SemIR::InstKind::VarStorage) { - continue; + if (auto var = sem_ir().insts().TryGetAs(inst_id)) { + global_variables_.Insert(inst_id, BuildGlobalVariableDecl(*var)); } - global_variables_.Insert(inst_id, BuildGlobalVariableDecl(inst_id)); } // Lower constants. @@ -479,9 +477,9 @@ auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* { } } -auto FileContext::BuildGlobalVariableDecl(SemIR::InstId inst_id) +auto FileContext::BuildGlobalVariableDecl(SemIR::VarStorage var_storage) -> llvm::GlobalVariable* { - auto var_storage = sem_ir().insts().Get(inst_id).As(); + // TODO: Mangle name. auto mangled_name = *sem_ir().names().GetAsStringIfIdentifier(var_storage.name_id); auto* type = diff --git a/toolchain/lower/file_context.h b/toolchain/lower/file_context.h index 76b81d5e9425a..dbc5607576e27 100644 --- a/toolchain/lower/file_context.h +++ b/toolchain/lower/file_context.h @@ -76,7 +76,10 @@ class FileContext { // the caller. auto BuildType(SemIR::InstId inst_id) -> llvm::Type*; - auto BuildGlobalVariableDecl(SemIR::InstId inst_id) -> llvm::GlobalVariable*; + // Builds the global for the given instruction, which should then be cached by + // the caller. + auto BuildGlobalVariableDecl(SemIR::VarStorage var_storage) + -> llvm::GlobalVariable*; // State for building the LLVM IR. llvm::LLVMContext* llvm_context_;