| // 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. |
| |
| // MARK-V is a compression format for SPIR-V binaries. It strips away |
| // non-essential information (such as result IDs which can be regenerated) and |
| // uses various bit reduction techniques to reduce the size of the binary. |
| |
| #include "source/comp/markv_codec.h" |
| |
| #include "source/comp/markv_logger.h" |
| #include "source/latest_version_glsl_std_450_header.h" |
| #include "source/latest_version_opencl_std_header.h" |
| #include "source/opcode.h" |
| #include "source/util/make_unique.h" |
| |
| namespace spvtools { |
| namespace comp { |
| namespace { |
| |
| // Custom hash function used to produce short descriptors. |
| uint32_t ShortHashU32Array(const std::vector<uint32_t>& words) { |
| // The hash function is a sum of hashes of each word seeded by word index. |
| // Knuth's multiplicative hash is used to hash the words. |
| const uint32_t kKnuthMulHash = 2654435761; |
| uint32_t val = 0; |
| for (uint32_t i = 0; i < words.size(); ++i) { |
| val += (words[i] + i + 123) * kKnuthMulHash; |
| } |
| return 1 + val % ((1 << MarkvCodec::kShortDescriptorNumBits) - 1); |
| } |
| |
| // Returns a set of mtf rank codecs based on a plausible hand-coded |
| // distribution. |
| std::map<uint64_t, std::unique_ptr<HuffmanCodec<uint32_t>>> |
| GetMtfHuffmanCodecs() { |
| std::map<uint64_t, std::unique_ptr<HuffmanCodec<uint32_t>>> codecs; |
| |
| std::unique_ptr<HuffmanCodec<uint32_t>> codec; |
| |
| codec = MakeUnique<HuffmanCodec<uint32_t>>(std::map<uint32_t, uint32_t>({ |
| {0, 5}, |
| {1, 40}, |
| {2, 10}, |
| {3, 5}, |
| {4, 5}, |
| {5, 5}, |
| {6, 3}, |
| {7, 3}, |
| {8, 3}, |
| {9, 3}, |
| {MarkvCodec::kMtfRankEncodedByValueSignal, 10}, |
| })); |
| codecs.emplace(kMtfAll, std::move(codec)); |
| |
| codec = MakeUnique<HuffmanCodec<uint32_t>>(std::map<uint32_t, uint32_t>({ |
| {1, 50}, |
| {2, 20}, |
| {3, 5}, |
| {4, 5}, |
| {5, 2}, |
| {6, 1}, |
| {7, 1}, |
| {8, 1}, |
| {9, 1}, |
| {MarkvCodec::kMtfRankEncodedByValueSignal, 10}, |
| })); |
| codecs.emplace(kMtfGenericNonZeroRank, std::move(codec)); |
| |
| return codecs; |
| } |
| |
| } // namespace |
| |
| const uint32_t MarkvCodec::kMarkvMagicNumber = 0x07230303; |
| |
| const uint32_t MarkvCodec::kMtfSmallestRankEncodedByValue = 10; |
| |
| const uint32_t MarkvCodec::kMtfRankEncodedByValueSignal = |
| std::numeric_limits<uint32_t>::max(); |
| |
| const uint32_t MarkvCodec::kShortDescriptorNumBits = 8; |
| |
| const size_t MarkvCodec::kByteBreakAfterInstIfLessThanUntilNextByte = 8; |
| |
| MarkvCodec::MarkvCodec(spv_const_context context, |
| spv_validator_options validator_options, |
| const MarkvModel* model) |
| : validator_options_(validator_options), |
| grammar_(context), |
| model_(model), |
| short_id_descriptors_(ShortHashU32Array), |
| mtf_huffman_codecs_(GetMtfHuffmanCodecs()), |
| context_(context) {} |
| |
| MarkvCodec::~MarkvCodec() { spvValidatorOptionsDestroy(validator_options_); } |
| |
| MarkvCodec::MarkvHeader::MarkvHeader() |
| : magic_number(MarkvCodec::kMarkvMagicNumber), |
| markv_version(MarkvCodec::GetMarkvVersion()) {} |
| |
| // Defines and returns current MARK-V version. |
| // static |
| uint32_t MarkvCodec::GetMarkvVersion() { |
| const uint32_t kVersionMajor = 1; |
| const uint32_t kVersionMinor = 4; |
| return kVersionMinor | (kVersionMajor << 16); |
| } |
| |
| size_t MarkvCodec::GetNumBitsToNextByte(size_t bit_pos) const { |
| return (8 - (bit_pos % 8)) % 8; |
| } |
| |
| // Returns true if the opcode has a fixed number of operands. May return a |
| // false negative. |
| bool MarkvCodec::OpcodeHasFixedNumberOfOperands(SpvOp opcode) const { |
| switch (opcode) { |
| // TODO(atgoo@github.com) This is not a complete list. |
| case SpvOpNop: |
| case SpvOpName: |
| case SpvOpUndef: |
| case SpvOpSizeOf: |
| case SpvOpLine: |
| case SpvOpNoLine: |
| case SpvOpDecorationGroup: |
| case SpvOpExtension: |
| case SpvOpExtInstImport: |
| case SpvOpMemoryModel: |
| case SpvOpCapability: |
| case SpvOpTypeVoid: |
| case SpvOpTypeBool: |
| case SpvOpTypeInt: |
| case SpvOpTypeFloat: |
| case SpvOpTypeVector: |
| case SpvOpTypeMatrix: |
| case SpvOpTypeSampler: |
| case SpvOpTypeSampledImage: |
| case SpvOpTypeArray: |
| case SpvOpTypePointer: |
| case SpvOpConstantTrue: |
| case SpvOpConstantFalse: |
| case SpvOpLabel: |
| case SpvOpBranch: |
| case SpvOpFunction: |
| case SpvOpFunctionParameter: |
| case SpvOpFunctionEnd: |
| case SpvOpBitcast: |
| case SpvOpCopyObject: |
| case SpvOpTranspose: |
| case SpvOpSNegate: |
| case SpvOpFNegate: |
| case SpvOpIAdd: |
| case SpvOpFAdd: |
| case SpvOpISub: |
| case SpvOpFSub: |
| case SpvOpIMul: |
| case SpvOpFMul: |
| case SpvOpUDiv: |
| case SpvOpSDiv: |
| case SpvOpFDiv: |
| case SpvOpUMod: |
| case SpvOpSRem: |
| case SpvOpSMod: |
| case SpvOpFRem: |
| case SpvOpFMod: |
| case SpvOpVectorTimesScalar: |
| case SpvOpMatrixTimesScalar: |
| case SpvOpVectorTimesMatrix: |
| case SpvOpMatrixTimesVector: |
| case SpvOpMatrixTimesMatrix: |
| case SpvOpOuterProduct: |
| case SpvOpDot: |
| return true; |
| default: |
| break; |
| } |
| return false; |
| } |
| |
| void MarkvCodec::ProcessCurInstruction() { |
| instructions_.emplace_back(new val::Instruction(&inst_)); |
| |
| const SpvOp opcode = SpvOp(inst_.opcode); |
| |
| if (inst_.result_id) { |
| id_to_def_instruction_.emplace(inst_.result_id, instructions_.back().get()); |
| |
| // Collect ids local to the current function. |
| if (cur_function_id_) { |
| ids_local_to_cur_function_.push_back(inst_.result_id); |
| } |
| |
| // Starting new function. |
| if (opcode == SpvOpFunction) { |
| cur_function_id_ = inst_.result_id; |
| cur_function_return_type_ = inst_.type_id; |
| if (model_->id_fallback_strategy() == |
| MarkvModel::IdFallbackStrategy::kRuleBased) { |
| multi_mtf_.Insert(GetMtfFunctionWithReturnType(inst_.type_id), |
| inst_.result_id); |
| } |
| |
| // Store function parameter types in a queue, so that we know which types |
| // to expect in the following OpFunctionParameter instructions. |
| const val::Instruction* def_inst = FindDef(inst_.words[4]); |
| assert(def_inst); |
| assert(def_inst->opcode() == SpvOpTypeFunction); |
| for (uint32_t i = 3; i < def_inst->words().size(); ++i) { |
| remaining_function_parameter_types_.push_back(def_inst->word(i)); |
| } |
| } |
| } |
| |
| // Remove local ids from MTFs if function end. |
| if (opcode == SpvOpFunctionEnd) { |
| cur_function_id_ = 0; |
| for (uint32_t id : ids_local_to_cur_function_) multi_mtf_.RemoveFromAll(id); |
| ids_local_to_cur_function_.clear(); |
| assert(remaining_function_parameter_types_.empty()); |
| } |
| |
| if (!inst_.result_id) return; |
| |
| { |
| // Save the result ID to type ID mapping. |
| // In the grammar, type ID always appears before result ID. |
| // A regular value maps to its type. Some instructions (e.g. OpLabel) |
| // have no type Id, and will map to 0. The result Id for a |
| // type-generating instruction (e.g. OpTypeInt) maps to itself. |
| auto insertion_result = id_to_type_id_.emplace( |
| inst_.result_id, spvOpcodeGeneratesType(SpvOp(inst_.opcode)) |
| ? inst_.result_id |
| : inst_.type_id); |
| (void)insertion_result; |
| assert(insertion_result.second); |
| } |
| |
| // Add result_id to MTFs. |
| if (model_->id_fallback_strategy() == |
| MarkvModel::IdFallbackStrategy::kRuleBased) { |
| switch (opcode) { |
| case SpvOpTypeFloat: |
| case SpvOpTypeInt: |
| case SpvOpTypeBool: |
| case SpvOpTypeVector: |
| case SpvOpTypePointer: |
| case SpvOpExtInstImport: |
| case SpvOpTypeSampledImage: |
| case SpvOpTypeImage: |
| case SpvOpTypeSampler: |
| multi_mtf_.Insert(GetMtfIdGeneratedByOpcode(opcode), inst_.result_id); |
| break; |
| default: |
| break; |
| } |
| |
| if (spvOpcodeIsComposite(opcode)) { |
| multi_mtf_.Insert(kMtfTypeComposite, inst_.result_id); |
| } |
| |
| if (opcode == SpvOpLabel) { |
| multi_mtf_.InsertOrPromote(kMtfLabel, inst_.result_id); |
| } |
| |
| if (opcode == SpvOpTypeInt) { |
| multi_mtf_.Insert(kMtfTypeScalar, inst_.result_id); |
| multi_mtf_.Insert(kMtfTypeIntScalarOrVector, inst_.result_id); |
| } |
| |
| if (opcode == SpvOpTypeFloat) { |
| multi_mtf_.Insert(kMtfTypeScalar, inst_.result_id); |
| multi_mtf_.Insert(kMtfTypeFloatScalarOrVector, inst_.result_id); |
| } |
| |
| if (opcode == SpvOpTypeBool) { |
| multi_mtf_.Insert(kMtfTypeScalar, inst_.result_id); |
| multi_mtf_.Insert(kMtfTypeBoolScalarOrVector, inst_.result_id); |
| } |
| |
| if (opcode == SpvOpTypeVector) { |
| const uint32_t component_type_id = inst_.words[2]; |
| const uint32_t size = inst_.words[3]; |
| if (multi_mtf_.HasValue(GetMtfIdGeneratedByOpcode(SpvOpTypeFloat), |
| component_type_id)) { |
| multi_mtf_.Insert(kMtfTypeFloatScalarOrVector, inst_.result_id); |
| } else if (multi_mtf_.HasValue(GetMtfIdGeneratedByOpcode(SpvOpTypeInt), |
| component_type_id)) { |
| multi_mtf_.Insert(kMtfTypeIntScalarOrVector, inst_.result_id); |
| } else if (multi_mtf_.HasValue(GetMtfIdGeneratedByOpcode(SpvOpTypeBool), |
| component_type_id)) { |
| multi_mtf_.Insert(kMtfTypeBoolScalarOrVector, inst_.result_id); |
| } |
| multi_mtf_.Insert(GetMtfTypeVectorOfSize(size), inst_.result_id); |
| } |
| |
| if (inst_.opcode == SpvOpTypeFunction) { |
| const uint32_t return_type = inst_.words[2]; |
| multi_mtf_.Insert(kMtfTypeReturnedByFunction, return_type); |
| multi_mtf_.Insert(GetMtfFunctionTypeWithReturnType(return_type), |
| inst_.result_id); |
| } |
| |
| if (inst_.type_id) { |
| const val::Instruction* type_inst = FindDef(inst_.type_id); |
| assert(type_inst); |
| |
| multi_mtf_.Insert(kMtfObject, inst_.result_id); |
| |
| multi_mtf_.Insert(GetMtfIdOfType(inst_.type_id), inst_.result_id); |
| |
| if (multi_mtf_.HasValue(kMtfTypeFloatScalarOrVector, inst_.type_id)) { |
| multi_mtf_.Insert(kMtfFloatScalarOrVector, inst_.result_id); |
| } |
| |
| if (multi_mtf_.HasValue(kMtfTypeIntScalarOrVector, inst_.type_id)) |
| multi_mtf_.Insert(kMtfIntScalarOrVector, inst_.result_id); |
| |
| if (multi_mtf_.HasValue(kMtfTypeBoolScalarOrVector, inst_.type_id)) |
| multi_mtf_.Insert(kMtfBoolScalarOrVector, inst_.result_id); |
| |
| if (multi_mtf_.HasValue(kMtfTypeComposite, inst_.type_id)) |
| multi_mtf_.Insert(kMtfComposite, inst_.result_id); |
| |
| switch (type_inst->opcode()) { |
| case SpvOpTypeInt: |
| case SpvOpTypeBool: |
| case SpvOpTypePointer: |
| case SpvOpTypeVector: |
| case SpvOpTypeImage: |
| case SpvOpTypeSampledImage: |
| case SpvOpTypeSampler: |
| multi_mtf_.Insert( |
| GetMtfIdWithTypeGeneratedByOpcode(type_inst->opcode()), |
| inst_.result_id); |
| break; |
| default: |
| break; |
| } |
| |
| if (type_inst->opcode() == SpvOpTypeVector) { |
| const uint32_t component_type = type_inst->word(2); |
| multi_mtf_.Insert(GetMtfVectorOfComponentType(component_type), |
| inst_.result_id); |
| } |
| |
| if (type_inst->opcode() == SpvOpTypePointer) { |
| assert(type_inst->operands().size() > 2); |
| assert(type_inst->words().size() > type_inst->operands()[2].offset); |
| const uint32_t data_type = |
| type_inst->word(type_inst->operands()[2].offset); |
| multi_mtf_.Insert(GetMtfPointerToType(data_type), inst_.result_id); |
| |
| if (multi_mtf_.HasValue(kMtfTypeComposite, data_type)) |
| multi_mtf_.Insert(kMtfTypePointerToComposite, inst_.result_id); |
| } |
| } |
| |
| if (spvOpcodeGeneratesType(opcode)) { |
| if (opcode != SpvOpTypeFunction) { |
| multi_mtf_.Insert(kMtfTypeNonFunction, inst_.result_id); |
| } |
| } |
| } |
| |
| if (model_->AnyDescriptorHasCodingScheme()) { |
| const uint32_t long_descriptor = |
| long_id_descriptors_.ProcessInstruction(inst_); |
| if (model_->DescriptorHasCodingScheme(long_descriptor)) |
| multi_mtf_.Insert(GetMtfLongIdDescriptor(long_descriptor), |
| inst_.result_id); |
| } |
| |
| if (model_->id_fallback_strategy() == |
| MarkvModel::IdFallbackStrategy::kShortDescriptor) { |
| const uint32_t short_descriptor = |
| short_id_descriptors_.ProcessInstruction(inst_); |
| multi_mtf_.Insert(GetMtfShortIdDescriptor(short_descriptor), |
| inst_.result_id); |
| } |
| } |
| |
| uint64_t MarkvCodec::GetRuleBasedMtf() { |
| // This function is only called for id operands (but not result ids). |
| assert(spvIsIdType(operand_.type) || |
| operand_.type == SPV_OPERAND_TYPE_OPTIONAL_ID); |
| assert(operand_.type != SPV_OPERAND_TYPE_RESULT_ID); |
| |
| const SpvOp opcode = static_cast<SpvOp>(inst_.opcode); |
| |
| // All operand slots which expect label id. |
| if ((inst_.opcode == SpvOpLoopMerge && operand_index_ <= 1) || |
| (inst_.opcode == SpvOpSelectionMerge && operand_index_ == 0) || |
| (inst_.opcode == SpvOpBranch && operand_index_ == 0) || |
| (inst_.opcode == SpvOpBranchConditional && |
| (operand_index_ == 1 || operand_index_ == 2)) || |
| (inst_.opcode == SpvOpPhi && operand_index_ >= 3 && |
| operand_index_ % 2 == 1) || |
| (inst_.opcode == SpvOpSwitch && operand_index_ > 0)) { |
| return kMtfLabel; |
| } |
| |
| switch (opcode) { |
| case SpvOpFAdd: |
| case SpvOpFSub: |
| case SpvOpFMul: |
| case SpvOpFDiv: |
| case SpvOpFRem: |
| case SpvOpFMod: |
| case SpvOpFNegate: { |
| if (operand_index_ == 0) return kMtfTypeFloatScalarOrVector; |
| return GetMtfIdOfType(inst_.type_id); |
| } |
| |
| case SpvOpISub: |
| case SpvOpIAdd: |
| case SpvOpIMul: |
| case SpvOpSDiv: |
| case SpvOpUDiv: |
| case SpvOpSMod: |
| case SpvOpUMod: |
| case SpvOpSRem: |
| case SpvOpSNegate: { |
| if (operand_index_ == 0) return kMtfTypeIntScalarOrVector; |
| |
| return kMtfIntScalarOrVector; |
| } |
| |
| // TODO(atgoo@github.com) Add OpConvertFToU and other opcodes. |
| |
| case SpvOpFOrdEqual: |
| case SpvOpFUnordEqual: |
| case SpvOpFOrdNotEqual: |
| case SpvOpFUnordNotEqual: |
| case SpvOpFOrdLessThan: |
| case SpvOpFUnordLessThan: |
| case SpvOpFOrdGreaterThan: |
| case SpvOpFUnordGreaterThan: |
| case SpvOpFOrdLessThanEqual: |
| case SpvOpFUnordLessThanEqual: |
| case SpvOpFOrdGreaterThanEqual: |
| case SpvOpFUnordGreaterThanEqual: { |
| if (operand_index_ == 0) return kMtfTypeBoolScalarOrVector; |
| if (operand_index_ == 2) return kMtfFloatScalarOrVector; |
| if (operand_index_ == 3) { |
| const uint32_t first_operand_id = GetInstWords()[3]; |
| const uint32_t first_operand_type = id_to_type_id_.at(first_operand_id); |
| return GetMtfIdOfType(first_operand_type); |
| } |
| break; |
| } |
| |
| case SpvOpVectorShuffle: { |
| if (operand_index_ == 0) { |
| assert(inst_.num_operands > 4); |
| return GetMtfTypeVectorOfSize(inst_.num_operands - 4); |
| } |
| |
| assert(inst_.type_id); |
| if (operand_index_ == 2 || operand_index_ == 3) |
| return GetMtfVectorOfComponentType( |
| GetVectorComponentType(inst_.type_id)); |
| break; |
| } |
| |
| case SpvOpVectorTimesScalar: { |
| if (operand_index_ == 0) { |
| // TODO(atgoo@github.com) Could be narrowed to vector of floats. |
| return GetMtfIdGeneratedByOpcode(SpvOpTypeVector); |
| } |
| |
| assert(inst_.type_id); |
| if (operand_index_ == 2) return GetMtfIdOfType(inst_.type_id); |
| if (operand_index_ == 3) |
| return GetMtfIdOfType(GetVectorComponentType(inst_.type_id)); |
| break; |
| } |
| |
| case SpvOpDot: { |
| if (operand_index_ == 0) return GetMtfIdGeneratedByOpcode(SpvOpTypeFloat); |
| |
| assert(inst_.type_id); |
| if (operand_index_ == 2) |
| return GetMtfVectorOfComponentType(inst_.type_id); |
| if (operand_index_ == 3) { |
| const uint32_t vector_id = GetInstWords()[3]; |
| const uint32_t vector_type = id_to_type_id_.at(vector_id); |
| return GetMtfIdOfType(vector_type); |
| } |
| break; |
| } |
| |
| case SpvOpTypeVector: { |
| if (operand_index_ == 1) { |
| return kMtfTypeScalar; |
| } |
| break; |
| } |
| |
| case SpvOpTypeMatrix: { |
| if (operand_index_ == 1) { |
| return GetMtfIdGeneratedByOpcode(SpvOpTypeVector); |
| } |
| break; |
| } |
| |
| case SpvOpTypePointer: { |
| if (operand_index_ == 2) { |
| return kMtfTypeNonFunction; |
| } |
| break; |
| } |
| |
| case SpvOpTypeStruct: { |
| if (operand_index_ >= 1) { |
| return kMtfTypeNonFunction; |
| } |
| break; |
| } |
| |
| case SpvOpTypeFunction: { |
| if (operand_index_ == 1) { |
| return kMtfTypeNonFunction; |
| } |
| |
| if (operand_index_ >= 2) { |
| return kMtfTypeNonFunction; |
| } |
| break; |
| } |
| |
| case SpvOpLoad: { |
| if (operand_index_ == 0) return kMtfTypeNonFunction; |
| |
| if (operand_index_ == 2) { |
| assert(inst_.type_id); |
| return GetMtfPointerToType(inst_.type_id); |
| } |
| break; |
| } |
| |
| case SpvOpStore: { |
| if (operand_index_ == 0) |
| return GetMtfIdWithTypeGeneratedByOpcode(SpvOpTypePointer); |
| if (operand_index_ == 1) { |
| const uint32_t pointer_id = GetInstWords()[1]; |
| const uint32_t pointer_type = id_to_type_id_.at(pointer_id); |
| const val::Instruction* pointer_inst = FindDef(pointer_type); |
| assert(pointer_inst); |
| assert(pointer_inst->opcode() == SpvOpTypePointer); |
| const uint32_t data_type = |
| pointer_inst->word(pointer_inst->operands()[2].offset); |
| return GetMtfIdOfType(data_type); |
| } |
| break; |
| } |
| |
| case SpvOpVariable: { |
| if (operand_index_ == 0) |
| return GetMtfIdGeneratedByOpcode(SpvOpTypePointer); |
| break; |
| } |
| |
| case SpvOpAccessChain: { |
| if (operand_index_ == 0) |
| return GetMtfIdGeneratedByOpcode(SpvOpTypePointer); |
| if (operand_index_ == 2) return kMtfTypePointerToComposite; |
| if (operand_index_ >= 3) |
| return GetMtfIdWithTypeGeneratedByOpcode(SpvOpTypeInt); |
| break; |
| } |
| |
| case SpvOpCompositeConstruct: { |
| if (operand_index_ == 0) return kMtfTypeComposite; |
| if (operand_index_ >= 2) { |
| const uint32_t composite_type = GetInstWords()[1]; |
| if (multi_mtf_.HasValue(kMtfTypeFloatScalarOrVector, composite_type)) |
| return kMtfFloatScalarOrVector; |
| if (multi_mtf_.HasValue(kMtfTypeIntScalarOrVector, composite_type)) |
| return kMtfIntScalarOrVector; |
| if (multi_mtf_.HasValue(kMtfTypeBoolScalarOrVector, composite_type)) |
| return kMtfBoolScalarOrVector; |
| } |
| break; |
| } |
| |
| case SpvOpCompositeExtract: { |
| if (operand_index_ == 2) return kMtfComposite; |
| break; |
| } |
| |
| case SpvOpConstantComposite: { |
| if (operand_index_ == 0) return kMtfTypeComposite; |
| if (operand_index_ >= 2) { |
| const val::Instruction* composite_type_inst = FindDef(inst_.type_id); |
| assert(composite_type_inst); |
| if (composite_type_inst->opcode() == SpvOpTypeVector) { |
| return GetMtfIdOfType(composite_type_inst->word(2)); |
| } |
| } |
| break; |
| } |
| |
| case SpvOpExtInst: { |
| if (operand_index_ == 2) |
| return GetMtfIdGeneratedByOpcode(SpvOpExtInstImport); |
| if (operand_index_ >= 4) { |
| const uint32_t return_type = GetInstWords()[1]; |
| const uint32_t ext_inst_type = inst_.ext_inst_type; |
| const uint32_t ext_inst_index = GetInstWords()[4]; |
| // TODO(atgoo@github.com) The list of extended instructions is |
| // incomplete. Only common instructions and low-hanging fruits listed. |
| if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) { |
| switch (ext_inst_index) { |
| case GLSLstd450FAbs: |
| case GLSLstd450FClamp: |
| case GLSLstd450FMax: |
| case GLSLstd450FMin: |
| case GLSLstd450FMix: |
| case GLSLstd450Step: |
| case GLSLstd450SmoothStep: |
| case GLSLstd450Fma: |
| case GLSLstd450Pow: |
| case GLSLstd450Exp: |
| case GLSLstd450Exp2: |
| case GLSLstd450Log: |
| case GLSLstd450Log2: |
| case GLSLstd450Sqrt: |
| case GLSLstd450InverseSqrt: |
| case GLSLstd450Fract: |
| case GLSLstd450Floor: |
| case GLSLstd450Ceil: |
| case GLSLstd450Radians: |
| case GLSLstd450Degrees: |
| case GLSLstd450Sin: |
| case GLSLstd450Cos: |
| case GLSLstd450Tan: |
| case GLSLstd450Sinh: |
| case GLSLstd450Cosh: |
| case GLSLstd450Tanh: |
| case GLSLstd450Asin: |
| case GLSLstd450Acos: |
| case GLSLstd450Atan: |
| case GLSLstd450Atan2: |
| case GLSLstd450Asinh: |
| case GLSLstd450Acosh: |
| case GLSLstd450Atanh: |
| case GLSLstd450MatrixInverse: |
| case GLSLstd450Cross: |
| case GLSLstd450Normalize: |
| case GLSLstd450Reflect: |
| case GLSLstd450FaceForward: |
| return GetMtfIdOfType(return_type); |
| case GLSLstd450Length: |
| case GLSLstd450Distance: |
| case GLSLstd450Refract: |
| return kMtfFloatScalarOrVector; |
| default: |
| break; |
| } |
| } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) { |
| switch (ext_inst_index) { |
| case OpenCLLIB::Fabs: |
| case OpenCLLIB::FClamp: |
| case OpenCLLIB::Fmax: |
| case OpenCLLIB::Fmin: |
| case OpenCLLIB::Step: |
| case OpenCLLIB::Smoothstep: |
| case OpenCLLIB::Fma: |
| case OpenCLLIB::Pow: |
| case OpenCLLIB::Exp: |
| case OpenCLLIB::Exp2: |
| case OpenCLLIB::Log: |
| case OpenCLLIB::Log2: |
| case OpenCLLIB::Sqrt: |
| case OpenCLLIB::Rsqrt: |
| case OpenCLLIB::Fract: |
| case OpenCLLIB::Floor: |
| case OpenCLLIB::Ceil: |
| case OpenCLLIB::Radians: |
| case OpenCLLIB::Degrees: |
| case OpenCLLIB::Sin: |
| case OpenCLLIB::Cos: |
| case OpenCLLIB::Tan: |
| case OpenCLLIB::Sinh: |
| case OpenCLLIB::Cosh: |
| case OpenCLLIB::Tanh: |
| case OpenCLLIB::Asin: |
| case OpenCLLIB::Acos: |
| case OpenCLLIB::Atan: |
| case OpenCLLIB::Atan2: |
| case OpenCLLIB::Asinh: |
| case OpenCLLIB::Acosh: |
| case OpenCLLIB::Atanh: |
| case OpenCLLIB::Cross: |
| case OpenCLLIB::Normalize: |
| return GetMtfIdOfType(return_type); |
| case OpenCLLIB::Length: |
| case OpenCLLIB::Distance: |
| return kMtfFloatScalarOrVector; |
| default: |
| break; |
| } |
| } |
| } |
| break; |
| } |
| |
| case SpvOpFunction: { |
| if (operand_index_ == 0) return kMtfTypeReturnedByFunction; |
| |
| if (operand_index_ == 3) { |
| const uint32_t return_type = GetInstWords()[1]; |
| return GetMtfFunctionTypeWithReturnType(return_type); |
| } |
| break; |
| } |
| |
| case SpvOpFunctionCall: { |
| if (operand_index_ == 0) return kMtfTypeReturnedByFunction; |
| |
| if (operand_index_ == 2) { |
| const uint32_t return_type = GetInstWords()[1]; |
| return GetMtfFunctionWithReturnType(return_type); |
| } |
| |
| if (operand_index_ >= 3) { |
| const uint32_t function_id = GetInstWords()[3]; |
| const val::Instruction* function_inst = FindDef(function_id); |
| if (!function_inst) return kMtfObject; |
| |
| assert(function_inst->opcode() == SpvOpFunction); |
| |
| const uint32_t function_type_id = function_inst->word(4); |
| const val::Instruction* function_type_inst = FindDef(function_type_id); |
| assert(function_type_inst); |
| assert(function_type_inst->opcode() == SpvOpTypeFunction); |
| |
| const uint32_t argument_type = function_type_inst->word(operand_index_); |
| return GetMtfIdOfType(argument_type); |
| } |
| break; |
| } |
| |
| case SpvOpReturnValue: { |
| if (operand_index_ == 0) return GetMtfIdOfType(cur_function_return_type_); |
| break; |
| } |
| |
| case SpvOpBranchConditional: { |
| if (operand_index_ == 0) |
| return GetMtfIdWithTypeGeneratedByOpcode(SpvOpTypeBool); |
| break; |
| } |
| |
| case SpvOpSampledImage: { |
| if (operand_index_ == 0) |
| return GetMtfIdGeneratedByOpcode(SpvOpTypeSampledImage); |
| if (operand_index_ == 2) |
| return GetMtfIdWithTypeGeneratedByOpcode(SpvOpTypeImage); |
| if (operand_index_ == 3) |
| return GetMtfIdWithTypeGeneratedByOpcode(SpvOpTypeSampler); |
| break; |
| } |
| |
| case SpvOpImageSampleImplicitLod: { |
| if (operand_index_ == 0) |
| return GetMtfIdGeneratedByOpcode(SpvOpTypeVector); |
| if (operand_index_ == 2) |
| return GetMtfIdWithTypeGeneratedByOpcode(SpvOpTypeSampledImage); |
| if (operand_index_ == 3) |
| return GetMtfIdWithTypeGeneratedByOpcode(SpvOpTypeVector); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| return kMtfNone; |
| } |
| |
| } // namespace comp |
| } // namespace spvtools |