From e91840e1b6df2ac22065fc935ca2844cbde739eb Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 2 Jun 2025 11:02:32 -0700 Subject: [PATCH] Split a cross-file `Lower::Context` out of `Lower::FileContext`. (#5583) In preparation for lowering information from multiple `SemIR::File`s into a single `llvm::Module`. The primary purpose of this is to support lowering a local specific for an imported generic function, where the instructions for the generic function are in a different file than the instructions for the specific. See #5475 for a draft PR implementing that functionality on top of this. The per-`llvm::Module` state now lives in `Lower::Context`, and `Lower::FileContext` tracks only the per-`SemIR::File` information. `Lower::Context` should not mention any `SemIR` IDs that are file-specific. For now, the C++ lowering and the specific coalescing logic are kept per-file for simplicity. --- toolchain/driver/compile_subcommand.cpp | 4 +- toolchain/lower/BUILD | 2 + toolchain/lower/context.cpp | 88 ++++++++++++++++ toolchain/lower/context.h | 133 ++++++++++++++++++++++++ toolchain/lower/file_context.cpp | 124 ++++++++-------------- toolchain/lower/file_context.h | 105 +++++-------------- toolchain/lower/lower.cpp | 12 ++- toolchain/lower/lower.h | 2 +- toolchain/lower/mangler.h | 11 +- 9 files changed, 309 insertions(+), 172 deletions(-) create mode 100644 toolchain/lower/context.cpp create mode 100644 toolchain/lower/context.h diff --git a/toolchain/driver/compile_subcommand.cpp b/toolchain/driver/compile_subcommand.cpp index 5d4ec46cbc21..af5b14459f05 100644 --- a/toolchain/driver/compile_subcommand.cpp +++ b/toolchain/driver/compile_subcommand.cpp @@ -713,8 +713,8 @@ auto CompilationUnit::RunLower() -> void { subtrees = cache_->tree_and_subtrees_getters(); } module_ = Lower::LowerToLLVM(*llvm_context_, driver_env_->fs, subtrees, - input_filename_, *sem_ir_, sem_ir_->cpp_ast(), - &inst_namer, vlog_stream_); + input_filename_, *sem_ir_, &inst_namer, + vlog_stream_); }); if (vlog_stream_) { CARBON_VLOG("*** llvm::Module ***\n"); diff --git a/toolchain/lower/BUILD b/toolchain/lower/BUILD index 44676d59181a..c86ca22509e2 100644 --- a/toolchain/lower/BUILD +++ b/toolchain/lower/BUILD @@ -30,6 +30,7 @@ cc_library( srcs = [ "constant.cpp", "constant.h", + "context.cpp", "file_context.cpp", "function_context.cpp", "mangler.cpp", @@ -40,6 +41,7 @@ cc_library( "handle*.cpp", ]), hdrs = [ + "context.h", "file_context.h", "function_context.h", ], diff --git a/toolchain/lower/context.cpp b/toolchain/lower/context.cpp new file mode 100644 index 000000000000..f7296bfc8b26 --- /dev/null +++ b/toolchain/lower/context.cpp @@ -0,0 +1,88 @@ +// 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 + +#include "toolchain/lower/context.h" + +#include "common/check.h" +#include "common/vlog.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +#include "toolchain/lower/file_context.h" +#include "toolchain/sem_ir/inst_namer.h" + +namespace Carbon::Lower { + +Context::Context(llvm::LLVMContext& llvm_context, + llvm::IntrusiveRefCntPtr fs, + std::optional> + tree_and_subtrees_getters_for_debug_info, + llvm::StringRef module_name, llvm::raw_ostream* vlog_stream) + : llvm_context_(&llvm_context), + llvm_module_(std::make_unique(module_name, llvm_context)), + file_system_(std::move(fs)), + di_builder_(*llvm_module_), + di_compile_unit_( + tree_and_subtrees_getters_for_debug_info + ? BuildDICompileUnit(module_name, *llvm_module_, di_builder_) + : nullptr), + tree_and_subtrees_getters_for_debug_info_( + tree_and_subtrees_getters_for_debug_info), + vlog_stream_(vlog_stream) {} + +auto Context::GetFileContext(const SemIR::File* file, + const SemIR::InstNamer* inst_namer) + -> FileContext& { + auto insert_result = file_contexts_.Insert(file->check_ir_id(), [&] { + auto file_context = + std::make_unique(*this, *file, inst_namer, vlog_stream_); + file_context->PrepareToLower(); + return file_context; + }); + return *insert_result.value(); +} + +auto Context::Finalize() && -> std::unique_ptr { + file_contexts_.ForEach( + [](auto, auto& file_context) { file_context->Finalize(); }); + return std::move(llvm_module_); +} + +auto Context::BuildDICompileUnit(llvm::StringRef module_name, + llvm::Module& llvm_module, + llvm::DIBuilder& di_builder) + -> llvm::DICompileUnit* { + llvm_module.addModuleFlag(llvm::Module::Max, "Dwarf Version", 5); + llvm_module.addModuleFlag(llvm::Module::Warning, "Debug Info Version", + llvm::DEBUG_METADATA_VERSION); + // TODO: Include directory path in the compile_unit_file. + llvm::DIFile* compile_unit_file = di_builder.createFile(module_name, ""); + // TODO: Introduce a new language code for Carbon. C works well for now since + // it's something debuggers will already know/have support for at least. + // Probably have to bump to C++ at some point for virtual functions, + // templates, etc. + return di_builder.createCompileUnit(llvm::dwarf::DW_LANG_C, compile_unit_file, + "carbon", + /*isOptimized=*/false, /*Flags=*/"", + /*RV=*/0); +} + +auto Context::GetLocForDI(SemIR::AbsoluteNodeId abs_node_id) -> LocForDI { + const auto& tree_and_subtrees = + (*tree_and_subtrees_getters_for_debug_info_)[abs_node_id.check_ir_id() + .index](); + const auto& tokens = tree_and_subtrees.tree().tokens(); + + if (abs_node_id.node_id().has_value()) { + auto token = + tree_and_subtrees.GetSubtreeTokenRange(abs_node_id.node_id()).begin; + return {.filename = tokens.source().filename(), + .line_number = tokens.GetLineNumber(token), + .column_number = tokens.GetColumnNumber(token)}; + } else { + return {.filename = tokens.source().filename(), + .line_number = 0, + .column_number = 0}; + } +} + +} // namespace Carbon::Lower diff --git a/toolchain/lower/context.h b/toolchain/lower/context.h new file mode 100644 index 000000000000..536b05df71a7 --- /dev/null +++ b/toolchain/lower/context.h @@ -0,0 +1,133 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_LOWER_CONTEXT_H_ +#define CARBON_TOOLCHAIN_LOWER_CONTEXT_H_ + +#include +#include +#include + +#include "llvm/IR/Constants.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "toolchain/parse/tree_and_subtrees.h" +#include "toolchain/sem_ir/absolute_node_id.h" +#include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/inst_namer.h" + +namespace Carbon::Lower { + +class FileContext; + +// Context for lowering to an LLVM module. +class Context { + public: + // Location information for use with DebugInfo. The line_number and + // column_number are >= 0, with 0 as unknown, so that they can be passed + // directly to DebugInfo. + struct LocForDI { + llvm::StringRef filename; + int32_t line_number; + int32_t column_number; + }; + + explicit Context(llvm::LLVMContext& llvm_context, + llvm::IntrusiveRefCntPtr fs, + std::optional> + tree_and_subtrees_getters_for_debug_info, + llvm::StringRef module_name, llvm::raw_ostream* vlog_stream); + + // Gets or creates the `FileContext` for a given SemIR file. If an + // `inst_namer` is specified the first time this is called for a file, it will + // be used for that file. Otherwise, no instruction namer will be used. + // TODO: Consider building an InstNamer if we're not given one. + auto GetFileContext(const SemIR::File* file, + const SemIR::InstNamer* inst_namer = nullptr) + -> FileContext&; + + // Finishes lowering and takes ownership of the LLVM module. The context + // cannot be used further after calling this. + auto Finalize() && -> std::unique_ptr; + + // Returns location information for use with DebugInfo. + auto GetLocForDI(SemIR::AbsoluteNodeId abs_node_id) -> LocForDI; + + // Returns a lowered value to use for a value of type `type`. + auto GetTypeAsValue() -> llvm::Constant* { + return llvm::ConstantStruct::get(GetTypeType()); + } + + // Returns a lowered value to use for a value of int literal type. + auto GetIntLiteralAsValue() -> llvm::Constant* { + // TODO: Consider adding a named struct type for integer literals. + return llvm::ConstantStruct::get(llvm::StructType::get(llvm_context())); + } + + // Returns the empty LLVM struct type used to represent the type `type`. + auto GetTypeType() -> llvm::StructType* { + if (!type_type_) { + // `type` is lowered to an empty LLVM StructType. + type_type_ = llvm::StructType::create(*llvm_context_, {}, "type"); + } + return type_type_; + } + + auto llvm_context() -> llvm::LLVMContext& { return *llvm_context_; } + auto llvm_module() -> llvm::Module& { return *llvm_module_; } + auto file_system() -> llvm::IntrusiveRefCntPtr& { + return file_system_; + } + auto di_builder() -> llvm::DIBuilder& { return di_builder_; } + auto di_compile_unit() -> llvm::DICompileUnit* { return di_compile_unit_; } + + auto printf_int_format_string() -> llvm::Value* { + return printf_int_format_string_; + } + auto SetPrintfIntFormatString(llvm::Value* printf_int_format_string) { + CARBON_CHECK(!printf_int_format_string_, + "PrintInt formatting string already generated"); + printf_int_format_string_ = printf_int_format_string; + } + + private: + // Create the DICompileUnit metadata for this compilation. + auto BuildDICompileUnit(llvm::StringRef module_name, + llvm::Module& llvm_module, + llvm::DIBuilder& di_builder) -> llvm::DICompileUnit*; + + // State for building the LLVM IR. + llvm::LLVMContext* llvm_context_; + std::unique_ptr llvm_module_; + + // The filesystem for source code. + llvm::IntrusiveRefCntPtr file_system_; + + // State for building the LLVM IR debug info metadata. + llvm::DIBuilder di_builder_; + + // The DICompileUnit, if any - null implies debug info is not being emitted. + llvm::DICompileUnit* di_compile_unit_; + + // The trees are only provided when debug info should be emitted. + std::optional> + tree_and_subtrees_getters_for_debug_info_; + + // The optional vlog stream. + llvm::raw_ostream* vlog_stream_; + + // The `FileContext`s for each IR that is involved in this lowering action. + Map> file_contexts_; + + // Lowered version of the builtin type `type`. + llvm::StructType* type_type_ = nullptr; + + // Global format string for `printf.int.format` used by the PrintInt builtin. + llvm::Value* printf_int_format_string_ = nullptr; +}; + +} // namespace Carbon::Lower + +#endif // CARBON_TOOLCHAIN_LOWER_CONTEXT_H_ diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 99f6ff4855e7..beb7812db976 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -36,26 +36,11 @@ namespace Carbon::Lower { -FileContext::FileContext( - llvm::LLVMContext& llvm_context, - llvm::IntrusiveRefCntPtr fs, - std::optional> - tree_and_subtrees_getters_for_debug_info, - llvm::StringRef module_name, const SemIR::File& sem_ir, - clang::ASTUnit* cpp_ast, const SemIR::InstNamer* inst_namer, - llvm::raw_ostream* vlog_stream) - : llvm_context_(&llvm_context), - llvm_module_(std::make_unique(module_name, llvm_context)), - fs_(std::move(fs)), - di_builder_(*llvm_module_), - di_compile_unit_( - tree_and_subtrees_getters_for_debug_info - ? BuildDICompileUnit(module_name, *llvm_module_, di_builder_) - : nullptr), - tree_and_subtrees_getters_for_debug_info_( - tree_and_subtrees_getters_for_debug_info), +FileContext::FileContext(Context& context, const SemIR::File& sem_ir, + const SemIR::InstNamer* inst_namer, + llvm::raw_ostream* vlog_stream) + : context_(&context), sem_ir_(&sem_ir), - cpp_ast_(cpp_ast), inst_namer_(inst_namer), vlog_stream_(vlog_stream) { // Initialization that relies on invariants of the class. @@ -65,11 +50,12 @@ FileContext::FileContext( } // TODO: Move this to lower.cpp. -auto FileContext::Run() -> std::unique_ptr { - CARBON_CHECK(llvm_module_, "Run can only be called once."); - +auto FileContext::PrepareToLower() -> void { if (cpp_code_generator_) { - cpp_code_generator_->Initialize(cpp_ast()->getASTContext()); + // Clang code generation should not actually modify the AST, but isn't + // const-correct. + cpp_code_generator_->Initialize( + const_cast(cpp_ast()->getASTContext())); } // Lower all types that were required to be complete. @@ -86,12 +72,6 @@ auto FileContext::Run() -> std::unique_ptr { functions_[id.index] = BuildFunctionDecl(id); } - for (const auto& class_info : sem_ir_->classes().array_ref()) { - if (auto* llvm_vtable = BuildVtable(class_info)) { - global_variables_.Insert(class_info.vtable_id, llvm_vtable); - } - } - // Specific functions are lowered when we emit a reference to them. specific_functions_.resize(sem_ir_->specifics().size()); // Additional data stored for specifics, for when attempting to coalesce. @@ -106,6 +86,15 @@ auto FileContext::Run() -> std::unique_ptr { // Lower constants. constants_.resize(sem_ir_->insts().size()); LowerConstants(*this, constants_); +} + +// TODO: Move this to lower.cpp. +auto FileContext::LowerDefinitions() -> void { + for (const auto& class_info : sem_ir_->classes().array_ref()) { + if (auto* llvm_vtable = BuildVtable(class_info)) { + global_variables_.Insert(class_info.vtable_id, llvm_vtable); + } + } // Lower global variable definitions. // TODO: Storing both a `constants_` array and a separate `global_variables_` @@ -148,10 +137,6 @@ auto FileContext::Run() -> std::unique_ptr { BuildFunctionDefinition(function_id, specific_id); } - // Find equivalent specifics (from the same generic), replace all uses and - // remove duplicately lowered function definitions. - CoalesceEquivalentSpecifics(); - // Append `__global_init` to `llvm::global_ctors` to initialize global // variables. if (sem_ir().global_ctor_id().has_value()) { @@ -161,15 +146,22 @@ auto FileContext::Run() -> std::unique_ptr { } if (cpp_code_generator_) { - cpp_code_generator_->HandleTranslationUnit(cpp_ast()->getASTContext()); + // Clang code generation should not actually modify the AST, but isn't + // const-correct. + cpp_code_generator_->HandleTranslationUnit( + const_cast(cpp_ast()->getASTContext())); bool link_error = llvm::Linker::linkModules( - /*Dest=*/*llvm_module_, + /*Dest=*/llvm_module(), /*Src=*/std::unique_ptr( cpp_code_generator_->ReleaseModule())); CARBON_CHECK(!link_error); } +} - return std::move(llvm_module_); +auto FileContext::Finalize() -> void { + // Find equivalent specifics (from the same generic), replace all uses and + // remove duplicately lowered function definitions. + CoalesceEquivalentSpecifics(); } auto FileContext::InsertPair( @@ -395,25 +387,6 @@ auto FileContext::AreFunctionBodiesEquivalent( return true; } -auto FileContext::BuildDICompileUnit(llvm::StringRef module_name, - llvm::Module& llvm_module, - llvm::DIBuilder& di_builder) - -> llvm::DICompileUnit* { - llvm_module.addModuleFlag(llvm::Module::Max, "Dwarf Version", 5); - llvm_module.addModuleFlag(llvm::Module::Warning, "Debug Info Version", - llvm::DEBUG_METADATA_VERSION); - // TODO: Include directory path in the compile_unit_file. - llvm::DIFile* compile_unit_file = di_builder.createFile(module_name, ""); - // TODO: Introduce a new language code for Carbon. C works well for now since - // it's something debuggers will already know/have support for at least. - // Probably have to bump to C++ at some point for virtual functions, - // templates, etc. - return di_builder.createCompileUnit(llvm::dwarf::DW_LANG_C, compile_unit_file, - "carbon", - /*isOptimized=*/false, /*Flags=*/"", - /*RV=*/0); -} - auto FileContext::CreateCppCodeGenerator() -> std::unique_ptr { if (!cpp_ast()) { @@ -421,15 +394,16 @@ auto FileContext::CreateCppCodeGenerator() } RawStringOstream clang_module_name_stream; - clang_module_name_stream << llvm_module_->getName() << ".clang"; + clang_module_name_stream << llvm_module().getName() << ".clang"; // Do not emit Clang's name and version as the creator of the output file. cpp_code_gen_options_.EmitVersionIdentMetadata = false; return std::unique_ptr(clang::CreateLLVMCodeGen( cpp_ast()->getASTContext().getDiagnostics(), - clang_module_name_stream.TakeStr(), fs_, cpp_header_search_options_, - cpp_preprocessor_options_, cpp_code_gen_options_, *llvm_context_)); + clang_module_name_stream.TakeStr(), context().file_system(), + cpp_header_search_options_, cpp_preprocessor_options_, + cpp_code_gen_options_, llvm_context())); } auto FileContext::GetGlobal(SemIR::InstId inst_id, @@ -858,7 +832,7 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id, auto FileContext::BuildDISubprogram(const SemIR::Function& function, const llvm::Function* llvm_function) -> llvm::DISubprogram* { - if (!di_compile_unit_) { + if (!context().di_compile_unit()) { return nullptr; } auto name = sem_ir().names().GetAsStringIfIdentifier(function.name_id); @@ -867,12 +841,12 @@ auto FileContext::BuildDISubprogram(const SemIR::Function& function, auto loc = GetLocForDI(function.definition_id); // TODO: Add more details here, including real subroutine type (once type // information is built), etc. - return di_builder_.createFunction( - di_compile_unit_, *name, llvm_function->getName(), - /*File=*/di_builder_.createFile(loc.filename, ""), + return context().di_builder().createFunction( + context().di_compile_unit(), *name, llvm_function->getName(), + /*File=*/context().di_builder().createFile(loc.filename, ""), /*LineNo=*/loc.line_number, - di_builder_.createSubroutineType( - di_builder_.getOrCreateTypeArray(std::nullopt)), + context().di_builder().createSubroutineType( + context().di_builder().getOrCreateTypeArray(std::nullopt)), /*ScopeLine=*/0, llvm::DINode::FlagZero, llvm::DISubprogram::SPFlagDefinition); } @@ -1079,25 +1053,9 @@ auto FileContext::BuildGlobalVariableDecl(SemIR::VarStorage var_storage) /*Initializer=*/nullptr, mangled_name); } -auto FileContext::GetLocForDI(SemIR::InstId inst_id) -> LocForDI { - SemIR::AbsoluteNodeId resolved = - GetAbsoluteNodeId(sem_ir_, SemIR::LocId(inst_id)).back(); - const auto& tree_and_subtrees = - (*tree_and_subtrees_getters_for_debug_info_)[resolved.check_ir_id() - .index](); - const auto& tokens = tree_and_subtrees.tree().tokens(); - - if (resolved.node_id().has_value()) { - auto token = - tree_and_subtrees.GetSubtreeTokenRange(resolved.node_id()).begin; - return {.filename = tokens.source().filename(), - .line_number = tokens.GetLineNumber(token), - .column_number = tokens.GetColumnNumber(token)}; - } else { - return {.filename = tokens.source().filename(), - .line_number = 0, - .column_number = 0}; - } +auto FileContext::GetLocForDI(SemIR::InstId inst_id) -> Context::LocForDI { + return context().GetLocForDI( + GetAbsoluteNodeId(sem_ir_, SemIR::LocId(inst_id)).back()); } auto FileContext::BuildVtable(const SemIR::Class& class_info) diff --git a/toolchain/lower/file_context.h b/toolchain/lower/file_context.h index f3e3c7a4370b..f2efb24674a9 100644 --- a/toolchain/lower/file_context.h +++ b/toolchain/lower/file_context.h @@ -8,13 +8,8 @@ #include "clang/Basic/CodeGenOptions.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Lex/PreprocessorOptions.h" -#include "common/raw_string_ostream.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DIBuilder.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Module.h" #include "llvm/Support/BLAKE3.h" +#include "toolchain/lower/context.h" #include "toolchain/parse/tree_and_subtrees.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/ids.h" @@ -22,18 +17,9 @@ namespace Carbon::Lower { -// Context and shared functionality for lowering handlers. +// Context and shared functionality for lowering within a SemIR file. class FileContext { public: - // Location information for use with DebugInfo. The line_number and - // column_number are >= 0, with 0 as unknown, so that they can be passed - // directly to DebugInfo. - struct LocForDI { - llvm::StringRef filename; - int32_t line_number; - int32_t column_number; - }; - // Describes a specific function's body fingerprint. struct SpecificFunctionFingerprint { // Fingerprint with all specific-dependent instructions, except specific @@ -50,28 +36,25 @@ class FileContext { llvm::SmallVector calls; }; - explicit FileContext( - llvm::LLVMContext& llvm_context, - llvm::IntrusiveRefCntPtr fs, - std::optional> - tree_and_subtrees_getters_for_debug_info, - llvm::StringRef module_name, const SemIR::File& sem_ir, - clang::ASTUnit* cpp_ast, const SemIR::InstNamer* inst_namer, - llvm::raw_ostream* vlog_stream); - - // Lowers the SemIR::File to LLVM IR. Should only be called once, and handles - // the main execution loop. - auto Run() -> std::unique_ptr; - - // Create the DICompileUnit metadata for this compilation. - auto BuildDICompileUnit(llvm::StringRef module_name, - llvm::Module& llvm_module, - llvm::DIBuilder& di_builder) -> llvm::DICompileUnit*; + explicit FileContext(Context& context, const SemIR::File& sem_ir, + const SemIR::InstNamer* inst_namer, + llvm::raw_ostream* vlog_stream); // Creates the Clang `CodeGenerator` to generate LLVM module from imported C++ // code. Returns null when not importing C++. auto CreateCppCodeGenerator() -> std::unique_ptr; + // Prepares to lower code in this IR, by precomputing needed LLVM types, + // constants, declarations, etc. Should only be called once, before we lower + // anything in this file. + auto PrepareToLower() -> void; + + // Lowers all the definitions provided by the SemIR::File to LLVM IR. + auto LowerDefinitions() -> void; + + // Perform final cleanup tasks once all lowering has been completed. + auto Finalize() -> void; + // Gets a callable's function. Returns nullptr for a builtin. auto GetFunction(SemIR::FunctionId function_id) -> llvm::Function* { return functions_[function_id.index]; @@ -92,17 +75,16 @@ class FileContext { } // Returns location information for use with DebugInfo. - auto GetLocForDI(SemIR::InstId inst_id) -> LocForDI; + auto GetLocForDI(SemIR::InstId inst_id) -> Context::LocForDI; // Returns a lowered value to use for a value of type `type`. auto GetTypeAsValue() -> llvm::Constant* { - return llvm::ConstantStruct::get(GetTypeType()); + return context().GetTypeAsValue(); } // Returns a lowered value to use for a value of int literal type. auto GetIntLiteralAsValue() -> llvm::Constant* { - // TODO: Consider adding a named struct type for integer literals. - return llvm::ConstantStruct::get(llvm::StructType::get(llvm_context())); + return context().GetIntLiteralAsValue(); } // Returns a global value for the given instruction. @@ -110,29 +92,22 @@ class FileContext { -> llvm::Value*; // Returns the empty LLVM struct type used to represent the type `type`. - auto GetTypeType() -> llvm::StructType* { - if (!type_type_) { - // `type` is lowered to an empty LLVM StructType. - type_type_ = llvm::StructType::create(*llvm_context_, {}, "type"); - } - return type_type_; - } + auto GetTypeType() -> llvm::StructType* { return context().GetTypeType(); } - auto llvm_context() -> llvm::LLVMContext& { return *llvm_context_; } - auto llvm_module() -> llvm::Module& { return *llvm_module_; } + auto context() -> Context& { return *context_; } + auto llvm_context() -> llvm::LLVMContext& { return context().llvm_context(); } + auto llvm_module() -> llvm::Module& { return context().llvm_module(); } auto sem_ir() -> const SemIR::File& { return *sem_ir_; } - auto cpp_ast() -> clang::ASTUnit* { return cpp_ast_; } + auto cpp_ast() -> const clang::ASTUnit* { return sem_ir().cpp_ast(); } auto inst_namer() -> const SemIR::InstNamer* { return inst_namer_; } auto global_variables() -> const Map& { return global_variables_; } auto printf_int_format_string() -> llvm::Value* { - return printf_int_format_string_; + return context().printf_int_format_string(); } auto SetPrintfIntFormatString(llvm::Value* printf_int_format_string) { - CARBON_CHECK(!printf_int_format_string_, - "PrintInt formatting string already generated"); - printf_int_format_string_ = printf_int_format_string; + context().SetPrintfIntFormatString(printf_int_format_string); } struct FunctionTypeInfo { @@ -253,30 +228,12 @@ class FileContext { const Set>& set_of_pairs) -> bool; - // State for building the LLVM IR. - llvm::LLVMContext* llvm_context_; - std::unique_ptr llvm_module_; - - // The filesystem for source code. - llvm::IntrusiveRefCntPtr fs_; - - // State for building the LLVM IR debug info metadata. - llvm::DIBuilder di_builder_; - - // The DICompileUnit, if any - null implies debug info is not being emitted. - llvm::DICompileUnit* di_compile_unit_; - - // The trees are only provided when debug info should be emitted. - std::optional> - tree_and_subtrees_getters_for_debug_info_; + // The overall lowering context. + Context* context_; // The input SemIR. const SemIR::File* const sem_ir_; - // A mutable Clang AST is necessary for lowering since using the AST in lower - // modifies it. - clang::ASTUnit* cpp_ast_; - // The options used to create the Clang Code Generator. clang::HeaderSearchOptions cpp_header_search_options_; clang::PreprocessorOptions cpp_preprocessor_options_; @@ -315,9 +272,6 @@ class FileContext { // resize this directly to the (often large) correct size. llvm::SmallVector types_; - // Lowered version of the builtin type `type`. - llvm::StructType* type_type_ = nullptr; - // Maps constants to their lowered values. // Vector indexes correspond to `InstId` indexes for constant instructions. We // resize this directly to the (often large) correct size. @@ -326,9 +280,6 @@ class FileContext { // Maps global variables to their lowered variant. Map global_variables_; - // Global format string for `printf.int.format` used by the PrintInt builtin. - llvm::Value* printf_int_format_string_ = nullptr; - // For a generic function, keep track of the specifics for which LLVM // function declarations were created. Those can be retrieved then from // `specific_functions_`. We resize this to the correct size. Vector indexes diff --git a/toolchain/lower/lower.cpp b/toolchain/lower/lower.cpp index 25b17d7fb0a3..46533cffe11e 100644 --- a/toolchain/lower/lower.cpp +++ b/toolchain/lower/lower.cpp @@ -7,6 +7,7 @@ #include #include +#include "toolchain/lower/context.h" #include "toolchain/lower/file_context.h" namespace Carbon::Lower { @@ -16,13 +17,14 @@ auto LowerToLLVM(llvm::LLVMContext& llvm_context, std::optional> tree_and_subtrees_getters_for_debug_info, llvm::StringRef module_name, const SemIR::File& sem_ir, - clang::ASTUnit* cpp_ast, const SemIR::InstNamer* inst_namer, + const SemIR::InstNamer* inst_namer, llvm::raw_ostream* vlog_stream) -> std::unique_ptr { - FileContext context(llvm_context, std::move(fs), - tree_and_subtrees_getters_for_debug_info, module_name, - sem_ir, cpp_ast, inst_namer, vlog_stream); - return context.Run(); + Context context(llvm_context, std::move(fs), + tree_and_subtrees_getters_for_debug_info, module_name, + vlog_stream); + context.GetFileContext(&sem_ir, inst_namer).LowerDefinitions(); + return std::move(context).Finalize(); } } // namespace Carbon::Lower diff --git a/toolchain/lower/lower.h b/toolchain/lower/lower.h index 48f356805db1..f60bde56f9d7 100644 --- a/toolchain/lower/lower.h +++ b/toolchain/lower/lower.h @@ -20,7 +20,7 @@ auto LowerToLLVM(llvm::LLVMContext& llvm_context, std::optional> tree_and_subtrees_getters_for_debug_info, llvm::StringRef module_name, const SemIR::File& sem_ir, - clang::ASTUnit* cpp_ast, const SemIR::InstNamer* inst_namer, + const SemIR::InstNamer* inst_namer, llvm::raw_ostream* vlog_stream) -> std::unique_ptr; diff --git a/toolchain/lower/mangler.h b/toolchain/lower/mangler.h index c0052f639f17..8500a5b9aac0 100644 --- a/toolchain/lower/mangler.h +++ b/toolchain/lower/mangler.h @@ -24,10 +24,13 @@ class Mangler { // specified `FileContext`. explicit Mangler(FileContext& file_context) : file_context_(file_context), - cpp_mangle_context_( - file_context.cpp_ast() - ? file_context.cpp_ast()->getASTContext().createMangleContext() - : nullptr) {} + cpp_mangle_context_(file_context.cpp_ast() + // Clang's createMangleContext is not + // const-correct, but doesn't modify the AST. + ? const_cast( + file_context.cpp_ast()->getASTContext()) + .createMangleContext() + : nullptr) {} // Produce a deterministically unique mangled name for the function specified // by `function_id` and `specific_id`.