markv_encoder.h 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright (c) 2018 Google LLC
  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 "source/comp/bit_stream.h"
  15. #include "source/comp/markv.h"
  16. #include "source/comp/markv_codec.h"
  17. #include "source/comp/markv_logger.h"
  18. #include "source/util/make_unique.h"
  19. #ifndef SOURCE_COMP_MARKV_ENCODER_H_
  20. #define SOURCE_COMP_MARKV_ENCODER_H_
  21. #include <cstring>
  22. namespace spvtools {
  23. namespace comp {
  24. // SPIR-V to MARK-V encoder. Exposes functions EncodeHeader and
  25. // EncodeInstruction which can be used as callback by spvBinaryParse.
  26. // Encoded binary is written to an internally maintained bitstream.
  27. // After the last instruction is encoded, the resulting MARK-V binary can be
  28. // acquired by calling GetMarkvBinary().
  29. //
  30. // The encoder uses SPIR-V validator to keep internal state, therefore
  31. // SPIR-V binary needs to be able to pass validator checks.
  32. // CreateCommentsLogger() can be used to enable the encoder to write comments
  33. // on how encoding was done, which can later be accessed with GetComments().
  34. class MarkvEncoder : public MarkvCodec {
  35. public:
  36. // |model| is owned by the caller, must be not null and valid during the
  37. // lifetime of MarkvEncoder.
  38. MarkvEncoder(spv_const_context context, const MarkvCodecOptions& options,
  39. const MarkvModel* model)
  40. : MarkvCodec(context, GetValidatorOptions(options), model),
  41. options_(options) {}
  42. ~MarkvEncoder() override = default;
  43. // Writes data from SPIR-V header to MARK-V header.
  44. spv_result_t EncodeHeader(spv_endianness_t /* endian */, uint32_t /* magic */,
  45. uint32_t version, uint32_t generator,
  46. uint32_t id_bound, uint32_t /* schema */) {
  47. SetIdBound(id_bound);
  48. header_.spirv_version = version;
  49. header_.spirv_generator = generator;
  50. return SPV_SUCCESS;
  51. }
  52. // Creates an internal logger which writes comments on the encoding process.
  53. void CreateLogger(MarkvLogConsumer log_consumer,
  54. MarkvDebugConsumer debug_consumer) {
  55. logger_ = MakeUnique<MarkvLogger>(log_consumer, debug_consumer);
  56. writer_.SetCallback(
  57. [this](const std::string& str) { logger_->AppendBitSequence(str); });
  58. }
  59. // Encodes SPIR-V instruction to MARK-V and writes to bit stream.
  60. // Operation can fail if the instruction fails to pass the validator or if
  61. // the encoder stubmles on something unexpected.
  62. spv_result_t EncodeInstruction(const spv_parsed_instruction_t& inst);
  63. // Concatenates MARK-V header and the bit stream with encoded instructions
  64. // into a single buffer and returns it as spv_markv_binary. The returned
  65. // value is owned by the caller and needs to be destroyed with
  66. // spvMarkvBinaryDestroy().
  67. std::vector<uint8_t> GetMarkvBinary() {
  68. header_.markv_length_in_bits =
  69. static_cast<uint32_t>(sizeof(header_) * 8 + writer_.GetNumBits());
  70. header_.markv_model =
  71. (model_->model_type() << 16) | model_->model_version();
  72. const size_t num_bytes = sizeof(header_) + writer_.GetDataSizeBytes();
  73. std::vector<uint8_t> markv(num_bytes);
  74. assert(writer_.GetData());
  75. std::memcpy(markv.data(), &header_, sizeof(header_));
  76. std::memcpy(markv.data() + sizeof(header_), writer_.GetData(),
  77. writer_.GetDataSizeBytes());
  78. return markv;
  79. }
  80. // Optionally adds disassembly to the comments.
  81. // Disassembly should contain all instructions in the module separated by
  82. // \n, and no header.
  83. void SetDisassembly(std::string&& disassembly) {
  84. disassembly_ = MakeUnique<std::stringstream>(std::move(disassembly));
  85. }
  86. // Extracts the next instruction line from the disassembly and logs it.
  87. void LogDisassemblyInstruction() {
  88. if (logger_ && disassembly_) {
  89. std::string line;
  90. std::getline(*disassembly_, line, '\n');
  91. logger_->AppendTextNewLine(line);
  92. }
  93. }
  94. private:
  95. // Creates and returns validator options. Returned value owned by the caller.
  96. static spv_validator_options GetValidatorOptions(
  97. const MarkvCodecOptions& options) {
  98. return options.validate_spirv_binary ? spvValidatorOptionsCreate()
  99. : nullptr;
  100. }
  101. // Writes a single word to bit stream. operand_.type determines if the word is
  102. // encoded and how.
  103. spv_result_t EncodeNonIdWord(uint32_t word);
  104. // Writes both opcode and num_operands as a single code.
  105. // Returns SPV_UNSUPPORTED iff no suitable codec was found.
  106. spv_result_t EncodeOpcodeAndNumOperands(uint32_t opcode,
  107. uint32_t num_operands);
  108. // Writes mtf rank to bit stream. |mtf| is used to determine the codec
  109. // scheme. |fallback_method| is used if no codec defined for |mtf|.
  110. spv_result_t EncodeMtfRankHuffman(uint32_t rank, uint64_t mtf,
  111. uint64_t fallback_method);
  112. // Writes id using coding based on mtf associated with the id descriptor.
  113. // Returns SPV_UNSUPPORTED iff fallback method needs to be used.
  114. spv_result_t EncodeIdWithDescriptor(uint32_t id);
  115. // Writes id using coding based on the given |mtf|, which is expected to
  116. // contain the given |id|.
  117. spv_result_t EncodeExistingId(uint64_t mtf, uint32_t id);
  118. // Writes type id of the current instruction if can't be inferred.
  119. spv_result_t EncodeTypeId();
  120. // Writes result id of the current instruction if can't be inferred.
  121. spv_result_t EncodeResultId();
  122. // Writes ids which are neither type nor result ids.
  123. spv_result_t EncodeRefId(uint32_t id);
  124. // Writes bits to the stream until the beginning of the next byte if the
  125. // number of bits until the next byte is less than |byte_break_if_less_than|.
  126. void AddByteBreak(size_t byte_break_if_less_than);
  127. // Encodes a literal number operand and writes it to the bit stream.
  128. spv_result_t EncodeLiteralNumber(const spv_parsed_operand_t& operand);
  129. MarkvCodecOptions options_;
  130. // Bit stream where encoded instructions are written.
  131. BitWriterWord64 writer_;
  132. // If not nullptr, disassembled instruction lines will be written to comments.
  133. // Format: \n separated instruction lines, no header.
  134. std::unique_ptr<std::stringstream> disassembly_;
  135. };
  136. } // namespace comp
  137. } // namespace spvtools
  138. #endif // SOURCE_COMP_MARKV_ENCODER_H_