Home | History | Annotate | Download | only in source
      1 // Copyright (c) 2015-2016 The Khronos Group Inc.
      2 //
      3 // Permission is hereby granted, free of charge, to any person obtaining a
      4 // copy of this software and/or associated documentation files (the
      5 // "Materials"), to deal in the Materials without restriction, including
      6 // without limitation the rights to use, copy, modify, merge, publish,
      7 // distribute, sublicense, and/or sell copies of the Materials, and to
      8 // permit persons to whom the Materials are furnished to do so, subject to
      9 // the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included
     12 // in all copies or substantial portions of the Materials.
     13 //
     14 // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
     15 // KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
     16 // SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
     17 //    https://www.khronos.org/registry/
     18 //
     19 // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     22 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
     23 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     24 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     25 // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
     26 
     27 #include "assembly_grammar.h"
     28 
     29 #include <algorithm>
     30 #include <cassert>
     31 #include <cstring>
     32 
     33 #include "ext_inst.h"
     34 #include "opcode.h"
     35 #include "operand.h"
     36 #include "table.h"
     37 
     38 namespace {
     39 
     40 /// @brief Parses a mask expression string for the given operand type.
     41 ///
     42 /// A mask expression is a sequence of one or more terms separated by '|',
     43 /// where each term a named enum value for the given type.  No whitespace
     44 /// is permitted.
     45 ///
     46 /// On success, the value is written to pValue.
     47 ///
     48 /// @param[in] operandTable operand lookup table
     49 /// @param[in] type of the operand
     50 /// @param[in] textValue word of text to be parsed
     51 /// @param[out] pValue where the resulting value is written
     52 ///
     53 /// @return result code
     54 spv_result_t spvTextParseMaskOperand(const spv_operand_table operandTable,
     55                                      const spv_operand_type_t type,
     56                                      const char* textValue, uint32_t* pValue) {
     57   if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT;
     58   size_t text_length = strlen(textValue);
     59   if (text_length == 0) return SPV_ERROR_INVALID_TEXT;
     60   const char* text_end = textValue + text_length;
     61 
     62   // We only support mask expressions in ASCII, so the separator value is a
     63   // char.
     64   const char separator = '|';
     65 
     66   // Accumulate the result by interpreting one word at a time, scanning
     67   // from left to right.
     68   uint32_t value = 0;
     69   const char* begin = textValue;  // The left end of the current word.
     70   const char* end = nullptr;  // One character past the end of the current word.
     71   do {
     72     end = std::find(begin, text_end, separator);
     73 
     74     spv_operand_desc entry = nullptr;
     75     if (spvOperandTableNameLookup(operandTable, type, begin, end - begin,
     76                                   &entry)) {
     77       return SPV_ERROR_INVALID_TEXT;
     78     }
     79     value |= entry->value;
     80 
     81     // Advance to the next word by skipping over the separator.
     82     begin = end + 1;
     83   } while (end != text_end);
     84 
     85   *pValue = value;
     86   return SPV_SUCCESS;
     87 }
     88 
     89 // Associates an opcode with its name.
     90 struct SpecConstantOpcodeEntry {
     91   SpvOp opcode;
     92   const char* name;
     93 };
     94 
     95 // All the opcodes allowed as the operation for OpSpecConstantOp.
     96 // The name does not have the usual "Op" prefix. For example opcode SpvOpIAdd
     97 // is associated with the name "IAdd".
     98 //
     99 // clang-format off
    100 #define CASE(NAME) { SpvOp##NAME, #NAME }
    101 const SpecConstantOpcodeEntry kOpSpecConstantOpcodes[] = {
    102     // Conversion
    103     CASE(SConvert),
    104     CASE(FConvert),
    105     CASE(ConvertFToS),
    106     CASE(ConvertSToF),
    107     CASE(ConvertFToU),
    108     CASE(ConvertUToF),
    109     CASE(UConvert),
    110     CASE(ConvertPtrToU),
    111     CASE(ConvertUToPtr),
    112     CASE(GenericCastToPtr),
    113     CASE(PtrCastToGeneric),
    114     CASE(Bitcast),
    115     CASE(QuantizeToF16),
    116     // Arithmetic
    117     CASE(SNegate),
    118     CASE(Not),
    119     CASE(IAdd),
    120     CASE(ISub),
    121     CASE(IMul),
    122     CASE(UDiv),
    123     CASE(SDiv),
    124     CASE(UMod),
    125     CASE(SRem),
    126     CASE(SMod),
    127     CASE(ShiftRightLogical),
    128     CASE(ShiftRightArithmetic),
    129     CASE(ShiftLeftLogical),
    130     CASE(BitwiseOr),
    131     CASE(BitwiseAnd),
    132     CASE(BitwiseXor),
    133     CASE(FNegate),
    134     CASE(FAdd),
    135     CASE(FSub),
    136     CASE(FMul),
    137     CASE(FDiv),
    138     CASE(FRem),
    139     CASE(FMod),
    140     // Composite
    141     CASE(VectorShuffle),
    142     CASE(CompositeExtract),
    143     CASE(CompositeInsert),
    144     // Logical
    145     CASE(LogicalOr),
    146     CASE(LogicalAnd),
    147     CASE(LogicalNot),
    148     CASE(LogicalEqual),
    149     CASE(LogicalNotEqual),
    150     CASE(Select),
    151     // Comparison
    152     CASE(IEqual),
    153     CASE(ULessThan),
    154     CASE(SLessThan),
    155     CASE(UGreaterThan),
    156     CASE(SGreaterThan),
    157     CASE(ULessThanEqual),
    158     CASE(SLessThanEqual),
    159     CASE(UGreaterThanEqual),
    160     CASE(SGreaterThanEqual),
    161     // Memory
    162     CASE(AccessChain),
    163     CASE(InBoundsAccessChain),
    164     CASE(PtrAccessChain),
    165     CASE(InBoundsPtrAccessChain),
    166 };
    167 
    168 // The 58 is determined by counting the opcodes listed in the spec.
    169 static_assert(58 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]),
    170               "OpSpecConstantOp opcode table is incomplete");
    171 #undef CASE
    172 // clang-format on
    173 
    174 const size_t kNumOpSpecConstantOpcodes =
    175     sizeof(kOpSpecConstantOpcodes) / sizeof(kOpSpecConstantOpcodes[0]);
    176 
    177 }  // anonymous namespace
    178 
    179 namespace libspirv {
    180 
    181 bool AssemblyGrammar::isValid() const {
    182   return operandTable_ && opcodeTable_ && extInstTable_;
    183 }
    184 
    185 spv_result_t AssemblyGrammar::lookupOpcode(const char* name,
    186                                            spv_opcode_desc* desc) const {
    187   return spvOpcodeTableNameLookup(opcodeTable_, name, desc);
    188 }
    189 
    190 spv_result_t AssemblyGrammar::lookupOpcode(SpvOp opcode,
    191                                            spv_opcode_desc* desc) const {
    192   return spvOpcodeTableValueLookup(opcodeTable_, opcode, desc);
    193 }
    194 
    195 spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type,
    196                                             const char* name, size_t name_len,
    197                                             spv_operand_desc* desc) const {
    198   return spvOperandTableNameLookup(operandTable_, type, name, name_len, desc);
    199 }
    200 
    201 spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type,
    202                                             uint32_t operand,
    203                                             spv_operand_desc* desc) const {
    204   return spvOperandTableValueLookup(operandTable_, type, operand, desc);
    205 }
    206 
    207 spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(const char* name,
    208                                                        SpvOp* opcode) const {
    209   const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes;
    210   const auto* found =
    211       std::find_if(kOpSpecConstantOpcodes, last,
    212                    [name](const SpecConstantOpcodeEntry& entry) {
    213                      return 0 == strcmp(name, entry.name);
    214                    });
    215   if (found == last) return SPV_ERROR_INVALID_LOOKUP;
    216   *opcode = found->opcode;
    217   return SPV_SUCCESS;
    218 }
    219 
    220 spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(SpvOp opcode) const {
    221   const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes;
    222   const auto* found =
    223       std::find_if(kOpSpecConstantOpcodes, last,
    224                    [opcode](const SpecConstantOpcodeEntry& entry) {
    225                      return opcode == entry.opcode;
    226                    });
    227   if (found == last) return SPV_ERROR_INVALID_LOOKUP;
    228   return SPV_SUCCESS;
    229 }
    230 
    231 spv_result_t AssemblyGrammar::parseMaskOperand(const spv_operand_type_t type,
    232                                                const char* textValue,
    233                                                uint32_t* pValue) const {
    234   return spvTextParseMaskOperand(operandTable_, type, textValue, pValue);
    235 }
    236 spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type,
    237                                             const char* textValue,
    238                                             spv_ext_inst_desc* extInst) const {
    239   return spvExtInstTableNameLookup(extInstTable_, type, textValue, extInst);
    240 }
    241 
    242 spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type,
    243                                             uint32_t firstWord,
    244                                             spv_ext_inst_desc* extInst) const {
    245   return spvExtInstTableValueLookup(extInstTable_, type, firstWord, extInst);
    246 }
    247 
    248 void AssemblyGrammar::prependOperandTypesForMask(
    249     const spv_operand_type_t type, const uint32_t mask,
    250     spv_operand_pattern_t* pattern) const {
    251   spvPrependOperandTypesForMask(operandTable_, type, mask, pattern);
    252 }
    253 }  // namespace libspirv
    254