mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-03 22:02:51 +01:00
Changes crash messages to start printing verbose forms of instructions,
rather than just the ID. Fixes some indentation issues with stacks. Also
switches unexpected inst formatting, because now there are lots, and
it'd be helpful to know where they are.
This uses a pimpl pattern for Formatter due to the number of member
functions on Formatter. Maybe we should refactor that, but this didn't
feel like a good place to do so.
Note, I have two concerns about this change... to note them here, to
make sure others are considering them when evaluating the
implementation:
1. Some instructions are very verbose to print, as evidenced by the
fn_decl printing (which includes function params) or scope printing
(which includes scope members).
- I'm not sure whether there's a way to simply reduce this, as it seems
essential to the requested printing of instructions.
- Long-term, we may at least want to limit the number of lines printed
here. However, I've already spent a fair amount of time here and I think
it's in a good state to evaluate.
2. Increased complexity in the crash handler may result in crash
messages failing to generate.
- For example, a crash in Formatter (and its deps, such as InstNamer or
location handling) prevents a stack from being printed. I'm pretty sure
I've written crashes in Formatter before.
Here's an example crash snippet (generated by adding a crash inside
`return` handling) before:
```
2. NodeStack:
0. FunctionDefinitionStart -> function2
1. ReturnStatementStart -> no value
2. IntLiteral -> inst+26
inst_block_stack_:
0. block<invalid> {inst+0, inst+1, inst+2, inst+23}
1. block9 {inst+26}
param_and_arg_refs_stack:
args_type_info_stack_:
```
And after:
```
2. Check::Context
NodeStack:
0. FunctionDefinitionStart: function2
1. ReturnStatementStart: no value
2. IntLiteral:
unexpected.inst+26.loc12_10: i32 = int_literal 0 [template = constants.%.2]
inst_block_stack_:
0. block<invalid> {
package: <namespace> = namespace [template] {
.Core = unexpected.inst+2
.F = unexpected.inst+23.loc11_22
}
unexpected.inst+1 = import Core
unexpected.inst+2: <namespace> = namespace unexpected.inst+1, [template] {}
unexpected.inst+23.loc11_22: %F.type = fn_decl @F [template = constants.%F] {
unexpected.inst+9.loc11_9: init type = call constants.%Bool() [template = bool]
unexpected.inst+10.loc11_9: type = value_of_initializer unexpected.inst+9.loc11_9 [template = bool]
unexpected.inst+11.loc11_9: type = converted unexpected.inst+9.loc11_9, unexpected.inst+10.loc11_9 [template = bool]
unexpected.inst+12.loc11_6: bool = param b
@F.%b: bool = bind_name b, unexpected.inst+12.loc11_6
unexpected.inst+19.loc11_18: init type = call constants.%Int32() [template = i32]
unexpected.inst+20.loc11_18: type = value_of_initializer unexpected.inst+19.loc11_18 [template = i32]
unexpected.inst+21.loc11_18: type = converted unexpected.inst+19.loc11_18, unexpected.inst+20.loc11_18 [template = i32]
@F.%return: ref i32 = var <return slot>
}
}
1. block9 {
unexpected.inst+26.loc12_10: i32 = int_literal 0 [template = constants.%.2]
}
param_and_arg_refs_stack:
args_type_info_stack_:
```
104 lines
3.7 KiB
C++
104 lines
3.7 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_CHECK_INST_BLOCK_STACK_H_
|
|
#define CARBON_TOOLCHAIN_CHECK_INST_BLOCK_STACK_H_
|
|
|
|
#include "common/array_stack.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "toolchain/sem_ir/file.h"
|
|
#include "toolchain/sem_ir/formatter.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
// A stack of instruction blocks that are currently being constructed in a
|
|
// Context. The contents of the instruction blocks are stored here until the
|
|
// instruction block is popped from the stack, at which point they are
|
|
// transferred into the SemIR::File for long-term storage.
|
|
//
|
|
// All pushes and pops will be vlogged.
|
|
class InstBlockStack {
|
|
public:
|
|
explicit InstBlockStack(llvm::StringLiteral name, SemIR::File& sem_ir,
|
|
llvm::raw_ostream* vlog_stream)
|
|
: name_(name), sem_ir_(&sem_ir), vlog_stream_(vlog_stream) {}
|
|
|
|
// Pushes an existing instruction block.
|
|
auto Push(SemIR::InstBlockId id) -> void;
|
|
|
|
// Pushes an existing instruction block with a set of instructions.
|
|
auto Push(SemIR::InstBlockId id, llvm::ArrayRef<SemIR::InstId> inst_ids)
|
|
-> void;
|
|
|
|
// Pushes a new instruction block. It will be invalid unless PeekOrAdd is
|
|
// called in order to support lazy allocation.
|
|
auto Push() -> void { Push(SemIR::InstBlockId::Invalid); }
|
|
|
|
// Pushes a new unreachable code block.
|
|
auto PushUnreachable() -> void { Push(SemIR::InstBlockId::Unreachable); }
|
|
|
|
// Returns the ID of the top instruction block, allocating one if necessary.
|
|
// If `depth` is specified, returns the instruction at `depth` levels from the
|
|
// top of the stack instead of the top block, where the top block is at depth
|
|
// 0.
|
|
auto PeekOrAdd(int depth = 0) -> SemIR::InstBlockId;
|
|
|
|
// Pops the top instruction block. This will always return a valid instruction
|
|
// block; SemIR::InstBlockId::Empty is returned if one wasn't allocated.
|
|
auto Pop() -> SemIR::InstBlockId;
|
|
|
|
// Pops the top instruction block, and discards it if it hasn't had an ID
|
|
// allocated.
|
|
auto PopAndDiscard() -> void;
|
|
|
|
// Adds the given instruction ID to the block at the top of the stack.
|
|
auto AddInstId(SemIR::InstId inst_id) -> void {
|
|
CARBON_CHECK(!empty()) << "no current block";
|
|
insts_stack_.AppendToTop(inst_id);
|
|
}
|
|
|
|
// Returns whether the current block is statically reachable.
|
|
auto is_current_block_reachable() -> bool {
|
|
return id_stack_.back() != SemIR::InstBlockId::Unreachable;
|
|
}
|
|
|
|
// Returns a view of the contents of the top instruction block on the stack.
|
|
auto PeekCurrentBlockContents() const -> llvm::ArrayRef<SemIR::InstId> {
|
|
CARBON_CHECK(!empty()) << "no current block";
|
|
return insts_stack_.PeekArray();
|
|
}
|
|
|
|
// Prints the stack for a stack dump.
|
|
auto PrintForStackDump(SemIR::Formatter& formatter, int indent,
|
|
llvm::raw_ostream& output) const -> void;
|
|
|
|
// Runs verification that the processing cleanly finished.
|
|
auto VerifyOnFinish() const -> void {
|
|
CARBON_CHECK(empty()) << id_stack_.size();
|
|
}
|
|
|
|
auto empty() const -> bool { return id_stack_.empty(); }
|
|
|
|
private:
|
|
// A name for debugging.
|
|
llvm::StringLiteral name_;
|
|
|
|
// The underlying SemIR::File instance. Always non-null.
|
|
SemIR::File* sem_ir_;
|
|
|
|
// Whether to print verbose output.
|
|
llvm::raw_ostream* vlog_stream_;
|
|
|
|
// The stack of block IDs. Valid if allocated, Invalid if no block has been
|
|
// allocated, or Unreachable if this block is known to be unreachable.
|
|
llvm::SmallVector<SemIR::InstBlockId> id_stack_;
|
|
|
|
// The stack of insts in each block.
|
|
ArrayStack<SemIR::InstId> insts_stack_;
|
|
};
|
|
|
|
} // namespace Carbon::Check
|
|
|
|
#endif // CARBON_TOOLCHAIN_CHECK_INST_BLOCK_STACK_H_
|