diff --git a/toolchain/check/testdata/basics/no_prelude/dump_sem_ir_range.carbon b/toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges.carbon similarity index 89% rename from toolchain/check/testdata/basics/no_prelude/dump_sem_ir_range.carbon rename to toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges.carbon index 0889fe248db5..9c42e4b642eb 100644 --- a/toolchain/check/testdata/basics/no_prelude/dump_sem_ir_range.carbon +++ b/toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges.carbon @@ -2,11 +2,13 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // +// The default behavior should be `--dump-sem-ir-ranges=if-present`. +// // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/no_prelude/dump_sem_ir_range.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/no_prelude/dump_sem_ir_range.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges.carbon // --- function.carbon @@ -78,6 +80,12 @@ fn G() { ); } +// --- file_without_ranges.carbon + +library "[[@TEST_NAME]]"; + +fn F(); + // CHECK:STDOUT: --- function.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -181,3 +189,19 @@ fn G() { // CHECK:STDOUT: %.loc17_7.3: %empty_tuple.type = converted %C.call, %tuple.loc17 [concrete = constants.%empty_tuple] // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- file_without_ranges.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges_ignore.carbon b/toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges_ignore.carbon new file mode 100644 index 000000000000..3148d88e040e --- /dev/null +++ b/toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges_ignore.carbon @@ -0,0 +1,58 @@ +// 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 +// +// EXTRA-ARGS: --dump-sem-ir-ranges=ignore +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges_ignore.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges_ignore.carbon + +// --- with-range.carbon + +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F(); +//@dump-sem-ir-end + +// --- without-range.carbon + +library "[[@TEST_NAME]]"; + +fn F(); + +// CHECK:STDOUT: --- with-range.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- without-range.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges_only.carbon b/toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges_only.carbon new file mode 100644 index 000000000000..ef50e7bf02f3 --- /dev/null +++ b/toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges_only.carbon @@ -0,0 +1,37 @@ +// 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 +// +// EXTRA-ARGS: --dump-sem-ir-ranges=only +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges_only.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/no_prelude/dump_sem_ir_ranges_only.carbon + +// --- with-range.carbon + +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F(); +//@dump-sem-ir-end + +// --- without-range.carbon + +library "[[@TEST_NAME]]"; + +fn F(); + +// CHECK:STDOUT: --- with-range.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: diff --git a/toolchain/driver/compile_subcommand.cpp b/toolchain/driver/compile_subcommand.cpp index cd83d58d93e5..56a25510d74f 100644 --- a/toolchain/driver/compile_subcommand.cpp +++ b/toolchain/driver/compile_subcommand.cpp @@ -180,10 +180,32 @@ Dump the raw JSON structure of SemIR to stdout when built. { .name = "dump-sem-ir", .help = R"""( -Dump the SemIR to stdout when built. +Dump the full SemIR to stdout when built. )""", }, [&](auto& arg_b) { arg_b.Set(&dump_sem_ir); }); + + b.AddOneOfOption( + { + .name = "dump-sem-ir-ranges", + .help = R"""( +Selects handling of `//@dump-sem-ir-[begin|end]` markers when dumping SemIR. +By default, `if-present` prints ranges for files that have them, and full SemIR +for files that don't. `only` skips files with no ranges, and `ignore` always +prints full SemIR. +)""", + }, + [&](auto& arg_b) { + arg_b.SetOneOf( + { + arg_b.OneOfValue("if-present", DumpSemIRRanges::IfPresent) + .Default(true), + arg_b.OneOfValue("only", DumpSemIRRanges::Only), + arg_b.OneOfValue("ignore", DumpSemIRRanges::Ignore), + }, + &dump_sem_ir_ranges); + }); + b.AddFlag( { .name = "builtin-sem-ir", @@ -399,6 +421,9 @@ class CompilationUnit { // significant overhead. Avoid constructing it when unused. auto GetParseTreeAndSubtrees() -> const Parse::TreeAndSubtrees&; + // Handles printing of formatted SemIR. + auto MaybePrintFormattedSemIR() -> void; + // Wraps a call with log statements to indicate start and end. Typically logs // with the actual function name, but marks timings with the appropriate // phase. @@ -624,6 +649,33 @@ auto CompilationUnit::GetCheckUnit() -> Check::Unit { .cpp_ast = &cpp_ast_}; } +auto CompilationUnit::MaybePrintFormattedSemIR() -> void { + bool print = options_->dump_sem_ir && IncludeInDumps(); + if (!vlog_stream_ && !print) { + return; + } + + if (options_->dump_sem_ir_ranges == CompileOptions::DumpSemIRRanges::Only && + !tokens_->has_dump_sem_ir_ranges()) { + return; + } + + bool use_dump_sem_ir_ranges = + options_->dump_sem_ir_ranges != CompileOptions::DumpSemIRRanges::Ignore && + tokens_->has_dump_sem_ir_ranges(); + SemIR::Formatter formatter(&*sem_ir_, *tree_and_subtrees_getter_, + cache_->include_in_dumps(), + use_dump_sem_ir_ranges); + formatter.Format(); + if (vlog_stream_) { + CARBON_VLOG("*** SemIR::File ***\n"); + formatter.Write(*vlog_stream_); + } + if (print) { + formatter.Write(*driver_env_->output_stream); + } +} + auto CompilationUnit::PostCheck() -> void { CARBON_CHECK(sem_ir_, "Must call GetCheckUnit first"); @@ -644,19 +696,7 @@ auto CompilationUnit::PostCheck() -> void { } } - bool print = options_->dump_sem_ir && IncludeInDumps(); - if (vlog_stream_ || print) { - SemIR::Formatter formatter(&*sem_ir_, *tree_and_subtrees_getter_, - cache_->include_in_dumps()); - formatter.Format(); - if (vlog_stream_) { - CARBON_VLOG("*** SemIR::File ***\n"); - formatter.Write(*vlog_stream_); - } - if (print) { - formatter.Write(*driver_env_->output_stream); - } - } + MaybePrintFormattedSemIR(); if (sem_ir_->has_errors()) { success_ = false; } diff --git a/toolchain/driver/compile_subcommand.h b/toolchain/driver/compile_subcommand.h index e04e30363b6f..2c5e993542fa 100644 --- a/toolchain/driver/compile_subcommand.h +++ b/toolchain/driver/compile_subcommand.h @@ -28,6 +28,12 @@ struct CompileOptions { CodeGen, }; + enum class DumpSemIRRanges : int8_t { + IfPresent, + Only, + Ignore, + }; + friend auto operator<<(llvm::raw_ostream& out, Phase phase) -> llvm::raw_ostream&; @@ -36,6 +42,7 @@ struct CompileOptions { CodegenOptions codegen_options; Phase phase; + DumpSemIRRanges dump_sem_ir_ranges; llvm::StringRef output_filename; llvm::SmallVector input_filenames; diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 51ff27680f04..027d7111505c 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -34,17 +34,19 @@ namespace Carbon::SemIR { Formatter::Formatter(const File* sem_ir, Parse::GetTreeAndSubtreesFn get_tree_and_subtrees, - llvm::ArrayRef include_ir_in_dumps) + llvm::ArrayRef include_ir_in_dumps, + bool use_dump_sem_ir_ranges) : sem_ir_(sem_ir), inst_namer_(sem_ir_), get_tree_and_subtrees_(get_tree_and_subtrees), - include_ir_in_dumps_(include_ir_in_dumps) { + include_ir_in_dumps_(include_ir_in_dumps), + use_dump_sem_ir_ranges_(use_dump_sem_ir_ranges) { // Create a placeholder visible chunk and assign it to all instructions that // don't have a chunk of their own. auto first_chunk = AddChunkNoFlush(true); tentative_inst_chunks_.resize(sem_ir_->insts().size(), first_chunk); - if (sem_ir_->parse_tree().tokens().has_dump_sem_ir_ranges()) { + if (use_dump_sem_ir_ranges_) { ComputeNodeParents(); } @@ -184,7 +186,7 @@ auto Formatter::ShouldFormatEntity(InstId decl_id, bool is_definition_start) return false; } - if (!sem_ir_->parse_tree().tokens().has_dump_sem_ir_ranges()) { + if (!use_dump_sem_ir_ranges_) { return true; } @@ -224,7 +226,7 @@ auto Formatter::ShouldFormatEntity(const EntityWithParamsBase& entity) -> bool { } auto Formatter::ShouldFormatInst(InstId inst_id) -> bool { - if (!sem_ir_->parse_tree().tokens().has_dump_sem_ir_ranges()) { + if (!use_dump_sem_ir_ranges_) { return true; } diff --git a/toolchain/sem_ir/formatter.h b/toolchain/sem_ir/formatter.h index f4f55b118fd4..24635517e0f2 100644 --- a/toolchain/sem_ir/formatter.h +++ b/toolchain/sem_ir/formatter.h @@ -19,7 +19,8 @@ class Formatter { public: explicit Formatter(const File* sem_ir, Parse::GetTreeAndSubtreesFn get_tree_and_subtrees, - llvm::ArrayRef include_ir_in_dumps); + llvm::ArrayRef include_ir_in_dumps, + bool use_dump_sem_ir_ranges); // Prints the SemIR into an internal buffer. // @@ -327,6 +328,9 @@ class Formatter { // For each CheckIRId, whether entities from it should be formatted. llvm::ArrayRef include_ir_in_dumps_; + // Whether to use ranges when dumping, or to dump the full SemIR. + bool use_dump_sem_ir_ranges_; + // The output stream buffer. std::string buffer_;