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 "text_handler.h"
     28 
     29 #include <cassert>
     30 #include <cstdlib>
     31 #include <cstring>
     32 #include <tuple>
     33 
     34 #include "assembly_grammar.h"
     35 #include "binary.h"
     36 #include "ext_inst.h"
     37 #include "instruction.h"
     38 #include "opcode.h"
     39 #include "text.h"
     40 #include "util/bitutils.h"
     41 #include "util/hex_float.h"
     42 
     43 namespace {
     44 
     45 using spvutils::BitwiseCast;
     46 using spvutils::FloatProxy;
     47 using spvutils::HexFloat;
     48 
     49 // Advances |text| to the start of the next line and writes the new position to
     50 // |position|.
     51 spv_result_t advanceLine(spv_text text, spv_position position) {
     52   while (true) {
     53     if (position->index >= text->length) return SPV_END_OF_STREAM;
     54     switch (text->str[position->index]) {
     55       case '\0':
     56         return SPV_END_OF_STREAM;
     57       case '\n':
     58         position->column = 0;
     59         position->line++;
     60         position->index++;
     61         return SPV_SUCCESS;
     62       default:
     63         position->column++;
     64         position->index++;
     65         break;
     66     }
     67   }
     68 }
     69 
     70 // Advances |text| to first non white space character and writes the new
     71 // position to |position|.
     72 // If a null terminator is found during the text advance, SPV_END_OF_STREAM is
     73 // returned, SPV_SUCCESS otherwise. No error checking is performed on the
     74 // parameters, its the users responsibility to ensure these are non null.
     75 spv_result_t advance(spv_text text, spv_position position) {
     76   // NOTE: Consume white space, otherwise don't advance.
     77   if (position->index >= text->length) return SPV_END_OF_STREAM;
     78   switch (text->str[position->index]) {
     79     case '\0':
     80       return SPV_END_OF_STREAM;
     81     case ';':
     82       if (spv_result_t error = advanceLine(text, position)) return error;
     83       return advance(text, position);
     84     case ' ':
     85     case '\t':
     86     case '\r':
     87       position->column++;
     88       position->index++;
     89       return advance(text, position);
     90     case '\n':
     91       position->column = 0;
     92       position->line++;
     93       position->index++;
     94       return advance(text, position);
     95     default:
     96       break;
     97   }
     98   return SPV_SUCCESS;
     99 }
    100 
    101 // Fetches the next word from the given text stream starting from the given
    102 // *position. On success, writes the decoded word into *word and updates
    103 // *position to the location past the returned word.
    104 //
    105 // A word ends at the next comment or whitespace.  However, double-quoted
    106 // strings remain intact, and a backslash always escapes the next character.
    107 spv_result_t getWord(spv_text text, spv_position position, std::string* word) {
    108   if (!text->str || !text->length) return SPV_ERROR_INVALID_TEXT;
    109   if (!position) return SPV_ERROR_INVALID_POINTER;
    110 
    111   const size_t start_index = position->index;
    112 
    113   bool quoting = false;
    114   bool escaping = false;
    115 
    116   // NOTE: Assumes first character is not white space!
    117   while (true) {
    118     if (position->index >= text->length) {
    119       word->assign(text->str + start_index, text->str + position->index);
    120       return SPV_SUCCESS;
    121     }
    122     const char ch = text->str[position->index];
    123     if (ch == '\\')
    124       escaping = !escaping;
    125     else {
    126       switch (ch) {
    127         case '"':
    128           if (!escaping) quoting = !quoting;
    129           break;
    130         case ' ':
    131         case ';':
    132         case '\t':
    133         case '\n':
    134         case '\r':
    135           if (escaping || quoting) break;
    136         // Fall through.
    137         case '\0': {  // NOTE: End of word found!
    138           word->assign(text->str + start_index, text->str + position->index);
    139           return SPV_SUCCESS;
    140         }
    141         default:
    142           break;
    143       }
    144       escaping = false;
    145     }
    146 
    147     position->column++;
    148     position->index++;
    149   }
    150 }
    151 
    152 // Returns true if the characters in the text as position represent
    153 // the start of an Opcode.
    154 bool startsWithOp(spv_text text, spv_position position) {
    155   if (text->length < position->index + 3) return false;
    156   char ch0 = text->str[position->index];
    157   char ch1 = text->str[position->index + 1];
    158   char ch2 = text->str[position->index + 2];
    159   return ('O' == ch0 && 'p' == ch1 && ('A' <= ch2 && ch2 <= 'Z'));
    160 }
    161 
    162 }  // anonymous namespace
    163 
    164 namespace libspirv {
    165 
    166 const IdType kUnknownType = {0, false, IdTypeClass::kBottom};
    167 
    168 // TODO(dneto): Reorder AssemblyContext definitions to match declaration order.
    169 
    170 // This represents all of the data that is only valid for the duration of
    171 // a single compilation.
    172 uint32_t AssemblyContext::spvNamedIdAssignOrGet(const char* textValue) {
    173   if (named_ids_.end() == named_ids_.find(textValue)) {
    174     named_ids_[std::string(textValue)] = bound_++;
    175   }
    176   return named_ids_[textValue];
    177 }
    178 uint32_t AssemblyContext::getBound() const { return bound_; }
    179 
    180 spv_result_t AssemblyContext::advance() {
    181   return ::advance(text_, &current_position_);
    182 }
    183 
    184 spv_result_t AssemblyContext::getWord(std::string* word,
    185                                       spv_position next_position) {
    186   *next_position = current_position_;
    187   return ::getWord(text_, next_position, word);
    188 }
    189 
    190 bool AssemblyContext::startsWithOp() {
    191   return ::startsWithOp(text_, &current_position_);
    192 }
    193 
    194 bool AssemblyContext::isStartOfNewInst() {
    195   spv_position_t pos = current_position_;
    196   if (::advance(text_, &pos)) return false;
    197   if (::startsWithOp(text_, &pos)) return true;
    198 
    199   std::string word;
    200   pos = current_position_;
    201   if (::getWord(text_, &pos, &word)) return false;
    202   if ('%' != word.front()) return false;
    203 
    204   if (::advance(text_, &pos)) return false;
    205   if (::getWord(text_, &pos, &word)) return false;
    206   if ("=" != word) return false;
    207 
    208   if (::advance(text_, &pos)) return false;
    209   if (::startsWithOp(text_, &pos)) return true;
    210   return false;
    211 }
    212 
    213 char AssemblyContext::peek() const {
    214   return text_->str[current_position_.index];
    215 }
    216 
    217 bool AssemblyContext::hasText() const {
    218   return text_->length > current_position_.index;
    219 }
    220 
    221 void AssemblyContext::seekForward(uint32_t size) {
    222   current_position_.index += size;
    223   current_position_.column += size;
    224 }
    225 
    226 spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value,
    227                                               spv_instruction_t* pInst) {
    228   pInst->words.insert(pInst->words.end(), value);
    229   return SPV_SUCCESS;
    230 }
    231 
    232 spv_result_t AssemblyContext::binaryEncodeU64(const uint64_t value,
    233                                               spv_instruction_t* pInst) {
    234   uint32_t low = uint32_t(0x00000000ffffffff & value);
    235   uint32_t high = uint32_t((0xffffffff00000000 & value) >> 32);
    236   binaryEncodeU32(low, pInst);
    237   binaryEncodeU32(high, pInst);
    238   return SPV_SUCCESS;
    239 }
    240 
    241 spv_result_t AssemblyContext::binaryEncodeNumericLiteral(
    242     const char* val, spv_result_t error_code, const IdType& type,
    243     spv_instruction_t* pInst) {
    244   const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom;
    245   const bool is_floating = libspirv::isScalarFloating(type);
    246   const bool is_integer = libspirv::isScalarIntegral(type);
    247 
    248   if (!is_bottom && !is_floating && !is_integer) {
    249     return diagnostic(SPV_ERROR_INTERNAL)
    250            << "The expected type is not a scalar integer or float type";
    251   }
    252 
    253   // If this is bottom, but looks like a float, we should treat it like a
    254   // float.
    255   const bool looks_like_float = is_bottom && strchr(val, '.');
    256 
    257   // If we explicitly expect a floating-point number, we should handle that
    258   // first.
    259   if (is_floating || looks_like_float)
    260     return binaryEncodeFloatingPointLiteral(val, error_code, type, pInst);
    261 
    262   return binaryEncodeIntegerLiteral(val, error_code, type, pInst);
    263 }
    264 
    265 spv_result_t AssemblyContext::binaryEncodeString(const char* value,
    266                                                  spv_instruction_t* pInst) {
    267   const size_t length = strlen(value);
    268   const size_t wordCount = (length / 4) + 1;
    269   const size_t oldWordCount = pInst->words.size();
    270   const size_t newWordCount = oldWordCount + wordCount;
    271 
    272   // TODO(dneto): We can just defer this check until later.
    273   if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
    274     return diagnostic() << "Instruction too long: more than "
    275                         << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words.";
    276   }
    277 
    278   pInst->words.resize(newWordCount);
    279 
    280   // Make sure all the bytes in the last word are 0, in case we only
    281   // write a partial word at the end.
    282   pInst->words.back() = 0;
    283 
    284   char* dest = (char*)&pInst->words[oldWordCount];
    285   strncpy(dest, value, length);
    286 
    287   return SPV_SUCCESS;
    288 }
    289 
    290 spv_result_t AssemblyContext::recordTypeDefinition(
    291     const spv_instruction_t* pInst) {
    292   uint32_t value = pInst->words[1];
    293   if (types_.find(value) != types_.end()) {
    294     return diagnostic() << "Value " << value
    295                         << " has already been used to generate a type";
    296   }
    297 
    298   if (pInst->opcode == SpvOpTypeInt) {
    299     if (pInst->words.size() != 4)
    300       return diagnostic() << "Invalid OpTypeInt instruction";
    301     types_[value] = {pInst->words[2], pInst->words[3] != 0,
    302                      IdTypeClass::kScalarIntegerType};
    303   } else if (pInst->opcode == SpvOpTypeFloat) {
    304     if (pInst->words.size() != 3)
    305       return diagnostic() << "Invalid OpTypeFloat instruction";
    306     types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType};
    307   } else {
    308     types_[value] = {0, false, IdTypeClass::kOtherType};
    309   }
    310   return SPV_SUCCESS;
    311 }
    312 
    313 IdType AssemblyContext::getTypeOfTypeGeneratingValue(uint32_t value) const {
    314   auto type = types_.find(value);
    315   if (type == types_.end()) {
    316     return kUnknownType;
    317   }
    318   return std::get<1>(*type);
    319 }
    320 
    321 IdType AssemblyContext::getTypeOfValueInstruction(uint32_t value) const {
    322   auto type_value = value_types_.find(value);
    323   if (type_value == value_types_.end()) {
    324     return {0, false, IdTypeClass::kBottom};
    325   }
    326   return getTypeOfTypeGeneratingValue(std::get<1>(*type_value));
    327 }
    328 
    329 spv_result_t AssemblyContext::recordTypeIdForValue(uint32_t value,
    330                                                    uint32_t type) {
    331   bool successfully_inserted = false;
    332   std::tie(std::ignore, successfully_inserted) =
    333       value_types_.insert(std::make_pair(value, type));
    334   if (!successfully_inserted)
    335     return diagnostic() << "Value is being defined a second time";
    336   return SPV_SUCCESS;
    337 }
    338 
    339 spv_result_t AssemblyContext::recordIdAsExtInstImport(
    340     uint32_t id, spv_ext_inst_type_t type) {
    341   bool successfully_inserted = false;
    342   std::tie(std::ignore, successfully_inserted) =
    343       import_id_to_ext_inst_type_.insert(std::make_pair(id, type));
    344   if (!successfully_inserted)
    345     return diagnostic() << "Import Id is being defined a second time";
    346   return SPV_SUCCESS;
    347 }
    348 
    349 spv_ext_inst_type_t AssemblyContext::getExtInstTypeForId(uint32_t id) const {
    350   auto type = import_id_to_ext_inst_type_.find(id);
    351   if (type == import_id_to_ext_inst_type_.end()) {
    352     return SPV_EXT_INST_TYPE_NONE;
    353   }
    354   return std::get<1>(*type);
    355 }
    356 
    357 spv_result_t AssemblyContext::binaryEncodeFloatingPointLiteral(
    358     const char* val, spv_result_t error_code, const IdType& type,
    359     spv_instruction_t* pInst) {
    360   const auto bit_width = assumedBitWidth(type);
    361   switch (bit_width) {
    362     case 16: {
    363       spvutils::HexFloat<FloatProxy<spvutils::Float16>> hVal(0);
    364       if (auto error = parseNumber(val, error_code, &hVal,
    365                                    "Invalid 16-bit float literal: "))
    366         return error;
    367       // getAsFloat will return the spvutils::Float16 value, and get_value
    368       // will return a uint16_t representing the bits of the float.
    369       // The encoding is therefore correct from the perspective of the SPIR-V
    370       // spec since the top 16 bits will be 0.
    371       return binaryEncodeU32(
    372           static_cast<uint32_t>(hVal.value().getAsFloat().get_value()), pInst);
    373     } break;
    374     case 32: {
    375       spvutils::HexFloat<FloatProxy<float>> fVal(0.0f);
    376       if (auto error = parseNumber(val, error_code, &fVal,
    377                                    "Invalid 32-bit float literal: "))
    378         return error;
    379       return binaryEncodeU32(BitwiseCast<uint32_t>(fVal), pInst);
    380     } break;
    381     case 64: {
    382       spvutils::HexFloat<FloatProxy<double>> dVal(0.0);
    383       if (auto error = parseNumber(val, error_code, &dVal,
    384                                    "Invalid 64-bit float literal: "))
    385         return error;
    386       return binaryEncodeU64(BitwiseCast<uint64_t>(dVal), pInst);
    387     } break;
    388     default:
    389       break;
    390   }
    391   return diagnostic() << "Unsupported " << bit_width << "-bit float literals";
    392 }
    393 
    394 // Returns SPV_SUCCESS if the given value fits within the target scalar
    395 // integral type.  The target type may have an unusual bit width.
    396 // If the value was originally specified as a hexadecimal number, then
    397 // the overflow bits should be zero.  If it was hex and the target type is
    398 // signed, then return the sign-extended value through the
    399 // updated_value_for_hex pointer argument.
    400 // On failure, return the given error code and emit a diagnostic if that error
    401 // code is not SPV_FAILED_MATCH.
    402 template <typename T>
    403 spv_result_t checkRangeAndIfHexThenSignExtend(T value, spv_result_t error_code,
    404                                               const IdType& type, bool is_hex,
    405                                               T* updated_value_for_hex) {
    406   // The encoded result has three regions of bits that are of interest, from
    407   // least to most significant:
    408   //   - magnitude bits, where the magnitude of the number would be stored if
    409   //     we were using a signed-magnitude representation.
    410   //   - an optional sign bit
    411   //   - overflow bits, up to bit 63 of a 64-bit number
    412   // For example:
    413   //   Type                Overflow      Sign       Magnitude
    414   //   ---------------     --------      ----       ---------
    415   //   unsigned 8 bit      8-63          n/a        0-7
    416   //   signed 8 bit        8-63          7          0-6
    417   //   unsigned 16 bit     16-63         n/a        0-15
    418   //   signed 16 bit       16-63         15         0-14
    419 
    420   // We'll use masks to define the three regions.
    421   // At first we'll assume the number is unsigned.
    422   const uint32_t bit_width = assumedBitWidth(type);
    423   uint64_t magnitude_mask =
    424       (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1);
    425   uint64_t sign_mask = 0;
    426   uint64_t overflow_mask = ~magnitude_mask;
    427 
    428   if (value < 0 || type.isSigned) {
    429     // Accommodate the sign bit.
    430     magnitude_mask >>= 1;
    431     sign_mask = magnitude_mask + 1;
    432   }
    433 
    434   bool failed = false;
    435   if (value < 0) {
    436     // The top bits must all be 1 for a negative signed value.
    437     failed = ((value & overflow_mask) != overflow_mask) ||
    438              ((value & sign_mask) != sign_mask);
    439   } else {
    440     if (is_hex) {
    441       // Hex values are a bit special. They decode as unsigned values, but
    442       // may represent a negative number.  In this case, the overflow bits
    443       // should be zero.
    444       failed = (value & overflow_mask) != 0;
    445     } else {
    446       const uint64_t value_as_u64 = static_cast<uint64_t>(value);
    447       // Check overflow in the ordinary case.
    448       failed = (value_as_u64 & magnitude_mask) != value_as_u64;
    449     }
    450   }
    451 
    452   if (failed) {
    453     return error_code;
    454   }
    455 
    456   // Sign extend hex the number.
    457   if (is_hex && (value & sign_mask))
    458     *updated_value_for_hex = (value | overflow_mask);
    459 
    460   return SPV_SUCCESS;
    461 }
    462 
    463 spv_result_t AssemblyContext::binaryEncodeIntegerLiteral(
    464     const char* val, spv_result_t error_code, const IdType& type,
    465     spv_instruction_t* pInst) {
    466   const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom;
    467   const uint32_t bit_width = assumedBitWidth(type);
    468 
    469   if (bit_width > 64)
    470     return diagnostic(SPV_ERROR_INTERNAL) << "Unsupported " << bit_width
    471                                           << "-bit integer literals";
    472 
    473   // Either we are expecting anything or integer.
    474   bool is_negative = val[0] == '-';
    475   bool can_be_signed = is_bottom || type.isSigned;
    476 
    477   if (is_negative && !can_be_signed) {
    478     return diagnostic()
    479            << "Cannot put a negative number in an unsigned literal";
    480   }
    481 
    482   const bool is_hex = val[0] == '0' && (val[1] == 'x' || val[1] == 'X');
    483 
    484   uint64_t decoded_bits;
    485   if (is_negative) {
    486     int64_t decoded_signed = 0;
    487 
    488     if (auto error = parseNumber(val, error_code, &decoded_signed,
    489                                  "Invalid signed integer literal: "))
    490       return error;
    491     if (auto error = checkRangeAndIfHexThenSignExtend(
    492             decoded_signed, error_code, type, is_hex, &decoded_signed)) {
    493       diagnostic(error_code)
    494           << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase
    495           << decoded_signed << " does not fit in a " << std::dec << bit_width
    496           << "-bit " << (type.isSigned ? "signed" : "unsigned") << " integer";
    497       return error;
    498     }
    499     decoded_bits = decoded_signed;
    500   } else {
    501     // There's no leading minus sign, so parse it as an unsigned integer.
    502     if (auto error = parseNumber(val, error_code, &decoded_bits,
    503                                  "Invalid unsigned integer literal: "))
    504       return error;
    505     if (auto error = checkRangeAndIfHexThenSignExtend(
    506             decoded_bits, error_code, type, is_hex, &decoded_bits)) {
    507       diagnostic(error_code)
    508           << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase
    509           << decoded_bits << " does not fit in a " << std::dec << bit_width
    510           << "-bit " << (type.isSigned ? "signed" : "unsigned") << " integer";
    511       return error;
    512     }
    513   }
    514   if (bit_width > 32) {
    515     return binaryEncodeU64(decoded_bits, pInst);
    516   } else {
    517     return binaryEncodeU32(uint32_t(decoded_bits), pInst);
    518   }
    519 }
    520 }  // namespace libspirv
    521