Skip to content

Support lowering specifics for imported generics. #5475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions toolchain/driver/compile_subcommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,9 +672,8 @@ auto CompilationUnit::RunLower() -> void {
if (options_->include_debug_info) {
subtrees = cache_->tree_and_subtrees_getters();
}
module_ =
Lower::LowerToLLVM(*llvm_context_, subtrees, input_filename_, *sem_ir_,
sem_ir_->cpp_ast(), &inst_namer, vlog_stream_);
module_ = Lower::LowerToLLVM(*llvm_context_, subtrees, input_filename_,
*sem_ir_, &inst_namer, vlog_stream_);
});
if (vlog_stream_) {
CARBON_VLOG("*** llvm::Module ***\n");
Expand Down
2 changes: 2 additions & 0 deletions toolchain/lower/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ cc_library(
srcs = [
"constant.cpp",
"constant.h",
"context.cpp",
"file_context.cpp",
"function_context.cpp",
"mangler.cpp",
Expand All @@ -40,6 +41,7 @@ cc_library(
"handle*.cpp",
]),
hdrs = [
"context.h",
"file_context.h",
"function_context.h",
],
Expand Down
92 changes: 92 additions & 0 deletions toolchain/lower/context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// 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,
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)),
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().get();
}

auto Context::LowerPendingDefinitions() -> void {
// Lower function definitions for generics.
// This cannot be a range-based loop, as new definitions can be added
// while building other definitions.
// NOLINTNEXTLINE
for (size_t i = 0; i != specific_function_definitions_.size(); ++i) {
auto [file_context, function_id, specific_id] =
specific_function_definitions_[i];
file_context->BuildFunctionDefinition(function_id, specific_id);
}
}

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
150 changes: 150 additions & 0 deletions toolchain/lower/context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// 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;
};

// A specific function whose definition needs to be lowered.
struct PendingSpecificFunctionDefinition {
FileContext* context;
SemIR::FunctionId function_id;
SemIR::SpecificId specific_id;
};

explicit Context(llvm::LLVMContext& llvm_context,
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*;

// Registers a specific function definition to be lowered later.
auto AddPendingSpecificFunctionDefinition(
PendingSpecificFunctionDefinition pending) -> void {
specific_function_definitions_.push_back(pending);
}

// Lower any definitions that have been registered for later lowering.
// Currently, this lowers specifics for generic functions.
auto LowerPendingDefinitions() -> void;

// Takes ownership of the LLVM module. The context cannot be used further
// after calling this.
auto TakeModule() -> std::unique_ptr<llvm::Module> {
return std::move(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 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_;

// 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;

// Tracks which specific functions need to have their definitions lowered.
// This list may grow while lowering generic definitions from this list.
llvm::SmallVector<PendingSpecificFunctionDefinition>
specific_function_definitions_;
};

} // namespace Carbon::Lower

#endif // CARBON_TOOLCHAIN_LOWER_CONTEXT_H_
Loading
Loading