disassemble.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. // Copyright (c) 2015-2020 The Khronos Group Inc.
  2. // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
  3. // reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. // This file contains a disassembler: It converts a SPIR-V binary
  17. // to text.
  18. #include "source/disassemble.h"
  19. #include <algorithm>
  20. #include <cassert>
  21. #include <cstring>
  22. #include <iomanip>
  23. #include <memory>
  24. #include <unordered_map>
  25. #include <utility>
  26. #include "source/assembly_grammar.h"
  27. #include "source/binary.h"
  28. #include "source/diagnostic.h"
  29. #include "source/ext_inst.h"
  30. #include "source/opcode.h"
  31. #include "source/parsed_operand.h"
  32. #include "source/print.h"
  33. #include "source/spirv_constant.h"
  34. #include "source/spirv_endian.h"
  35. #include "source/util/hex_float.h"
  36. #include "source/util/make_unique.h"
  37. #include "spirv-tools/libspirv.h"
  38. namespace spvtools {
  39. namespace {
  40. // A Disassembler instance converts a SPIR-V binary to its assembly
  41. // representation.
  42. class Disassembler {
  43. public:
  44. Disassembler(const AssemblyGrammar& grammar, uint32_t options,
  45. NameMapper name_mapper)
  46. : print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)),
  47. text_(),
  48. out_(print_ ? out_stream() : out_stream(text_)),
  49. instruction_disassembler_(grammar, out_.get(), options, name_mapper),
  50. header_(!spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, options)),
  51. byte_offset_(0) {}
  52. // Emits the assembly header for the module, and sets up internal state
  53. // so subsequent callbacks can handle the cases where the entire module
  54. // is either big-endian or little-endian.
  55. spv_result_t HandleHeader(spv_endianness_t endian, uint32_t version,
  56. uint32_t generator, uint32_t id_bound,
  57. uint32_t schema);
  58. // Emits the assembly text for the given instruction.
  59. spv_result_t HandleInstruction(const spv_parsed_instruction_t& inst);
  60. // If not printing, populates text_result with the accumulated text.
  61. // Returns SPV_SUCCESS on success.
  62. spv_result_t SaveTextResult(spv_text* text_result) const;
  63. private:
  64. const bool print_; // Should we also print to the standard output stream?
  65. spv_endianness_t endian_; // The detected endianness of the binary.
  66. std::stringstream text_; // Captures the text, if not printing.
  67. out_stream out_; // The Output stream. Either to text_ or standard output.
  68. disassemble::InstructionDisassembler instruction_disassembler_;
  69. const bool header_; // Should we output header as the leading comment?
  70. size_t byte_offset_; // The number of bytes processed so far.
  71. bool inserted_decoration_space_ = false;
  72. bool inserted_debug_space_ = false;
  73. bool inserted_type_space_ = false;
  74. };
  75. spv_result_t Disassembler::HandleHeader(spv_endianness_t endian,
  76. uint32_t version, uint32_t generator,
  77. uint32_t id_bound, uint32_t schema) {
  78. endian_ = endian;
  79. if (header_) {
  80. instruction_disassembler_.EmitHeaderSpirv();
  81. instruction_disassembler_.EmitHeaderVersion(version);
  82. instruction_disassembler_.EmitHeaderGenerator(generator);
  83. instruction_disassembler_.EmitHeaderIdBound(id_bound);
  84. instruction_disassembler_.EmitHeaderSchema(schema);
  85. }
  86. byte_offset_ = SPV_INDEX_INSTRUCTION * sizeof(uint32_t);
  87. return SPV_SUCCESS;
  88. }
  89. spv_result_t Disassembler::HandleInstruction(
  90. const spv_parsed_instruction_t& inst) {
  91. instruction_disassembler_.EmitSectionComment(inst, inserted_decoration_space_,
  92. inserted_debug_space_,
  93. inserted_type_space_);
  94. instruction_disassembler_.EmitInstruction(inst, byte_offset_);
  95. byte_offset_ += inst.num_words * sizeof(uint32_t);
  96. return SPV_SUCCESS;
  97. }
  98. spv_result_t Disassembler::SaveTextResult(spv_text* text_result) const {
  99. if (!print_) {
  100. size_t length = text_.str().size();
  101. char* str = new char[length + 1];
  102. if (!str) return SPV_ERROR_OUT_OF_MEMORY;
  103. strncpy(str, text_.str().c_str(), length + 1);
  104. spv_text text = new spv_text_t();
  105. if (!text) {
  106. delete[] str;
  107. return SPV_ERROR_OUT_OF_MEMORY;
  108. }
  109. text->str = str;
  110. text->length = length;
  111. *text_result = text;
  112. }
  113. return SPV_SUCCESS;
  114. }
  115. spv_result_t DisassembleHeader(void* user_data, spv_endianness_t endian,
  116. uint32_t /* magic */, uint32_t version,
  117. uint32_t generator, uint32_t id_bound,
  118. uint32_t schema) {
  119. assert(user_data);
  120. auto disassembler = static_cast<Disassembler*>(user_data);
  121. return disassembler->HandleHeader(endian, version, generator, id_bound,
  122. schema);
  123. }
  124. spv_result_t DisassembleInstruction(
  125. void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
  126. assert(user_data);
  127. auto disassembler = static_cast<Disassembler*>(user_data);
  128. return disassembler->HandleInstruction(*parsed_instruction);
  129. }
  130. // Simple wrapper class to provide extra data necessary for targeted
  131. // instruction disassembly.
  132. class WrappedDisassembler {
  133. public:
  134. WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc)
  135. : disassembler_(dis), inst_binary_(binary), word_count_(wc) {}
  136. Disassembler* disassembler() { return disassembler_; }
  137. const uint32_t* inst_binary() const { return inst_binary_; }
  138. size_t word_count() const { return word_count_; }
  139. private:
  140. Disassembler* disassembler_;
  141. const uint32_t* inst_binary_;
  142. const size_t word_count_;
  143. };
  144. spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian,
  145. uint32_t /* magic */, uint32_t version,
  146. uint32_t generator, uint32_t id_bound,
  147. uint32_t schema) {
  148. assert(user_data);
  149. auto wrapped = static_cast<WrappedDisassembler*>(user_data);
  150. return wrapped->disassembler()->HandleHeader(endian, version, generator,
  151. id_bound, schema);
  152. }
  153. spv_result_t DisassembleTargetInstruction(
  154. void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
  155. assert(user_data);
  156. auto wrapped = static_cast<WrappedDisassembler*>(user_data);
  157. // Check if this is the instruction we want to disassemble.
  158. if (wrapped->word_count() == parsed_instruction->num_words &&
  159. std::equal(wrapped->inst_binary(),
  160. wrapped->inst_binary() + wrapped->word_count(),
  161. parsed_instruction->words)) {
  162. // Found the target instruction. Disassemble it and signal that we should
  163. // stop searching so we don't output the same instruction again.
  164. if (auto error =
  165. wrapped->disassembler()->HandleInstruction(*parsed_instruction))
  166. return error;
  167. return SPV_REQUESTED_TERMINATION;
  168. }
  169. return SPV_SUCCESS;
  170. }
  171. constexpr int kStandardIndent = 15;
  172. } // namespace
  173. namespace disassemble {
  174. InstructionDisassembler::InstructionDisassembler(const AssemblyGrammar& grammar,
  175. std::ostream& stream,
  176. uint32_t options,
  177. NameMapper name_mapper)
  178. : grammar_(grammar),
  179. stream_(stream),
  180. print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)),
  181. color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)),
  182. indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options)
  183. ? kStandardIndent
  184. : 0),
  185. comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)),
  186. show_byte_offset_(
  187. spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)),
  188. name_mapper_(std::move(name_mapper)) {}
  189. void InstructionDisassembler::EmitHeaderSpirv() { stream_ << "; SPIR-V\n"; }
  190. void InstructionDisassembler::EmitHeaderVersion(uint32_t version) {
  191. stream_ << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "."
  192. << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n";
  193. }
  194. void InstructionDisassembler::EmitHeaderGenerator(uint32_t generator) {
  195. const char* generator_tool =
  196. spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator));
  197. stream_ << "; Generator: " << generator_tool;
  198. // For unknown tools, print the numeric tool value.
  199. if (0 == strcmp("Unknown", generator_tool)) {
  200. stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")";
  201. }
  202. // Print the miscellaneous part of the generator word on the same
  203. // line as the tool name.
  204. stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n";
  205. }
  206. void InstructionDisassembler::EmitHeaderIdBound(uint32_t id_bound) {
  207. stream_ << "; Bound: " << id_bound << "\n";
  208. }
  209. void InstructionDisassembler::EmitHeaderSchema(uint32_t schema) {
  210. stream_ << "; Schema: " << schema << "\n";
  211. }
  212. void InstructionDisassembler::EmitInstruction(
  213. const spv_parsed_instruction_t& inst, size_t inst_byte_offset) {
  214. auto opcode = static_cast<spv::Op>(inst.opcode);
  215. if (inst.result_id) {
  216. SetBlue();
  217. const std::string id_name = name_mapper_(inst.result_id);
  218. if (indent_)
  219. stream_ << std::setw(std::max(0, indent_ - 3 - int(id_name.size())));
  220. stream_ << "%" << id_name;
  221. ResetColor();
  222. stream_ << " = ";
  223. } else {
  224. stream_ << std::string(indent_, ' ');
  225. }
  226. stream_ << "Op" << spvOpcodeString(opcode);
  227. for (uint16_t i = 0; i < inst.num_operands; i++) {
  228. const spv_operand_type_t type = inst.operands[i].type;
  229. assert(type != SPV_OPERAND_TYPE_NONE);
  230. if (type == SPV_OPERAND_TYPE_RESULT_ID) continue;
  231. stream_ << " ";
  232. EmitOperand(inst, i);
  233. }
  234. if (comment_ && opcode == spv::Op::OpName) {
  235. const spv_parsed_operand_t& operand = inst.operands[0];
  236. const uint32_t word = inst.words[operand.offset];
  237. stream_ << " ; id %" << word;
  238. }
  239. if (show_byte_offset_) {
  240. SetGrey();
  241. auto saved_flags = stream_.flags();
  242. auto saved_fill = stream_.fill();
  243. stream_ << " ; 0x" << std::setw(8) << std::hex << std::setfill('0')
  244. << inst_byte_offset;
  245. stream_.flags(saved_flags);
  246. stream_.fill(saved_fill);
  247. ResetColor();
  248. }
  249. stream_ << "\n";
  250. }
  251. void InstructionDisassembler::EmitSectionComment(
  252. const spv_parsed_instruction_t& inst, bool& inserted_decoration_space,
  253. bool& inserted_debug_space, bool& inserted_type_space) {
  254. auto opcode = static_cast<spv::Op>(inst.opcode);
  255. if (comment_ && opcode == spv::Op::OpFunction) {
  256. stream_ << std::endl;
  257. stream_ << std::string(indent_, ' ');
  258. stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl;
  259. }
  260. if (comment_ && !inserted_decoration_space && spvOpcodeIsDecoration(opcode)) {
  261. inserted_decoration_space = true;
  262. stream_ << std::endl;
  263. stream_ << std::string(indent_, ' ');
  264. stream_ << "; Annotations" << std::endl;
  265. }
  266. if (comment_ && !inserted_debug_space && spvOpcodeIsDebug(opcode)) {
  267. inserted_debug_space = true;
  268. stream_ << std::endl;
  269. stream_ << std::string(indent_, ' ');
  270. stream_ << "; Debug Information" << std::endl;
  271. }
  272. if (comment_ && !inserted_type_space && spvOpcodeGeneratesType(opcode)) {
  273. inserted_type_space = true;
  274. stream_ << std::endl;
  275. stream_ << std::string(indent_, ' ');
  276. stream_ << "; Types, variables and constants" << std::endl;
  277. }
  278. }
  279. void InstructionDisassembler::EmitOperand(const spv_parsed_instruction_t& inst,
  280. const uint16_t operand_index) {
  281. assert(operand_index < inst.num_operands);
  282. const spv_parsed_operand_t& operand = inst.operands[operand_index];
  283. const uint32_t word = inst.words[operand.offset];
  284. switch (operand.type) {
  285. case SPV_OPERAND_TYPE_RESULT_ID:
  286. assert(false && "<result-id> is not supposed to be handled here");
  287. SetBlue();
  288. stream_ << "%" << name_mapper_(word);
  289. break;
  290. case SPV_OPERAND_TYPE_ID:
  291. case SPV_OPERAND_TYPE_TYPE_ID:
  292. case SPV_OPERAND_TYPE_SCOPE_ID:
  293. case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
  294. SetYellow();
  295. stream_ << "%" << name_mapper_(word);
  296. break;
  297. case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
  298. spv_ext_inst_desc ext_inst;
  299. SetRed();
  300. if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) ==
  301. SPV_SUCCESS) {
  302. stream_ << ext_inst->name;
  303. } else {
  304. if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) {
  305. assert(false && "should have caught this earlier");
  306. } else {
  307. // for non-semantic instruction sets we can just print the number
  308. stream_ << word;
  309. }
  310. }
  311. } break;
  312. case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
  313. spv_opcode_desc opcode_desc;
  314. if (grammar_.lookupOpcode(spv::Op(word), &opcode_desc))
  315. assert(false && "should have caught this earlier");
  316. SetRed();
  317. stream_ << opcode_desc->name;
  318. } break;
  319. case SPV_OPERAND_TYPE_LITERAL_INTEGER:
  320. case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: {
  321. SetRed();
  322. EmitNumericLiteral(&stream_, inst, operand);
  323. ResetColor();
  324. } break;
  325. case SPV_OPERAND_TYPE_LITERAL_STRING: {
  326. stream_ << "\"";
  327. SetGreen();
  328. std::string str = spvDecodeLiteralStringOperand(inst, operand_index);
  329. for (char const& c : str) {
  330. if (c == '"' || c == '\\') stream_ << '\\';
  331. stream_ << c;
  332. }
  333. ResetColor();
  334. stream_ << '"';
  335. } break;
  336. case SPV_OPERAND_TYPE_CAPABILITY:
  337. case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
  338. case SPV_OPERAND_TYPE_EXECUTION_MODEL:
  339. case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
  340. case SPV_OPERAND_TYPE_MEMORY_MODEL:
  341. case SPV_OPERAND_TYPE_EXECUTION_MODE:
  342. case SPV_OPERAND_TYPE_STORAGE_CLASS:
  343. case SPV_OPERAND_TYPE_DIMENSIONALITY:
  344. case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
  345. case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
  346. case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT:
  347. case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
  348. case SPV_OPERAND_TYPE_LINKAGE_TYPE:
  349. case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
  350. case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
  351. case SPV_OPERAND_TYPE_DECORATION:
  352. case SPV_OPERAND_TYPE_BUILT_IN:
  353. case SPV_OPERAND_TYPE_GROUP_OPERATION:
  354. case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
  355. case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
  356. case SPV_OPERAND_TYPE_RAY_FLAGS:
  357. case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
  358. case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
  359. case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
  360. case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
  361. case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
  362. case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
  363. case SPV_OPERAND_TYPE_DEBUG_OPERATION:
  364. case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
  365. case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
  366. case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
  367. case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
  368. case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
  369. case SPV_OPERAND_TYPE_FPDENORM_MODE:
  370. case SPV_OPERAND_TYPE_FPOPERATION_MODE:
  371. case SPV_OPERAND_TYPE_QUANTIZATION_MODES:
  372. case SPV_OPERAND_TYPE_OVERFLOW_MODES: {
  373. spv_operand_desc entry;
  374. if (grammar_.lookupOperand(operand.type, word, &entry))
  375. assert(false && "should have caught this earlier");
  376. stream_ << entry->name;
  377. } break;
  378. case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
  379. case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
  380. case SPV_OPERAND_TYPE_LOOP_CONTROL:
  381. case SPV_OPERAND_TYPE_IMAGE:
  382. case SPV_OPERAND_TYPE_MEMORY_ACCESS:
  383. case SPV_OPERAND_TYPE_SELECTION_CONTROL:
  384. case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
  385. case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
  386. EmitMaskOperand(operand.type, word);
  387. break;
  388. default:
  389. if (spvOperandIsConcreteMask(operand.type)) {
  390. EmitMaskOperand(operand.type, word);
  391. } else if (spvOperandIsConcrete(operand.type)) {
  392. spv_operand_desc entry;
  393. if (grammar_.lookupOperand(operand.type, word, &entry))
  394. assert(false && "should have caught this earlier");
  395. stream_ << entry->name;
  396. } else {
  397. assert(false && "unhandled or invalid case");
  398. }
  399. break;
  400. }
  401. ResetColor();
  402. }
  403. void InstructionDisassembler::EmitMaskOperand(const spv_operand_type_t type,
  404. const uint32_t word) {
  405. // Scan the mask from least significant bit to most significant bit. For each
  406. // set bit, emit the name of that bit. Separate multiple names with '|'.
  407. uint32_t remaining_word = word;
  408. uint32_t mask;
  409. int num_emitted = 0;
  410. for (mask = 1; remaining_word; mask <<= 1) {
  411. if (remaining_word & mask) {
  412. remaining_word ^= mask;
  413. spv_operand_desc entry;
  414. if (grammar_.lookupOperand(type, mask, &entry))
  415. assert(false && "should have caught this earlier");
  416. if (num_emitted) stream_ << "|";
  417. stream_ << entry->name;
  418. num_emitted++;
  419. }
  420. }
  421. if (!num_emitted) {
  422. // An operand value of 0 was provided, so represent it by the name
  423. // of the 0 value. In many cases, that's "None".
  424. spv_operand_desc entry;
  425. if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry))
  426. stream_ << entry->name;
  427. }
  428. }
  429. void InstructionDisassembler::ResetColor() {
  430. if (color_) stream_ << spvtools::clr::reset{print_};
  431. }
  432. void InstructionDisassembler::SetGrey() {
  433. if (color_) stream_ << spvtools::clr::grey{print_};
  434. }
  435. void InstructionDisassembler::SetBlue() {
  436. if (color_) stream_ << spvtools::clr::blue{print_};
  437. }
  438. void InstructionDisassembler::SetYellow() {
  439. if (color_) stream_ << spvtools::clr::yellow{print_};
  440. }
  441. void InstructionDisassembler::SetRed() {
  442. if (color_) stream_ << spvtools::clr::red{print_};
  443. }
  444. void InstructionDisassembler::SetGreen() {
  445. if (color_) stream_ << spvtools::clr::green{print_};
  446. }
  447. } // namespace disassemble
  448. std::string spvInstructionBinaryToText(const spv_target_env env,
  449. const uint32_t* instCode,
  450. const size_t instWordCount,
  451. const uint32_t* code,
  452. const size_t wordCount,
  453. const uint32_t options) {
  454. spv_context context = spvContextCreate(env);
  455. const AssemblyGrammar grammar(context);
  456. if (!grammar.isValid()) {
  457. spvContextDestroy(context);
  458. return "";
  459. }
  460. // Generate friendly names for Ids if requested.
  461. std::unique_ptr<FriendlyNameMapper> friendly_mapper;
  462. NameMapper name_mapper = GetTrivialNameMapper();
  463. if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) {
  464. friendly_mapper = MakeUnique<FriendlyNameMapper>(context, code, wordCount);
  465. name_mapper = friendly_mapper->GetNameMapper();
  466. }
  467. // Now disassemble!
  468. Disassembler disassembler(grammar, options, name_mapper);
  469. WrappedDisassembler wrapped(&disassembler, instCode, instWordCount);
  470. spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader,
  471. DisassembleTargetInstruction, nullptr);
  472. spv_text text = nullptr;
  473. std::string output;
  474. if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) {
  475. output.assign(text->str, text->str + text->length);
  476. // Drop trailing newline characters.
  477. while (!output.empty() && output.back() == '\n') output.pop_back();
  478. }
  479. spvTextDestroy(text);
  480. spvContextDestroy(context);
  481. return output;
  482. }
  483. } // namespace spvtools
  484. spv_result_t spvBinaryToText(const spv_const_context context,
  485. const uint32_t* code, const size_t wordCount,
  486. const uint32_t options, spv_text* pText,
  487. spv_diagnostic* pDiagnostic) {
  488. spv_context_t hijack_context = *context;
  489. if (pDiagnostic) {
  490. *pDiagnostic = nullptr;
  491. spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic);
  492. }
  493. const spvtools::AssemblyGrammar grammar(&hijack_context);
  494. if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE;
  495. // Generate friendly names for Ids if requested.
  496. std::unique_ptr<spvtools::FriendlyNameMapper> friendly_mapper;
  497. spvtools::NameMapper name_mapper = spvtools::GetTrivialNameMapper();
  498. if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) {
  499. friendly_mapper = spvtools::MakeUnique<spvtools::FriendlyNameMapper>(
  500. &hijack_context, code, wordCount);
  501. name_mapper = friendly_mapper->GetNameMapper();
  502. }
  503. // Now disassemble!
  504. spvtools::Disassembler disassembler(grammar, options, name_mapper);
  505. if (auto error =
  506. spvBinaryParse(&hijack_context, &disassembler, code, wordCount,
  507. spvtools::DisassembleHeader,
  508. spvtools::DisassembleInstruction, pDiagnostic)) {
  509. return error;
  510. }
  511. return disassembler.SaveTextResult(pText);
  512. }