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