| // 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. | |
| #ifndef SOURCE_COMP_MARKV_CODEC_H_ | |
| #define SOURCE_COMP_MARKV_CODEC_H_ | |
| #include <list> | |
| #include <map> | |
| #include <memory> | |
| #include <vector> | |
| #include "source/assembly_grammar.h" | |
| #include "source/comp/huffman_codec.h" | |
| #include "source/comp/markv_model.h" | |
| #include "source/comp/move_to_front.h" | |
| #include "source/diagnostic.h" | |
| #include "source/id_descriptor.h" | |
| #include "source/val/instruction.h" | |
| // Base class for MARK-V encoder and decoder. Contains common functionality | |
| // such as: | |
| // - Validator connection and validation state. | |
| // - SPIR-V grammar and helper functions. | |
| namespace spvtools { | |
| namespace comp { | |
| class MarkvLogger; | |
| // Handles for move-to-front sequences. Enums which end with "Begin" define | |
| // handle spaces which start at that value and span 16 or 32 bit wide. | |
| enum : uint64_t { | |
| kMtfNone = 0, | |
| // All ids. | |
| kMtfAll, | |
| // All forward declared ids. | |
| kMtfForwardDeclared, | |
| // All type ids except for generated by OpTypeFunction. | |
| kMtfTypeNonFunction, | |
| // All labels. | |
| kMtfLabel, | |
| // All ids created by instructions which had type_id. | |
| kMtfObject, | |
| // All types generated by OpTypeFloat, OpTypeInt, OpTypeBool. | |
| kMtfTypeScalar, | |
| // All composite types. | |
| kMtfTypeComposite, | |
| // Boolean type or any vector type of it. | |
| kMtfTypeBoolScalarOrVector, | |
| // All float types or any vector floats type. | |
| kMtfTypeFloatScalarOrVector, | |
| // All int types or any vector int type. | |
| kMtfTypeIntScalarOrVector, | |
| // All types declared as return types in OpTypeFunction. | |
| kMtfTypeReturnedByFunction, | |
| // All composite objects. | |
| kMtfComposite, | |
| // All bool objects or vectors of bools. | |
| kMtfBoolScalarOrVector, | |
| // All float objects or vectors of float. | |
| kMtfFloatScalarOrVector, | |
| // All int objects or vectors of int. | |
| kMtfIntScalarOrVector, | |
| // All pointer types which point to composited. | |
| kMtfTypePointerToComposite, | |
| // Used by EncodeMtfRankHuffman. | |
| kMtfGenericNonZeroRank, | |
| // Handle space for ids of specific type. | |
| kMtfIdOfTypeBegin = 0x10000, | |
| // Handle space for ids generated by specific opcode. | |
| kMtfIdGeneratedByOpcode = 0x20000, | |
| // Handle space for ids of objects with type generated by specific opcode. | |
| kMtfIdWithTypeGeneratedByOpcodeBegin = 0x30000, | |
| // All vectors of specific component type. | |
| kMtfVectorOfComponentTypeBegin = 0x40000, | |
| // All vector types of specific size. | |
| kMtfTypeVectorOfSizeBegin = 0x50000, | |
| // All pointer types to specific type. | |
| kMtfPointerToTypeBegin = 0x60000, | |
| // All function types which return specific type. | |
| kMtfFunctionTypeWithReturnTypeBegin = 0x70000, | |
| // All function objects which return specific type. | |
| kMtfFunctionWithReturnTypeBegin = 0x80000, | |
| // Short id descriptor space (max 16-bit). | |
| kMtfShortIdDescriptorSpaceBegin = 0x90000, | |
| // Long id descriptor space (32-bit). | |
| kMtfLongIdDescriptorSpaceBegin = 0x100000000, | |
| }; | |
| class MarkvCodec { | |
| public: | |
| static const uint32_t kMarkvMagicNumber; | |
| // Mtf ranks smaller than this are encoded with Huffman coding. | |
| static const uint32_t kMtfSmallestRankEncodedByValue; | |
| // Signals that the mtf rank is too large to be encoded with Huffman. | |
| static const uint32_t kMtfRankEncodedByValueSignal; | |
| static const uint32_t kShortDescriptorNumBits; | |
| static const size_t kByteBreakAfterInstIfLessThanUntilNextByte; | |
| static uint32_t GetMarkvVersion(); | |
| virtual ~MarkvCodec(); | |
| protected: | |
| struct MarkvHeader { | |
| MarkvHeader(); | |
| uint32_t magic_number; | |
| uint32_t markv_version; | |
| // Magic number to identify or verify MarkvModel used for encoding. | |
| uint32_t markv_model = 0; | |
| uint32_t markv_length_in_bits = 0; | |
| uint32_t spirv_version = 0; | |
| uint32_t spirv_generator = 0; | |
| }; | |
| // |model| is owned by the caller, must be not null and valid during the | |
| // lifetime of the codec. | |
| MarkvCodec(spv_const_context context, spv_validator_options validator_options, | |
| const MarkvModel* model); | |
| // Returns instruction which created |id| or nullptr if such instruction was | |
| // not registered. | |
| const val::Instruction* FindDef(uint32_t id) const { | |
| const auto it = id_to_def_instruction_.find(id); | |
| if (it == id_to_def_instruction_.end()) return nullptr; | |
| return it->second; | |
| } | |
| size_t GetNumBitsToNextByte(size_t bit_pos) const; | |
| bool OpcodeHasFixedNumberOfOperands(SpvOp opcode) const; | |
| // Returns type id of vector type component. | |
| uint32_t GetVectorComponentType(uint32_t vector_type_id) const { | |
| const val::Instruction* type_inst = FindDef(vector_type_id); | |
| assert(type_inst); | |
| assert(type_inst->opcode() == SpvOpTypeVector); | |
| const uint32_t component_type = | |
| type_inst->word(type_inst->operands()[1].offset); | |
| return component_type; | |
| } | |
| // Returns mtf handle for ids of given type. | |
| uint64_t GetMtfIdOfType(uint32_t type_id) const { | |
| return kMtfIdOfTypeBegin + type_id; | |
| } | |
| // Returns mtf handle for ids generated by given opcode. | |
| uint64_t GetMtfIdGeneratedByOpcode(SpvOp opcode) const { | |
| return kMtfIdGeneratedByOpcode + opcode; | |
| } | |
| // Returns mtf handle for ids of type generated by given opcode. | |
| uint64_t GetMtfIdWithTypeGeneratedByOpcode(SpvOp opcode) const { | |
| return kMtfIdWithTypeGeneratedByOpcodeBegin + opcode; | |
| } | |
| // Returns mtf handle for vectors of specific component type. | |
| uint64_t GetMtfVectorOfComponentType(uint32_t type_id) const { | |
| return kMtfVectorOfComponentTypeBegin + type_id; | |
| } | |
| // Returns mtf handle for vector type of specific size. | |
| uint64_t GetMtfTypeVectorOfSize(uint32_t size) const { | |
| return kMtfTypeVectorOfSizeBegin + size; | |
| } | |
| // Returns mtf handle for pointers to specific size. | |
| uint64_t GetMtfPointerToType(uint32_t type_id) const { | |
| return kMtfPointerToTypeBegin + type_id; | |
| } | |
| // Returns mtf handle for function types with given return type. | |
| uint64_t GetMtfFunctionTypeWithReturnType(uint32_t type_id) const { | |
| return kMtfFunctionTypeWithReturnTypeBegin + type_id; | |
| } | |
| // Returns mtf handle for functions with given return type. | |
| uint64_t GetMtfFunctionWithReturnType(uint32_t type_id) const { | |
| return kMtfFunctionWithReturnTypeBegin + type_id; | |
| } | |
| // Returns mtf handle for the given long id descriptor. | |
| uint64_t GetMtfLongIdDescriptor(uint32_t descriptor) const { | |
| return kMtfLongIdDescriptorSpaceBegin + descriptor; | |
| } | |
| // Returns mtf handle for the given short id descriptor. | |
| uint64_t GetMtfShortIdDescriptor(uint32_t descriptor) const { | |
| return kMtfShortIdDescriptorSpaceBegin + descriptor; | |
| } | |
| // Process data from the current instruction. This would update MTFs and | |
| // other data containers. | |
| void ProcessCurInstruction(); | |
| // Returns move-to-front handle to be used for the current operand slot. | |
| // Mtf handle is chosen based on a set of rules defined by SPIR-V grammar. | |
| uint64_t GetRuleBasedMtf(); | |
| // Returns words of the current instruction. Decoder has a different | |
| // implementation and the array is valid only until the previously decoded | |
| // word. | |
| virtual const uint32_t* GetInstWords() const { return inst_.words; } | |
| // Returns the opcode of the previous instruction. | |
| SpvOp GetPrevOpcode() const { | |
| if (instructions_.empty()) return SpvOpNop; | |
| return instructions_.back()->opcode(); | |
| } | |
| // Returns diagnostic stream, position index is set to instruction number. | |
| DiagnosticStream Diag(spv_result_t error_code) const { | |
| return DiagnosticStream({0, 0, instructions_.size()}, context_->consumer, | |
| "", error_code); | |
| } | |
| // Returns current id bound. | |
| uint32_t GetIdBound() const { return id_bound_; } | |
| // Sets current id bound, expected to be no lower than the previous one. | |
| void SetIdBound(uint32_t id_bound) { | |
| assert(id_bound >= id_bound_); | |
| id_bound_ = id_bound; | |
| } | |
| // Returns Huffman codec for ranks of the mtf with given |handle|. | |
| // Different mtfs can use different rank distributions. | |
| // May return nullptr if the codec doesn't exist. | |
| const HuffmanCodec<uint32_t>* GetMtfHuffmanCodec(uint64_t handle) const { | |
| const auto it = mtf_huffman_codecs_.find(handle); | |
| if (it == mtf_huffman_codecs_.end()) return nullptr; | |
| return it->second.get(); | |
| } | |
| // Promotes id in all move-to-front sequences if ids can be shared by multiple | |
| // sequences. | |
| void PromoteIfNeeded(uint32_t id) { | |
| if (!model_->AnyDescriptorHasCodingScheme() && | |
| model_->id_fallback_strategy() == | |
| MarkvModel::IdFallbackStrategy::kShortDescriptor) { | |
| // Move-to-front sequences do not share ids. Nothing to do. | |
| return; | |
| } | |
| multi_mtf_.Promote(id); | |
| } | |
| spv_validator_options validator_options_ = nullptr; | |
| const AssemblyGrammar grammar_; | |
| MarkvHeader header_; | |
| // MARK-V model, not owned. | |
| const MarkvModel* model_ = nullptr; | |
| // Current instruction, current operand and current operand index. | |
| spv_parsed_instruction_t inst_; | |
| spv_parsed_operand_t operand_; | |
| uint32_t operand_index_; | |
| // Maps a result ID to its type ID. By convention: | |
| // - a result ID that is a type definition maps to itself. | |
| // - a result ID without a type maps to 0. (E.g. for OpLabel) | |
| std::unordered_map<uint32_t, uint32_t> id_to_type_id_; | |
| // Container for all move-to-front sequences. | |
| MultiMoveToFront multi_mtf_; | |
| // Id of the current function or zero if outside of function. | |
| uint32_t cur_function_id_ = 0; | |
| // Return type of the current function. | |
| uint32_t cur_function_return_type_ = 0; | |
| // Remaining function parameter types. This container is filled on OpFunction, | |
| // and drained on OpFunctionParameter. | |
| std::list<uint32_t> remaining_function_parameter_types_; | |
| // List of ids local to the current function. | |
| std::vector<uint32_t> ids_local_to_cur_function_; | |
| // List of instructions in the order they are given in the module. | |
| std::vector<std::unique_ptr<const val::Instruction>> instructions_; | |
| // Container/computer for long (32-bit) id descriptors. | |
| IdDescriptorCollection long_id_descriptors_; | |
| // Container/computer for short id descriptors. | |
| // Short descriptors are stored in uint32_t, but their actual bit width is | |
| // defined with kShortDescriptorNumBits. | |
| // It doesn't seem logical to have a different computer for short id | |
| // descriptors, since one could actually map/truncate long descriptors. | |
| // But as short descriptors have collisions, the efficiency of | |
| // compression depends on the collision pattern, and short descriptors | |
| // produced by function ShortHashU32Array have been empirically proven to | |
| // produce better results. | |
| IdDescriptorCollection short_id_descriptors_; | |
| // Huffman codecs for move-to-front ranks. The map key is mtf handle. Doesn't | |
| // need to contain a different codec for every handle as most use one and the | |
| // same. | |
| std::map<uint64_t, std::unique_ptr<HuffmanCodec<uint32_t>>> | |
| mtf_huffman_codecs_; | |
| // If not nullptr, codec will log comments on the compression process. | |
| std::unique_ptr<MarkvLogger> logger_; | |
| spv_const_context context_ = nullptr; | |
| private: | |
| // Maps result id to the instruction which defined it. | |
| std::unordered_map<uint32_t, const val::Instruction*> id_to_def_instruction_; | |
| uint32_t id_bound_ = 1; | |
| }; | |
| } // namespace comp | |
| } // namespace spvtools | |
| #endif // SOURCE_COMP_MARKV_CODEC_H_ |