disassemble.cpp 18 KB

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