blob: 88f7827de277ffe23954be73c1b270e5c0f9638f [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"
Andrew Woloszynf731cbf2015-10-23 09:53:58 -040040#include "util/bitutils.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}
Andrew Woloszyn4274f932015-10-20 09:12:32 -0400315
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400316char AssemblyContext::peek() const {
317 return text_->str[current_position_.index];
318}
319
320bool AssemblyContext::hasText() const {
321 return text_->length > current_position_.index;
322}
Andrew Woloszyn4274f932015-10-20 09:12:32 -0400323
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400324std::string AssemblyContext::getWord() const {
Andrew Woloszyn4274f932015-10-20 09:12:32 -0400325 uint64_t index = current_position_.index;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400326 while (true) {
327 switch (text_->str[index]) {
328 case '\0':
329 case '\t':
330 case '\v':
331 case '\r':
332 case '\n':
333 case ' ':
334 return std::string(text_->str, text_->str + index);
335 default:
336 index++;
337 }
338 }
339 assert(0 && "Unreachable");
340 return ""; // Make certain compilers happy.
341}
342
343void AssemblyContext::seekForward(uint32_t size) {
344 current_position_.index += size;
345 current_position_.column += size;
346}
347
348spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value,
349 spv_instruction_t *pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400350 spvInstructionAddWord(pInst, value);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400351 return SPV_SUCCESS;
352}
353
354spv_result_t AssemblyContext::binaryEncodeU64(const uint64_t value,
355 spv_instruction_t *pInst) {
David Neto78e677b2015-10-05 13:28:46 -0400356 uint32_t low = uint32_t(0x00000000ffffffff & value);
357 uint32_t high = uint32_t((0xffffffff00000000 & value) >> 32);
David Netoac508b02015-10-09 15:48:09 -0400358 binaryEncodeU32(low, pInst);
359 binaryEncodeU32(high, pInst);
360 return SPV_SUCCESS;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400361}
362
David Neto78e677b2015-10-05 13:28:46 -0400363spv_result_t AssemblyContext::binaryEncodeNumericLiteral(
David Neto51013d12015-10-14 11:31:51 -0400364 const char *val, spv_result_t error_code, const IdType &type,
David Neto78e677b2015-10-05 13:28:46 -0400365 spv_instruction_t *pInst) {
366 const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom;
367 const bool is_floating = libspirv::isScalarFloating(type);
368 const bool is_integer = libspirv::isScalarIntegral(type);
369
370 if (!is_bottom && !is_floating && !is_integer) {
371 return diagnostic(SPV_ERROR_INTERNAL)
372 << "The expected type is not a scalar integer or float type";
373 }
374
375 // If this is bottom, but looks like a float, we should treat it like a
376 // float.
377 const bool looks_like_float = is_bottom && strchr(val, '.');
378
379 // If we explicitly expect a floating-point number, we should handle that
380 // first.
381 if (is_floating || looks_like_float)
David Neto51013d12015-10-14 11:31:51 -0400382 return binaryEncodeFloatingPointLiteral(val, error_code, type, pInst);
David Neto78e677b2015-10-05 13:28:46 -0400383
David Neto51013d12015-10-14 11:31:51 -0400384 return binaryEncodeIntegerLiteral(val, error_code, type, pInst);
David Neto78e677b2015-10-05 13:28:46 -0400385}
386
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400387spv_result_t AssemblyContext::binaryEncodeString(
388 const char *value, spv_instruction_t *pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400389 const size_t length = strlen(value);
390 const size_t wordCount = (length / 4) + 1;
391 const size_t oldWordCount = pInst->words.size();
392 const size_t newWordCount = oldWordCount + wordCount;
393
394 // TODO(dneto): We can just defer this check until later.
395 if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
David Neto78e677b2015-10-05 13:28:46 -0400396 return diagnostic() << "Instruction too long: more than "
397 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words.";
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400398 }
399
David Netob5dc8fc2015-10-06 16:22:00 -0400400 pInst->words.resize(newWordCount);
401
402 // Make sure all the bytes in the last word are 0, in case we only
403 // write a partial word at the end.
404 pInst->words.back() = 0;
405
406 char *dest = (char *)&pInst->words[oldWordCount];
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400407 strncpy(dest, value, length);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400408
409 return SPV_SUCCESS;
410}
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400411
412spv_result_t AssemblyContext::recordTypeDefinition(
413 const spv_instruction_t *pInst) {
414 uint32_t value = pInst->words[1];
415 if (types_.find(value) != types_.end()) {
David Neto78e677b2015-10-05 13:28:46 -0400416 return diagnostic()
417 << "Value " << value << " has already been used to generate a type";
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400418 }
419
420 if (pInst->opcode == OpTypeInt) {
David Neto78e677b2015-10-05 13:28:46 -0400421 if (pInst->words.size() != 4)
422 return diagnostic() << "Invalid OpTypeInt instruction";
423 types_[value] = {pInst->words[2], pInst->words[3] != 0,
424 IdTypeClass::kScalarIntegerType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400425 } else if (pInst->opcode == OpTypeFloat) {
David Neto78e677b2015-10-05 13:28:46 -0400426 if (pInst->words.size() != 3)
427 return diagnostic() << "Invalid OpTypeFloat instruction";
428 types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400429 } else {
David Neto78e677b2015-10-05 13:28:46 -0400430 types_[value] = {0, false, IdTypeClass::kOtherType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400431 }
432 return SPV_SUCCESS;
433}
434
435IdType AssemblyContext::getTypeOfTypeGeneratingValue(uint32_t value) const {
436 auto type = types_.find(value);
437 if (type == types_.end()) {
David Neto78e677b2015-10-05 13:28:46 -0400438 return kUnknownType;
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400439 }
440 return std::get<1>(*type);
441}
442
443IdType AssemblyContext::getTypeOfValueInstruction(uint32_t value) const {
444 auto type_value = value_types_.find(value);
445 if (type_value == value_types_.end()) {
David Neto78e677b2015-10-05 13:28:46 -0400446 return { 0, false, IdTypeClass::kBottom};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400447 }
448 return getTypeOfTypeGeneratingValue(std::get<1>(*type_value));
449}
450
451spv_result_t AssemblyContext::recordTypeIdForValue(uint32_t value,
452 uint32_t type) {
453 bool successfully_inserted = false;
454 std::tie(std::ignore, successfully_inserted) =
455 value_types_.insert(std::make_pair(value, type));
David Neto78e677b2015-10-05 13:28:46 -0400456 if (!successfully_inserted)
457 return diagnostic() << "Value is being defined a second time";
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400458 return SPV_SUCCESS;
459}
460
David Neto78e677b2015-10-05 13:28:46 -0400461spv_result_t AssemblyContext::binaryEncodeFloatingPointLiteral(
David Neto51013d12015-10-14 11:31:51 -0400462 const char *val, spv_result_t error_code, const IdType &type,
David Neto78e677b2015-10-05 13:28:46 -0400463 spv_instruction_t *pInst) {
464 const auto bit_width = assumedBitWidth(type);
465 switch (bit_width) {
466 case 16:
467 return diagnostic(SPV_ERROR_INTERNAL)
468 << "Unsupported yet: 16-bit float constants.";
469 case 32: {
470 float fVal;
David Neto51013d12015-10-14 11:31:51 -0400471 if (auto error = parseNumber(val, error_code, &fVal,
David Neto78e677b2015-10-05 13:28:46 -0400472 "Invalid 32-bit float literal: "))
473 return error;
474 return binaryEncodeU32(BitwiseCast<uint32_t>(fVal), pInst);
475 } break;
476 case 64: {
477 double dVal;
David Neto51013d12015-10-14 11:31:51 -0400478 if (auto error = parseNumber(val, error_code, &dVal,
David Neto78e677b2015-10-05 13:28:46 -0400479 "Invalid 64-bit float literal: "))
480 return error;
481 return binaryEncodeU64(BitwiseCast<uint64_t>(dVal), pInst);
482 } break;
483 default:
484 break;
485 }
486 return diagnostic() << "Unsupported " << bit_width << "-bit float literals";
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400487}
488
David Neto78e677b2015-10-05 13:28:46 -0400489spv_result_t AssemblyContext::binaryEncodeIntegerLiteral(
David Neto51013d12015-10-14 11:31:51 -0400490 const char *val, spv_result_t error_code, const IdType &type,
David Neto78e677b2015-10-05 13:28:46 -0400491 spv_instruction_t *pInst) {
492 const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom;
493 const auto bit_width = assumedBitWidth(type);
494
495 if (bit_width > 64)
496 return diagnostic(SPV_ERROR_INTERNAL) << "Unsupported " << bit_width
497 << "-bit integer literals";
498
499 // Either we are expecting anything or integer.
500 bool is_negative = val[0] == '-';
501 bool can_be_signed = is_bottom || type.isSigned;
502
503 if (is_negative && !can_be_signed) {
504 return diagnostic()
505 << "Cannot put a negative number in an unsigned literal";
506 }
507
508 const bool is_hex = val[0] == '0' && (val[1] == 'x' || val[1] == 'X');
509
510 uint64_t decoded_bits;
511 if (is_negative) {
512 int64_t decoded_signed = 0;
David Neto51013d12015-10-14 11:31:51 -0400513 if (auto error = parseNumber(val, error_code, &decoded_signed,
David Neto78e677b2015-10-05 13:28:46 -0400514 "Invalid signed integer literal: "))
515 return error;
516 if (auto error = checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400517 decoded_signed, error_code, type, is_hex, &decoded_signed))
David Neto78e677b2015-10-05 13:28:46 -0400518 return error;
519 decoded_bits = decoded_signed;
520 } else {
521 // There's no leading minus sign, so parse it as an unsigned integer.
David Neto51013d12015-10-14 11:31:51 -0400522 if (auto error = parseNumber(val, error_code, &decoded_bits,
David Neto78e677b2015-10-05 13:28:46 -0400523 "Invalid unsigned integer literal: "))
524 return error;
525 if (auto error = checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400526 decoded_bits, error_code, type, is_hex, &decoded_bits))
David Neto78e677b2015-10-05 13:28:46 -0400527 return error;
528 }
529 if (bit_width > 32) {
530 return binaryEncodeU64(decoded_bits, pInst);
531 } else {
532 return binaryEncodeU32(uint32_t(decoded_bits), pInst);
533 }
534}
535
536template <typename T>
537spv_result_t AssemblyContext::checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400538 T value, spv_result_t error_code, const IdType &type, bool is_hex,
David Neto78e677b2015-10-05 13:28:46 -0400539 T *updated_value_for_hex) {
540 // The encoded result has three regions of bits that are of interest, from
541 // least to most significant:
542 // - magnitude bits, where the magnitude of the number would be stored if
543 // we were using a signed-magnitude representation.
544 // - an optional sign bit
545 // - overflow bits, up to bit 63 of a 64-bit number
546 // For example:
547 // Type Overflow Sign Magnitude
548 // --------------- -------- ---- ---------
549 // unsigned 8 bit 8-63 n/a 0-7
550 // signed 8 bit 8-63 7 0-6
551 // unsigned 16 bit 16-63 n/a 0-15
552 // signed 16 bit 16-63 15 0-14
553
554 // We'll use masks to define the three regions.
555 // At first we'll assume the number is unsigned.
556 const uint32_t bit_width = assumedBitWidth(type);
557 uint64_t magnitude_mask =
558 (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1);
559 uint64_t sign_mask = 0;
560 uint64_t overflow_mask = ~magnitude_mask;
561
562 if (value < 0 || type.isSigned) {
563 // Accommodate the sign bit.
564 magnitude_mask >>= 1;
565 sign_mask = magnitude_mask + 1;
566 }
567
568 bool failed = false;
569 if (value < 0) {
570 // The top bits must all be 1 for a negative signed value.
571 failed = ((value & overflow_mask) != overflow_mask) ||
572 ((value & sign_mask) != sign_mask);
573 } else {
574 if (is_hex) {
575 // Hex values are a bit special. They decode as unsigned values, but
576 // may represent a negative number. In this case, the overflow bits
577 // should be zero.
578 failed = (value & overflow_mask);
579 } else {
580 // Check overflow in the ordinary case.
581 failed = (value & magnitude_mask) != value;
582 }
583 }
584
585 if (failed) {
David Neto51013d12015-10-14 11:31:51 -0400586 return diagnostic(error_code)
587 << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase
588 << value << " does not fit in a " << std::dec << bit_width << "-bit "
589 << (type.isSigned ? "signed" : "unsigned") << " integer";
David Neto78e677b2015-10-05 13:28:46 -0400590 }
591
592 // Sign extend hex the number.
593 if (is_hex && (value & sign_mask))
594 *updated_value_for_hex = (value | overflow_mask);
595
596 return SPV_SUCCESS;
597}
598} // namespace libspirv