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