mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
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.
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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<llvm::vfs::FileSystem> fs,
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
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<llvm::Module>(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<FileContext>(*this, *file, inst_namer, vlog_stream_);
|
||||
file_context->PrepareToLower();
|
||||
return file_context;
|
||||
});
|
||||
return *insert_result.value();
|
||||
}
|
||||
|
||||
auto Context::Finalize() && -> std::unique_ptr<llvm::Module> {
|
||||
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
|
||||
@@ -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 <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#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<llvm::vfs::FileSystem> fs,
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
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<llvm::Module>;
|
||||
|
||||
// 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<llvm::vfs::FileSystem>& {
|
||||
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> llvm_module_;
|
||||
|
||||
// The filesystem for source code.
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 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<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
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<SemIR::CheckIRId, std::unique_ptr<FileContext>> 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_
|
||||
@@ -36,26 +36,11 @@
|
||||
|
||||
namespace Carbon::Lower {
|
||||
|
||||
FileContext::FileContext(
|
||||
llvm::LLVMContext& llvm_context,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
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<llvm::Module>(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<llvm::Module> {
|
||||
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<clang::ASTContext&>(cpp_ast()->getASTContext()));
|
||||
}
|
||||
|
||||
// Lower all types that were required to be complete.
|
||||
@@ -86,12 +72,6 @@ auto FileContext::Run() -> std::unique_ptr<llvm::Module> {
|
||||
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<llvm::Module> {
|
||||
// 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<llvm::Module> {
|
||||
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<llvm::Module> {
|
||||
}
|
||||
|
||||
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<clang::ASTContext&>(cpp_ast()->getASTContext()));
|
||||
bool link_error = llvm::Linker::linkModules(
|
||||
/*Dest=*/*llvm_module_,
|
||||
/*Dest=*/llvm_module(),
|
||||
/*Src=*/std::unique_ptr<llvm::Module>(
|
||||
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<clang::CodeGenerator> {
|
||||
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::CodeGenerator>(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)
|
||||
|
||||
@@ -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<SemIR::SpecificId> calls;
|
||||
};
|
||||
|
||||
explicit FileContext(
|
||||
llvm::LLVMContext& llvm_context,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
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<llvm::Module>;
|
||||
|
||||
// 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<clang::CodeGenerator>;
|
||||
|
||||
// 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<SemIR::InstId, llvm::GlobalVariable*>& {
|
||||
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<std::pair<SemIR::SpecificId, SemIR::SpecificId>>& set_of_pairs)
|
||||
-> bool;
|
||||
|
||||
// State for building the LLVM IR.
|
||||
llvm::LLVMContext* llvm_context_;
|
||||
std::unique_ptr<llvm::Module> llvm_module_;
|
||||
|
||||
// The filesystem for source code.
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 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<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
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<llvm::Type*, 0> 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<SemIR::InstId, llvm::GlobalVariable*> 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
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#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<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
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<llvm::Module> {
|
||||
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
|
||||
|
||||
@@ -20,7 +20,7 @@ auto LowerToLLVM(llvm::LLVMContext& llvm_context,
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
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<llvm::Module>;
|
||||
|
||||
|
||||
@@ -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<clang::ASTContext&>(
|
||||
file_context.cpp_ast()->getASTContext())
|
||||
.createMangleContext()
|
||||
: nullptr) {}
|
||||
|
||||
// Produce a deterministically unique mangled name for the function specified
|
||||
// by `function_id` and `specific_id`.
|
||||
|
||||
Reference in New Issue
Block a user