[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:
parent
55aa801705
commit
a764437138
|
@ -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;
|
||||
|
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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; });
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue