bin_to_dot.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // Copyright (c) 2016 Google Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "tools/cfg/bin_to_dot.h"
  15. #include <cassert>
  16. #include <iostream>
  17. #include <utility>
  18. #include <vector>
  19. #include "source/assembly_grammar.h"
  20. #include "source/name_mapper.h"
  21. namespace {
  22. const char* kMergeStyle = "style=dashed";
  23. const char* kContinueStyle = "style=dotted";
  24. // A DotConverter can be used to dump the GraphViz "dot" graph for
  25. // a SPIR-V module.
  26. class DotConverter {
  27. public:
  28. DotConverter(spvtools::NameMapper name_mapper, std::iostream* out)
  29. : name_mapper_(std::move(name_mapper)), out_(*out) {}
  30. // Emits the graph preamble.
  31. void Begin() const {
  32. out_ << "digraph {\n";
  33. // Emit a simple legend
  34. out_ << "legend_merge_src [shape=plaintext, label=\"\"];\n"
  35. << "legend_merge_dest [shape=plaintext, label=\"\"];\n"
  36. << "legend_merge_src -> legend_merge_dest [label=\" merge\","
  37. << kMergeStyle << "];\n"
  38. << "legend_continue_src [shape=plaintext, label=\"\"];\n"
  39. << "legend_continue_dest [shape=plaintext, label=\"\"];\n"
  40. << "legend_continue_src -> legend_continue_dest [label=\" continue\","
  41. << kContinueStyle << "];\n";
  42. }
  43. // Emits the graph postamble.
  44. void End() const { out_ << "}\n"; }
  45. // Emits the Dot commands for the given instruction.
  46. spv_result_t HandleInstruction(const spv_parsed_instruction_t& inst);
  47. private:
  48. // Ends processing for the current block, emitting its dot code.
  49. void FlushBlock(const std::vector<uint32_t>& successors);
  50. // The ID of the current functio, or 0 if outside of a function.
  51. uint32_t current_function_id_ = 0;
  52. // The ID of the current basic block, or 0 if outside of a block.
  53. uint32_t current_block_id_ = 0;
  54. // Have we completed processing for the entry block to this fuction?
  55. bool seen_function_entry_block_ = false;
  56. // The Id of the merge block for this block if it exists, or 0 otherwise.
  57. uint32_t merge_ = 0;
  58. // The Id of the continue target block for this block if it exists, or 0
  59. // otherwise.
  60. uint32_t continue_target_ = 0;
  61. // An object for mapping Ids to names.
  62. spvtools::NameMapper name_mapper_;
  63. // The output stream.
  64. std::ostream& out_;
  65. };
  66. spv_result_t DotConverter::HandleInstruction(
  67. const spv_parsed_instruction_t& inst) {
  68. switch (inst.opcode) {
  69. case SpvOpFunction:
  70. current_function_id_ = inst.result_id;
  71. seen_function_entry_block_ = false;
  72. break;
  73. case SpvOpFunctionEnd:
  74. current_function_id_ = 0;
  75. break;
  76. case SpvOpLabel:
  77. current_block_id_ = inst.result_id;
  78. break;
  79. case SpvOpBranch:
  80. FlushBlock({inst.words[1]});
  81. break;
  82. case SpvOpBranchConditional:
  83. FlushBlock({inst.words[2], inst.words[3]});
  84. break;
  85. case SpvOpSwitch: {
  86. std::vector<uint32_t> successors{inst.words[2]};
  87. for (size_t i = 3; i < inst.num_operands; i += 2) {
  88. successors.push_back(inst.words[inst.operands[i].offset]);
  89. }
  90. FlushBlock(successors);
  91. } break;
  92. case SpvOpKill:
  93. case SpvOpReturn:
  94. case SpvOpUnreachable:
  95. case SpvOpReturnValue:
  96. FlushBlock({});
  97. break;
  98. case SpvOpLoopMerge:
  99. merge_ = inst.words[1];
  100. continue_target_ = inst.words[2];
  101. break;
  102. case SpvOpSelectionMerge:
  103. merge_ = inst.words[1];
  104. break;
  105. default:
  106. break;
  107. }
  108. return SPV_SUCCESS;
  109. }
  110. void DotConverter::FlushBlock(const std::vector<uint32_t>& successors) {
  111. out_ << current_block_id_;
  112. if (!seen_function_entry_block_) {
  113. out_ << " [label=\"" << name_mapper_(current_block_id_) << "\nFn "
  114. << name_mapper_(current_function_id_) << " entry\", shape=box];\n";
  115. } else {
  116. out_ << " [label=\"" << name_mapper_(current_block_id_) << "\"];\n";
  117. }
  118. for (auto successor : successors) {
  119. out_ << current_block_id_ << " -> " << successor << ";\n";
  120. }
  121. if (merge_) {
  122. out_ << current_block_id_ << " -> " << merge_ << " [" << kMergeStyle
  123. << "];\n";
  124. }
  125. if (continue_target_) {
  126. out_ << current_block_id_ << " -> " << continue_target_ << " ["
  127. << kContinueStyle << "];\n";
  128. }
  129. // Reset the book-keeping for a block.
  130. seen_function_entry_block_ = true;
  131. merge_ = 0;
  132. continue_target_ = 0;
  133. }
  134. spv_result_t HandleInstruction(
  135. void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
  136. assert(user_data);
  137. auto converter = static_cast<DotConverter*>(user_data);
  138. return converter->HandleInstruction(*parsed_instruction);
  139. }
  140. } // anonymous namespace
  141. spv_result_t BinaryToDot(const spv_const_context context, const uint32_t* words,
  142. size_t num_words, std::iostream* out,
  143. spv_diagnostic* diagnostic) {
  144. // Invalid arguments return error codes, but don't necessarily generate
  145. // diagnostics. These are programmer errors, not user errors.
  146. if (!diagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
  147. const spvtools::AssemblyGrammar grammar(context);
  148. if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE;
  149. spvtools::FriendlyNameMapper friendly_mapper(context, words, num_words);
  150. DotConverter converter(friendly_mapper.GetNameMapper(), out);
  151. converter.Begin();
  152. if (auto error = spvBinaryParse(context, &converter, words, num_words,
  153. nullptr, HandleInstruction, diagnostic)) {
  154. return error;
  155. }
  156. converter.End();
  157. return SPV_SUCCESS;
  158. }