[ir] Allow branching arguments.

When branching to a different flow node, there is a need to pass
arguments to the branch. These arguments could be the value of the
return.

This extracts a `Branch` out to an object with a target and arguments
and then updates the IR to use the new Branch structure.

Bug: tint:1718
Change-Id: Ic8de8046f58056327a04c8afe3b597810c80ccdb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/116546
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
dan sinclair 2023-01-07 23:13:35 +00:00 committed by Dawn LUCI CQ
parent 55aa801705
commit a764437138
13 changed files with 576 additions and 476 deletions

View File

@ -15,6 +15,7 @@
#ifndef SRC_TINT_IR_BLOCK_H_
#define SRC_TINT_IR_BLOCK_H_
#include "src/tint/ir/branch.h"
#include "src/tint/ir/flow_node.h"
#include "src/tint/ir/instruction.h"
#include "src/tint/utils/vector.h"
@ -31,7 +32,7 @@ class Block : public Castable<Block, FlowNode> {
~Block() override;
/// The node this block branches too.
const FlowNode* branch_target = nullptr;
Branch branch = {};
/// The instructions in the block
utils::Vector<const Instruction*, 16> instructions;

36
src/tint/ir/branch.h Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2023 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_IR_BRANCH_H_
#define SRC_TINT_IR_BRANCH_H_
#include "src/tint/ir/flow_node.h"
#include "src/tint/ir/value.h"
namespace tint::ir {
/// A information on a branch to another block
struct Branch {
/// The block being branched too.
FlowNode* target = nullptr;
/// The arguments provided for that branch. These arguments could be the
/// return value in the case of a branch to the terminator, or they could
/// be the basic block arguments passed into the block.
utils::Vector<Value*, 2> args;
};
} // namespace tint::ir
#endif // SRC_TINT_IR_BRANCH_H_

View File

@ -47,48 +47,49 @@ Function* Builder::CreateFunction() {
If* Builder::CreateIf() {
auto* ir_if = ir.flow_nodes.Create<If>();
ir_if->true_target = CreateBlock();
ir_if->false_target = CreateBlock();
ir_if->merge_target = CreateBlock();
ir_if->true_.target = CreateBlock();
ir_if->false_.target = CreateBlock();
ir_if->merge.target = CreateBlock();
// An if always branches to both the true and false block.
ir_if->true_target->inbound_branches.Push(ir_if);
ir_if->false_target->inbound_branches.Push(ir_if);
ir_if->true_.target->inbound_branches.Push(ir_if);
ir_if->false_.target->inbound_branches.Push(ir_if);
return ir_if;
}
Loop* Builder::CreateLoop() {
auto* ir_loop = ir.flow_nodes.Create<Loop>();
ir_loop->start_target = CreateBlock();
ir_loop->continuing_target = CreateBlock();
ir_loop->merge_target = CreateBlock();
ir_loop->start.target = CreateBlock();
ir_loop->continuing.target = CreateBlock();
ir_loop->merge.target = CreateBlock();
// A loop always branches to the start block.
ir_loop->start_target->inbound_branches.Push(ir_loop);
ir_loop->start.target->inbound_branches.Push(ir_loop);
return ir_loop;
}
Switch* Builder::CreateSwitch() {
auto* ir_switch = ir.flow_nodes.Create<Switch>();
ir_switch->merge_target = CreateBlock();
ir_switch->merge.target = CreateBlock();
return ir_switch;
}
Block* Builder::CreateCase(Switch* s, utils::VectorRef<Switch::CaseSelector> selectors) {
s->cases.Push(Switch::Case{selectors, CreateBlock()});
s->cases.Push(Switch::Case{selectors, {CreateBlock(), utils::Empty}});
Block* b = s->cases.Back().start_target;
Block* b = s->cases.Back().start.target->As<Block>();
// Switch branches into the case block
b->inbound_branches.Push(s);
return b;
}
void Builder::Branch(Block* from, FlowNode* to) {
void Builder::Branch(Block* from, FlowNode* to, utils::VectorRef<Value*> args) {
TINT_ASSERT(IR, from);
TINT_ASSERT(IR, to);
from->branch_target = to;
from->branch.target = to;
from->branch.args = args;
to->inbound_branches.Push(from);
}

View File

@ -79,7 +79,8 @@ class Builder {
/// Branches the given block to the given flow node.
/// @param from the block to branch from
/// @param to the node to branch too
void Branch(Block* from, FlowNode* to);
/// @param args arguments to the branch
void Branch(Block* from, FlowNode* to, utils::VectorRef<Value*> args);
/// Creates a constant::Value
/// @param args the arguments

View File

@ -64,7 +64,7 @@ class FlowStackScope {
};
bool IsBranched(const Block* b) {
return b->branch_target != nullptr;
return b->branch.target != nullptr;
}
bool IsConnected(const FlowNode* b) {
@ -92,11 +92,11 @@ BuilderImpl::BuilderImpl(const Program* program)
BuilderImpl::~BuilderImpl() = default;
void BuilderImpl::BranchTo(FlowNode* node) {
void BuilderImpl::BranchTo(FlowNode* node, utils::VectorRef<Value*> args) {
TINT_ASSERT(IR, current_flow_block);
TINT_ASSERT(IR, !IsBranched(current_flow_block));
builder.Branch(current_flow_block, node);
builder.Branch(current_flow_block, node, args);
current_flow_block = nullptr;
}
@ -265,27 +265,27 @@ bool BuilderImpl::EmitIf(const ast::IfStatement* stmt) {
{
FlowStackScope scope(this, if_node);
current_flow_block = if_node->true_target;
current_flow_block = if_node->true_.target->As<Block>();
if (!EmitStatement(stmt->body)) {
return false;
}
// If the true branch did not execute control flow, then go to the merge target
BranchToIfNeeded(if_node->merge_target);
BranchToIfNeeded(if_node->merge.target);
current_flow_block = if_node->false_target;
current_flow_block = if_node->false_.target->As<Block>();
if (stmt->else_statement && !EmitStatement(stmt->else_statement)) {
return false;
}
// If the false branch did not execute control flow, then go to the merge target
BranchToIfNeeded(if_node->merge_target);
BranchToIfNeeded(if_node->merge.target);
}
current_flow_block = nullptr;
// If both branches went somewhere, then they both returned, continued or broke. So,
// there is no need for the if merge-block and there is nothing to branch to the merge
// block anyway.
if (IsConnected(if_node->merge_target)) {
current_flow_block = if_node->merge_target;
if (IsConnected(if_node->merge.target)) {
current_flow_block = if_node->merge.target->As<Block>();
}
return true;
@ -301,15 +301,15 @@ bool BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
{
FlowStackScope scope(this, loop_node);
current_flow_block = loop_node->start_target;
current_flow_block = loop_node->start.target->As<Block>();
if (!EmitStatement(stmt->body)) {
return false;
}
// The current block didn't `break`, `return` or `continue`, go to the continuing block.
BranchToIfNeeded(loop_node->continuing_target);
BranchToIfNeeded(loop_node->continuing.target);
current_flow_block = loop_node->continuing_target;
current_flow_block = loop_node->continuing.target->As<Block>();
if (stmt->continuing) {
if (!EmitStatement(stmt->continuing)) {
return false;
@ -317,13 +317,13 @@ bool BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
}
// Branch back to the start node if the continue target didn't branch out already
BranchToIfNeeded(loop_node->start_target);
BranchToIfNeeded(loop_node->start.target);
}
// The loop merge can get disconnected if the loop returns directly, or the continuing target
// branches, eventually, to the merge, but nothing branched to the continuing target.
current_flow_block = loop_node->merge_target;
if (!IsConnected(loop_node->merge_target)) {
current_flow_block = loop_node->merge.target->As<Block>();
if (!IsConnected(loop_node->merge.target)) {
current_flow_block = nullptr;
}
return true;
@ -332,7 +332,9 @@ bool BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
bool BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
auto* loop_node = builder.CreateLoop();
// Continue is always empty, just go back to the start
builder.Branch(loop_node->continuing_target, loop_node->start_target);
TINT_ASSERT(IR, loop_node->continuing.target->Is<Block>());
builder.Branch(loop_node->continuing.target->As<Block>(), loop_node->start.target,
utils::Empty);
BranchTo(loop_node);
@ -341,7 +343,7 @@ bool BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
{
FlowStackScope scope(this, loop_node);
current_flow_block = loop_node->start_target;
current_flow_block = loop_node->start.target->As<Block>();
// Emit the while condition into the start target of the loop
auto reg = EmitExpression(stmt->condition);
@ -351,28 +353,33 @@ bool BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
// Create an `if (cond) {} else {break;}` control flow
auto* if_node = builder.CreateIf();
builder.Branch(if_node->true_target, if_node->merge_target);
builder.Branch(if_node->false_target, loop_node->merge_target);
TINT_ASSERT(IR, if_node->true_.target->Is<Block>());
builder.Branch(if_node->true_.target->As<Block>(), if_node->merge.target, utils::Empty);
TINT_ASSERT(IR, if_node->false_.target->Is<Block>());
builder.Branch(if_node->false_.target->As<Block>(), loop_node->merge.target, utils::Empty);
if_node->condition = reg.Get();
BranchTo(if_node);
current_flow_block = if_node->merge_target;
current_flow_block = if_node->merge.target->As<Block>();
if (!EmitStatement(stmt->body)) {
return false;
}
BranchToIfNeeded(loop_node->continuing_target);
BranchToIfNeeded(loop_node->continuing.target);
}
// The while loop always has a path to the merge target as the break statement comes before
// anything inside the loop.
current_flow_block = loop_node->merge_target;
current_flow_block = loop_node->merge.target->As<Block>();
return true;
}
bool BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
auto* loop_node = builder.CreateLoop();
builder.Branch(loop_node->continuing_target, loop_node->start_target);
TINT_ASSERT(IR, loop_node->continuing.target->Is<Block>());
builder.Branch(loop_node->continuing.target->As<Block>(), loop_node->start.target,
utils::Empty);
if (stmt->initializer) {
// Emit the for initializer before branching to the loop
@ -388,7 +395,7 @@ bool BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
{
FlowStackScope scope(this, loop_node);
current_flow_block = loop_node->start_target;
current_flow_block = loop_node->start.target->As<Block>();
if (stmt->condition) {
// Emit the condition into the target target of the loop
@ -399,22 +406,26 @@ bool BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
// Create an `if (cond) {} else {break;}` control flow
auto* if_node = builder.CreateIf();
builder.Branch(if_node->true_target, if_node->merge_target);
builder.Branch(if_node->false_target, loop_node->merge_target);
TINT_ASSERT(IR, if_node->true_.target->Is<Block>());
builder.Branch(if_node->true_.target->As<Block>(), if_node->merge.target, utils::Empty);
TINT_ASSERT(IR, if_node->false_.target->Is<Block>());
builder.Branch(if_node->false_.target->As<Block>(), loop_node->merge.target,
utils::Empty);
if_node->condition = reg.Get();
BranchTo(if_node);
current_flow_block = if_node->merge_target;
current_flow_block = if_node->merge.target->As<Block>();
}
if (!EmitStatement(stmt->body)) {
return false;
}
BranchToIfNeeded(loop_node->continuing_target);
BranchToIfNeeded(loop_node->continuing.target);
if (stmt->continuing) {
current_flow_block = loop_node->continuing_target;
current_flow_block = loop_node->continuing.target->As<Block>();
if (!EmitStatement(stmt->continuing)) {
return false;
}
@ -422,7 +433,7 @@ bool BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
}
// The while loop always has a path to the merge target as the break statement comes before
// anything inside the loop.
current_flow_block = loop_node->merge_target;
current_flow_block = loop_node->merge.target->As<Block>();
return true;
}
@ -458,24 +469,29 @@ bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
if (!EmitStatement(c->Body()->Declaration())) {
return false;
}
BranchToIfNeeded(switch_node->merge_target);
BranchToIfNeeded(switch_node->merge.target);
}
}
current_flow_block = nullptr;
if (IsConnected(switch_node->merge_target)) {
current_flow_block = switch_node->merge_target;
if (IsConnected(switch_node->merge.target)) {
current_flow_block = switch_node->merge.target->As<Block>();
}
return true;
}
bool BuilderImpl::EmitReturn(const ast::ReturnStatement*) {
// TODO(dsinclair): Emit the return value. Need to determine how we want to
// emit this. Emit a `return_value %yy` instruction? There is no `return`
// instruction as we just branch.
bool BuilderImpl::EmitReturn(const ast::ReturnStatement* stmt) {
utils::Vector<Value*, 1> ret_value;
if (stmt->value) {
auto ret = EmitExpression(stmt->value);
if (!ret) {
return false;
}
ret_value.Push(ret.Get());
}
BranchTo(current_function_->end_target);
BranchTo(current_function_->end_target, std::move(ret_value));
return true;
}
@ -484,9 +500,9 @@ bool BuilderImpl::EmitBreak(const ast::BreakStatement*) {
TINT_ASSERT(IR, current_control);
if (auto* c = current_control->As<Loop>()) {
BranchTo(c->merge_target);
BranchTo(c->merge.target);
} else if (auto* s = current_control->As<Switch>()) {
BranchTo(s->merge_target);
BranchTo(s->merge.target);
} else {
TINT_UNREACHABLE(IR, diagnostics_);
return false;
@ -500,7 +516,7 @@ bool BuilderImpl::EmitContinue(const ast::ContinueStatement*) {
TINT_ASSERT(IR, current_control);
if (auto* c = current_control->As<Loop>()) {
BranchTo(c->continuing_target);
BranchTo(c->continuing.target);
} else {
TINT_UNREACHABLE(IR, diagnostics_);
}
@ -528,17 +544,17 @@ bool BuilderImpl::EmitBreakIf(const ast::BreakIfStatement* stmt) {
auto* loop = current_control->As<Loop>();
current_flow_block = if_node->true_target;
BranchTo(loop->merge_target);
current_flow_block = if_node->true_.target->As<Block>();
BranchTo(loop->merge.target);
current_flow_block = if_node->false_target;
BranchTo(if_node->merge_target);
current_flow_block = if_node->false_.target->As<Block>();
BranchTo(if_node->merge.target);
current_flow_block = if_node->merge_target;
current_flow_block = if_node->merge.target->As<Block>();
// The `break-if` has to be the last item in the continuing block. The false branch of the
// `break-if` will always take us back to the start of the loop.
BranchTo(loop->start_target);
BranchTo(loop->start.target);
return true;
}

View File

@ -202,7 +202,7 @@ class BuilderImpl {
private:
enum class ControlFlags { kNone, kExcludeSwitch };
void BranchTo(ir::FlowNode* node);
void BranchTo(ir::FlowNode* node, utils::VectorRef<Value*> args = {});
void BranchToIfNeeded(ir::FlowNode* node);
FlowNode* FindEnclosingControl(ControlFlags flags);

File diff suppressed because it is too large Load Diff

View File

@ -59,24 +59,24 @@ std::string Debug::AsDotGraph(const Module* mod) {
if (node_to_name.count(b) == 0) {
out << name_for(b) << R"( [label="block"])" << std::endl;
}
out << name_for(b) << " -> " << name_for(b->branch_target);
out << name_for(b) << " -> " << name_for(b->branch.target);
// Dashed lines to merge blocks
if (merge_nodes.count(b->branch_target) != 0) {
if (merge_nodes.count(b->branch.target) != 0) {
out << " [style=dashed]";
}
out << std::endl;
Graph(b->branch_target);
Graph(b->branch.target);
},
[&](const ir::Switch* s) {
out << name_for(s) << R"( [label="switch"])" << std::endl;
out << name_for(s->merge_target) << R"( [label="switch merge"])" << std::endl;
merge_nodes.insert(s->merge_target);
out << name_for(s->merge.target) << R"( [label="switch merge"])" << std::endl;
merge_nodes.insert(s->merge.target);
size_t i = 0;
for (const auto& c : s->cases) {
out << name_for(c.start_target)
out << name_for(c.start.target)
<< R"( [label="case )" + std::to_string(i++) + R"("])" << std::endl;
}
out << name_for(s) << " -> {";
@ -84,56 +84,56 @@ std::string Debug::AsDotGraph(const Module* mod) {
if (&c != &(s->cases[0])) {
out << ", ";
}
out << name_for(c.start_target);
out << name_for(c.start.target);
}
out << "}" << std::endl;
for (const auto& c : s->cases) {
Graph(c.start_target);
Graph(c.start.target);
}
Graph(s->merge_target);
Graph(s->merge.target);
},
[&](const ir::If* i) {
out << name_for(i) << R"( [label="if"])" << std::endl;
out << name_for(i->true_target) << R"( [label="true"])" << std::endl;
out << name_for(i->false_target) << R"( [label="false"])" << std::endl;
out << name_for(i->merge_target) << R"( [label="if merge"])" << std::endl;
merge_nodes.insert(i->merge_target);
out << name_for(i->true_.target) << R"( [label="true"])" << std::endl;
out << name_for(i->false_.target) << R"( [label="false"])" << std::endl;
out << name_for(i->merge.target) << R"( [label="if merge"])" << std::endl;
merge_nodes.insert(i->merge.target);
out << name_for(i) << " -> {";
out << name_for(i->true_target) << ", " << name_for(i->false_target);
out << name_for(i->true_.target) << ", " << name_for(i->false_.target);
out << "}" << std::endl;
// Subgraph if true/false branches so they draw on the same line
out << "subgraph sub_" << name_for(i) << " {" << std::endl;
out << R"(rank="same")" << std::endl;
out << name_for(i->true_target) << std::endl;
out << name_for(i->false_target) << std::endl;
out << name_for(i->true_.target) << std::endl;
out << name_for(i->false_.target) << std::endl;
out << "}" << std::endl;
Graph(i->true_target);
Graph(i->false_target);
Graph(i->merge_target);
Graph(i->true_.target);
Graph(i->false_.target);
Graph(i->merge.target);
},
[&](const ir::Loop* l) {
out << name_for(l) << R"( [label="loop"])" << std::endl;
out << name_for(l->start_target) << R"( [label="start"])" << std::endl;
out << name_for(l->continuing_target) << R"( [label="continuing"])" << std::endl;
out << name_for(l->merge_target) << R"( [label="loop merge"])" << std::endl;
merge_nodes.insert(l->merge_target);
out << name_for(l->start.target) << R"( [label="start"])" << std::endl;
out << name_for(l->continuing.target) << R"( [label="continuing"])" << std::endl;
out << name_for(l->merge.target) << R"( [label="loop merge"])" << std::endl;
merge_nodes.insert(l->merge.target);
// Subgraph the continuing and merge so they get drawn on the same line
out << "subgraph sub_" << name_for(l) << " {" << std::endl;
out << R"(rank="same")" << std::endl;
out << name_for(l->continuing_target) << std::endl;
out << name_for(l->merge_target) << std::endl;
out << name_for(l->continuing.target) << std::endl;
out << name_for(l->merge.target) << std::endl;
out << "}" << std::endl;
out << name_for(l) << " -> " << name_for(l->start_target) << std::endl;
out << name_for(l) << " -> " << name_for(l->start.target) << std::endl;
Graph(l->start_target);
Graph(l->continuing_target);
Graph(l->merge_target);
Graph(l->start.target);
Graph(l->continuing.target);
Graph(l->merge.target);
},
[&](const ir::Terminator*) {
// Already done

View File

@ -66,6 +66,16 @@ void Disassembler::EmitBlockInstructions(const Block* b) {
}
}
size_t Disassembler::GetIdForNode(const FlowNode* node) {
auto it = flow_node_to_id_.find(node);
if (it != flow_node_to_id_.end()) {
return it->second;
}
size_t id = next_node_id_++;
flow_node_to_id_[node] = id;
return id;
}
void Disassembler::Walk(const FlowNode* node) {
if ((visited_.count(node) > 0) || (stop_nodes_.count(node) > 0)) {
return;
@ -75,7 +85,7 @@ void Disassembler::Walk(const FlowNode* node) {
tint::Switch(
node,
[&](const ir::Function* f) {
Indent() << "Function" << std::endl;
Indent() << "%" << GetIdForNode(f) << " = Function" << std::endl;
{
ScopedIndent func_indent(&indent_size_);
@ -85,60 +95,74 @@ void Disassembler::Walk(const FlowNode* node) {
Walk(f->end_target);
},
[&](const ir::Block* b) {
Indent() << "Block" << std::endl;
Indent() << "%" << GetIdForNode(b) << " = Block" << std::endl;
EmitBlockInstructions(b);
Walk(b->branch_target);
if (b->branch.target->Is<Terminator>()) {
Indent() << "Return ";
} else {
Indent() << "Branch ";
}
out_ << GetIdForNode(b->branch.target);
for (const auto* v : b->branch.args) {
out_ << " ";
v->ToString(out_, mod_.symbols);
}
out_ << std::endl;
Walk(b->branch.target);
},
[&](const ir::Switch* s) {
Indent() << "Switch (" << s->condition << ")" << std::endl;
Indent() << "%" << GetIdForNode(s) << " = Switch (" << s->condition << ")" << std::endl;
{
ScopedIndent switch_indent(&indent_size_);
ScopedStopNode scope(&stop_nodes_, s->merge_target);
ScopedStopNode scope(&stop_nodes_, s->merge.target);
for (const auto& c : s->cases) {
Indent() << "Case" << std::endl;
ScopedIndent case_indent(&indent_size_);
Walk(c.start_target);
Walk(c.start.target);
}
}
Indent() << "Switch Merge" << std::endl;
Walk(s->merge_target);
Walk(s->merge.target);
},
[&](const ir::If* i) {
Indent() << "if (" << i->condition << ")" << std::endl;
Indent() << "%" << GetIdForNode(i) << " = if (" << i->condition << ")" << std::endl;
{
ScopedIndent if_indent(&indent_size_);
ScopedStopNode scope(&stop_nodes_, i->merge_target);
ScopedStopNode scope(&stop_nodes_, i->merge.target);
Indent() << "true branch" << std::endl;
Walk(i->true_target);
Walk(i->true_.target);
Indent() << "false branch" << std::endl;
Walk(i->false_target);
Walk(i->false_.target);
}
Indent() << "if merge" << std::endl;
Walk(i->merge_target);
Walk(i->merge.target);
},
[&](const ir::Loop* l) {
Indent() << "loop" << std::endl;
Indent() << "%" << GetIdForNode(l) << " = loop" << std::endl;
{
ScopedStopNode loop_scope(&stop_nodes_, l->merge_target);
ScopedStopNode loop_scope(&stop_nodes_, l->merge.target);
ScopedIndent loop_indent(&indent_size_);
{
ScopedStopNode inner_scope(&stop_nodes_, l->continuing_target);
ScopedStopNode inner_scope(&stop_nodes_, l->continuing.target);
Indent() << "loop start" << std::endl;
Walk(l->start_target);
Walk(l->start.target);
}
Indent() << "loop continuing" << std::endl;
ScopedIndent continuing_indent(&indent_size_);
Walk(l->continuing_target);
Walk(l->continuing.target);
}
Indent() << "loop merge" << std::endl;
Walk(l->merge_target);
Walk(l->merge.target);
},
[&](const ir::Terminator*) { Indent() << "Function end" << std::endl; });
}

View File

@ -17,6 +17,7 @@
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "src/tint/ir/flow_node.h"
@ -46,11 +47,14 @@ class Disassembler {
private:
std::ostream& Indent();
void Walk(const FlowNode* node);
size_t GetIdForNode(const FlowNode* node);
const Module& mod_;
std::stringstream out_;
std::unordered_set<const FlowNode*> visited_;
std::unordered_set<const FlowNode*> stop_nodes_;
std::unordered_map<const FlowNode*, size_t> flow_node_to_id_;
size_t next_node_id_ = 0;
uint32_t indent_size_ = 0;
};

View File

@ -15,6 +15,7 @@
#ifndef SRC_TINT_IR_IF_H_
#define SRC_TINT_IR_IF_H_
#include "src/tint/ir/branch.h"
#include "src/tint/ir/flow_node.h"
#include "src/tint/ir/value.h"
@ -33,12 +34,12 @@ class If : public Castable<If, FlowNode> {
~If() override;
/// The true branch block
Block* true_target = nullptr;
Branch true_ = {};
/// The false branch block
Block* false_target = nullptr;
Branch false_ = {};
/// An block to reconvert the true/false barnches. The block always exists, but there maybe no
/// branches into it. (e.g. if both branches `return`)
Block* merge_target = nullptr;
Branch merge = {};
/// Value holding the condition result
const Value* condition = nullptr;
};

View File

@ -16,6 +16,7 @@
#define SRC_TINT_IR_LOOP_H_
#include "src/tint/ir/block.h"
#include "src/tint/ir/branch.h"
#include "src/tint/ir/flow_node.h"
namespace tint::ir {
@ -28,13 +29,13 @@ class Loop : public Castable<Loop, FlowNode> {
~Loop() override;
/// The start block is the first block in a loop.
Block* start_target = nullptr;
Branch start = {};
/// The continue target of the block.
Block* continuing_target = nullptr;
Branch continuing = {};
/// The loop merge target. If the `loop` does a `return` then this block may not actually
/// end up in the control flow. We need it if the loop does a `break` we know where to break
/// too.
Block* merge_target = nullptr;
Branch merge = {};
};
} // namespace tint::ir

View File

@ -17,6 +17,7 @@
#include "src/tint/constant/value.h"
#include "src/tint/ir/block.h"
#include "src/tint/ir/branch.h"
#include "src/tint/ir/flow_node.h"
#include "src/tint/ir/value.h"
@ -39,7 +40,7 @@ class Switch : public Castable<Switch, FlowNode> {
/// The case selector for this node
utils::Vector<CaseSelector, 4> selectors;
/// The start block for the case block.
Block* start_target;
Branch start = {};
};
/// Constructor
@ -47,7 +48,7 @@ class Switch : public Castable<Switch, FlowNode> {
~Switch() override;
/// The switch merge target
Block* merge_target;
Branch merge = {};
/// The switch case statements
utils::Vector<Case, 4> cases;