dawn-cmake/src/tint/ir/disassembler.cc
Ben Clayton 23946b3606 tint: Move Switch() to own header
castable.h is bigger than it needs to be, and pretty much every tint .cc file includes castable.h
Reduce the amount of code that .cc files that don't use Switch() need to compile.

Change-Id: Ibb4e8b0bc7104ad33a7f2f39587c7d9e749fee97
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/123401
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
2023-03-09 16:50:19 +00:00

213 lines
6.4 KiB
C++

// Copyright 2022 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.
#include "src/tint/ir/disassembler.h"
#include "src/tint/ir/block.h"
#include "src/tint/ir/if.h"
#include "src/tint/ir/loop.h"
#include "src/tint/ir/switch.h"
#include "src/tint/ir/terminator.h"
#include "src/tint/switch.h"
namespace tint::ir {
namespace {
class ScopedStopNode {
public:
ScopedStopNode(std::unordered_set<const FlowNode*>* stop_nodes, const FlowNode* node)
: stop_nodes_(stop_nodes), node_(node) {
stop_nodes_->insert(node_);
}
~ScopedStopNode() { stop_nodes_->erase(node_); }
private:
std::unordered_set<const FlowNode*>* stop_nodes_;
const FlowNode* node_;
};
class ScopedIndent {
public:
explicit ScopedIndent(uint32_t* indent) : indent_(indent) { (*indent_) += 2; }
~ScopedIndent() { (*indent_) -= 2; }
private:
uint32_t* indent_;
};
} // namespace
Disassembler::Disassembler(const Module& mod) : mod_(mod) {}
Disassembler::~Disassembler() = default;
utils::StringStream& Disassembler::Indent() {
for (uint32_t i = 0; i < indent_size_; i++) {
out_ << " ";
}
return out_;
}
void Disassembler::EmitBlockInstructions(const Block* b) {
for (const auto* instr : b->instructions) {
Indent();
instr->ToString(out_, mod_.symbols) << std::endl;
}
}
size_t Disassembler::GetIdForNode(const FlowNode* node) {
TINT_ASSERT(IR, 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;
}
visited_.insert(node);
tint::Switch(
node,
[&](const ir::Function* f) {
Indent() << "%bb" << GetIdForNode(f) << " = Function " << mod_.symbols.NameFor(f->name)
<< std::endl;
{
ScopedIndent func_indent(&indent_size_);
ScopedStopNode scope(&stop_nodes_, f->end_target);
Walk(f->start_target);
}
Walk(f->end_target);
},
[&](const ir::Block* b) {
// If this block is dead, nothing to do
if (b->IsDead()) {
Indent() << "# Dead" << std::endl;
return;
}
Indent() << "%bb" << GetIdForNode(b) << " = Block" << std::endl;
EmitBlockInstructions(b);
if (b->branch.target->Is<Terminator>()) {
Indent() << "Return";
} else {
Indent() << "BranchTo "
<< "%bb" << GetIdForNode(b->branch.target);
}
out_ << " (";
for (const auto* v : b->branch.args) {
if (v != b->branch.args.Front()) {
out_ << ", ";
}
v->ToString(out_, mod_.symbols);
}
out_ << ")" << std::endl;
if (!b->branch.target->Is<Terminator>()) {
out_ << std::endl;
}
Walk(b->branch.target);
},
[&](const ir::Switch* s) {
Indent() << "%bb" << GetIdForNode(s) << " = Switch (";
s->condition->ToString(out_, mod_.symbols);
out_ << ")" << std::endl;
{
ScopedIndent switch_indent(&indent_size_);
ScopedStopNode scope(&stop_nodes_, s->merge.target);
for (const auto& c : s->cases) {
Indent() << "# Case ";
for (const auto& selector : c.selectors) {
if (&selector != &c.selectors.Front()) {
out_ << " ";
}
if (selector.IsDefault()) {
out_ << "default";
} else {
selector.val->ToString(out_, mod_.symbols);
}
}
out_ << std::endl;
Walk(c.start.target);
}
}
Indent() << "# Switch Merge" << std::endl;
Walk(s->merge.target);
},
[&](const ir::If* i) {
Indent() << "%bb" << GetIdForNode(i) << " = if (";
i->condition->ToString(out_, mod_.symbols);
out_ << ")" << std::endl;
{
ScopedIndent if_indent(&indent_size_);
ScopedStopNode scope(&stop_nodes_, i->merge.target);
Indent() << "# true branch" << std::endl;
Walk(i->true_.target);
Indent() << "# false branch" << std::endl;
Walk(i->false_.target);
}
if (!i->merge.target->IsDisconnected()) {
Indent() << "# if merge" << std::endl;
Walk(i->merge.target);
}
},
[&](const ir::Loop* l) {
Indent() << "%bb" << GetIdForNode(l) << " = loop" << std::endl;
{
ScopedStopNode loop_scope(&stop_nodes_, l->merge.target);
ScopedIndent loop_indent(&indent_size_);
{
ScopedStopNode inner_scope(&stop_nodes_, l->continuing.target);
Indent() << "# loop start" << std::endl;
Walk(l->start.target);
}
Indent() << "# loop continuing" << std::endl;
Walk(l->continuing.target);
}
Indent() << "# loop merge" << std::endl;
Walk(l->merge.target);
},
[&](const ir::Terminator*) { Indent() << "FunctionEnd" << std::endl
<< std::endl; });
}
std::string Disassembler::Disassemble() {
for (const auto* func : mod_.functions) {
Walk(func);
}
return out_.str();
}
} // namespace tint::ir