Home | History | Annotate | Download | only in comp
      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