1 // Copyright (c) 2015-2016 The Khronos Group Inc. 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/assembly_grammar.h" 16 17 #include <algorithm> 18 #include <cassert> 19 #include <cstring> 20 21 #include "source/ext_inst.h" 22 #include "source/opcode.h" 23 #include "source/operand.h" 24 #include "source/table.h" 25 26 namespace spvtools { 27 namespace { 28 29 /// @brief Parses a mask expression string for the given operand type. 30 /// 31 /// A mask expression is a sequence of one or more terms separated by '|', 32 /// where each term a named enum value for the given type. No whitespace 33 /// is permitted. 34 /// 35 /// On success, the value is written to pValue. 36 /// 37 /// @param[in] operandTable operand lookup table 38 /// @param[in] type of the operand 39 /// @param[in] textValue word of text to be parsed 40 /// @param[out] pValue where the resulting value is written 41 /// 42 /// @return result code 43 spv_result_t spvTextParseMaskOperand(spv_target_env env, 44 const spv_operand_table operandTable, 45 const spv_operand_type_t type, 46 const char* textValue, uint32_t* pValue) { 47 if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT; 48 size_t text_length = strlen(textValue); 49 if (text_length == 0) return SPV_ERROR_INVALID_TEXT; 50 const char* text_end = textValue + text_length; 51 52 // We only support mask expressions in ASCII, so the separator value is a 53 // char. 54 const char separator = '|'; 55 56 // Accumulate the result by interpreting one word at a time, scanning 57 // from left to right. 58 uint32_t value = 0; 59 const char* begin = textValue; // The left end of the current word. 60 const char* end = nullptr; // One character past the end of the current word. 61 do { 62 end = std::find(begin, text_end, separator); 63 64 spv_operand_desc entry = nullptr; 65 if (spvOperandTableNameLookup(env, operandTable, type, begin, end - begin, 66 &entry)) { 67 return SPV_ERROR_INVALID_TEXT; 68 } 69 value |= entry->value; 70 71 // Advance to the next word by skipping over the separator. 72 begin = end + 1; 73 } while (end != text_end); 74 75 *pValue = value; 76 return SPV_SUCCESS; 77 } 78 79 // Associates an opcode with its name. 80 struct SpecConstantOpcodeEntry { 81 SpvOp opcode; 82 const char* name; 83 }; 84 85 // All the opcodes allowed as the operation for OpSpecConstantOp. 86 // The name does not have the usual "Op" prefix. For example opcode SpvOpIAdd 87 // is associated with the name "IAdd". 88 // 89 // clang-format off 90 #define CASE(NAME) { SpvOp##NAME, #NAME } 91 const SpecConstantOpcodeEntry kOpSpecConstantOpcodes[] = { 92 // Conversion 93 CASE(SConvert), 94 CASE(FConvert), 95 CASE(ConvertFToS), 96 CASE(ConvertSToF), 97 CASE(ConvertFToU), 98 CASE(ConvertUToF), 99 CASE(UConvert), 100 CASE(ConvertPtrToU), 101 CASE(ConvertUToPtr), 102 CASE(GenericCastToPtr), 103 CASE(PtrCastToGeneric), 104 CASE(Bitcast), 105 CASE(QuantizeToF16), 106 // Arithmetic 107 CASE(SNegate), 108 CASE(Not), 109 CASE(IAdd), 110 CASE(ISub), 111 CASE(IMul), 112 CASE(UDiv), 113 CASE(SDiv), 114 CASE(UMod), 115 CASE(SRem), 116 CASE(SMod), 117 CASE(ShiftRightLogical), 118 CASE(ShiftRightArithmetic), 119 CASE(ShiftLeftLogical), 120 CASE(BitwiseOr), 121 CASE(BitwiseAnd), 122 CASE(BitwiseXor), 123 CASE(FNegate), 124 CASE(FAdd), 125 CASE(FSub), 126 CASE(FMul), 127 CASE(FDiv), 128 CASE(FRem), 129 CASE(FMod), 130 // Composite 131 CASE(VectorShuffle), 132 CASE(CompositeExtract), 133 CASE(CompositeInsert), 134 // Logical 135 CASE(LogicalOr), 136 CASE(LogicalAnd), 137 CASE(LogicalNot), 138 CASE(LogicalEqual), 139 CASE(LogicalNotEqual), 140 CASE(Select), 141 // Comparison 142 CASE(IEqual), 143 CASE(INotEqual), 144 CASE(ULessThan), 145 CASE(SLessThan), 146 CASE(UGreaterThan), 147 CASE(SGreaterThan), 148 CASE(ULessThanEqual), 149 CASE(SLessThanEqual), 150 CASE(UGreaterThanEqual), 151 CASE(SGreaterThanEqual), 152 // Memory 153 CASE(AccessChain), 154 CASE(InBoundsAccessChain), 155 CASE(PtrAccessChain), 156 CASE(InBoundsPtrAccessChain), 157 }; 158 159 // The 59 is determined by counting the opcodes listed in the spec. 160 static_assert(59 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]), 161 "OpSpecConstantOp opcode table is incomplete"); 162 #undef CASE 163 // clang-format on 164 165 const size_t kNumOpSpecConstantOpcodes = 166 sizeof(kOpSpecConstantOpcodes) / sizeof(kOpSpecConstantOpcodes[0]); 167 168 } // namespace 169 170 bool AssemblyGrammar::isValid() const { 171 return operandTable_ && opcodeTable_ && extInstTable_; 172 } 173 174 CapabilitySet AssemblyGrammar::filterCapsAgainstTargetEnv( 175 const SpvCapability* cap_array, uint32_t count) const { 176 CapabilitySet cap_set; 177 for (uint32_t i = 0; i < count; ++i) { 178 spv_operand_desc cap_desc = {}; 179 if (SPV_SUCCESS == lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, 180 static_cast<uint32_t>(cap_array[i]), 181 &cap_desc)) { 182 // spvOperandTableValueLookup() filters capabilities internally 183 // according to the current target environment by itself. So we 184 // should be safe to add this capability if the lookup succeeds. 185 cap_set.Add(cap_array[i]); 186 } 187 } 188 return cap_set; 189 } 190 191 spv_result_t AssemblyGrammar::lookupOpcode(const char* name, 192 spv_opcode_desc* desc) const { 193 return spvOpcodeTableNameLookup(target_env_, opcodeTable_, name, desc); 194 } 195 196 spv_result_t AssemblyGrammar::lookupOpcode(SpvOp opcode, 197 spv_opcode_desc* desc) const { 198 return spvOpcodeTableValueLookup(target_env_, opcodeTable_, opcode, desc); 199 } 200 201 spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type, 202 const char* name, size_t name_len, 203 spv_operand_desc* desc) const { 204 return spvOperandTableNameLookup(target_env_, operandTable_, type, name, 205 name_len, desc); 206 } 207 208 spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type, 209 uint32_t operand, 210 spv_operand_desc* desc) const { 211 return spvOperandTableValueLookup(target_env_, operandTable_, type, operand, 212 desc); 213 } 214 215 spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(const char* name, 216 SpvOp* opcode) const { 217 const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes; 218 const auto* found = 219 std::find_if(kOpSpecConstantOpcodes, last, 220 [name](const SpecConstantOpcodeEntry& entry) { 221 return 0 == strcmp(name, entry.name); 222 }); 223 if (found == last) return SPV_ERROR_INVALID_LOOKUP; 224 *opcode = found->opcode; 225 return SPV_SUCCESS; 226 } 227 228 spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(SpvOp opcode) const { 229 const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes; 230 const auto* found = 231 std::find_if(kOpSpecConstantOpcodes, last, 232 [opcode](const SpecConstantOpcodeEntry& entry) { 233 return opcode == entry.opcode; 234 }); 235 if (found == last) return SPV_ERROR_INVALID_LOOKUP; 236 return SPV_SUCCESS; 237 } 238 239 spv_result_t AssemblyGrammar::parseMaskOperand(const spv_operand_type_t type, 240 const char* textValue, 241 uint32_t* pValue) const { 242 return spvTextParseMaskOperand(target_env_, operandTable_, type, textValue, 243 pValue); 244 } 245 spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type, 246 const char* textValue, 247 spv_ext_inst_desc* extInst) const { 248 return spvExtInstTableNameLookup(extInstTable_, type, textValue, extInst); 249 } 250 251 spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type, 252 uint32_t firstWord, 253 spv_ext_inst_desc* extInst) const { 254 return spvExtInstTableValueLookup(extInstTable_, type, firstWord, extInst); 255 } 256 257 void AssemblyGrammar::pushOperandTypesForMask( 258 const spv_operand_type_t type, const uint32_t mask, 259 spv_operand_pattern_t* pattern) const { 260 spvPushOperandTypesForMask(target_env_, operandTable_, type, mask, pattern); 261 } 262 263 } // namespace spvtools 264