blob: 803d91424177b9af437b542caa94f54b95498c38 [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
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040029#include <cassert>
David Neto78e677b2015-10-05 13:28:46 -040030#include <cstdlib>
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040031#include <cstring>
Andrew Woloszyn537e7762015-09-29 11:28:34 -040032#include <tuple>
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040033
David Netofcc7d582015-10-27 15:31:10 -040034#include "assembly_grammar.h"
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040035#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
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400166} // anonymous namespace
167
168namespace libspirv {
169
David Neto78e677b2015-10-05 13:28:46 -0400170const IdType kUnknownType = {0, false, IdTypeClass::kBottom};
171
David Neto78e677b2015-10-05 13:28:46 -0400172// TODO(dneto): Reorder AssemblyContext definitions to match declaration order.
173
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400174// This represents all of the data that is only valid for the duration of
175// a single compilation.
176uint32_t AssemblyContext::spvNamedIdAssignOrGet(const char *textValue) {
177 if (named_ids_.end() == named_ids_.find(textValue)) {
178 named_ids_[std::string(textValue)] = bound_++;
179 }
180 return named_ids_[textValue];
181}
182uint32_t AssemblyContext::getBound() const { return bound_; }
183
184spv_result_t AssemblyContext::advance() {
185 return ::advance(text_, &current_position_);
186}
187
188spv_result_t AssemblyContext::getWord(std::string &word,
189 spv_position endPosition) {
190 return ::getWord(text_, &current_position_, word, endPosition);
191}
192
193bool AssemblyContext::startsWithOp() {
194 return ::startsWithOp(text_, &current_position_);
195}
196
197bool AssemblyContext::isStartOfNewInst() {
198 spv_position_t nextPosition = current_position_;
199 if (::advance(text_, &nextPosition)) return false;
200 if (::startsWithOp(text_, &nextPosition)) return true;
201
202 std::string word;
203 spv_position_t startPosition = current_position_;
204 if (::getWord(text_, &startPosition, word, &nextPosition)) return false;
205 if ('%' != word.front()) return false;
206
207 if (::advance(text_, &nextPosition)) return false;
208 startPosition = nextPosition;
209 if (::getWord(text_, &startPosition, word, &nextPosition)) return false;
210 if ("=" != word) return false;
211
212 if (::advance(text_, &nextPosition)) return false;
213 startPosition = nextPosition;
214 if (::startsWithOp(text_, &startPosition)) return true;
215 return false;
216}
Andrew Woloszyn4274f932015-10-20 09:12:32 -0400217
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400218char AssemblyContext::peek() const {
219 return text_->str[current_position_.index];
220}
221
222bool AssemblyContext::hasText() const {
223 return text_->length > current_position_.index;
224}
Andrew Woloszyn4274f932015-10-20 09:12:32 -0400225
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400226std::string AssemblyContext::getWord() const {
Andrew Woloszyn4274f932015-10-20 09:12:32 -0400227 uint64_t index = current_position_.index;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400228 while (true) {
229 switch (text_->str[index]) {
230 case '\0':
231 case '\t':
232 case '\v':
233 case '\r':
234 case '\n':
235 case ' ':
236 return std::string(text_->str, text_->str + index);
237 default:
238 index++;
239 }
240 }
241 assert(0 && "Unreachable");
242 return ""; // Make certain compilers happy.
243}
244
245void AssemblyContext::seekForward(uint32_t size) {
246 current_position_.index += size;
247 current_position_.column += size;
248}
249
250spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value,
251 spv_instruction_t *pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400252 spvInstructionAddWord(pInst, value);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400253 return SPV_SUCCESS;
254}
255
256spv_result_t AssemblyContext::binaryEncodeU64(const uint64_t value,
257 spv_instruction_t *pInst) {
David Neto78e677b2015-10-05 13:28:46 -0400258 uint32_t low = uint32_t(0x00000000ffffffff & value);
259 uint32_t high = uint32_t((0xffffffff00000000 & value) >> 32);
David Netoac508b02015-10-09 15:48:09 -0400260 binaryEncodeU32(low, pInst);
261 binaryEncodeU32(high, pInst);
262 return SPV_SUCCESS;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400263}
264
David Neto78e677b2015-10-05 13:28:46 -0400265spv_result_t AssemblyContext::binaryEncodeNumericLiteral(
David Neto51013d12015-10-14 11:31:51 -0400266 const char *val, spv_result_t error_code, const IdType &type,
David Neto78e677b2015-10-05 13:28:46 -0400267 spv_instruction_t *pInst) {
268 const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom;
269 const bool is_floating = libspirv::isScalarFloating(type);
270 const bool is_integer = libspirv::isScalarIntegral(type);
271
272 if (!is_bottom && !is_floating && !is_integer) {
273 return diagnostic(SPV_ERROR_INTERNAL)
274 << "The expected type is not a scalar integer or float type";
275 }
276
277 // If this is bottom, but looks like a float, we should treat it like a
278 // float.
279 const bool looks_like_float = is_bottom && strchr(val, '.');
280
281 // If we explicitly expect a floating-point number, we should handle that
282 // first.
283 if (is_floating || looks_like_float)
David Neto51013d12015-10-14 11:31:51 -0400284 return binaryEncodeFloatingPointLiteral(val, error_code, type, pInst);
David Neto78e677b2015-10-05 13:28:46 -0400285
David Neto51013d12015-10-14 11:31:51 -0400286 return binaryEncodeIntegerLiteral(val, error_code, type, pInst);
David Neto78e677b2015-10-05 13:28:46 -0400287}
288
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400289spv_result_t AssemblyContext::binaryEncodeString(
290 const char *value, spv_instruction_t *pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400291 const size_t length = strlen(value);
292 const size_t wordCount = (length / 4) + 1;
293 const size_t oldWordCount = pInst->words.size();
294 const size_t newWordCount = oldWordCount + wordCount;
295
296 // TODO(dneto): We can just defer this check until later.
297 if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
David Neto78e677b2015-10-05 13:28:46 -0400298 return diagnostic() << "Instruction too long: more than "
299 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words.";
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400300 }
301
David Netob5dc8fc2015-10-06 16:22:00 -0400302 pInst->words.resize(newWordCount);
303
304 // Make sure all the bytes in the last word are 0, in case we only
305 // write a partial word at the end.
306 pInst->words.back() = 0;
307
308 char *dest = (char *)&pInst->words[oldWordCount];
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400309 strncpy(dest, value, length);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400310
311 return SPV_SUCCESS;
312}
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400313
314spv_result_t AssemblyContext::recordTypeDefinition(
315 const spv_instruction_t *pInst) {
316 uint32_t value = pInst->words[1];
317 if (types_.find(value) != types_.end()) {
David Neto78e677b2015-10-05 13:28:46 -0400318 return diagnostic()
319 << "Value " << value << " has already been used to generate a type";
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400320 }
321
322 if (pInst->opcode == OpTypeInt) {
David Neto78e677b2015-10-05 13:28:46 -0400323 if (pInst->words.size() != 4)
324 return diagnostic() << "Invalid OpTypeInt instruction";
325 types_[value] = {pInst->words[2], pInst->words[3] != 0,
326 IdTypeClass::kScalarIntegerType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400327 } else if (pInst->opcode == OpTypeFloat) {
David Neto78e677b2015-10-05 13:28:46 -0400328 if (pInst->words.size() != 3)
329 return diagnostic() << "Invalid OpTypeFloat instruction";
330 types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400331 } else {
David Neto78e677b2015-10-05 13:28:46 -0400332 types_[value] = {0, false, IdTypeClass::kOtherType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400333 }
334 return SPV_SUCCESS;
335}
336
337IdType AssemblyContext::getTypeOfTypeGeneratingValue(uint32_t value) const {
338 auto type = types_.find(value);
339 if (type == types_.end()) {
David Neto78e677b2015-10-05 13:28:46 -0400340 return kUnknownType;
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400341 }
342 return std::get<1>(*type);
343}
344
345IdType AssemblyContext::getTypeOfValueInstruction(uint32_t value) const {
346 auto type_value = value_types_.find(value);
347 if (type_value == value_types_.end()) {
David Neto78e677b2015-10-05 13:28:46 -0400348 return { 0, false, IdTypeClass::kBottom};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400349 }
350 return getTypeOfTypeGeneratingValue(std::get<1>(*type_value));
351}
352
353spv_result_t AssemblyContext::recordTypeIdForValue(uint32_t value,
354 uint32_t type) {
355 bool successfully_inserted = false;
356 std::tie(std::ignore, successfully_inserted) =
357 value_types_.insert(std::make_pair(value, type));
David Neto78e677b2015-10-05 13:28:46 -0400358 if (!successfully_inserted)
359 return diagnostic() << "Value is being defined a second time";
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400360 return SPV_SUCCESS;
361}
362
David Neto78e677b2015-10-05 13:28:46 -0400363spv_result_t AssemblyContext::binaryEncodeFloatingPointLiteral(
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 auto bit_width = assumedBitWidth(type);
367 switch (bit_width) {
368 case 16:
369 return diagnostic(SPV_ERROR_INTERNAL)
370 << "Unsupported yet: 16-bit float constants.";
371 case 32: {
372 float fVal;
David Neto51013d12015-10-14 11:31:51 -0400373 if (auto error = parseNumber(val, error_code, &fVal,
David Neto78e677b2015-10-05 13:28:46 -0400374 "Invalid 32-bit float literal: "))
375 return error;
376 return binaryEncodeU32(BitwiseCast<uint32_t>(fVal), pInst);
377 } break;
378 case 64: {
379 double dVal;
David Neto51013d12015-10-14 11:31:51 -0400380 if (auto error = parseNumber(val, error_code, &dVal,
David Neto78e677b2015-10-05 13:28:46 -0400381 "Invalid 64-bit float literal: "))
382 return error;
383 return binaryEncodeU64(BitwiseCast<uint64_t>(dVal), pInst);
384 } break;
385 default:
386 break;
387 }
388 return diagnostic() << "Unsupported " << bit_width << "-bit float literals";
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400389}
390
David Neto78e677b2015-10-05 13:28:46 -0400391spv_result_t AssemblyContext::binaryEncodeIntegerLiteral(
David Neto51013d12015-10-14 11:31:51 -0400392 const char *val, spv_result_t error_code, const IdType &type,
David Neto78e677b2015-10-05 13:28:46 -0400393 spv_instruction_t *pInst) {
394 const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom;
395 const auto bit_width = assumedBitWidth(type);
396
397 if (bit_width > 64)
398 return diagnostic(SPV_ERROR_INTERNAL) << "Unsupported " << bit_width
399 << "-bit integer literals";
400
401 // Either we are expecting anything or integer.
402 bool is_negative = val[0] == '-';
403 bool can_be_signed = is_bottom || type.isSigned;
404
405 if (is_negative && !can_be_signed) {
406 return diagnostic()
407 << "Cannot put a negative number in an unsigned literal";
408 }
409
410 const bool is_hex = val[0] == '0' && (val[1] == 'x' || val[1] == 'X');
411
412 uint64_t decoded_bits;
413 if (is_negative) {
414 int64_t decoded_signed = 0;
David Neto51013d12015-10-14 11:31:51 -0400415 if (auto error = parseNumber(val, error_code, &decoded_signed,
David Neto78e677b2015-10-05 13:28:46 -0400416 "Invalid signed integer literal: "))
417 return error;
418 if (auto error = checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400419 decoded_signed, error_code, type, is_hex, &decoded_signed))
David Neto78e677b2015-10-05 13:28:46 -0400420 return error;
421 decoded_bits = decoded_signed;
422 } else {
423 // There's no leading minus sign, so parse it as an unsigned integer.
David Neto51013d12015-10-14 11:31:51 -0400424 if (auto error = parseNumber(val, error_code, &decoded_bits,
David Neto78e677b2015-10-05 13:28:46 -0400425 "Invalid unsigned integer literal: "))
426 return error;
427 if (auto error = checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400428 decoded_bits, error_code, type, is_hex, &decoded_bits))
David Neto78e677b2015-10-05 13:28:46 -0400429 return error;
430 }
431 if (bit_width > 32) {
432 return binaryEncodeU64(decoded_bits, pInst);
433 } else {
434 return binaryEncodeU32(uint32_t(decoded_bits), pInst);
435 }
436}
437
438template <typename T>
439spv_result_t AssemblyContext::checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400440 T value, spv_result_t error_code, const IdType &type, bool is_hex,
David Neto78e677b2015-10-05 13:28:46 -0400441 T *updated_value_for_hex) {
442 // The encoded result has three regions of bits that are of interest, from
443 // least to most significant:
444 // - magnitude bits, where the magnitude of the number would be stored if
445 // we were using a signed-magnitude representation.
446 // - an optional sign bit
447 // - overflow bits, up to bit 63 of a 64-bit number
448 // For example:
449 // Type Overflow Sign Magnitude
450 // --------------- -------- ---- ---------
451 // unsigned 8 bit 8-63 n/a 0-7
452 // signed 8 bit 8-63 7 0-6
453 // unsigned 16 bit 16-63 n/a 0-15
454 // signed 16 bit 16-63 15 0-14
455
456 // We'll use masks to define the three regions.
457 // At first we'll assume the number is unsigned.
458 const uint32_t bit_width = assumedBitWidth(type);
459 uint64_t magnitude_mask =
460 (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1);
461 uint64_t sign_mask = 0;
462 uint64_t overflow_mask = ~magnitude_mask;
463
464 if (value < 0 || type.isSigned) {
465 // Accommodate the sign bit.
466 magnitude_mask >>= 1;
467 sign_mask = magnitude_mask + 1;
468 }
469
470 bool failed = false;
471 if (value < 0) {
472 // The top bits must all be 1 for a negative signed value.
473 failed = ((value & overflow_mask) != overflow_mask) ||
474 ((value & sign_mask) != sign_mask);
475 } else {
476 if (is_hex) {
477 // Hex values are a bit special. They decode as unsigned values, but
478 // may represent a negative number. In this case, the overflow bits
479 // should be zero.
480 failed = (value & overflow_mask);
481 } else {
482 // Check overflow in the ordinary case.
483 failed = (value & magnitude_mask) != value;
484 }
485 }
486
487 if (failed) {
David Neto51013d12015-10-14 11:31:51 -0400488 return diagnostic(error_code)
489 << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase
490 << value << " does not fit in a " << std::dec << bit_width << "-bit "
491 << (type.isSigned ? "signed" : "unsigned") << " integer";
David Neto78e677b2015-10-05 13:28:46 -0400492 }
493
494 // Sign extend hex the number.
495 if (is_hex && (value & sign_mask))
496 *updated_value_for_hex = (value | overflow_mask);
497
498 return SPV_SUCCESS;
499}
500} // namespace libspirv