Switch compile functions to use options structs (#5742)

I've been mulling this mainly for the parameter complexity of
check/lower, but doing lex/parse for symmetry.

I'm motivated by the plan to move dumping for all of them into the
respective functions, because of discussion about llvm-verifier. That
basically would add another bool parameter (or more) to each of these.
My instinct is we're going to probably accrue a little more over time,
so I'm suggesting this as maybe adding the boundary a little simpler
and/or easier to read.

Note it may make sense to refactor a little further, e.g. maybe
Lower::Context could receive the full set of options and pick out what
it wants, but I figured creating the struct itself would be a decent
start.

I'm trying to put things into options when we can produce a reasonable
default if the user doesn't assign a value. I'm using an explicit
constructor so that values can be added without affecting every caller.

A different factoring would be to pass in everything through the param
struct, but that just felt weird when I was trying it out.

Removing `inst_namer` and `module_name` from `LowerToLLVM` params --
both of these can be inferred from `sem_ir`, and I'm not seeing a
particular reason to maintain them at the call site.
This commit is contained in:
Jon Ross-Perkins
2025-06-27 22:08:47 +00:00
committed by GitHub
parent 0722dab0ef
commit 2de746e83c
16 changed files with 149 additions and 67 deletions
+10 -9
View File
@@ -325,9 +325,8 @@ static auto BuildApiMapAndDiagnosePackaging(
auto CheckParseTrees(
llvm::MutableArrayRef<Unit> units,
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
bool prelude_import, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
llvm::StringRef target, llvm::raw_ostream* vlog_stream, bool fuzzing)
-> void {
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, llvm::StringRef target,
const CheckParseTreesOptions& options) -> void {
// UnitAndImports is big due to its SmallVectors, so we default to 0 on the
// stack.
llvm::SmallVector<UnitAndImports, 0> unit_infos(
@@ -348,14 +347,14 @@ auto CheckParseTrees(
// An `impl` has an implicit import of its `api`.
auto implicit_names = packaging->names;
implicit_names.package_id = PackageNameId::None;
TrackImport(api_map, nullptr, unit_info, implicit_names, fuzzing);
TrackImport(api_map, nullptr, unit_info, implicit_names, options.fuzzing);
}
Map<ImportKey, Parse::NodeId> explicit_import_map;
// Add the prelude import. It's added to explicit_import_map so that it can
// conflict with an explicit import of the prelude.
if (prelude_import &&
if (options.prelude_import &&
!(packaging && packaging->names.package_id == PackageNameId::Core)) {
auto prelude_id =
unit_info.unit->value_stores->string_literal_values().Add("prelude");
@@ -363,11 +362,12 @@ auto CheckParseTrees(
{.node_id = Parse::NoneNodeId(),
.package_id = PackageNameId::Core,
.library_id = prelude_id},
fuzzing);
options.fuzzing);
}
for (const auto& import : unit_info.parse_tree().imports()) {
TrackImport(api_map, &explicit_import_map, unit_info, import, fuzzing);
TrackImport(api_map, &explicit_import_map, unit_info, import,
options.fuzzing);
}
// If there were no imports, mark the file as ready to check for below.
@@ -381,7 +381,8 @@ auto CheckParseTrees(
for (int check_index = 0;
check_index < static_cast<int>(ready_to_check.size()); ++check_index) {
auto* unit_info = ready_to_check[check_index];
CheckUnit(unit_info, tree_and_subtrees_getters, fs, target, vlog_stream)
CheckUnit(unit_info, tree_and_subtrees_getters, fs, target,
options.vlog_stream)
.Run();
for (auto* incoming_import : unit_info->incoming_imports) {
--incoming_import->imports_remaining;
@@ -430,7 +431,7 @@ auto CheckParseTrees(
for (auto& unit_info : unit_infos) {
if (unit_info.imports_remaining > 0) {
CheckUnit(&unit_info, tree_and_subtrees_getters, fs, target,
vlog_stream)
options.vlog_stream)
.Run();
}
}
+17 -3
View File
@@ -31,14 +31,28 @@ struct Unit {
std::unique_ptr<clang::ASTUnit>* cpp_ast;
};
struct CheckParseTreesOptions {
// Options must be set individually, not through initialization.
explicit CheckParseTreesOptions() = default;
// Whether to import the prelude.
bool prelude_import = false;
// If set, enables verbose output.
llvm::raw_ostream* vlog_stream = nullptr;
// Whether fuzzing is being run. Used to disable features we don't want to
// fuzz.
bool fuzzing = false;
};
// Checks a group of parse trees. This will use imports to decide the order of
// checking.
auto CheckParseTrees(
llvm::MutableArrayRef<Unit> units,
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
bool prelude_import, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
llvm::StringRef target, llvm::raw_ostream* vlog_stream, bool fuzzing)
-> void;
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, llvm::StringRef target,
const CheckParseTreesOptions& options) -> void;
} // namespace Carbon::Check
+23 -16
View File
@@ -606,8 +606,11 @@ auto CompilationUnit::RunLex() -> void {
CARBON_VLOG("*** SourceBuffer ***\n```\n{0}\n```\n", source_->text());
LogCall("Lex::Lex", "lex",
[&] { tokens_ = Lex::Lex(value_stores_, *source_, *consumer_); });
LogCall("Lex::Lex", "lex", [&] {
Lex::LexOptions options;
options.consumer = consumer_;
tokens_ = Lex::Lex(value_stores_, *source_, options);
});
if (options_->dump_tokens && IncludeInDumps()) {
consumer_->Flush();
tokens_->Print(*driver_env_->output_stream,
@@ -624,7 +627,10 @@ auto CompilationUnit::RunLex() -> void {
auto CompilationUnit::RunParse() -> void {
LogCall("Parse::Parse", "parse", [&] {
parse_tree_ = Parse::Parse(*tokens_, *consumer_, vlog_stream_);
Parse::ParseOptions options;
options.consumer = consumer_;
options.vlog_stream = vlog_stream_;
parse_tree_ = Parse::Parse(*tokens_, options);
});
if (options_->dump_parse_tree && IncludeInDumps()) {
consumer_->Flush();
@@ -717,16 +723,14 @@ auto CompilationUnit::PostCheck() -> void {
auto CompilationUnit::RunLower() -> void {
LogCall("Lower::LowerToLLVM", "lower", [&] {
llvm_context_ = std::make_unique<llvm::LLVMContext>();
// TODO: Consider disabling instruction naming by default if we're not
// producing textual LLVM IR.
SemIR::InstNamer inst_namer(&*sem_ir_);
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> subtrees =
cache_->tree_and_subtrees_getters();
module_ = Lower::LowerToLLVM(
*llvm_context_, driver_env_->fs,
options_->run_llvm_verifier ? driver_env_->error_stream : nullptr,
options_->include_debug_info, subtrees, input_filename_, *sem_ir_,
&inst_namer, vlog_stream_);
Lower::LowerToLLVMOptions options;
options.llvm_verifier_stream =
options_->run_llvm_verifier ? driver_env_->error_stream : nullptr;
options.want_debug_info = options_->include_debug_info;
options.vlog_stream = vlog_stream_;
module_ = Lower::LowerToLLVM(*llvm_context_, driver_env_->fs,
cache_->tree_and_subtrees_getters(), *sem_ir_,
options);
});
if (vlog_stream_) {
CARBON_VLOG("*** llvm::Module ***\n");
@@ -977,10 +981,13 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
// Execute the actual checking.
CARBON_VLOG_TO(driver_env.vlog_stream, "*** Check::CheckParseTrees ***\n");
Check::CheckParseTreesOptions options;
options.prelude_import = options_.prelude_import;
options.vlog_stream = driver_env.vlog_stream;
options.fuzzing = driver_env.fuzzing;
Check::CheckParseTrees(check_units, cache.tree_and_subtrees_getters(),
options_.prelude_import, driver_env.fs,
options_.codegen_options.target,
driver_env.vlog_stream, driver_env.fuzzing);
driver_env.fs, options_.codegen_options.target,
options);
CARBON_VLOG_TO(driver_env.vlog_stream,
"*** Check::CheckParseTrees done ***\n");
for (auto& unit : units) {
+3 -1
View File
@@ -82,7 +82,9 @@ auto FormatSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
continue;
}
SharedValueStores value_stores;
auto tokens = Lex::Lex(value_stores, *source, driver_env.consumer);
Lex::LexOptions lex_options;
lex_options.consumer = &driver_env.consumer;
auto tokens = Lex::Lex(value_stores, *source, lex_options);
RawStringOstream buffer;
if (Format::Format(tokens, buffer)) {
+14 -6
View File
@@ -131,10 +131,16 @@ auto Context::File::SetText(Context& context, std::optional<int64_t> version,
}
source_ = std::make_unique<SourceBuffer>(std::move(*source));
value_stores_ = std::make_unique<SharedValueStores>();
Lex::LexOptions lex_options;
lex_options.consumer = &consumer;
tokens_ = std::make_unique<Lex::TokenizedBuffer>(
Lex::Lex(*value_stores_, *source_, consumer));
tree_ = std::make_unique<Parse::Tree>(
Parse::Parse(*tokens_, consumer, context.vlog_stream()));
Lex::Lex(*value_stores_, *source_, lex_options));
Parse::ParseOptions parse_options;
parse_options.consumer = &consumer;
parse_options.vlog_stream = context.vlog_stream();
tree_ = std::make_unique<Parse::Tree>(Parse::Parse(*tokens_, parse_options));
tree_and_subtrees_ =
std::make_unique<Parse::TreeAndSubtrees>(*tokens_, *tree_);
@@ -153,11 +159,13 @@ auto Context::File::SetText(Context& context, std::optional<int64_t> version,
};
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> fs =
new llvm::vfs::InMemoryFileSystem;
// TODO: Include the prelude.
Check::CheckParseTreesOptions check_options;
check_options.vlog_stream = context.vlog_stream();
Check::CheckParseTrees(
units, llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>(getter),
/*prelude_import=*/false, fs, llvm::sys::getDefaultTargetTriple(),
context.vlog_stream(), /*fuzzing=*/false);
units, llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>(getter), fs,
llvm::sys::getDefaultTargetTriple(), check_options);
// Note we need to publish diagnostics even when empty.
// TODO: Consider caching previously published diagnostics and only publishing
+4 -2
View File
@@ -1694,8 +1694,10 @@ auto Lexer::DiagnoseAndFixMismatchedBrackets() -> void {
}
auto Lex(SharedValueStores& value_stores, SourceBuffer& source,
Diagnostics::Consumer& consumer) -> TokenizedBuffer {
return Lexer(value_stores, source, consumer).Lex();
LexOptions options) -> TokenizedBuffer {
auto* consumer =
options.consumer ? options.consumer : &Diagnostics::ConsoleConsumer();
return Lexer(value_stores, source, *consumer).Lex();
}
} // namespace Carbon::Lex
+10 -2
View File
@@ -12,13 +12,21 @@
namespace Carbon::Lex {
struct LexOptions {
// Options must be set individually, not through initialization.
explicit LexOptions() = default;
// If set, a consumer for diagnostics. Otherwise, diagnostics go to stderr.
Diagnostics::Consumer* consumer = nullptr;
};
// Lexes a buffer of source code into a tokenized buffer.
//
// The provided source buffer must outlive any returned `TokenizedBuffer`
// which will refer into the source.
auto Lex(SharedValueStores& value_stores,
SourceBuffer& source [[clang::lifetimebound]],
Diagnostics::Consumer& consumer) -> TokenizedBuffer;
SourceBuffer& source [[clang::lifetimebound]], LexOptions options)
-> TokenizedBuffer;
} // namespace Carbon::Lex
+6 -2
View File
@@ -217,13 +217,17 @@ class LexerBenchHelper {
auto Lex() -> TokenizedBuffer {
Diagnostics::Consumer& consumer = Diagnostics::NullConsumer();
return Lex::Lex(value_stores_, source_, consumer);
Lex::LexOptions options;
options.consumer = &consumer;
return Lex::Lex(value_stores_, source_, options);
}
auto DiagnoseErrors() -> std::string {
RawStringOstream result;
Diagnostics::StreamConsumer consumer(&result);
auto buffer = Lex::Lex(value_stores_, source_, consumer);
Lex::LexOptions options;
options.consumer = &consumer;
auto buffer = Lex::Lex(value_stores_, source_, options);
consumer.Flush();
CARBON_CHECK(buffer.has_errors(),
"Asked to diagnose errors but none found!");
+3 -1
View File
@@ -35,7 +35,9 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
SourceBuffer::MakeFromFile(fs, TestFileName, Diagnostics::NullConsumer());
SharedValueStores value_stores;
auto buffer = Lex::Lex(value_stores, *source, Diagnostics::NullConsumer());
Lex::LexOptions options;
options.consumer = &Diagnostics::NullConsumer();
auto buffer = Lex::Lex(value_stores, *source, options);
if (buffer.has_errors()) {
return 0;
}
+11 -7
View File
@@ -15,15 +15,19 @@ namespace Carbon::Lower {
auto LowerToLLVM(
llvm::LLVMContext& llvm_context,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
llvm::raw_ostream* llvm_verifier_stream, bool want_debug_info,
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
llvm::StringRef module_name, const SemIR::File& sem_ir,
const SemIR::InstNamer* inst_namer, llvm::raw_ostream* vlog_stream)
const SemIR::File& sem_ir, const LowerToLLVMOptions& options)
-> std::unique_ptr<llvm::Module> {
Context context(llvm_context, std::move(fs), want_debug_info,
tree_and_subtrees_getters, module_name, vlog_stream);
context.GetFileContext(&sem_ir, inst_namer).LowerDefinitions();
return std::move(context).Finalize(llvm_verifier_stream);
Context context(llvm_context, std::move(fs), options.want_debug_info,
tree_and_subtrees_getters, sem_ir.filename(),
options.vlog_stream);
// TODO: Consider disabling instruction naming by default if we're not
// producing textual LLVM IR.
SemIR::InstNamer inst_namer(&sem_ir);
context.GetFileContext(&sem_ir, &inst_namer).LowerDefinitions();
return std::move(context).Finalize(options.llvm_verifier_stream);
}
} // namespace Carbon::Lower
+15 -6
View File
@@ -14,17 +14,26 @@
namespace Carbon::Lower {
struct LowerToLLVMOptions {
// Options must be set individually, not through initialization.
explicit LowerToLLVMOptions() = default;
// If set, enables LLVM IR verification.
llvm::raw_ostream* llvm_verifier_stream = nullptr;
// Whether to include debug info in lowered output.
bool want_debug_info = false;
// If set, enables verbose output.
llvm::raw_ostream* vlog_stream = nullptr;
};
// Lowers SemIR to LLVM IR.
//
// `llvm_verifier_stream` should be non-null when verification is desired.
// TODO: Switch to a struct for parameters.
auto LowerToLLVM(
llvm::LLVMContext& llvm_context,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
llvm::raw_ostream* llvm_verifier_stream, bool want_debug_info,
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
llvm::StringRef module_name, const SemIR::File& sem_ir,
const SemIR::InstNamer* inst_namer, llvm::raw_ostream* vlog_stream)
const SemIR::File& sem_ir, const LowerToLLVMOptions& options)
-> std::unique_ptr<llvm::Module>;
} // namespace Carbon::Lower
+5 -3
View File
@@ -17,11 +17,13 @@ auto HandleInvalid(Context& context) -> void {
context.PopState());
}
auto Parse(Lex::TokenizedBuffer& tokens, Diagnostics::Consumer& consumer,
llvm::raw_ostream* vlog_stream) -> Tree {
auto Parse(Lex::TokenizedBuffer& tokens, ParseOptions options) -> Tree {
auto* consumer =
options.consumer ? options.consumer : &Diagnostics::ConsoleConsumer();
// Delegate to the parser.
Tree tree(tokens);
Context context(&tree, &tokens, &consumer, vlog_stream);
Context context(&tree, &tokens, consumer, options.vlog_stream);
PrettyStackTraceFunction context_dumper(
[&](llvm::raw_ostream& output) { context.PrintForStackDump(output); });
+12 -2
View File
@@ -12,11 +12,21 @@
namespace Carbon::Parse {
struct ParseOptions {
// Options must be set individually, not through initialization.
explicit ParseOptions() = default;
// If set, a consumer for diagnostics. Otherwise, diagnostics go to stderr.
Diagnostics::Consumer* consumer = nullptr;
// If set, enables verbose output.
llvm::raw_ostream* vlog_stream = nullptr;
};
// Parses the token buffer into a `Tree`.
//
// This is the factory function which is used to build parse trees.
auto Parse(Lex::TokenizedBuffer& tokens, Diagnostics::Consumer& consumer,
llvm::raw_ostream* vlog_stream) -> Tree;
auto Parse(Lex::TokenizedBuffer& tokens, ParseOptions options) -> Tree;
} // namespace Carbon::Parse
+6 -2
View File
@@ -33,14 +33,18 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
// Lex the input.
SharedValueStores value_stores;
auto tokens = Lex::Lex(value_stores, *source, Diagnostics::NullConsumer());
Lex::LexOptions lex_options;
lex_options.consumer = &Diagnostics::NullConsumer();
auto tokens = Lex::Lex(value_stores, *source, lex_options);
if (tokens.has_errors()) {
return 0;
}
// Now parse it into a tree. Note that parsing will (when asserts are enabled)
// walk the entire tree to verify it so we don't have to do that here.
Parse::Parse(tokens, Diagnostics::NullConsumer(), /*vlog_stream=*/nullptr);
Parse::ParseOptions parse_options;
parse_options.consumer = &Diagnostics::NullConsumer();
Parse::Parse(tokens, parse_options);
return 0;
}
+3 -1
View File
@@ -170,7 +170,9 @@ TEST_F(TreeTest, HighRecursion) {
Lex::TokenizedBuffer& tokens = compile_helper_.GetTokenizedBuffer(code);
ASSERT_FALSE(tokens.has_errors());
Testing::MockDiagnosticConsumer consumer;
Tree tree = Parse(tokens, consumer, /*vlog_stream=*/nullptr);
Parse::ParseOptions options;
options.consumer = &consumer;
Tree tree = Parse(tokens, options);
EXPECT_FALSE(tree.has_errors());
}
+7 -4
View File
@@ -15,8 +15,10 @@ auto CompileHelper::GetTokenizedBuffer(llvm::StringRef text,
auto& source = GetSourceBuffer(text);
value_store_storage_.emplace_front();
token_storage_.push_front(Lex::Lex(value_store_storage_.front(), source,
consumer ? *consumer : consumer_));
Lex::LexOptions options;
options.consumer = consumer ? consumer : &consumer_;
token_storage_.push_front(
Lex::Lex(value_store_storage_.front(), source, options));
return token_storage_.front();
}
@@ -29,8 +31,9 @@ auto CompileHelper::GetTokenizedBufferWithSharedValueStore(
auto CompileHelper::GetTree(llvm::StringRef text) -> Parse::Tree& {
auto& tokens = GetTokenizedBuffer(text);
tree_storage_.push_front(Parse::Parse(tokens, consumer_,
/*vlog_stream=*/nullptr));
Parse::ParseOptions options;
options.consumer = &consumer_;
tree_storage_.push_front(Parse::Parse(tokens, options));
return tree_storage_.front();
}