[IR] Add ability to dump IR graph to dot

This CL adds the ability to dump an IR graph to a dot file. The
`--dump-ir-graph` option is added to the main tint command. The IR code
is moved behind a TINT_BUILD_IR flag in order to allow the GN build to
continue to build the tint program.

Bug: tint:1718
Change-Id: I0953fe2a59a34c21bb6cd288cb90e9d0298af793
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/107860
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
dan sinclair 2022-11-01 18:15:50 +00:00 committed by Dawn LUCI CQ
parent 1c755b68e6
commit 9261261da8
7 changed files with 272 additions and 28 deletions

1
.gitignore vendored
View File

@ -128,3 +128,4 @@ lcov.info
### Clang-Tidy files
all_findings.json
tint.dot

View File

@ -155,6 +155,7 @@ option_if_not_defined(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" ${D
option_if_not_defined(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON)
option_if_not_defined(TINT_BUILD_RUN_GENERATOR "Run the intrinsic generator" OFF)
option_if_not_defined(TINT_BUILD_IR "Build the IR" ON)
option_if_not_defined(TINT_BUILD_FUZZERS "Build fuzzers" OFF)
option_if_not_defined(TINT_BUILD_SPIRV_TOOLS_FUZZER "Build SPIRV-Tools fuzzer" OFF)
@ -290,6 +291,7 @@ message(STATUS "Tint build MSL writer: ${TINT_BUILD_MSL_WRITER}")
message(STATUS "Tint build SPIR-V writer: ${TINT_BUILD_SPV_WRITER}")
message(STATUS "Tint build WGSL writer: ${TINT_BUILD_WGSL_WRITER}")
message(STATUS "Tint build run generator: ${TINT_BUILD_RUN_GENERATOR}")
message(STATUS "Tint build IR: ${TINT_BUILD_IR}")
message(STATUS "Tint build fuzzers: ${TINT_BUILD_FUZZERS}")
message(STATUS "Tint build SPIRV-Tools fuzzer: ${TINT_BUILD_SPIRV_TOOLS_FUZZER}")
message(STATUS "Tint build AST fuzzer: ${TINT_BUILD_AST_FUZZER}")
@ -495,6 +497,7 @@ function(tint_core_compile_options TARGET)
target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_MSL_WRITER=$<BOOL:${TINT_BUILD_MSL_WRITER}>)
target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_SPV_WRITER=$<BOOL:${TINT_BUILD_SPV_WRITER}>)
target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_WGSL_WRITER=$<BOOL:${TINT_BUILD_WGSL_WRITER}>)
target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_IR=$<BOOL:${TINT_BUILD_IR}>)
common_compile_options(${TARGET})

View File

@ -238,26 +238,6 @@ set(TINT_LIB_SRCS
inspector/resource_binding.h
inspector/scalar.cc
inspector/scalar.h
ir/block.cc
ir/block.h
ir/builder.cc
ir/builder.h
ir/builder_impl.cc
ir/builder_impl.h
ir/flow_node.cc
ir/flow_node.h
ir/function.cc
ir/function.h
ir/if.cc
ir/if.h
ir/loop.cc
ir/loop.h
ir/module.cc
ir/module.h
ir/switch.cc
ir/switch.h
ir/terminator.cc
ir/terminator.h
number.cc
number.h
program_builder.cc
@ -701,6 +681,33 @@ if(${TINT_BUILD_HLSL_WRITER})
)
endif()
if(${TINT_BUILD_IR})
list(APPEND TINT_LIB_SRCS
ir/block.cc
ir/block.h
ir/builder.cc
ir/builder.h
ir/builder_impl.cc
ir/builder_impl.h
ir/debug.cc
ir/debug.h
ir/flow_node.cc
ir/flow_node.h
ir/function.cc
ir/function.h
ir/if.cc
ir/if.h
ir/loop.cc
ir/loop.h
ir/module.cc
ir/module.h
ir/switch.cc
ir/switch.h
ir/terminator.cc
ir/terminator.h
)
endif()
if(MSVC)
list(APPEND TINT_LIB_SRCS
tint.natvis
@ -864,8 +871,6 @@ if(TINT_BUILD_TESTS)
diagnostic/diagnostic_test.cc
diagnostic/formatter_test.cc
diagnostic/printer_test.cc
ir/builder_impl_test.cc
ir/test_helper.h
number_test.cc
program_builder_test.cc
program_test.cc
@ -1368,6 +1373,13 @@ if(TINT_BUILD_TESTS)
)
endif()
if (${TINT_BUILD_IR})
list(APPEND TINT_TEST_SRCS
ir/builder_impl_test.cc
ir/test_helper.h
)
endif()
if (${TINT_BUILD_FUZZERS})
list(APPEND TINT_TEST_SRCS
fuzzers/mersenne_twister_engine.cc

View File

@ -26,7 +26,7 @@
#if TINT_BUILD_GLSL_WRITER
#include "StandAlone/ResourceLimits.h"
#include "glslang/Public/ShaderLang.h"
#endif
#endif // TINT_BUILD_GLSL_WRITER
#if TINT_BUILD_SPV_READER
#include "spirv-tools/libspirv.hpp"
@ -39,6 +39,11 @@
#include "src/tint/val/val.h"
#include "tint/tint.h"
#if TINT_BUILD_IR
#include "src/tint/ir/debug.h"
#include "src/tint/ir/module.h"
#endif // TINT_BUILD_IR
namespace {
[[noreturn]] void TintInternalCompilerErrorReporter(const tint::diag::List& diagnostics) {
@ -94,6 +99,10 @@ struct Options {
std::string xcrun_path;
std::unordered_map<std::string, double> overrides;
std::optional<tint::sem::BindingPoint> hlsl_root_constant_binding_point;
#if TINT_BUILD_IR
bool dump_ir_graph = false;
#endif // TINT_BUILD_IR
};
const char kUsage[] = R"(Usage: tint [options] <input-file>
@ -436,6 +445,10 @@ bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
return false;
}
opts->dxc_path = args[i];
#if TINT_BUILD_IR
} else if (arg == "--dump-ir-graph") {
opts->dump_ir_graph = true;
#endif // TINT_BUILD_IR
} else if (arg == "--xcrun") {
++i;
if (i >= args.size()) {
@ -1135,6 +1148,11 @@ int main(int argc, const char** argv) {
if (options.show_help) {
std::string usage = tint::utils::ReplaceAll(kUsage, "${transforms}", transform_names());
#if TINT_BUILD_IR
usage +=
" --dump-ir-graph -- Writes the IR graph to 'tint.dot' as a dot graph\n";
#endif // TINT_BUILD_IR
std::cout << usage << std::endl;
return 0;
}
@ -1253,6 +1271,19 @@ int main(int argc, const char** argv) {
return 1;
}
#if TINT_BUILD_IR
if (options.dump_ir_graph) {
auto result = tint::ir::Module::FromProgram(program.get());
if (!result) {
std::cerr << "Failed to build IR from program: " << result.Failure() << std::endl;
} else {
auto mod = result.Move();
auto graph = tint::ir::Debug::AsDotGraph(&mod);
WriteFile("tint.dot", "w", graph);
}
}
#endif // TINT_BUILD_IR
tint::inspector::Inspector inspector(program.get());
if (options.dump_inspector_bindings) {

View File

@ -138,8 +138,10 @@ ResultType BuilderImpl::Build() {
return true;
},
[&](Default) {
TINT_ICE(IR, diagnostics_) << "unhandled type: " << decl->TypeInfo().name;
return false;
diagnostics_.add_warning(tint::diag::System::IR,
"unknown type: " + std::string(decl->TypeInfo().name),
decl->source);
return true;
});
if (!ok) {
return utils::Failure;
@ -220,9 +222,10 @@ bool BuilderImpl::EmitStatement(const ast::Statement* stmt) {
return true; // Not emitted
},
[&](Default) {
TINT_ICE(IR, diagnostics_)
<< "unknown statement type: " << std::string(stmt->TypeInfo().name);
return false;
diagnostics_.add_warning(
tint::diag::System::IR,
"unknown statement type: " + std::string(stmt->TypeInfo().name), stmt->source);
return true;
});
}

159
src/tint/ir/debug.cc Normal file
View File

@ -0,0 +1,159 @@
// 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/debug.h"
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#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/program.h"
namespace tint::ir {
// static
std::string Debug::AsDotGraph(const Module* mod) {
size_t node_count = 0;
std::unordered_set<const FlowNode*> visited;
std::unordered_set<const FlowNode*> merge_nodes;
std::unordered_map<const FlowNode*, std::string> node_to_name;
std::stringstream out;
auto name_for = [&](const FlowNode* node) -> std::string {
if (node_to_name.count(node) > 0) {
return node_to_name[node];
}
std::string name = "node_" + std::to_string(node_count);
node_count += 1;
node_to_name[node] = name;
return name;
};
std::function<void(const FlowNode*)> Graph = [&](const FlowNode* node) {
if (visited.count(node) > 0) {
return;
}
visited.insert(node);
tint::Switch(
node,
[&](const ir::Block* b) {
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);
// Dashed lines to merge blocks
if (merge_nodes.count(b->branch_target) != 0) {
out << " [style=dashed]";
}
out << std::endl;
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);
size_t i = 0;
for (const auto& c : s->cases) {
out << name_for(c.start_target)
<< R"( [label="case )" + std::to_string(i++) + R"("])" << std::endl;
}
out << name_for(s) << " -> {";
for (const auto& c : s->cases) {
if (&c != &(s->cases[0])) {
out << ", ";
}
out << name_for(c.start_target);
}
out << "}" << std::endl;
for (const auto& c : s->cases) {
Graph(c.start_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) << " -> {";
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 << "}" << std::endl;
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);
// 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 << "}" << 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);
},
[&](const ir::Terminator*) {
// Already done
});
};
out << "digraph G {" << std::endl;
for (const auto* func : mod->functions) {
// Cluster each function to label and draw a box around it.
out << "subgraph cluster_" << name_for(func) << " {" << std::endl;
out << R"(label=")" << mod->program->Symbols().NameFor(func->source->symbol) << R"(")"
<< std::endl;
out << name_for(func->start_target) << R"( [label="start"])" << std::endl;
out << name_for(func->end_target) << R"( [label="end"])" << std::endl;
Graph(func->start_target);
out << "}" << std::endl;
}
out << "}";
return out.str();
}
} // namespace tint::ir

35
src/tint/ir/debug.h Normal file
View File

@ -0,0 +1,35 @@
// 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.
#ifndef SRC_TINT_IR_DEBUG_H_
#define SRC_TINT_IR_DEBUG_H_
#include <string>
#include "src/tint/ir/module.h"
namespace tint::ir {
/// Helper class to debug IR.
class Debug {
public:
/// Returns the module as a dot graph
/// @param mod the module to emit
/// @returns the dot graph for the given module
static std::string AsDotGraph(const Module* mod);
};
} // namespace tint::ir
#endif // SRC_TINT_IR_DEBUG_H_