Skip to content

Commit 519e633

Browse files
authored
Improve backtrace for lowering crashes. (#5651)
Factor out the logic for mapping from a `LocId` into a diagnostic location from check into sem_ir so it can be reused by lowering. Include the function and instruction being lowered in the pretty stack trace. Example stack trace: ```carbon 2. filename: examples/sieve.carbon 3. core/prelude/types/int.carbon:213:3: lowering function Core.Op(Core.IntLiteral as Core.ImplicitAs(i32)) fn Op[addr self: Self*](other: Self) = "int.sadd_assign"; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4. core/prelude/operators/arithmetic.carbon:22:27: lowering call fn Op[addr self: Self*](other: Other); ^~~~~~~~~~~~ ```
1 parent ac56057 commit 519e633

16 files changed

+276
-149
lines changed

toolchain/check/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ cc_library(
285285
"//toolchain/lex:token_index",
286286
"//toolchain/parse:tree",
287287
"//toolchain/sem_ir:absolute_node_id",
288+
"//toolchain/sem_ir:diagnostic_loc_converter",
288289
"//toolchain/sem_ir:file",
289290
"//toolchain/sem_ir:stringify",
290291
"//toolchain/sem_ir:typed_insts",

toolchain/check/check_unit.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ auto CheckUnit::ProcessNodeIds() -> bool {
377377
const auto& tree = tree_and_subtrees_getter_();
378378
auto converted = tree.NodeToDiagnosticLoc(node_id, /*token_only=*/false);
379379
converted.loc.FormatLocation(output);
380-
output << "checking " << context_.parse_tree().node_kind(node_id) << "\n";
380+
output << "Checking " << context_.parse_tree().node_kind(node_id) << "\n";
381381
// Crash output has a tab indent; try to indent slightly past that.
382382
converted.loc.FormatSnippet(output, /*indent=*/10);
383383
});

toolchain/check/diagnostic_emitter.cpp

Lines changed: 6 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ namespace Carbon::Check {
1919
auto DiagnosticEmitter::ConvertLoc(LocIdForDiagnostics loc_id,
2020
ContextFnT context_fn) const
2121
-> Diagnostics::ConvertedLoc {
22-
auto converted =
23-
ConvertLocImpl(loc_id.loc_id(), loc_id.is_token_only(), context_fn);
22+
auto [imports, converted] = loc_converter_.ConvertWithImports(
23+
loc_id.loc_id(), loc_id.is_token_only());
24+
for (const auto& import : imports) {
25+
CARBON_DIAGNOSTIC(InImport, LocationInfo, "in import");
26+
context_fn(import.loc, InImport);
27+
}
2428

2529
// Use the token when possible, but -1 is the default value.
2630
auto last_offset = -1;
@@ -40,73 +44,6 @@ auto DiagnosticEmitter::ConvertLoc(LocIdForDiagnostics loc_id,
4044
return converted;
4145
}
4246

43-
auto DiagnosticEmitter::ConvertLocImpl(SemIR::LocId loc_id, bool is_token_only,
44-
ContextFnT context_fn) const
45-
-> Diagnostics::ConvertedLoc {
46-
llvm::SmallVector<SemIR::AbsoluteNodeId> absolute_node_ids =
47-
SemIR::GetAbsoluteNodeId(sem_ir_, loc_id);
48-
49-
auto final_node_id = absolute_node_ids.pop_back_val();
50-
for (const auto& absolute_node_id : absolute_node_ids) {
51-
if (!absolute_node_id.node_id().has_value()) {
52-
// TODO: Add an "In implicit import of prelude." note for the case where
53-
// we don't have a location.
54-
continue;
55-
}
56-
// TODO: Include the name of the imported library in the diagnostic.
57-
auto diag_loc =
58-
ConvertLocInFile(absolute_node_id, is_token_only, context_fn);
59-
AddInImport(diag_loc.loc, context_fn);
60-
}
61-
62-
return ConvertLocInFile(final_node_id, is_token_only, context_fn);
63-
}
64-
65-
auto DiagnosticEmitter::ConvertLocInFile(SemIR::AbsoluteNodeId absolute_node_id,
66-
bool token_only,
67-
ContextFnT context_fn) const
68-
-> Diagnostics::ConvertedLoc {
69-
if (absolute_node_id.check_ir_id() == SemIR::CheckIRId::Cpp) {
70-
// Special handling of Clang source locations.
71-
CARBON_CHECK(sem_ir_->import_cpps().size() > 0);
72-
// TODO: Use information on the specific C++ import extract from Clang error
73-
// message and propagated here instead of using first C++ import
74-
// arbitrarily.
75-
Parse::NodeId import_node_id =
76-
sem_ir_->import_cpps().values().begin()->node_id;
77-
AddInImport(ConvertLocInCarbonFile(sem_ir_->check_ir_id(), import_node_id,
78-
/*token_only=*/false)
79-
.loc,
80-
context_fn);
81-
82-
clang::SourceLocation clang_loc = sem_ir_->clang_source_locs().Get(
83-
absolute_node_id.clang_source_loc_id());
84-
85-
CARBON_CHECK(sem_ir_->cpp_ast());
86-
clang::PresumedLoc presumed_loc =
87-
sem_ir_->cpp_ast()->getSourceManager().getPresumedLoc(clang_loc);
88-
89-
return Diagnostics::ConvertedLoc{
90-
.loc = {.filename = presumed_loc.getFilename(),
91-
.line_number = static_cast<int32_t>(presumed_loc.getLine())},
92-
// TODO: Set `last_byte_offset` based on the `import Cpp` location.
93-
.last_byte_offset = 0};
94-
}
95-
96-
return ConvertLocInCarbonFile(absolute_node_id.check_ir_id(),
97-
absolute_node_id.node_id(), token_only);
98-
}
99-
100-
auto DiagnosticEmitter::ConvertLocInCarbonFile(SemIR::CheckIRId check_ir_id,
101-
Parse::NodeId node_id,
102-
bool token_only) const
103-
-> Diagnostics::ConvertedLoc {
104-
CARBON_CHECK(check_ir_id != SemIR::CheckIRId::Cpp);
105-
const auto& tree_and_subtrees =
106-
tree_and_subtrees_getters_[check_ir_id.index]();
107-
return tree_and_subtrees.NodeToDiagnosticLoc(node_id, token_only);
108-
}
109-
11047
auto DiagnosticEmitter::ConvertArg(llvm::Any arg) const -> llvm::Any {
11148
if (auto* library_name_id = llvm::any_cast<SemIR::LibraryNameId>(&arg)) {
11249
std::string library_name;
@@ -178,10 +115,4 @@ auto DiagnosticEmitter::ConvertArg(llvm::Any arg) const -> llvm::Any {
178115
return DiagnosticEmitterBase::ConvertArg(arg);
179116
}
180117

181-
auto DiagnosticEmitter::AddInImport(Diagnostics::Loc loc, ContextFnT context_fn)
182-
-> void {
183-
CARBON_DIAGNOSTIC(InImport, LocationInfo, "in import");
184-
context_fn(loc, InImport);
185-
}
186-
187118
} // namespace Carbon::Check

toolchain/check/diagnostic_emitter.h

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99
#include "toolchain/check/diagnostic_helpers.h"
1010
#include "toolchain/diagnostics/diagnostic_emitter.h"
1111
#include "toolchain/lex/token_index.h"
12-
#include "toolchain/parse/tree_and_subtrees.h"
13-
#include "toolchain/sem_ir/absolute_node_id.h"
14-
#include "toolchain/sem_ir/file.h"
15-
#include "toolchain/sem_ir/ids.h"
12+
#include "toolchain/sem_ir/diagnostic_loc_converter.h"
1613

1714
namespace Carbon::Check {
1815

@@ -24,8 +21,8 @@ class DiagnosticEmitter : public DiagnosticEmitterBase {
2421
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
2522
const SemIR::File* sem_ir)
2623
: DiagnosticEmitterBase(consumer),
27-
tree_and_subtrees_getters_(tree_and_subtrees_getters),
28-
sem_ir_(sem_ir) {}
24+
sem_ir_(sem_ir),
25+
loc_converter_(tree_and_subtrees_getters, sem_ir) {}
2926

3027
// If a byte offset is past the current last byte offset, advances forward.
3128
// Earlier offsets are ignored.
@@ -47,31 +44,12 @@ class DiagnosticEmitter : public DiagnosticEmitterBase {
4744
-> Diagnostics::ConvertedLoc override;
4845

4946
private:
50-
// Implements `ConvertLoc`, but without `last_token_` applied.
51-
auto ConvertLocImpl(SemIR::LocId loc_id, bool is_token_only,
52-
ContextFnT context_fn) const -> Diagnostics::ConvertedLoc;
53-
54-
// Converts an `absolute_node_id` in either a Carbon file or C++ import to a
55-
// diagnostic location.
56-
auto ConvertLocInFile(SemIR::AbsoluteNodeId absolute_node_id, bool token_only,
57-
ContextFnT context_fn) const
58-
-> Diagnostics::ConvertedLoc;
59-
60-
// Converts a `node_id` corresponding to a specific sem_ir to a diagnostic
61-
// location.
62-
auto ConvertLocInCarbonFile(SemIR::CheckIRId check_ir_id,
63-
Parse::NodeId node_id, bool token_only) const
64-
-> Diagnostics::ConvertedLoc;
65-
66-
// Adds `in import` note.
67-
static auto AddInImport(Diagnostics::Loc loc, ContextFnT context_fn) -> void;
68-
69-
// Converters for each SemIR.
70-
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters_;
71-
7247
// The current SemIR being processed.
7348
const SemIR::File* sem_ir_;
7449

50+
// Converter for locations.
51+
SemIR::DiagnosticLocConverter loc_converter_;
52+
7553
// The last token encountered during processing.
7654
Lex::TokenIndex last_token_ = Lex::TokenIndex::None;
7755
};

toolchain/driver/compile_subcommand.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -708,13 +708,11 @@ auto CompilationUnit::RunLower() -> void {
708708
// TODO: Consider disabling instruction naming by default if we're not
709709
// producing textual LLVM IR.
710710
SemIR::InstNamer inst_namer(&*sem_ir_);
711-
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>> subtrees;
712-
if (options_->include_debug_info) {
713-
subtrees = cache_->tree_and_subtrees_getters();
714-
}
715-
module_ = Lower::LowerToLLVM(*llvm_context_, driver_env_->fs, subtrees,
716-
input_filename_, *sem_ir_, &inst_namer,
717-
vlog_stream_);
711+
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> subtrees =
712+
cache_->tree_and_subtrees_getters();
713+
module_ = Lower::LowerToLLVM(
714+
*llvm_context_, driver_env_->fs, options_->include_debug_info, subtrees,
715+
input_filename_, *sem_ir_, &inst_namer, vlog_stream_);
718716
});
719717
if (vlog_stream_) {
720718
CARBON_VLOG("*** llvm::Module ***\n");
@@ -846,7 +844,7 @@ auto CompilationUnit::LogCall(llvm::StringLiteral logging_label,
846844
llvm::StringLiteral timing_label,
847845
llvm::function_ref<auto()->void> fn) -> void {
848846
PrettyStackTraceFunction trace_file([&](llvm::raw_ostream& out) {
849-
out << "filename: " << input_filename_ << "\n";
847+
out << "Filename: " << input_filename_ << "\n";
850848
});
851849
CARBON_VLOG("*** {0}: {1} ***\n", logging_label, input_filename_);
852850
Timings::ScopedTiming timing(timings_ ? &*timings_ : nullptr, timing_label);

toolchain/lower/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,15 @@ cc_library(
5353
"//common:vlog",
5454
"//toolchain/base:fixed_size_value_store",
5555
"//toolchain/base:kind_switch",
56+
"//toolchain/base:pretty_stack_trace_function",
5657
"//toolchain/parse:tree",
5758
"//toolchain/sem_ir:absolute_node_id",
59+
"//toolchain/sem_ir:diagnostic_loc_converter",
5860
"//toolchain/sem_ir:entry_point",
5961
"//toolchain/sem_ir:expr_info",
6062
"//toolchain/sem_ir:file",
6163
"//toolchain/sem_ir:inst_namer",
64+
"//toolchain/sem_ir:stringify",
6265
"//toolchain/sem_ir:typed_insts",
6366
"@llvm-project//clang:ast",
6467
"@llvm-project//clang:basic",

toolchain/lower/context.cpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,20 @@
1313

1414
namespace Carbon::Lower {
1515

16-
Context::Context(llvm::LLVMContext& llvm_context,
17-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
18-
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
19-
tree_and_subtrees_getters_for_debug_info,
20-
llvm::StringRef module_name, llvm::raw_ostream* vlog_stream)
16+
Context::Context(
17+
llvm::LLVMContext& llvm_context,
18+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, bool want_debug_info,
19+
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
20+
llvm::StringRef module_name, llvm::raw_ostream* vlog_stream)
2121
: llvm_context_(&llvm_context),
2222
llvm_module_(std::make_unique<llvm::Module>(module_name, llvm_context)),
2323
file_system_(std::move(fs)),
2424
di_builder_(*llvm_module_),
2525
di_compile_unit_(
26-
tree_and_subtrees_getters_for_debug_info
26+
want_debug_info
2727
? BuildDICompileUnit(module_name, *llvm_module_, di_builder_)
2828
: nullptr),
29-
tree_and_subtrees_getters_for_debug_info_(
30-
tree_and_subtrees_getters_for_debug_info),
29+
tree_and_subtrees_getters_(tree_and_subtrees_getters),
3130
vlog_stream_(vlog_stream) {}
3231

3332
auto Context::GetFileContext(const SemIR::File* file,
@@ -79,8 +78,7 @@ auto Context::BuildDICompileUnit(llvm::StringRef module_name,
7978

8079
auto Context::GetLocForDI(SemIR::AbsoluteNodeId abs_node_id) -> LocForDI {
8180
const auto& tree_and_subtrees =
82-
(*tree_and_subtrees_getters_for_debug_info_)[abs_node_id.check_ir_id()
83-
.index]();
81+
tree_and_subtrees_getters()[abs_node_id.check_ir_id().index]();
8482
const auto& tokens = tree_and_subtrees.tree().tokens();
8583

8684
if (abs_node_id.node_id().has_value()) {

toolchain/lower/context.h

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ class Context {
4141
SemIR::SpecificId specific_id;
4242
};
4343

44-
explicit Context(llvm::LLVMContext& llvm_context,
45-
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
46-
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
47-
tree_and_subtrees_getters_for_debug_info,
48-
llvm::StringRef module_name, llvm::raw_ostream* vlog_stream);
44+
explicit Context(
45+
llvm::LLVMContext& llvm_context,
46+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, bool want_debug_info,
47+
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
48+
llvm::StringRef module_name, llvm::raw_ostream* vlog_stream);
4949

5050
// Gets or creates the `FileContext` for a given SemIR file. If an
5151
// `inst_namer` is specified the first time this is called for a file, it will
@@ -95,6 +95,10 @@ class Context {
9595
}
9696
auto di_builder() -> llvm::DIBuilder& { return di_builder_; }
9797
auto di_compile_unit() -> llvm::DICompileUnit* { return di_compile_unit_; }
98+
auto tree_and_subtrees_getters()
99+
-> llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> {
100+
return tree_and_subtrees_getters_;
101+
}
98102

99103
auto printf_int_format_string() -> llvm::Value* {
100104
return printf_int_format_string_;
@@ -128,9 +132,8 @@ class Context {
128132
// The DICompileUnit, if any - null implies debug info is not being emitted.
129133
llvm::DICompileUnit* di_compile_unit_;
130134

131-
// The trees are only provided when debug info should be emitted.
132-
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
133-
tree_and_subtrees_getters_for_debug_info_;
135+
// Parse trees. Used for debug information and crash diagnostics.
136+
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters_;
134137

135138
// The optional vlog stream.
136139
llvm::raw_ostream* vlog_stream_;

toolchain/lower/file_context.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
2020
#include "llvm/Transforms/Utils/ModuleUtils.h"
2121
#include "toolchain/base/kind_switch.h"
22+
#include "toolchain/base/pretty_stack_trace_function.h"
2223
#include "toolchain/lower/constant.h"
2324
#include "toolchain/lower/function_context.h"
2425
#include "toolchain/lower/mangler.h"
2526
#include "toolchain/sem_ir/absolute_node_id.h"
27+
#include "toolchain/sem_ir/diagnostic_loc_converter.h"
2628
#include "toolchain/sem_ir/entry_point.h"
2729
#include "toolchain/sem_ir/expr_info.h"
2830
#include "toolchain/sem_ir/file.h"
@@ -33,6 +35,7 @@
3335
#include "toolchain/sem_ir/inst_categories.h"
3436
#include "toolchain/sem_ir/inst_kind.h"
3537
#include "toolchain/sem_ir/pattern.h"
38+
#include "toolchain/sem_ir/stringify.h"
3639
#include "toolchain/sem_ir/typed_insts.h"
3740

3841
namespace Carbon::Lower {
@@ -757,6 +760,26 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id,
757760
FileContext& definition_context,
758761
const SemIR::Function& definition_function)
759762
-> void {
763+
// On crash, report the function we were lowering.
764+
PrettyStackTraceFunction stack_trace_entry([&](llvm::raw_ostream& output) {
765+
SemIR::DiagnosticLocConverter converter(
766+
context().tree_and_subtrees_getters(), &sem_ir());
767+
auto converted =
768+
converter.Convert(SemIR::LocId(declaration_function.definition_id),
769+
/*token_only=*/false);
770+
converted.loc.FormatLocation(output);
771+
output << "Lowering function ";
772+
if (specific_id.has_value()) {
773+
output << SemIR::StringifySpecific(sem_ir(), specific_id);
774+
} else {
775+
output << SemIR::StringifyConstantInst(
776+
sem_ir(), declaration_function.definition_id);
777+
}
778+
output << "\n";
779+
// Crash output has a tab indent; try to indent slightly past that.
780+
converted.loc.FormatSnippet(output, /*indent=*/10);
781+
});
782+
760783
// Note that `definition_function` is potentially from a different SemIR::File
761784
// than the one that this file context represents. Any lowering done for
762785
// values derived from `definition_function` should use `definition_context`

toolchain/lower/function_context.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#include "common/vlog.h"
88
#include "toolchain/base/kind_switch.h"
9+
#include "toolchain/base/pretty_stack_trace_function.h"
10+
#include "toolchain/sem_ir/diagnostic_loc_converter.h"
911
#include "toolchain/sem_ir/file.h"
1012
#include "toolchain/sem_ir/generic.h"
1113

@@ -55,7 +57,25 @@ auto FunctionContext::TryToReuseBlock(SemIR::InstBlockId block_id,
5557
}
5658

5759
auto FunctionContext::LowerBlockContents(SemIR::InstBlockId block_id) -> void {
60+
auto inst_id_for_stack_trace = SemIR::InstId::None;
61+
62+
// On crash, report the instruction we were lowering.
63+
PrettyStackTraceFunction stack_trace_entry([&](llvm::raw_ostream& output) {
64+
SemIR::DiagnosticLocConverter converter(
65+
file_context_->context().tree_and_subtrees_getters(), &sem_ir());
66+
auto converted = converter.Convert(SemIR::LocId(inst_id_for_stack_trace),
67+
/*token_only=*/false);
68+
converted.loc.FormatLocation(output);
69+
// TODO: Format SemIR for the instruction we were lowering?
70+
output << "Lowering "
71+
<< sem_ir().insts().Get(inst_id_for_stack_trace).kind().ir_name()
72+
<< "\n";
73+
// Crash output has a tab indent; try to indent slightly past that.
74+
converted.loc.FormatSnippet(output, /*indent=*/10);
75+
});
76+
5877
for (auto inst_id : sem_ir().inst_blocks().Get(block_id)) {
78+
inst_id_for_stack_trace = inst_id;
5979
LowerInst(inst_id);
6080
}
6181
}

0 commit comments

Comments
 (0)