blob: 0f6f1354cb894c89396560b780cb3fb28ba9a8bd [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>
31#include <cstring>
Andrew Woloszyn537e7762015-09-29 11:28:34 -040032#include <tuple>
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040033
34#include "binary.h"
35#include "ext_inst.h"
David Netob5dc8fc2015-10-06 16:22:00 -040036#include "instruction.h"
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040037#include "opcode.h"
38#include "text.h"
39
40namespace {
41
42/// @brief Advance text to the start of the next line
43///
44/// @param[in] text to be parsed
45/// @param[in,out] position position text has been advanced to
46///
47/// @return result code
48spv_result_t advanceLine(spv_text text, spv_position position) {
49 while (true) {
50 switch (text->str[position->index]) {
51 case '\0':
52 return SPV_END_OF_STREAM;
53 case '\n':
54 position->column = 0;
55 position->line++;
56 position->index++;
57 return SPV_SUCCESS;
58 default:
59 position->column++;
60 position->index++;
61 break;
62 }
63 }
64}
65
66/// @brief Advance text to first non white space character
67/// If a null terminator is found during the text advance SPV_END_OF_STREAM is
68/// returned, SPV_SUCCESS otherwise. No error checking is performed on the
69/// parameters, its the users responsibility to ensure these are non null.
70///
71/// @param[in] text to be parsed
72/// @param[in,out] position text has been advanced to
73///
74/// @return result code
75spv_result_t advance(spv_text text, spv_position position) {
76 // NOTE: Consume white space, otherwise don't advance.
77 switch (text->str[position->index]) {
78 case '\0':
79 return SPV_END_OF_STREAM;
80 case ';':
81 if (spv_result_t error = advanceLine(text, position)) return error;
82 return advance(text, position);
83 case ' ':
84 case '\t':
85 position->column++;
86 position->index++;
87 return advance(text, position);
88 case '\n':
89 position->column = 0;
90 position->line++;
91 position->index++;
92 return advance(text, position);
93 default:
94 break;
95 }
96 return SPV_SUCCESS;
97}
98
99/// @brief Fetch the next word from the text stream.
100///
101/// A word ends at the next comment or whitespace. However, double-quoted
102/// strings remain intact, and a backslash always escapes the next character.
103///
104/// @param[in] text stream to read from
105/// @param[in] position current position in text stream
106/// @param[out] word returned word
107/// @param[out] endPosition one past the end of the returned word
108///
109/// @return result code
110spv_result_t getWord(spv_text text, spv_position position, std::string &word,
111 spv_position endPosition) {
112 if (!text->str || !text->length) return SPV_ERROR_INVALID_TEXT;
113 if (!position || !endPosition) return SPV_ERROR_INVALID_POINTER;
114
115 *endPosition = *position;
116
117 bool quoting = false;
118 bool escaping = false;
119
120 // NOTE: Assumes first character is not white space!
121 while (true) {
122 const char ch = text->str[endPosition->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 if (escaping || quoting) break;
135 // Fall through.
136 case '\0': { // NOTE: End of word found!
137 word.assign(text->str + position->index,
138 (size_t)(endPosition->index - position->index));
139 return SPV_SUCCESS;
140 }
141 default:
142 break;
143 }
144 escaping = false;
145 }
146
147 endPosition->column++;
148 endPosition->index++;
149 }
150}
151
152// Returns true if the characters in the text as position represent
153// the start of an Opcode.
154bool 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/// @brief Parses a mask expression string for the given operand type.
163///
164/// A mask expression is a sequence of one or more terms separated by '|',
165/// where each term a named enum value for the given type. No whitespace
166/// is permitted.
167///
168/// On success, the value is written to pValue.
169///
170/// @param[in] operandTable operand lookup table
171/// @param[in] type of the operand
172/// @param[in] textValue word of text to be parsed
173/// @param[out] pValue where the resulting value is written
174///
175/// @return result code
176spv_result_t spvTextParseMaskOperand(const spv_operand_table operandTable,
177 const spv_operand_type_t type,
178 const char *textValue, uint32_t *pValue) {
179 if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT;
180 size_t text_length = strlen(textValue);
181 if (text_length == 0) return SPV_ERROR_INVALID_TEXT;
182 const char *text_end = textValue + text_length;
183
184 // We only support mask expressions in ASCII, so the separator value is a
185 // char.
186 const char separator = '|';
187
188 // Accumulate the result by interpreting one word at a time, scanning
189 // from left to right.
190 uint32_t value = 0;
191 const char *begin = textValue; // The left end of the current word.
192 const char *end = nullptr; // One character past the end of the current word.
193 do {
194 end = std::find(begin, text_end, separator);
195
196 spv_operand_desc entry = nullptr;
197 if (spvOperandTableNameLookup(operandTable, type, begin, end - begin,
198 &entry)) {
199 return SPV_ERROR_INVALID_TEXT;
200 }
201 value |= entry->value;
202
203 // Advance to the next word by skipping over the separator.
204 begin = end + 1;
205 } while (end != text_end);
206
207 *pValue = value;
208 return SPV_SUCCESS;
209}
210
211} // anonymous namespace
212
213namespace libspirv {
214
215bool AssemblyGrammar::isValid() const {
216 return operandTable_ && opcodeTable_ && extInstTable_;
217}
218
219spv_result_t AssemblyGrammar::lookupOpcode(const char *name,
220 spv_opcode_desc *desc) const {
221 return spvOpcodeTableNameLookup(opcodeTable_, name, desc);
222}
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400223
224spv_result_t AssemblyGrammar::lookupOpcode(Op opcode,
225 spv_opcode_desc *desc) const {
226 return spvOpcodeTableValueLookup(opcodeTable_, opcode, desc);
227}
228
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400229spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type,
230 const char *name, size_t name_len,
231 spv_operand_desc *desc) const {
232 return spvOperandTableNameLookup(operandTable_, type, name, name_len, desc);
233}
234
235spv_result_t AssemblyGrammar::parseMaskOperand(const spv_operand_type_t type,
236 const char *textValue,
237 uint32_t *pValue) const {
238 return spvTextParseMaskOperand(operandTable_, type, textValue, pValue);
239}
240spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type,
241 const char *textValue,
242 spv_ext_inst_desc *extInst) const {
243 return spvExtInstTableNameLookup(extInstTable_, type, textValue, extInst);
244}
245
246void AssemblyGrammar::prependOperandTypesForMask(
247 const spv_operand_type_t type, const uint32_t mask,
248 spv_operand_pattern_t *pattern) const {
249 spvPrependOperandTypesForMask(operandTable_, type, mask, pattern);
250}
251
252// This represents all of the data that is only valid for the duration of
253// a single compilation.
254uint32_t AssemblyContext::spvNamedIdAssignOrGet(const char *textValue) {
255 if (named_ids_.end() == named_ids_.find(textValue)) {
256 named_ids_[std::string(textValue)] = bound_++;
257 }
258 return named_ids_[textValue];
259}
260uint32_t AssemblyContext::getBound() const { return bound_; }
261
262spv_result_t AssemblyContext::advance() {
263 return ::advance(text_, &current_position_);
264}
265
266spv_result_t AssemblyContext::getWord(std::string &word,
267 spv_position endPosition) {
268 return ::getWord(text_, &current_position_, word, endPosition);
269}
270
271bool AssemblyContext::startsWithOp() {
272 return ::startsWithOp(text_, &current_position_);
273}
274
275bool AssemblyContext::isStartOfNewInst() {
276 spv_position_t nextPosition = current_position_;
277 if (::advance(text_, &nextPosition)) return false;
278 if (::startsWithOp(text_, &nextPosition)) return true;
279
280 std::string word;
281 spv_position_t startPosition = current_position_;
282 if (::getWord(text_, &startPosition, word, &nextPosition)) return false;
283 if ('%' != word.front()) return false;
284
285 if (::advance(text_, &nextPosition)) return false;
286 startPosition = nextPosition;
287 if (::getWord(text_, &startPosition, word, &nextPosition)) return false;
288 if ("=" != word) return false;
289
290 if (::advance(text_, &nextPosition)) return false;
291 startPosition = nextPosition;
292 if (::startsWithOp(text_, &startPosition)) return true;
293 return false;
294}
295char AssemblyContext::peek() const {
296 return text_->str[current_position_.index];
297}
298
299bool AssemblyContext::hasText() const {
300 return text_->length > current_position_.index;
301}
302std::string AssemblyContext::getWord() const {
303 size_t index = current_position_.index;
304 while (true) {
305 switch (text_->str[index]) {
306 case '\0':
307 case '\t':
308 case '\v':
309 case '\r':
310 case '\n':
311 case ' ':
312 return std::string(text_->str, text_->str + index);
313 default:
314 index++;
315 }
316 }
317 assert(0 && "Unreachable");
318 return ""; // Make certain compilers happy.
319}
320
321void AssemblyContext::seekForward(uint32_t size) {
322 current_position_.index += size;
323 current_position_.column += size;
324}
325
326spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value,
327 spv_instruction_t *pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400328 spvInstructionAddWord(pInst, value);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400329 return SPV_SUCCESS;
330}
331
332spv_result_t AssemblyContext::binaryEncodeU64(const uint64_t value,
333 spv_instruction_t *pInst) {
334 uint32_t low = (uint32_t)(0x00000000ffffffff & value);
335 uint32_t high = (uint32_t)((0xffffffff00000000 & value) >> 32);
336 spv_result_t err = binaryEncodeU32(low, pInst);
337 if (err != SPV_SUCCESS) {
338 return err;
339 }
340 return binaryEncodeU32(high, pInst);
341}
342
343spv_result_t AssemblyContext::binaryEncodeString(
344 const char *value, spv_instruction_t *pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400345 const size_t length = strlen(value);
346 const size_t wordCount = (length / 4) + 1;
347 const size_t oldWordCount = pInst->words.size();
348 const size_t newWordCount = oldWordCount + wordCount;
349
350 // TODO(dneto): We can just defer this check until later.
351 if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
David Netof6b86512015-10-08 15:20:25 -0400352 diagnostic() << "Instruction too long: more than "
353 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words.";
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400354 return SPV_ERROR_INVALID_TEXT;
355 }
356
David Netob5dc8fc2015-10-06 16:22:00 -0400357 pInst->words.resize(newWordCount);
358
359 // Make sure all the bytes in the last word are 0, in case we only
360 // write a partial word at the end.
361 pInst->words.back() = 0;
362
363 char *dest = (char *)&pInst->words[oldWordCount];
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400364 strncpy(dest, value, length);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400365
366 return SPV_SUCCESS;
367}
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400368
369spv_result_t AssemblyContext::recordTypeDefinition(
370 const spv_instruction_t *pInst) {
371 uint32_t value = pInst->words[1];
372 if (types_.find(value) != types_.end()) {
373 diagnostic() << "Value " << value
374 << " has already been used to generate a type";
375 return SPV_ERROR_INVALID_VALUE;
376 }
377
378 if (pInst->opcode == OpTypeInt) {
379 if (pInst->words.size() != 4) {
380 diagnostic() << "Invalid OpTypeInt instruction";
381 return SPV_ERROR_INVALID_VALUE;
382 }
383 types_[value] = { pInst->words[2], IdTypeClass::kScalarIntegerType };
384 } else if (pInst->opcode == OpTypeFloat) {
385 if (pInst->words.size() != 3) {
386 diagnostic() << "Invalid OpTypeFloat instruction";
387 return SPV_ERROR_INVALID_VALUE;
388 }
389 types_[value] = { pInst->words[2], IdTypeClass::kScalarFloatType };
390 } else {
391 types_[value] = { 0, IdTypeClass::kOtherType };
392 }
393 return SPV_SUCCESS;
394}
395
396IdType AssemblyContext::getTypeOfTypeGeneratingValue(uint32_t value) const {
397 auto type = types_.find(value);
398 if (type == types_.end()) {
399 return {0, IdTypeClass::kBottom};
400 }
401 return std::get<1>(*type);
402}
403
404IdType AssemblyContext::getTypeOfValueInstruction(uint32_t value) const {
405 auto type_value = value_types_.find(value);
406 if (type_value == value_types_.end()) {
407 return { 0, IdTypeClass::kBottom};
408 }
409 return getTypeOfTypeGeneratingValue(std::get<1>(*type_value));
410}
411
412spv_result_t AssemblyContext::recordTypeIdForValue(uint32_t value,
413 uint32_t type) {
414 bool successfully_inserted = false;
415 std::tie(std::ignore, successfully_inserted) =
416 value_types_.insert(std::make_pair(value, type));
417 if (!successfully_inserted) {
418 diagnostic() << "Value is being defined a second time";
419 return SPV_ERROR_INVALID_VALUE;
420 }
421 return SPV_SUCCESS;
422}
423
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400424}
425