Files
carbon-lang/toolchain/lower/file_context.h
T
Jon Ross-PerkinsandChandler Carruth 7eee9a3489 Refactor resolving a location into a SemIR library (#4876)
At present, lower depends on `Check::SemIRDiagnosticConverter` for debug
info. That was to support a quick implementation of debug info, but
isn't great because it's both an unusual dependency on check's
implementation, and relying on diagnostic structures for debug info.

This cleans that up by splitting relevant logic out to a library in
sem_ir, and having lowering use sem_ir's library instead of check's.
Additionally, a small refactoring of `Parse::TreeAndSubtrees` to allow
getting locations in lowering without going through a `DiagnosticLoc`.
I'm adding `Parse::GetTreeAndSubtreesFn` in because it's a complex
signature to have in so many spots.

I chose to have `ResolveNodeId` return a `SmallVector` because it seemed
likely to be fairly compact, but that could also be using an optional
callback to handle resolved node IDs, possibly just returning the last
entry. This could be switched if preferred.

Note this change shouldn't affect behavior, it's just moving code
around.

---------

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
2025-02-06 00:55:20 +00:00

175 lines
6.6 KiB
C++

// 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_FILE_CONTEXT_H_
#define CARBON_TOOLCHAIN_LOWER_FILE_CONTEXT_H_
#include "llvm/IR/Constants.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "toolchain/check/sem_ir_diagnostic_converter.h"
#include "toolchain/sem_ir/file.h"
#include "toolchain/sem_ir/inst_namer.h"
namespace Carbon::Lower {
// Context and shared functionality for lowering handlers.
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;
};
explicit FileContext(
llvm::LLVMContext& llvm_context,
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
all_trees_and_subtrees_for_debug_info,
llvm::StringRef module_name, const SemIR::File& sem_ir,
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*;
// Gets a callable's function. Returns nullptr for a builtin.
auto GetFunction(SemIR::FunctionId function_id) -> llvm::Function* {
return functions_[function_id.index];
}
// Gets a or creates callable's function. Returns nullptr for a builtin.
auto GetOrCreateFunction(SemIR::FunctionId function_id,
SemIR::SpecificId specific_id) -> llvm::Function*;
// Returns a lowered type for the given type_id.
auto GetType(SemIR::TypeId type_id) -> llvm::Type* {
CARBON_CHECK(type_id.has_value(), "Should not be called with `None`");
CARBON_CHECK(types_[type_id.index], "Missing type {0}: {1}", type_id,
sem_ir().types().GetAsInst(type_id));
return types_[type_id.index];
}
// Returns location information for use with DebugInfo.
auto GetLocForDI(SemIR::InstId inst_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 a global value for the given instruction.
auto GetGlobal(SemIR::InstId inst_id) -> 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 llvm_context() -> llvm::LLVMContext& { return *llvm_context_; }
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<SemIR::InstId, llvm::GlobalVariable*>& {
return global_variables_;
}
private:
// Builds the declaration for the given function, which should then be cached
// by the caller.
auto BuildFunctionDecl(SemIR::FunctionId function_id,
SemIR::SpecificId specific_id =
SemIR::SpecificId::None) -> llvm::Function*;
// Builds the definition for the given function. If the function is only a
// declaration with no definition, does nothing.
auto BuildFunctionDefinition(SemIR::FunctionId function_id) -> void;
// Build the DISubprogram metadata for the given function.
auto BuildDISubprogram(const SemIR::Function& function,
const llvm::Function* llvm_function)
-> llvm::DISubprogram*;
// Builds the type for the given instruction, which should then be cached by
// the caller.
auto BuildType(SemIR::InstId inst_id) -> llvm::Type*;
// 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_;
std::unique_ptr<llvm::Module> llvm_module_;
// 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>>
all_trees_and_subtrees_for_debug_info_;
// The input SemIR.
const SemIR::File* const sem_ir_;
// The instruction namer, if given.
const SemIR::InstNamer* const inst_namer_;
// The optional vlog stream.
llvm::raw_ostream* vlog_stream_;
// Maps callables to lowered functions. SemIR treats callables as the
// canonical form of a function, so lowering needs to do the same.
// Vector indexes correspond to `FunctionId` indexes. We resize this directly
// to the correct size.
llvm::SmallVector<llvm::Function*, 0> functions_;
// Maps specific callables to lowered functions. Vector indexes correspond to
// `SpecificId` indexes. We resize this directly to the correct size.
llvm::SmallVector<llvm::Function*, 0> specific_functions_;
// Provides lowered versions of types.
// Vector indexes correspond to `TypeId` indexes for non-symbolic types. We
// 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.
llvm::SmallVector<llvm::Constant*, 0> constants_;
// Maps global variables to their lowered variant.
Map<SemIR::InstId, llvm::GlobalVariable*> global_variables_;
};
} // namespace Carbon::Lower
#endif // CARBON_TOOLCHAIN_LOWER_FILE_CONTEXT_H_