Store contents of incomplete node blocks on the node block stack. (#3223)

`SemIR` now only stores complete blocks, and placeholders for blocks
where we need a block ID before we know the block contents.

This is a preparation step towards the new node block allocation design.
This commit is contained in:
Richard Smith
2023-09-13 23:28:13 +00:00
committed by GitHub
parent 9ba1669472
commit a66f35e92c
9 changed files with 123 additions and 84 deletions
+3 -3
View File
@@ -153,7 +153,7 @@ static auto AddDominatedBlockAndBranchImpl(Context& context,
if (!context.node_block_stack().is_current_block_reachable()) {
return SemIR::NodeBlockId::Unreachable;
}
auto block_id = context.semantics_ir().AddNodeBlock();
auto block_id = context.semantics_ir().AddNodeBlockId();
context.AddNode(BranchNode::Make(parse_node, block_id, args...));
return block_id;
}
@@ -185,7 +185,7 @@ auto Context::AddConvergenceBlockAndPush(Parse::Node parse_node, int num_blocks)
for ([[maybe_unused]] auto _ : llvm::seq(num_blocks)) {
if (node_block_stack().is_current_block_reachable()) {
if (new_block_id == SemIR::NodeBlockId::Unreachable) {
new_block_id = semantics_ir().AddNodeBlock();
new_block_id = semantics_ir().AddNodeBlockId();
}
AddNode(SemIR::Node::Branch::Make(parse_node, new_block_id));
}
@@ -203,7 +203,7 @@ auto Context::AddConvergenceBlockWithArgAndPush(
for (auto arg_id : block_args) {
if (node_block_stack().is_current_block_reachable()) {
if (new_block_id == SemIR::NodeBlockId::Unreachable) {
new_block_id = semantics_ir().AddNodeBlock();
new_block_id = semantics_ir().AddNodeBlockId();
}
AddNode(
SemIR::Node::BranchWithArg::Make(parse_node, new_block_id, arg_id));
+37 -16
View File
@@ -6,43 +6,64 @@
#include "common/vlog.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "toolchain/sem_ir/node.h"
namespace Carbon::Check {
auto NodeBlockStack::Push(SemIR::NodeBlockId id) -> void {
CARBON_VLOG() << name_ << " Push " << stack_.size() << "\n";
CARBON_CHECK(stack_.size() < (1 << 20))
CARBON_VLOG() << name_ << " Push " << size_ << "\n";
CARBON_CHECK(size_ < (1 << 20))
<< "Excessive stack size: likely infinite loop";
stack_.push_back(id);
if (size_ == static_cast<int>(stack_.size())) {
stack_.emplace_back();
}
stack_[size_].Reset(id);
++size_;
}
auto NodeBlockStack::PeekForAdd(int depth) -> SemIR::NodeBlockId {
CARBON_CHECK(static_cast<int>(stack_.size()) > depth) << "no such block";
int index = stack_.size() - depth - 1;
CARBON_CHECK(size() > depth) << "no such block";
int index = size() - depth - 1;
auto& slot = stack_[index];
if (!slot.is_valid()) {
slot = semantics_ir_->AddNodeBlock();
CARBON_VLOG() << name_ << " Add " << index << ": " << slot << "\n";
if (!slot.id.is_valid()) {
slot.id = semantics_ir_->AddNodeBlockId();
CARBON_VLOG() << name_ << " Add " << index << ": " << slot.id << "\n";
}
return slot;
return slot.id;
}
auto NodeBlockStack::Pop() -> SemIR::NodeBlockId {
CARBON_CHECK(!stack_.empty()) << "no current block";
auto back = stack_.pop_back_val();
CARBON_VLOG() << name_ << " Pop " << stack_.size() << ": " << back << "\n";
if (!back.is_valid()) {
CARBON_CHECK(!empty()) << "no current block";
--size_;
auto& back = stack_[size_];
// Finalize the block.
if (!back.content.empty() && back.id != SemIR::NodeBlockId::Unreachable) {
if (back.id.is_valid()) {
semantics_ir_->SetNodeBlock(back.id, back.content);
} else {
back.id = semantics_ir_->AddNodeBlock(back.content);
}
}
CARBON_VLOG() << name_ << " Pop " << size_ << ": " << back.id << "\n";
if (!back.id.is_valid()) {
return SemIR::NodeBlockId::Empty;
}
return back;
return back.id;
}
auto NodeBlockStack::PrintForStackDump(llvm::raw_ostream& output) const
-> void {
output << name_ << ":\n";
for (auto [i, entry] : llvm::enumerate(stack_)) {
output << "\t" << i << ".\t" << entry << "\n";
for (const auto& [i, entry] : llvm::enumerate(stack_)) {
output << "\t" << i << ".\t" << entry.id << "\t{";
llvm::ListSeparator sep;
for (auto id : entry.content) {
output << sep << id;
}
output << "}\n";
}
}
+31 -15
View File
@@ -39,8 +39,8 @@ class NodeBlockStack {
// Peeks at the top node block. This does not trigger lazy allocation, so the
// returned node block may be invalid.
auto Peek() -> SemIR::NodeBlockId {
CARBON_CHECK(!stack_.empty()) << "no current block";
return stack_.back();
CARBON_CHECK(!empty()) << "no current block";
return stack_[size() - 1].id;
}
// Returns the top node block, allocating one if it's still invalid. If
@@ -52,16 +52,10 @@ class NodeBlockStack {
// SemIR::NodeBlockId::Empty is returned if one wasn't allocated.
auto Pop() -> SemIR::NodeBlockId;
// Pops the top node block, ensuring that it is lazily allocated if it's
// empty. For use when more nodes will be added to the block later.
auto PopForAdd() -> SemIR::NodeBlockId {
PeekForAdd();
return Pop();
}
// Adds the given node ID to the block at the top of the stack.
auto AddNodeId(SemIR::NodeId node_id) -> void {
semantics_ir_->AddNodeIdForNodeBlockStack(PeekForAdd(), node_id);
CARBON_CHECK(!empty()) << "no current block";
stack_[size_ - 1].content.push_back(node_id);
}
// Returns whether the current block is statically reachable.
@@ -72,10 +66,30 @@ class NodeBlockStack {
// Prints the stack for a stack dump.
auto PrintForStackDump(llvm::raw_ostream& output) const -> void;
auto empty() const -> bool { return stack_.empty(); }
auto size() const -> size_t { return stack_.size(); }
auto empty() const -> bool { return size() == 0; }
auto size() const -> int { return size_; }
private:
struct StackEntry {
// Preallocate an arbitrary size for the stack entries.
// TODO: Perform measurements to pick a good starting size to avoid
// reallocation.
StackEntry() { content.reserve(32); }
auto Reset(SemIR::NodeBlockId new_id) {
id = new_id;
content.clear();
}
// The block ID, if one has been allocated, Invalid if no block has been
// allocated, or Unreachable if this block is known to be unreachable.
SemIR::NodeBlockId id = SemIR::NodeBlockId::Invalid;
// The content of the block. Stored as a vector rather than as a SmallVector
// to reduce the cost of resizing `stack_` and performing swaps.
std::vector<SemIR::NodeId> content;
};
// A name for debugging.
llvm::StringLiteral name_;
@@ -86,9 +100,11 @@ class NodeBlockStack {
llvm::raw_ostream* vlog_stream_;
// The actual stack.
// PushEntry and PopEntry control modification in order to centralize
// vlogging.
llvm::SmallVector<SemIR::NodeBlockId> stack_;
llvm::SmallVector<StackEntry> stack_;
// The size of the stack. Entries after this in `stack_` are kept around so
// that we can reuse the allocated buffer for their content.
int size_ = 0;
};
} // namespace Carbon::Check
@@ -12,7 +12,7 @@
// CHECK:STDOUT: sem_ir:
// CHECK:STDOUT: - cross_reference_irs_size: 1
// CHECK:STDOUT: functions: [
// CHECK:STDOUT: {name: str0, param_refs: block0, body: [block2]},
// CHECK:STDOUT: {name: str0, param_refs: block0, body: [block1]},
// CHECK:STDOUT: ]
// CHECK:STDOUT: integer_literals: [
// CHECK:STDOUT: ]
@@ -33,10 +33,10 @@
// CHECK:STDOUT: [
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: ],
// CHECK:STDOUT: ]
// CHECK:STDOUT:
@@ -52,7 +52,7 @@
// CHECK:STDOUT: sem_ir:
// CHECK:STDOUT: - cross_reference_irs_size: 1
// CHECK:STDOUT: functions: [
// CHECK:STDOUT: {name: str0, param_refs: block0, body: [block2]},
// CHECK:STDOUT: {name: str0, param_refs: block0, body: [block1]},
// CHECK:STDOUT: ]
// CHECK:STDOUT: integer_literals: [
// CHECK:STDOUT: ]
@@ -73,10 +73,10 @@
// CHECK:STDOUT: [
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: ],
// CHECK:STDOUT: ]
// CHECK:STDOUT:
+6 -6
View File
@@ -12,7 +12,7 @@
// CHECK:STDOUT: sem_ir:
// CHECK:STDOUT: - cross_reference_irs_size: 1
// CHECK:STDOUT: functions: [
// CHECK:STDOUT: {name: str0, param_refs: block0, body: [block2]},
// CHECK:STDOUT: {name: str0, param_refs: block0, body: [block1]},
// CHECK:STDOUT: ]
// CHECK:STDOUT: integer_literals: [
// CHECK:STDOUT: ]
@@ -33,17 +33,17 @@
// CHECK:STDOUT: [
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: ],
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: b.carbon
// CHECK:STDOUT: sem_ir:
// CHECK:STDOUT: - cross_reference_irs_size: 1
// CHECK:STDOUT: functions: [
// CHECK:STDOUT: {name: str0, param_refs: block0, body: [block2]},
// CHECK:STDOUT: {name: str0, param_refs: block0, body: [block1]},
// CHECK:STDOUT: ]
// CHECK:STDOUT: integer_literals: [
// CHECK:STDOUT: ]
@@ -64,10 +64,10 @@
// CHECK:STDOUT: [
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: ],
// CHECK:STDOUT: ]
// --- a.carbon
+13 -13
View File
@@ -16,7 +16,7 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: sem_ir:
// CHECK:STDOUT: - cross_reference_irs_size: 1
// CHECK:STDOUT: functions: [
// CHECK:STDOUT: {name: str0, param_refs: block2, return_type: type3, return_slot: node+6, body: [block5]},
// CHECK:STDOUT: {name: str0, param_refs: block1, return_type: type3, return_slot: node+6, body: [block4]},
// CHECK:STDOUT: ]
// CHECK:STDOUT: integer_literals: [
// CHECK:STDOUT: 2,
@@ -50,7 +50,7 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
// CHECK:STDOUT: {kind: StubReference, arg0: nodeFloatingPointType, type: typeTypeType},
// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
// CHECK:STDOUT: {kind: TupleLiteral, arg0: block3, type: type1},
// CHECK:STDOUT: {kind: TupleLiteral, arg0: block2, type: type1},
// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
// CHECK:STDOUT: {kind: VarStorage, arg0: str2, type: type3},
// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0},
@@ -59,7 +59,7 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: {kind: StubReference, arg0: node+9, type: type0},
// CHECK:STDOUT: {kind: RealLiteral, arg0: real0, type: type2},
// CHECK:STDOUT: {kind: StubReference, arg0: node+11, type: type2},
// CHECK:STDOUT: {kind: TupleLiteral, arg0: block6, type: type3},
// CHECK:STDOUT: {kind: TupleLiteral, arg0: block5, type: type3},
// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+13},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [
@@ -67,6 +67,13 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: node+2,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: node+2,
// CHECK:STDOUT: node+3,
@@ -75,16 +82,6 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: node+6,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: node+2,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+7,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+8,
// CHECK:STDOUT: node+9,
// CHECK:STDOUT: node+10,
@@ -97,6 +94,9 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: node+10,
// CHECK:STDOUT: node+12,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+7,
// CHECK:STDOUT: ],
// CHECK:STDOUT: ]
// CHECK:STDOUT:
// CHECK:STDOUT: file "raw_and_textual_ir.carbon" {
+13 -13
View File
@@ -16,7 +16,7 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: sem_ir:
// CHECK:STDOUT: - cross_reference_irs_size: 1
// CHECK:STDOUT: functions: [
// CHECK:STDOUT: {name: str0, param_refs: block2, return_type: type3, return_slot: node+6, body: [block5]},
// CHECK:STDOUT: {name: str0, param_refs: block1, return_type: type3, return_slot: node+6, body: [block4]},
// CHECK:STDOUT: ]
// CHECK:STDOUT: integer_literals: [
// CHECK:STDOUT: 2,
@@ -50,7 +50,7 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: {kind: StubReference, arg0: nodeIntegerType, type: typeTypeType},
// CHECK:STDOUT: {kind: StubReference, arg0: nodeFloatingPointType, type: typeTypeType},
// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
// CHECK:STDOUT: {kind: TupleLiteral, arg0: block3, type: type1},
// CHECK:STDOUT: {kind: TupleLiteral, arg0: block2, type: type1},
// CHECK:STDOUT: {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
// CHECK:STDOUT: {kind: VarStorage, arg0: str2, type: type3},
// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0},
@@ -59,7 +59,7 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: {kind: StubReference, arg0: node+9, type: type0},
// CHECK:STDOUT: {kind: RealLiteral, arg0: real0, type: type2},
// CHECK:STDOUT: {kind: StubReference, arg0: node+11, type: type2},
// CHECK:STDOUT: {kind: TupleLiteral, arg0: block6, type: type3},
// CHECK:STDOUT: {kind: TupleLiteral, arg0: block5, type: type3},
// CHECK:STDOUT: {kind: ReturnExpression, arg0: node+13},
// CHECK:STDOUT: ]
// CHECK:STDOUT: node_blocks: [
@@ -67,6 +67,13 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: node+2,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: node+2,
// CHECK:STDOUT: node+3,
@@ -75,16 +82,6 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: node+6,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+0,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+1,
// CHECK:STDOUT: node+2,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+7,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+8,
// CHECK:STDOUT: node+9,
// CHECK:STDOUT: node+10,
@@ -97,4 +94,7 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: node+10,
// CHECK:STDOUT: node+12,
// CHECK:STDOUT: ],
// CHECK:STDOUT: [
// CHECK:STDOUT: node+7,
// CHECK:STDOUT: ],
// CHECK:STDOUT: ]
+1 -1
View File
@@ -8,7 +8,7 @@
// NOAUTOUPDATE
// SET-CHECK-SUBSET
// CHECK:STDERR: Node Push 0: FunctionIntroducer -> <none>
// CHECK:STDERR: AddNode block{{[0-9]+}}: {kind: FunctionDeclaration, arg0: function{{[0-9]+}}}
// CHECK:STDERR: AddNode block{{[0-9]+}}: {kind: Return}
fn Foo() {
return;
+13 -11
View File
@@ -157,15 +157,6 @@ class File : public Printable<File> {
return node_id;
}
// Adds the ID of an existing node to the specified block. Temporary, should
// only be called by Check::NodeBlockStack.
auto AddNodeIdForNodeBlockStack(NodeBlockId block_id, NodeId node_id)
-> void {
if (block_id != NodeBlockId::Unreachable) {
node_blocks_[block_id.index].push_back(node_id);
}
}
// Overwrites a given node with a new value.
auto ReplaceNode(NodeId node_id, Node node) -> void {
nodes_[node_id.index] = node;
@@ -174,13 +165,24 @@ class File : public Printable<File> {
// Returns the requested node.
auto GetNode(NodeId node_id) const -> Node { return nodes_[node_id.index]; }
// Adds an empty node block, returning an ID to reference it.
auto AddNodeBlock() -> NodeBlockId {
// Reserves and returns a node block ID. The contents of the node block
// should be specified by calling SetNodeBlock, or by pushing the ID onto the
// NodeBlockStack.
auto AddNodeBlockId() -> NodeBlockId {
NodeBlockId id(node_blocks_.size());
node_blocks_.push_back({});
return id;
}
// Sets the contents of an empty node block to the given content.
auto SetNodeBlock(NodeBlockId block_id, llvm::ArrayRef<NodeId> content)
-> void {
CARBON_CHECK(block_id != NodeBlockId::Unreachable);
CARBON_CHECK(node_blocks_[block_id.index].empty())
<< "node block content set more than once";
node_blocks_[block_id.index].assign(content.begin(), content.end());
}
// Adds a node block with the given content, returning an ID to reference it.
auto AddNodeBlock(llvm::ArrayRef<NodeId> content) -> NodeBlockId {
NodeBlockId id(node_blocks_.size());