| // Copyright (c) 2018 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "source/comp/bit_stream.h" | |
| #include "source/comp/markv.h" | |
| #include "source/comp/markv_codec.h" | |
| #include "source/comp/markv_logger.h" | |
| #include "source/util/make_unique.h" | |
| #ifndef SOURCE_COMP_MARKV_ENCODER_H_ | |
| #define SOURCE_COMP_MARKV_ENCODER_H_ | |
| #include <cstring> | |
| namespace spvtools { | |
| namespace comp { | |
| // SPIR-V to MARK-V encoder. Exposes functions EncodeHeader and | |
| // EncodeInstruction which can be used as callback by spvBinaryParse. | |
| // Encoded binary is written to an internally maintained bitstream. | |
| // After the last instruction is encoded, the resulting MARK-V binary can be | |
| // acquired by calling GetMarkvBinary(). | |
| // | |
| // The encoder uses SPIR-V validator to keep internal state, therefore | |
| // SPIR-V binary needs to be able to pass validator checks. | |
| // CreateCommentsLogger() can be used to enable the encoder to write comments | |
| // on how encoding was done, which can later be accessed with GetComments(). | |
| class MarkvEncoder : public MarkvCodec { | |
| public: | |
| // |model| is owned by the caller, must be not null and valid during the | |
| // lifetime of MarkvEncoder. | |
| MarkvEncoder(spv_const_context context, const MarkvCodecOptions& options, | |
| const MarkvModel* model) | |
| : MarkvCodec(context, GetValidatorOptions(options), model), | |
| options_(options) {} | |
| ~MarkvEncoder() override = default; | |
| // Writes data from SPIR-V header to MARK-V header. | |
| spv_result_t EncodeHeader(spv_endianness_t /* endian */, uint32_t /* magic */, | |
| uint32_t version, uint32_t generator, | |
| uint32_t id_bound, uint32_t /* schema */) { | |
| SetIdBound(id_bound); | |
| header_.spirv_version = version; | |
| header_.spirv_generator = generator; | |
| return SPV_SUCCESS; | |
| } | |
| // Creates an internal logger which writes comments on the encoding process. | |
| void CreateLogger(MarkvLogConsumer log_consumer, | |
| MarkvDebugConsumer debug_consumer) { | |
| logger_ = MakeUnique<MarkvLogger>(log_consumer, debug_consumer); | |
| writer_.SetCallback( | |
| [this](const std::string& str) { logger_->AppendBitSequence(str); }); | |
| } | |
| // Encodes SPIR-V instruction to MARK-V and writes to bit stream. | |
| // Operation can fail if the instruction fails to pass the validator or if | |
| // the encoder stubmles on something unexpected. | |
| spv_result_t EncodeInstruction(const spv_parsed_instruction_t& inst); | |
| // Concatenates MARK-V header and the bit stream with encoded instructions | |
| // into a single buffer and returns it as spv_markv_binary. The returned | |
| // value is owned by the caller and needs to be destroyed with | |
| // spvMarkvBinaryDestroy(). | |
| std::vector<uint8_t> GetMarkvBinary() { | |
| header_.markv_length_in_bits = | |
| static_cast<uint32_t>(sizeof(header_) * 8 + writer_.GetNumBits()); | |
| header_.markv_model = | |
| (model_->model_type() << 16) | model_->model_version(); | |
| const size_t num_bytes = sizeof(header_) + writer_.GetDataSizeBytes(); | |
| std::vector<uint8_t> markv(num_bytes); | |
| assert(writer_.GetData()); | |
| std::memcpy(markv.data(), &header_, sizeof(header_)); | |
| std::memcpy(markv.data() + sizeof(header_), writer_.GetData(), | |
| writer_.GetDataSizeBytes()); | |
| return markv; | |
| } | |
| // Optionally adds disassembly to the comments. | |
| // Disassembly should contain all instructions in the module separated by | |
| // \n, and no header. | |
| void SetDisassembly(std::string&& disassembly) { | |
| disassembly_ = MakeUnique<std::stringstream>(std::move(disassembly)); | |
| } | |
| // Extracts the next instruction line from the disassembly and logs it. | |
| void LogDisassemblyInstruction() { | |
| if (logger_ && disassembly_) { | |
| std::string line; | |
| std::getline(*disassembly_, line, '\n'); | |
| logger_->AppendTextNewLine(line); | |
| } | |
| } | |
| private: | |
| // Creates and returns validator options. Returned value owned by the caller. | |
| static spv_validator_options GetValidatorOptions( | |
| const MarkvCodecOptions& options) { | |
| return options.validate_spirv_binary ? spvValidatorOptionsCreate() | |
| : nullptr; | |
| } | |
| // Writes a single word to bit stream. operand_.type determines if the word is | |
| // encoded and how. | |
| spv_result_t EncodeNonIdWord(uint32_t word); | |
| // Writes both opcode and num_operands as a single code. | |
| // Returns SPV_UNSUPPORTED iff no suitable codec was found. | |
| spv_result_t EncodeOpcodeAndNumOperands(uint32_t opcode, | |
| uint32_t num_operands); | |
| // Writes mtf rank to bit stream. |mtf| is used to determine the codec | |
| // scheme. |fallback_method| is used if no codec defined for |mtf|. | |
| spv_result_t EncodeMtfRankHuffman(uint32_t rank, uint64_t mtf, | |
| uint64_t fallback_method); | |
| // Writes id using coding based on mtf associated with the id descriptor. | |
| // Returns SPV_UNSUPPORTED iff fallback method needs to be used. | |
| spv_result_t EncodeIdWithDescriptor(uint32_t id); | |
| // Writes id using coding based on the given |mtf|, which is expected to | |
| // contain the given |id|. | |
| spv_result_t EncodeExistingId(uint64_t mtf, uint32_t id); | |
| // Writes type id of the current instruction if can't be inferred. | |
| spv_result_t EncodeTypeId(); | |
| // Writes result id of the current instruction if can't be inferred. | |
| spv_result_t EncodeResultId(); | |
| // Writes ids which are neither type nor result ids. | |
| spv_result_t EncodeRefId(uint32_t id); | |
| // Writes bits to the stream until the beginning of the next byte if the | |
| // number of bits until the next byte is less than |byte_break_if_less_than|. | |
| void AddByteBreak(size_t byte_break_if_less_than); | |
| // Encodes a literal number operand and writes it to the bit stream. | |
| spv_result_t EncodeLiteralNumber(const spv_parsed_operand_t& operand); | |
| MarkvCodecOptions options_; | |
| // Bit stream where encoded instructions are written. | |
| BitWriterWord64 writer_; | |
| // If not nullptr, disassembled instruction lines will be written to comments. | |
| // Format: \n separated instruction lines, no header. | |
| std::unique_ptr<std::stringstream> disassembly_; | |
| }; | |
| } // namespace comp | |
| } // namespace spvtools | |
| #endif // SOURCE_COMP_MARKV_ENCODER_H_ |