disassemble.cpp 20 KB

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