blob: b46fa01667bb7f6a846426617778b232f8b82706 [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.
Lei Zhang8f6ba142015-11-06 15:09:04 -050081 if (position->index >= text->length) return SPV_END_OF_STREAM;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040082 switch (text->str[position->index]) {
83 case '\0':
84 return SPV_END_OF_STREAM;
85 case ';':
86 if (spv_result_t error = advanceLine(text, position)) return error;
87 return advance(text, position);
88 case ' ':
89 case '\t':
90 position->column++;
91 position->index++;
92 return advance(text, position);
93 case '\n':
94 position->column = 0;
95 position->line++;
96 position->index++;
97 return advance(text, position);
98 default:
99 break;
100 }
101 return SPV_SUCCESS;
102}
103
104/// @brief Fetch the next word from the text stream.
105///
106/// A word ends at the next comment or whitespace. However, double-quoted
107/// strings remain intact, and a backslash always escapes the next character.
108///
109/// @param[in] text stream to read from
110/// @param[in] position current position in text stream
111/// @param[out] word returned word
112/// @param[out] endPosition one past the end of the returned word
113///
114/// @return result code
Lei Zhang1a0334e2015-11-02 09:41:20 -0500115spv_result_t getWord(spv_text text, spv_position position, std::string& word,
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400116 spv_position endPosition) {
117 if (!text->str || !text->length) return SPV_ERROR_INVALID_TEXT;
118 if (!position || !endPosition) return SPV_ERROR_INVALID_POINTER;
119
120 *endPosition = *position;
121
122 bool quoting = false;
123 bool escaping = false;
124
125 // NOTE: Assumes first character is not white space!
126 while (true) {
127 const char ch = text->str[endPosition->index];
128 if (ch == '\\')
129 escaping = !escaping;
130 else {
131 switch (ch) {
132 case '"':
133 if (!escaping) quoting = !quoting;
134 break;
135 case ' ':
136 case ';':
137 case '\t':
138 case '\n':
139 if (escaping || quoting) break;
140 // Fall through.
141 case '\0': { // NOTE: End of word found!
142 word.assign(text->str + position->index,
143 (size_t)(endPosition->index - position->index));
144 return SPV_SUCCESS;
145 }
146 default:
147 break;
148 }
149 escaping = false;
150 }
151
152 endPosition->column++;
153 endPosition->index++;
154 }
155}
156
157// Returns true if the characters in the text as position represent
158// the start of an Opcode.
159bool startsWithOp(spv_text text, spv_position position) {
160 if (text->length < position->index + 3) return false;
161 char ch0 = text->str[position->index];
162 char ch1 = text->str[position->index + 1];
163 char ch2 = text->str[position->index + 2];
164 return ('O' == ch0 && 'p' == ch1 && ('A' <= ch2 && ch2 <= 'Z'));
165}
166
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400167} // anonymous namespace
168
169namespace libspirv {
170
David Neto78e677b2015-10-05 13:28:46 -0400171const IdType kUnknownType = {0, false, IdTypeClass::kBottom};
172
David Neto78e677b2015-10-05 13:28:46 -0400173// TODO(dneto): Reorder AssemblyContext definitions to match declaration order.
174
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400175// This represents all of the data that is only valid for the duration of
176// a single compilation.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500177uint32_t AssemblyContext::spvNamedIdAssignOrGet(const char* textValue) {
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400178 if (named_ids_.end() == named_ids_.find(textValue)) {
179 named_ids_[std::string(textValue)] = bound_++;
180 }
181 return named_ids_[textValue];
182}
183uint32_t AssemblyContext::getBound() const { return bound_; }
184
185spv_result_t AssemblyContext::advance() {
186 return ::advance(text_, &current_position_);
187}
188
Lei Zhang1a0334e2015-11-02 09:41:20 -0500189spv_result_t AssemblyContext::getWord(std::string& word,
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400190 spv_position endPosition) {
191 return ::getWord(text_, &current_position_, word, endPosition);
192}
193
194bool AssemblyContext::startsWithOp() {
195 return ::startsWithOp(text_, &current_position_);
196}
197
198bool AssemblyContext::isStartOfNewInst() {
199 spv_position_t nextPosition = current_position_;
200 if (::advance(text_, &nextPosition)) return false;
201 if (::startsWithOp(text_, &nextPosition)) return true;
202
203 std::string word;
204 spv_position_t startPosition = current_position_;
205 if (::getWord(text_, &startPosition, word, &nextPosition)) return false;
206 if ('%' != word.front()) return false;
207
208 if (::advance(text_, &nextPosition)) return false;
209 startPosition = nextPosition;
210 if (::getWord(text_, &startPosition, word, &nextPosition)) return false;
211 if ("=" != word) return false;
212
213 if (::advance(text_, &nextPosition)) return false;
214 startPosition = nextPosition;
215 if (::startsWithOp(text_, &startPosition)) return true;
216 return false;
217}
Andrew Woloszyn4274f932015-10-20 09:12:32 -0400218
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400219char AssemblyContext::peek() const {
220 return text_->str[current_position_.index];
221}
222
223bool AssemblyContext::hasText() const {
224 return text_->length > current_position_.index;
225}
Andrew Woloszyn4274f932015-10-20 09:12:32 -0400226
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400227std::string AssemblyContext::getWord() const {
Andrew Woloszyn4274f932015-10-20 09:12:32 -0400228 uint64_t index = current_position_.index;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400229 while (true) {
230 switch (text_->str[index]) {
231 case '\0':
232 case '\t':
233 case '\v':
234 case '\r':
235 case '\n':
236 case ' ':
237 return std::string(text_->str, text_->str + index);
238 default:
239 index++;
240 }
241 }
242 assert(0 && "Unreachable");
243 return ""; // Make certain compilers happy.
244}
245
246void AssemblyContext::seekForward(uint32_t size) {
247 current_position_.index += size;
248 current_position_.column += size;
249}
250
251spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500252 spv_instruction_t* pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400253 spvInstructionAddWord(pInst, value);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400254 return SPV_SUCCESS;
255}
256
257spv_result_t AssemblyContext::binaryEncodeU64(const uint64_t value,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500258 spv_instruction_t* pInst) {
David Neto78e677b2015-10-05 13:28:46 -0400259 uint32_t low = uint32_t(0x00000000ffffffff & value);
260 uint32_t high = uint32_t((0xffffffff00000000 & value) >> 32);
David Netoac508b02015-10-09 15:48:09 -0400261 binaryEncodeU32(low, pInst);
262 binaryEncodeU32(high, pInst);
263 return SPV_SUCCESS;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400264}
265
David Neto78e677b2015-10-05 13:28:46 -0400266spv_result_t AssemblyContext::binaryEncodeNumericLiteral(
Lei Zhang1a0334e2015-11-02 09:41:20 -0500267 const char* val, spv_result_t error_code, const IdType& type,
268 spv_instruction_t* pInst) {
David Neto78e677b2015-10-05 13:28:46 -0400269 const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom;
270 const bool is_floating = libspirv::isScalarFloating(type);
271 const bool is_integer = libspirv::isScalarIntegral(type);
272
273 if (!is_bottom && !is_floating && !is_integer) {
274 return diagnostic(SPV_ERROR_INTERNAL)
275 << "The expected type is not a scalar integer or float type";
276 }
277
278 // If this is bottom, but looks like a float, we should treat it like a
279 // float.
280 const bool looks_like_float = is_bottom && strchr(val, '.');
281
282 // If we explicitly expect a floating-point number, we should handle that
283 // first.
284 if (is_floating || looks_like_float)
David Neto51013d12015-10-14 11:31:51 -0400285 return binaryEncodeFloatingPointLiteral(val, error_code, type, pInst);
David Neto78e677b2015-10-05 13:28:46 -0400286
David Neto51013d12015-10-14 11:31:51 -0400287 return binaryEncodeIntegerLiteral(val, error_code, type, pInst);
David Neto78e677b2015-10-05 13:28:46 -0400288}
289
Lei Zhang1a0334e2015-11-02 09:41:20 -0500290spv_result_t AssemblyContext::binaryEncodeString(const char* value,
291 spv_instruction_t* pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400292 const size_t length = strlen(value);
293 const size_t wordCount = (length / 4) + 1;
294 const size_t oldWordCount = pInst->words.size();
295 const size_t newWordCount = oldWordCount + wordCount;
296
297 // TODO(dneto): We can just defer this check until later.
298 if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
David Neto78e677b2015-10-05 13:28:46 -0400299 return diagnostic() << "Instruction too long: more than "
300 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words.";
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400301 }
302
David Netob5dc8fc2015-10-06 16:22:00 -0400303 pInst->words.resize(newWordCount);
304
305 // Make sure all the bytes in the last word are 0, in case we only
306 // write a partial word at the end.
307 pInst->words.back() = 0;
308
Lei Zhang1a0334e2015-11-02 09:41:20 -0500309 char* dest = (char*)&pInst->words[oldWordCount];
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400310 strncpy(dest, value, length);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400311
312 return SPV_SUCCESS;
313}
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400314
315spv_result_t AssemblyContext::recordTypeDefinition(
Lei Zhang1a0334e2015-11-02 09:41:20 -0500316 const spv_instruction_t* pInst) {
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400317 uint32_t value = pInst->words[1];
318 if (types_.find(value) != types_.end()) {
Lei Zhang1a0334e2015-11-02 09:41:20 -0500319 return diagnostic() << "Value " << value
320 << " has already been used to generate a type";
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400321 }
322
Lei Zhangb36e7042015-10-28 13:40:52 -0400323 if (pInst->opcode == SpvOpTypeInt) {
David Neto78e677b2015-10-05 13:28:46 -0400324 if (pInst->words.size() != 4)
325 return diagnostic() << "Invalid OpTypeInt instruction";
326 types_[value] = {pInst->words[2], pInst->words[3] != 0,
327 IdTypeClass::kScalarIntegerType};
Lei Zhangb36e7042015-10-28 13:40:52 -0400328 } else if (pInst->opcode == SpvOpTypeFloat) {
David Neto78e677b2015-10-05 13:28:46 -0400329 if (pInst->words.size() != 3)
330 return diagnostic() << "Invalid OpTypeFloat instruction";
331 types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400332 } else {
David Neto78e677b2015-10-05 13:28:46 -0400333 types_[value] = {0, false, IdTypeClass::kOtherType};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400334 }
335 return SPV_SUCCESS;
336}
337
338IdType AssemblyContext::getTypeOfTypeGeneratingValue(uint32_t value) const {
339 auto type = types_.find(value);
340 if (type == types_.end()) {
David Neto78e677b2015-10-05 13:28:46 -0400341 return kUnknownType;
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400342 }
343 return std::get<1>(*type);
344}
345
346IdType AssemblyContext::getTypeOfValueInstruction(uint32_t value) const {
347 auto type_value = value_types_.find(value);
348 if (type_value == value_types_.end()) {
Lei Zhang1a0334e2015-11-02 09:41:20 -0500349 return {0, false, IdTypeClass::kBottom};
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400350 }
351 return getTypeOfTypeGeneratingValue(std::get<1>(*type_value));
352}
353
354spv_result_t AssemblyContext::recordTypeIdForValue(uint32_t value,
355 uint32_t type) {
356 bool successfully_inserted = false;
357 std::tie(std::ignore, successfully_inserted) =
358 value_types_.insert(std::make_pair(value, type));
David Neto78e677b2015-10-05 13:28:46 -0400359 if (!successfully_inserted)
360 return diagnostic() << "Value is being defined a second time";
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400361 return SPV_SUCCESS;
362}
363
David Neto78e677b2015-10-05 13:28:46 -0400364spv_result_t AssemblyContext::binaryEncodeFloatingPointLiteral(
Lei Zhang1a0334e2015-11-02 09:41:20 -0500365 const char* val, spv_result_t error_code, const IdType& type,
366 spv_instruction_t* pInst) {
David Neto78e677b2015-10-05 13:28:46 -0400367 const auto bit_width = assumedBitWidth(type);
368 switch (bit_width) {
369 case 16:
370 return diagnostic(SPV_ERROR_INTERNAL)
371 << "Unsupported yet: 16-bit float constants.";
372 case 32: {
373 float fVal;
David Neto51013d12015-10-14 11:31:51 -0400374 if (auto error = parseNumber(val, error_code, &fVal,
David Neto78e677b2015-10-05 13:28:46 -0400375 "Invalid 32-bit float literal: "))
376 return error;
377 return binaryEncodeU32(BitwiseCast<uint32_t>(fVal), pInst);
378 } break;
379 case 64: {
380 double dVal;
David Neto51013d12015-10-14 11:31:51 -0400381 if (auto error = parseNumber(val, error_code, &dVal,
David Neto78e677b2015-10-05 13:28:46 -0400382 "Invalid 64-bit float literal: "))
383 return error;
384 return binaryEncodeU64(BitwiseCast<uint64_t>(dVal), pInst);
385 } break;
386 default:
387 break;
388 }
389 return diagnostic() << "Unsupported " << bit_width << "-bit float literals";
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400390}
391
David Neto78e677b2015-10-05 13:28:46 -0400392spv_result_t AssemblyContext::binaryEncodeIntegerLiteral(
Lei Zhang1a0334e2015-11-02 09:41:20 -0500393 const char* val, spv_result_t error_code, const IdType& type,
394 spv_instruction_t* pInst) {
David Neto78e677b2015-10-05 13:28:46 -0400395 const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom;
396 const auto bit_width = assumedBitWidth(type);
397
398 if (bit_width > 64)
399 return diagnostic(SPV_ERROR_INTERNAL) << "Unsupported " << bit_width
400 << "-bit integer literals";
401
402 // Either we are expecting anything or integer.
403 bool is_negative = val[0] == '-';
404 bool can_be_signed = is_bottom || type.isSigned;
405
406 if (is_negative && !can_be_signed) {
407 return diagnostic()
408 << "Cannot put a negative number in an unsigned literal";
409 }
410
411 const bool is_hex = val[0] == '0' && (val[1] == 'x' || val[1] == 'X');
412
413 uint64_t decoded_bits;
414 if (is_negative) {
415 int64_t decoded_signed = 0;
David Neto51013d12015-10-14 11:31:51 -0400416 if (auto error = parseNumber(val, error_code, &decoded_signed,
David Neto78e677b2015-10-05 13:28:46 -0400417 "Invalid signed integer literal: "))
418 return error;
419 if (auto error = checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400420 decoded_signed, error_code, type, is_hex, &decoded_signed))
David Neto78e677b2015-10-05 13:28:46 -0400421 return error;
422 decoded_bits = decoded_signed;
423 } else {
424 // There's no leading minus sign, so parse it as an unsigned integer.
David Neto51013d12015-10-14 11:31:51 -0400425 if (auto error = parseNumber(val, error_code, &decoded_bits,
David Neto78e677b2015-10-05 13:28:46 -0400426 "Invalid unsigned integer literal: "))
427 return error;
428 if (auto error = checkRangeAndIfHexThenSignExtend(
David Neto51013d12015-10-14 11:31:51 -0400429 decoded_bits, error_code, type, is_hex, &decoded_bits))
David Neto78e677b2015-10-05 13:28:46 -0400430 return error;
431 }
432 if (bit_width > 32) {
433 return binaryEncodeU64(decoded_bits, pInst);
434 } else {
435 return binaryEncodeU32(uint32_t(decoded_bits), pInst);
436 }
437}
438
439template <typename T>
440spv_result_t AssemblyContext::checkRangeAndIfHexThenSignExtend(
Lei Zhang1a0334e2015-11-02 09:41:20 -0500441 T value, spv_result_t error_code, const IdType& type, bool is_hex,
442 T* updated_value_for_hex) {
David Neto78e677b2015-10-05 13:28:46 -0400443 // The encoded result has three regions of bits that are of interest, from
444 // least to most significant:
445 // - magnitude bits, where the magnitude of the number would be stored if
446 // we were using a signed-magnitude representation.
447 // - an optional sign bit
448 // - overflow bits, up to bit 63 of a 64-bit number
449 // For example:
450 // Type Overflow Sign Magnitude
451 // --------------- -------- ---- ---------
452 // unsigned 8 bit 8-63 n/a 0-7
453 // signed 8 bit 8-63 7 0-6
454 // unsigned 16 bit 16-63 n/a 0-15
455 // signed 16 bit 16-63 15 0-14
456
457 // We'll use masks to define the three regions.
458 // At first we'll assume the number is unsigned.
459 const uint32_t bit_width = assumedBitWidth(type);
460 uint64_t magnitude_mask =
461 (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1);
462 uint64_t sign_mask = 0;
463 uint64_t overflow_mask = ~magnitude_mask;
464
465 if (value < 0 || type.isSigned) {
466 // Accommodate the sign bit.
467 magnitude_mask >>= 1;
468 sign_mask = magnitude_mask + 1;
469 }
470
471 bool failed = false;
472 if (value < 0) {
473 // The top bits must all be 1 for a negative signed value.
474 failed = ((value & overflow_mask) != overflow_mask) ||
475 ((value & sign_mask) != sign_mask);
476 } else {
477 if (is_hex) {
478 // Hex values are a bit special. They decode as unsigned values, but
479 // may represent a negative number. In this case, the overflow bits
480 // should be zero.
481 failed = (value & overflow_mask);
482 } else {
483 // Check overflow in the ordinary case.
484 failed = (value & magnitude_mask) != value;
485 }
486 }
487
488 if (failed) {
David Neto51013d12015-10-14 11:31:51 -0400489 return diagnostic(error_code)
490 << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase
491 << value << " does not fit in a " << std::dec << bit_width << "-bit "
492 << (type.isSigned ? "signed" : "unsigned") << " integer";
David Neto78e677b2015-10-05 13:28:46 -0400493 }
494
495 // Sign extend hex the number.
496 if (is_hex && (value & sign_mask))
497 *updated_value_for_hex = (value | overflow_mask);
498
499 return SPV_SUCCESS;
500}
Lei Zhang1a0334e2015-11-02 09:41:20 -0500501} // namespace libspirv