blob: 21308aec43c2f77f124d991d1790044e8bebd93f [file] [log] [blame]
Andrew Woloszyn71fc0552015-09-24 10:26:51 -04001// Copyright (c) 2015 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 <algorithm>
30#include <cassert>
David Neto78e677b2015-10-05 13:28:46 -040031#include <cstdlib>
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040032#include <cstring>
Andrew Woloszyn537e7762015-09-29 11:28:34 -040033#include <tuple>
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040034
35#include "binary.h"
36#include "ext_inst.h"
David Netob5dc8fc2015-10-06 16:22:00 -040037#include "instruction.h"
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040038#include "opcode.h"
39#include "text.h"
Lei Zhang6483bd72015-10-14 17:02:39 -040040#include "util/bitwisecast.h"
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040041
42namespace {
43
David Neto78e677b2015-10-05 13:28:46 -040044using spvutils::BitwiseCast;
45
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040046/// @brief Advance text to the start of the next line
47///
48/// @param[in] text to be parsed
49/// @param[in,out] position position text has been advanced to
50///
51/// @return result code
52spv_result_t advanceLine(spv_text text, spv_position position) {
53 while (true) {
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/// @brief Advance text to first non white space character
71/// If a null terminator is found during the text advance SPV_END_OF_STREAM is
72/// returned, SPV_SUCCESS otherwise. No error checking is performed on the
73/// parameters, its the users responsibility to ensure these are non null.
74///
75/// @param[in] text to be parsed
76/// @param[in,out] position text has been advanced to
77///
78/// @return result code
79spv_result_t advance(spv_text text, spv_position position) {
80 // NOTE: Consume white space, otherwise don't advance.
81 switch (text->str[position->index]) {
82 case '\0':
83 return SPV_END_OF_STREAM;
84 case ';':
85 if (spv_result_t error = advanceLine(text, position)) return error;
86 return advance(text, position);
87 case ' ':
88 case '\t':
89 position->column++;
90 position->index++;
91 return advance(text, position);
92 case '\n':
93 position->column = 0;
94 position->line++;
95 position->index++;
96 return advance(text, position);
97 default:
98 break;
99 }
100 return SPV_SUCCESS;
101}
102
103/// @brief Fetch the next word from the text stream.
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///
108/// @param[in] text stream to read from
109/// @param[in] position current position in text stream
110/// @param[out] word returned word
111/// @param[out] endPosition one past the end of the returned word
112///
113/// @return result code
114spv_result_t getWord(spv_text text, spv_position position, std::string &word,
115 spv_position endPosition) {
116 if (!text->str || !text->length) return SPV_ERROR_INVALID_TEXT;
117 if (!position || !endPosition) return SPV_ERROR_INVALID_POINTER;
118
119 *endPosition = *position;
120
121 bool quoting = false;
122 bool escaping = false;
123
124 // NOTE: Assumes first character is not white space!
125 while (true) {
126 const char ch = text->str[endPosition->index];
127 if (ch == '\\')
128 escaping = !escaping;
129 else {
130 switch (ch) {
131 case '"':
132 if (!escaping) quoting = !quoting;
133 break;
134 case ' ':
135 case ';':
136 case '\t':
137 case '\n':
138 if (escaping || quoting) break;
139 // Fall through.
140 case '\0': { // NOTE: End of word found!
141 word.assign(text->str + position->index,
142 (size_t)(endPosition->index - position->index));
143 return SPV_SUCCESS;
144 }
145 default:
146 break;
147 }
148 escaping = false;
149 }
150
151 endPosition->column++;
152 endPosition->index++;
153 }
154}
155
156// Returns true if the characters in the text as position represent
157// the start of an Opcode.
158bool startsWithOp(spv_text text, spv_position position) {
159 if (text->length < position->index + 3) return false;
160 char ch0 = text->str[position->index];
161 char ch1 = text->str[position->index + 1];
162 char ch2 = text->str[position->index + 2];
163 return ('O' == ch0 && 'p' == ch1 && ('A' <= ch2 && ch2 <= 'Z'));
164}
165
166/// @brief Parses a mask expression string for the given operand type.
167///
168/// A mask expression is a sequence of one or more terms separated by '|',
169/// where each term a named enum value for the given type. No whitespace
170/// is permitted.
171///
172/// On success, the value is written to pValue.
173///
174/// @param[in] operandTable operand lookup table
175/// @param[in] type of the operand
176/// @param[in] textValue word of text to be parsed
177/// @param[out] pValue where the resulting value is written
178///
179/// @return result code
180spv_result_t spvTextParseMaskOperand(const spv_operand_table operandTable,
181 const spv_operand_type_t type,
182 const char *textValue, uint32_t *pValue) {
183 if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT;
184 size_t text_length = strlen(textValue);
185 if (text_length == 0) return SPV_ERROR_INVALID_TEXT;
186 const char *text_end = textValue + text_length;
187
188 // We only support mask expressions in ASCII, so the separator value is a
189 // char.
190 const char separator = '|';
191
192 // Accumulate the result by interpreting one word at a time, scanning
193 // from left to right.
194 uint32_t value = 0;
195 const char *begin = textValue; // The left end of the current word.
196 const char *end = nullptr; // One character past the end of the current word.
197 do {
198 end = std::find(begin, text_end, separator);
199
200 spv_operand_desc entry = nullptr;
201 if (spvOperandTableNameLookup(operandTable, type, begin, end - begin,
202 &entry)) {
203 return SPV_ERROR_INVALID_TEXT;
204 }
205 value |= entry->value;
206
207 // Advance to the next word by skipping over the separator.
208 begin = end + 1;
209 } while (end != text_end);
210
211 *pValue = value;
212 return SPV_SUCCESS;
213}
214
215} // anonymous namespace
216
217namespace libspirv {
218
David Neto78e677b2015-10-05 13:28:46 -0400219const IdType kUnknownType = {0, false, IdTypeClass::kBottom};
220
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400221bool AssemblyGrammar::isValid() const {
222 return operandTable_ && opcodeTable_ && extInstTable_;
223}
224
225spv_result_t AssemblyGrammar::lookupOpcode(const char *name,
226 spv_opcode_desc *desc) const {
227 return spvOpcodeTableNameLookup(opcodeTable_, name, desc);
228}
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400229
230spv_result_t AssemblyGrammar::lookupOpcode(Op opcode,
231 spv_opcode_desc *desc) const {
232 return spvOpcodeTableValueLookup(opcodeTable_, opcode, desc);
233}
234
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400235spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type,
236 const char *name, size_t name_len,
237 spv_operand_desc *desc) const {
238 return spvOperandTableNameLookup(operandTable_, type, name, name_len, desc);
239}
240
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400241spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type,
242 uint32_t operand,
243 spv_operand_desc *desc) const {
244 return spvOperandTableValueLookup(operandTable_, type, operand, desc);
245}
246
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400247spv_result_t AssemblyGrammar::parseMaskOperand(const spv_operand_type_t type,
248 const char *textValue,
249 uint32_t *pValue) const {
250 return spvTextParseMaskOperand(operandTable_, type, textValue, pValue);
251}
252spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type,
253 const char *textValue,
254 spv_ext_inst_desc *extInst) const {
255 return spvExtInstTableNameLookup(extInstTable_, type, textValue, extInst);
256}
257
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400258spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type,
259 uint32_t firstWord,
260 spv_ext_inst_desc *extInst) const {
261 return spvExtInstTableValueLookup(extInstTable_, type, firstWord, extInst);
262}
263
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400264void AssemblyGrammar::prependOperandTypesForMask(
265 const spv_operand_type_t type, const uint32_t mask,
266 spv_operand_pattern_t *pattern) const {
267 spvPrependOperandTypesForMask(operandTable_, type, mask, pattern);
268}
269
David Neto78e677b2015-10-05 13:28:46 -0400270// TODO(dneto): Reorder AssemblyContext definitions to match declaration order.
271
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400272// This represents all of the data that is only valid for the duration of
273// a single compilation.
274uint32_t AssemblyContext::spvNamedIdAssignOrGet(const char *textValue) {
275 if (named_ids_.end() == named_ids_.find(textValue)) {
276 named_ids_[std::string(textValue)] = bound_++;
277 }
278 return named_ids_[textValue];
279}
280uint32_t AssemblyContext::getBound() const { return bound_; }
281
282spv_result_t AssemblyContext::advance() {
283 return ::advance(text_, &current_position_);
284}
285
286spv_result_t AssemblyContext::getWord(std::string &word,
287 spv_position endPosition) {
288 return ::getWord(text_, &current_position_, word, endPosition);
289}
290
291bool AssemblyContext::startsWithOp() {
292 return ::startsWithOp(text_, &current_position_);
293}
294
295bool AssemblyContext::isStartOfNewInst() {
296 spv_position_t nextPosition = current_position_;
297 if (::advance(text_, &nextPosition)) return false;
298 if (::startsWithOp(text_, &nextPosition)) return true;
299
300 std::string word;
301 spv_position_t startPosition = current_position_;
302 if (::getWord(text_, &startPosition, word, &nextPosition)) return false;
303 if ('%' != word.front()) return false;
304
305 if (::advance(text_, &nextPosition)) return false;
306 startPosition = nextPosition;
307 if (::getWord(text_, &startPosition, word, &nextPosition)) return false;
308 if ("=" != word) return false;
309
310 if (::advance(text_, &nextPosition)) return false;
311 startPosition = nextPosition;
312 if (::startsWithOp(text_, &startPosition)) return true;
313 return false;
314}
315char AssemblyContext::peek() const {
316 return text_->str[current_position_.index];
317}
318
319bool AssemblyContext::hasText() const {
320 return text_->length > current_position_.index;
321}
322std::string AssemblyContext::getWord() const {
323 size_t index = current_position_.index;
324 while (true) {
325 switch (text_->str[index]) {
326 case '\0':
327 case '\t':
328 case '\v':
329 case '\r':
330 case '\n':
331 case ' ':
332 return std::string(text_->str, text_->str + index);
333 default:
334 index++;
335 }
336 }
337 assert(0 && "Unreachable");
338 return ""; // Make certain compilers happy.
339}
340
341void AssemblyContext::seekForward(uint32_t size) {
342 current_position_.index += size;
343 current_position_.column += size;
344}
345
346spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value,
347 spv_instruction_t *pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400348 spvInstructionAddWord(pInst, value);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400349 return SPV_SUCCESS;
350}
351
352spv_result_t AssemblyContext::binaryEncodeU64(const uint64_t value,
353 spv_instruction_t *pInst) {
David Neto78e677b2015-10-05 13:28:46 -0400354 uint32_t low = uint32_t(0x00000000ffffffff & value);
355 uint32_t high = uint32_t((0xffffffff00000000 & value) >> 32);
David Netoac508b02015-10-09 15:48:09 -0400356 binaryEncodeU32(low, pInst);
357 binaryEncodeU32(high, pInst);
358 return SPV_SUCCESS;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400359}
360
David Neto78e677b2015-10-05 13:28:46 -0400361spv_result_t AssemblyContext::binaryEncodeNumericLiteral(
David Neto51013d12015-10-14 11:31:51 -0400362 const char *val, spv_result_t error_code, const IdType &type,
David Neto78e677b2015-10-05 13:28:46 -0400363 spv_instruction_t *pInst) {
364 const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom;
365 const bool is_floating = libspirv::isScalarFloating(type);
366 const bool is_integer = libspirv::isScalarIntegral(type);
367
368 if (!is_bottom && !is_floating && !is_integer) {
369 return diagnostic(SPV_ERROR_INTERNAL)
370 << "The expected type is not a scalar integer or float type";
371 }
372
373 // If this is bottom, but looks like a float, we should treat it like a
374 // float.
375 const bool looks_like_float = is_bottom && strchr(val, '.');
376
377 // If we explicitly expect a floating-point number, we should handle that
378 // first.
379 if (is_floating || looks_like_float)
David Neto51013d12015-10-14 11:31:51 -0400380 return binaryEncodeFloatingPointLiteral(val, error_code, type, pInst);
David Neto78e677b2015-10-05 13:28:46 -0400381
David Neto51013d12015-10-14 11:31:51 -0400382 return binaryEncodeIntegerLiteral(val, error_code, type, pInst);
David Neto78e677b2015-10-05 13:28:46 -0400383}
384
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400385spv_result_t AssemblyContext::binaryEncodeString(
386 const char *value, spv_instruction_t *pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400387 const size_t length = strlen(value);
388 const size_t wordCount = (length / 4) + 1;
389 const size_t oldWordCount = pInst->words.size();
390 const size_t newWordCount = oldWordCount + wordCount;
391
392 // TODO(dneto): We can just defer this check until later.
393 if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
David Neto78e677b2015-10-05 13:28:46 -0400394 return diagnostic() << "Instruction too long: more than "
395 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words.";
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400396 }
397
David Netob5dc8fc2015-10-06 16:22:00 -0400398 pInst->words.resize(newWordCount);
399
400 // Make sure all the bytes in the last word are 0, in case we only
401 // write a partial word at the end.
402 pInst->words.back() = 0;
403
404 char *dest = (char *)&pInst->words[oldWordCount];
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400405 strncpy(dest, value, length);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400406
407 return SPV_SUCCESS;
408}
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400409
410spv_result_t AssemblyContext::recordTypeDefinition(
411 const spv_instruction_t *pInst) {
412 uint32_t value = pInst->words[1];
413 if (types_.find(value) != types_.end()) {
David Neto78e677b2015-10-05 13:28:46 -0400414 return diagnostic()
415 << "Value " << value << " has already been used to generate a type";
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400416 }
417
418 if (pInst->opcode == OpTypeInt) {
David Neto78e677b2015-10-05 13:28:46 -0400419 if (pInst->words.size() != 4)
420 return diagnostic() << "Invalid OpTypeInt instruction";
421 types_[value] = {pInst->words[2], pInst->words[3] != 0,
422 IdTypeClass::kScalarIntegerType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400423 } else if (pInst->opcode == OpTypeFloat) {
David Neto78e677b2015-10-05 13:28:46 -0400424 if (pInst->words.size() != 3)
425 return diagnostic() << "Invalid OpTypeFloat instruction";
426 types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400427 } else {
David Neto78e677b2015-10-05 13:28:46 -0400428 types_[value] = {0, false, IdTypeClass::kOtherType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400429 }
430 return SPV_SUCCESS;
431}
432
433IdType AssemblyContext::getTypeOfTypeGeneratingValue(uint32_t value) const {
434 auto type = types_.find(value);
435 if (type == types_.end()) {
David Neto78e677b2015-10-05 13:28:46 -0400436 return kUnknownType;
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400437 }
438 return std::get<1>(*type);
439}
440
441IdType AssemblyContext::getTypeOfValueInstruction(uint32_t value) const {
442 auto type_value = value_types_.find(value);
443 if (type_value == value_types_.end()) {
David Neto78e677b2015-10-05 13:28:46 -0400444 return { 0, false, IdTypeClass::kBottom};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400445 }
446 return getTypeOfTypeGeneratingValue(std::get<1>(*type_value));
447}
448
449spv_result_t AssemblyContext::recordTypeIdForValue(uint32_t value,
450 uint32_t type) {
451 bool successfully_inserted = false;
452 std::tie(std::ignore, successfully_inserted) =
453 value_types_.insert(std::make_pair(value, type));
David Neto78e677b2015-10-05 13:28:46 -0400454 if (!successfully_inserted)
455 return diagnostic() << "Value is being defined a second time";
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400456 return SPV_SUCCESS;
457}
458
David Neto78e677b2015-10-05 13:28:46 -0400459spv_result_t AssemblyContext::binaryEncodeFloatingPointLiteral(
David Neto51013d12015-10-14 11:31:51 -0400460 const char *val, spv_result_t error_code, const IdType &type,
David Neto78e677b2015-10-05 13:28:46 -0400461 spv_instruction_t *pInst) {
462 const auto bit_width = assumedBitWidth(type);
463 switch (bit_width) {
464 case 16:
465 return diagnostic(SPV_ERROR_INTERNAL)
466 << "Unsupported yet: 16-bit float constants.";
467 case 32: {
468 float fVal;
David Neto51013d12015-10-14 11:31:51 -0400469 if (auto error = parseNumber(val, error_code, &fVal,
David Neto78e677b2015-10-05 13:28:46 -0400470 "Invalid 32-bit float literal: "))
471 return error;
472 return binaryEncodeU32(BitwiseCast<uint32_t>(fVal), pInst);
473 } break;
474 case 64: {
475 double dVal;
David Neto51013d12015-10-14 11:31:51 -0400476 if (auto error = parseNumber(val, error_code, &dVal,
David Neto78e677b2015-10-05 13:28:46 -0400477 "Invalid 64-bit float literal: "))
478 return error;
479 return binaryEncodeU64(BitwiseCast<uint64_t>(dVal), pInst);
480 } break;
481 default:
482 break;
483 }
484 return diagnostic() << "Unsupported " << bit_width << "-bit float literals";
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400485}
486
David Neto78e677b2015-10-05 13:28:46 -0400487spv_result_t AssemblyContext::binaryEncodeIntegerLiteral(
David Neto51013d12015-10-14 11:31:51 -0400488 const char *val, spv_result_t error_code, const IdType &type,
David Neto78e677b2015-10-05 13:28:46 -0400489 spv_instruction_t *pInst) {
490 const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom;
491 const auto bit_width = assumedBitWidth(type);
492
493 if (bit_width > 64)
494 return diagnostic(SPV_ERROR_INTERNAL) << "Unsupported " << bit_width
495 << "-bit integer literals";
496
497 // Either we are expecting anything or integer.
498 bool is_negative = val[0] == '-';
499 bool can_be_signed = is_bottom || type.isSigned;
500
501 if (is_negative && !can_be_signed) {
502 return diagnostic()
503 << "Cannot put a negative number in an unsigned literal";
504 }
505
506 const bool is_hex = val[0] == '0' && (val[1] == 'x' || val[1] == 'X');
507
508 uint64_t decoded_bits;
509 if (is_negative) {
510 int64_t decoded_signed = 0;
David Neto51013d12015-10-14 11:31:51 -0400511 if (auto error = parseNumber(val, error_code, &decoded_signed,
David Neto78e677b2015-10-05 13:28:46 -0400512 "Invalid signed integer literal: "))
513 return error;
514 if (auto error = checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400515 decoded_signed, error_code, type, is_hex, &decoded_signed))
David Neto78e677b2015-10-05 13:28:46 -0400516 return error;
517 decoded_bits = decoded_signed;
518 } else {
519 // There's no leading minus sign, so parse it as an unsigned integer.
David Neto51013d12015-10-14 11:31:51 -0400520 if (auto error = parseNumber(val, error_code, &decoded_bits,
David Neto78e677b2015-10-05 13:28:46 -0400521 "Invalid unsigned integer literal: "))
522 return error;
523 if (auto error = checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400524 decoded_bits, error_code, type, is_hex, &decoded_bits))
David Neto78e677b2015-10-05 13:28:46 -0400525 return error;
526 }
527 if (bit_width > 32) {
528 return binaryEncodeU64(decoded_bits, pInst);
529 } else {
530 return binaryEncodeU32(uint32_t(decoded_bits), pInst);
531 }
532}
533
534template <typename T>
535spv_result_t AssemblyContext::checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400536 T value, spv_result_t error_code, const IdType &type, bool is_hex,
David Neto78e677b2015-10-05 13:28:46 -0400537 T *updated_value_for_hex) {
538 // The encoded result has three regions of bits that are of interest, from
539 // least to most significant:
540 // - magnitude bits, where the magnitude of the number would be stored if
541 // we were using a signed-magnitude representation.
542 // - an optional sign bit
543 // - overflow bits, up to bit 63 of a 64-bit number
544 // For example:
545 // Type Overflow Sign Magnitude
546 // --------------- -------- ---- ---------
547 // unsigned 8 bit 8-63 n/a 0-7
548 // signed 8 bit 8-63 7 0-6
549 // unsigned 16 bit 16-63 n/a 0-15
550 // signed 16 bit 16-63 15 0-14
551
552 // We'll use masks to define the three regions.
553 // At first we'll assume the number is unsigned.
554 const uint32_t bit_width = assumedBitWidth(type);
555 uint64_t magnitude_mask =
556 (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1);
557 uint64_t sign_mask = 0;
558 uint64_t overflow_mask = ~magnitude_mask;
559
560 if (value < 0 || type.isSigned) {
561 // Accommodate the sign bit.
562 magnitude_mask >>= 1;
563 sign_mask = magnitude_mask + 1;
564 }
565
566 bool failed = false;
567 if (value < 0) {
568 // The top bits must all be 1 for a negative signed value.
569 failed = ((value & overflow_mask) != overflow_mask) ||
570 ((value & sign_mask) != sign_mask);
571 } else {
572 if (is_hex) {
573 // Hex values are a bit special. They decode as unsigned values, but
574 // may represent a negative number. In this case, the overflow bits
575 // should be zero.
576 failed = (value & overflow_mask);
577 } else {
578 // Check overflow in the ordinary case.
579 failed = (value & magnitude_mask) != value;
580 }
581 }
582
583 if (failed) {
David Neto51013d12015-10-14 11:31:51 -0400584 return diagnostic(error_code)
585 << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase
586 << value << " does not fit in a " << std::dec << bit_width << "-bit "
587 << (type.isSigned ? "signed" : "unsigned") << " integer";
David Neto78e677b2015-10-05 13:28:46 -0400588 }
589
590 // Sign extend hex the number.
591 if (is_hex && (value & sign_mask))
592 *updated_value_for_hex = (value | overflow_mask);
593
594 return SPV_SUCCESS;
595}
596} // namespace libspirv