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 "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