// Copyright (c) 2015-2016 The Khronos Group Inc. // // 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/assembly_grammar.h" #include <algorithm> #include <cassert> #include <cstring> #include "source/ext_inst.h" #include "source/opcode.h" #include "source/operand.h" #include "source/table.h" namespace spvtools { namespace { /// @brief Parses a mask expression string for the given operand type. /// /// A mask expression is a sequence of one or more terms separated by '|', /// where each term a named enum value for the given type. No whitespace /// is permitted. /// /// On success, the value is written to pValue. /// /// @param[in] operandTable operand lookup table /// @param[in] type of the operand /// @param[in] textValue word of text to be parsed /// @param[out] pValue where the resulting value is written /// /// @return result code spv_result_t spvTextParseMaskOperand(spv_target_env env, const spv_operand_table operandTable, const spv_operand_type_t type, const char* textValue, uint32_t* pValue) { if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT; size_t text_length = strlen(textValue); if (text_length == 0) return SPV_ERROR_INVALID_TEXT; const char* text_end = textValue + text_length; // We only support mask expressions in ASCII, so the separator value is a // char. const char separator = '|'; // Accumulate the result by interpreting one word at a time, scanning // from left to right. uint32_t value = 0; const char* begin = textValue; // The left end of the current word. const char* end = nullptr; // One character past the end of the current word. do { end = std::find(begin, text_end, separator); spv_operand_desc entry = nullptr; if (spvOperandTableNameLookup(env, operandTable, type, begin, end - begin, &entry)) { return SPV_ERROR_INVALID_TEXT; } value |= entry->value; // Advance to the next word by skipping over the separator. begin = end + 1; } while (end != text_end); *pValue = value; return SPV_SUCCESS; } // Associates an opcode with its name. struct SpecConstantOpcodeEntry { SpvOp opcode; const char* name; }; // All the opcodes allowed as the operation for OpSpecConstantOp. // The name does not have the usual "Op" prefix. For example opcode SpvOpIAdd // is associated with the name "IAdd". // // clang-format off #define CASE(NAME) { SpvOp##NAME, #NAME } const SpecConstantOpcodeEntry kOpSpecConstantOpcodes[] = { // Conversion CASE(SConvert), CASE(FConvert), CASE(ConvertFToS), CASE(ConvertSToF), CASE(ConvertFToU), CASE(ConvertUToF), CASE(UConvert), CASE(ConvertPtrToU), CASE(ConvertUToPtr), CASE(GenericCastToPtr), CASE(PtrCastToGeneric), CASE(Bitcast), CASE(QuantizeToF16), // Arithmetic CASE(SNegate), CASE(Not), CASE(IAdd), CASE(ISub), CASE(IMul), CASE(UDiv), CASE(SDiv), CASE(UMod), CASE(SRem), CASE(SMod), CASE(ShiftRightLogical), CASE(ShiftRightArithmetic), CASE(ShiftLeftLogical), CASE(BitwiseOr), CASE(BitwiseAnd), CASE(BitwiseXor), CASE(FNegate), CASE(FAdd), CASE(FSub), CASE(FMul), CASE(FDiv), CASE(FRem), CASE(FMod), // Composite CASE(VectorShuffle), CASE(CompositeExtract), CASE(CompositeInsert), // Logical CASE(LogicalOr), CASE(LogicalAnd), CASE(LogicalNot), CASE(LogicalEqual), CASE(LogicalNotEqual), CASE(Select), // Comparison CASE(IEqual), CASE(INotEqual), CASE(ULessThan), CASE(SLessThan), CASE(UGreaterThan), CASE(SGreaterThan), CASE(ULessThanEqual), CASE(SLessThanEqual), CASE(UGreaterThanEqual), CASE(SGreaterThanEqual), // Memory CASE(AccessChain), CASE(InBoundsAccessChain), CASE(PtrAccessChain), CASE(InBoundsPtrAccessChain), }; // The 59 is determined by counting the opcodes listed in the spec. static_assert(59 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]), "OpSpecConstantOp opcode table is incomplete"); #undef CASE // clang-format on const size_t kNumOpSpecConstantOpcodes = sizeof(kOpSpecConstantOpcodes) / sizeof(kOpSpecConstantOpcodes[0]); } // namespace bool AssemblyGrammar::isValid() const { return operandTable_ && opcodeTable_ && extInstTable_; } CapabilitySet AssemblyGrammar::filterCapsAgainstTargetEnv( const SpvCapability* cap_array, uint32_t count) const { CapabilitySet cap_set; for (uint32_t i = 0; i < count; ++i) { spv_operand_desc cap_desc = {}; if (SPV_SUCCESS == lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, static_cast<uint32_t>(cap_array[i]), &cap_desc)) { // spvOperandTableValueLookup() filters capabilities internally // according to the current target environment by itself. So we // should be safe to add this capability if the lookup succeeds. cap_set.Add(cap_array[i]); } } return cap_set; } spv_result_t AssemblyGrammar::lookupOpcode(const char* name, spv_opcode_desc* desc) const { return spvOpcodeTableNameLookup(target_env_, opcodeTable_, name, desc); } spv_result_t AssemblyGrammar::lookupOpcode(SpvOp opcode, spv_opcode_desc* desc) const { return spvOpcodeTableValueLookup(target_env_, opcodeTable_, opcode, desc); } spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type, const char* name, size_t name_len, spv_operand_desc* desc) const { return spvOperandTableNameLookup(target_env_, operandTable_, type, name, name_len, desc); } spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type, uint32_t operand, spv_operand_desc* desc) const { return spvOperandTableValueLookup(target_env_, operandTable_, type, operand, desc); } spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(const char* name, SpvOp* opcode) const { const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes; const auto* found = std::find_if(kOpSpecConstantOpcodes, last, [name](const SpecConstantOpcodeEntry& entry) { return 0 == strcmp(name, entry.name); }); if (found == last) return SPV_ERROR_INVALID_LOOKUP; *opcode = found->opcode; return SPV_SUCCESS; } spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(SpvOp opcode) const { const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes; const auto* found = std::find_if(kOpSpecConstantOpcodes, last, [opcode](const SpecConstantOpcodeEntry& entry) { return opcode == entry.opcode; }); if (found == last) return SPV_ERROR_INVALID_LOOKUP; return SPV_SUCCESS; } spv_result_t AssemblyGrammar::parseMaskOperand(const spv_operand_type_t type, const char* textValue, uint32_t* pValue) const { return spvTextParseMaskOperand(target_env_, operandTable_, type, textValue, pValue); } spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type, const char* textValue, spv_ext_inst_desc* extInst) const { return spvExtInstTableNameLookup(extInstTable_, type, textValue, extInst); } spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type, uint32_t firstWord, spv_ext_inst_desc* extInst) const { return spvExtInstTableValueLookup(extInstTable_, type, firstWord, extInst); } void AssemblyGrammar::pushOperandTypesForMask( const spv_operand_type_t type, const uint32_t mask, spv_operand_pattern_t* pattern) const { spvPushOperandTypesForMask(target_env_, operandTable_, type, mask, pattern); } } // namespace spvtools