blob: 9a412aa957b05ab974f5f090adc795b72bb9bff0 [file] [log] [blame]
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +01001// 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 <libspirv/libspirv.h>
28#include "binary.h"
29#include "diagnostic.h"
30#include "ext_inst.h"
31#include "opcode.h"
32#include "operand.h"
33#include "text.h"
34
35#include <assert.h>
36#include <stdio.h>
David Netoaffa6962015-08-24 15:33:14 -040037#include <cstdlib>
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010038#include <string.h>
39
40#include <string>
41#include <vector>
42#include <unordered_map>
43
44// Structures
45
46struct spv_named_id_table_t {
47 std::unordered_map<std::string, uint32_t> namedIds;
48};
49
50// Text API
51
52std::string getWord(const char *str) {
53 size_t index = 0;
54 while (true) {
55 switch (str[index]) {
56 case '\0':
57 case '\t':
58 case '\n':
59 case ' ':
60 break;
61 default:
62 index++;
63 }
64 }
65 return std::string(str, str + index);
66}
67
68spv_named_id_table spvNamedIdTableCreate() {
69 return new spv_named_id_table_t();
70}
71
72void spvNamedIdTableDestory(spv_named_id_table table) { delete table; }
73
74uint32_t spvNamedIdAssignOrGet(spv_named_id_table table, const char *textValue,
75 uint32_t *pBound) {
76 if (table->namedIds.end() == table->namedIds.find(textValue)) {
77 table->namedIds[textValue] = *pBound;
78 }
79 return table->namedIds[textValue];
80}
81
82int32_t spvTextIsNamedId(const char *textValue) {
83 // TODO: Strengthen the parsing of textValue to only include allow names that
84 // match: ([a-z]|[A-Z])(_|[a-z]|[A-Z]|[0-9])*
85 switch (textValue[0]) {
86 case '0':
87 case '1':
88 case '2':
89 case '3':
90 case '4':
91 case '5':
92 case '6':
93 case '7':
94 case '8':
95 case '9':
96 return false;
97 default:
98 break;
99 }
100 return true;
101}
102
103spv_result_t spvTextAdvanceLine(const spv_text text, spv_position position) {
104 while (true) {
105 switch (text->str[position->index]) {
106 case '\0':
107 return SPV_END_OF_STREAM;
108 case '\n':
109 position->column = 0;
110 position->line++;
111 position->index++;
112 return SPV_SUCCESS;
113 default:
Lei Zhangee87cc22015-08-21 11:51:28 -0400114 position->column++;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100115 position->index++;
116 break;
117 }
118 }
119}
120
121spv_result_t spvTextAdvance(const spv_text text, spv_position position) {
122 // NOTE: Consume white space, otherwise don't advance.
123 switch (text->str[position->index]) {
124 case '\0':
125 return SPV_END_OF_STREAM;
126 case ';':
Lei Zhangee87cc22015-08-21 11:51:28 -0400127 if (spv_result_t error = spvTextAdvanceLine(text, position)) return error;
128 return spvTextAdvance(text, position);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100129 case ' ':
130 case '\t':
131 position->column++;
132 position->index++;
133 return spvTextAdvance(text, position);
134 case '\n':
135 position->column = 0;
136 position->line++;
137 position->index++;
138 return spvTextAdvance(text, position);
139 default:
140 break;
141 }
142
143 return SPV_SUCCESS;
144}
145
146spv_result_t spvTextWordGet(const spv_text text,
147 const spv_position startPosition, std::string &word,
148 spv_position endPosition) {
149 spvCheck(!text->str || !text->length, return SPV_ERROR_INVALID_TEXT);
150 spvCheck(!startPosition || !endPosition, return SPV_ERROR_INVALID_POINTER);
151
152 *endPosition = *startPosition;
153
154 // NOTE: Assumes first character is not white space!
155 while (true) {
156 switch (text->str[endPosition->index]) {
157 case ' ':
158 case '\t':
159 case '\n':
160 case '\0': { // NOTE: End of word found!
161 word.assign(text->str + startPosition->index,
162 (size_t)(endPosition->index - startPosition->index));
163 return SPV_SUCCESS;
164 }
165 default:
166 break;
167 }
168
169 endPosition->column++;
170 endPosition->index++;
171 }
172}
173
Lei Zhangdfc50082015-08-21 11:50:55 -0400174// Returns true if the string at the given position in text starts with "Op".
175static bool spvStartsWithOp(const spv_text text, const spv_position position) {
176 if (text->length < position->index + 2) return false;
177 return ('O' == text->str[position->index] &&
178 'p' == text->str[position->index + 1]);
179}
180
181// Returns true if a new instruction begins at the given position in text.
182static bool spvTextIsStartOfNewInst(const spv_text text,
183 const spv_position position) {
184 spv_position_t nextPosition = *position;
185 if (spvTextAdvance(text, &nextPosition)) return false;
186 if (spvStartsWithOp(text, position)) return true;
187
188 std::string word;
189 spv_position_t startPosition = nextPosition;
190 if (spvTextWordGet(text, &startPosition, word, &nextPosition)) return false;
191 if ('%' != word.front()) return false;
192
193 if (spvTextAdvance(text, &nextPosition)) return false;
194 startPosition = nextPosition;
195 if (spvTextWordGet(text, &startPosition, word, &nextPosition)) return false;
196 if ("=" != word) return false;
197
198 if (spvTextAdvance(text, &nextPosition)) return false;
199 startPosition = nextPosition;
200 if (spvStartsWithOp(text, &startPosition)) return true;
201 return false;
202}
203
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100204spv_result_t spvTextStringGet(const spv_text text,
205 const spv_position startPosition,
206 std::string &string, spv_position endPosition) {
207 spvCheck(!text->str || !text->length, return SPV_ERROR_INVALID_TEXT);
208 spvCheck(!startPosition || !endPosition, return SPV_ERROR_INVALID_POINTER);
209
210 spvCheck('"' != text->str[startPosition->index],
211 return SPV_ERROR_INVALID_TEXT);
212
213 *endPosition = *startPosition;
214
215 // NOTE: Assumes first character is not white space
216 while (true) {
217 endPosition->column++;
218 endPosition->index++;
219
220 switch (text->str[endPosition->index]) {
221 case '"': {
222 endPosition->column++;
223 endPosition->index++;
224
225 string.assign(text->str + startPosition->index,
226 (size_t)(endPosition->index - startPosition->index));
227
228 return SPV_SUCCESS;
229 }
230 case '\n':
231 case '\0':
232 return SPV_ERROR_INVALID_TEXT;
233 default:
234 break;
235 }
236 }
237}
238
239spv_result_t spvTextToUInt32(const char *textValue, uint32_t *pValue) {
240 char *endPtr = nullptr;
241 *pValue = strtoul(textValue, &endPtr, 0);
242 if (0 == *pValue && textValue == endPtr) {
243 return SPV_ERROR_INVALID_TEXT;
244 }
245 return SPV_SUCCESS;
246}
247
248spv_result_t spvTextToLiteral(const char *textValue, spv_literal_t *pLiteral) {
249 bool isSigned = false;
David Netoaffa6962015-08-24 15:33:14 -0400250 int numPeriods = 0;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100251 bool isString = false;
252
David Netoaffa6962015-08-24 15:33:14 -0400253 const size_t len = strlen(textValue);
254 for (uint64_t index = 0; index < len; ++index) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100255 switch (textValue[index]) {
256 case '0':
257 case '1':
258 case '2':
259 case '3':
260 case '4':
261 case '5':
262 case '6':
263 case '7':
264 case '8':
265 case '9':
266 break;
267 case '.':
David Netoaffa6962015-08-24 15:33:14 -0400268 numPeriods++;
269 break;
270 case '-':
271 if (index == 0) {
272 isSigned = true;
273 } else {
274 isString = true;
275 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100276 break;
277 default:
278 isString = true;
David Netoaffa6962015-08-24 15:33:14 -0400279 index = len; // break out of the loop too.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100280 break;
281 }
282 }
283
David Netoaffa6962015-08-24 15:33:14 -0400284 pLiteral->type = spv_literal_type_t(99);
285
286 if (isString || numPeriods > 1 || (isSigned && len==1)) {
287 // TODO(dneto): Quotes should be required, and stripped.
288 // TODO(dneto): Allow escaping.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100289 pLiteral->type = SPV_LITERAL_TYPE_STRING;
David Netoaffa6962015-08-24 15:33:14 -0400290 // Need room for the null-terminator.
291 if (len + 1 > sizeof(pLiteral->value.str)) return SPV_ERROR_OUT_OF_MEMORY;
292 strncpy(pLiteral->value.str, textValue, len+1);
293 } else if (numPeriods == 1) {
294 double d = std::strtod(textValue, nullptr);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100295 float f = (float)d;
296 if (d == (double)f) {
297 pLiteral->type = SPV_LITERAL_TYPE_FLOAT_32;
298 pLiteral->value.f = f;
299 } else {
300 pLiteral->type = SPV_LITERAL_TYPE_FLOAT_64;
301 pLiteral->value.d = d;
302 }
303 } else if (isSigned) {
304 int64_t i64 = strtoll(textValue, nullptr, 10);
305 int32_t i32 = (int32_t)i64;
306 if (i64 == (int64_t)i32) {
307 pLiteral->type = SPV_LITERAL_TYPE_INT_32;
308 pLiteral->value.i32 = i32;
309 } else {
310 pLiteral->type = SPV_LITERAL_TYPE_INT_64;
311 pLiteral->value.i64 = i64;
312 }
313 } else {
314 uint64_t u64 = strtoull(textValue, nullptr, 10);
315 uint32_t u32 = (uint32_t)u64;
316 if (u64 == (uint64_t)u32) {
317 pLiteral->type = SPV_LITERAL_TYPE_UINT_32;
318 pLiteral->value.u32 = u32;
319 } else {
320 pLiteral->type = SPV_LITERAL_TYPE_UINT_64;
321 pLiteral->value.u64 = u64;
322 }
323 }
324
325 return SPV_SUCCESS;
326}
327
328spv_result_t spvTextEncodeOperand(
329 const spv_operand_type_t type, const char *textValue,
330 const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
331 spv_named_id_table namedIdTable, spv_instruction_t *pInst,
332 const spv_operand_type_t **ppExtraOperands, uint32_t *pBound,
333 const spv_position position, spv_diagnostic *pDiagnostic) {
334 // NOTE: Handle immediate int in the stream
335 if ('!' == textValue[0]) {
336 const char *begin = textValue + 1;
337 char *end = nullptr;
338 uint32_t immediateInt = strtoul(begin, &end, 0);
339 size_t size = strlen(textValue);
340 size_t length = (end - begin);
341 spvCheck(size - 1 != length, DIAGNOSTIC << "Invalid immediate integer '"
342 << textValue << "'.";
343 return SPV_ERROR_INVALID_TEXT);
344 position->column += size;
345 position->index += size;
346 pInst->words[pInst->wordCount] = immediateInt;
347 pInst->wordCount += 1;
348 return SPV_SUCCESS;
349 }
350
351 switch (type) {
352 case SPV_OPERAND_TYPE_ID: {
Lei Zhangabafd5e2015-08-21 11:52:29 -0400353 if ('%' == textValue[0]) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100354 textValue++;
355 }
Lei Zhangabafd5e2015-08-21 11:52:29 -0400356 // TODO: Force all ID's to be prefixed with '%'.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100357 uint32_t id = 0;
358 if (spvTextIsNamedId(textValue)) {
359 id = spvNamedIdAssignOrGet(namedIdTable, textValue, pBound);
360 } else {
361 spvCheck(spvTextToUInt32(textValue, &id),
362 DIAGNOSTIC << "Invalid ID '" << textValue << "'.";
363 return SPV_ERROR_INVALID_TEXT);
364 }
365 pInst->words[pInst->wordCount++] = id;
366 if (*pBound <= id) {
367 *pBound = id + 1;
368 }
369 } break;
370 case SPV_OPERAND_TYPE_RESULT_ID: {
371 if ('%' == textValue[0]) {
372 textValue++;
373 }
374 // TODO: Force all Result ID's to be prefixed with '%'.
375 uint32_t id = 0;
376 if (spvTextIsNamedId(textValue)) {
377 id = spvNamedIdAssignOrGet(namedIdTable, textValue, pBound);
378 } else {
379 spvCheck(spvTextToUInt32(textValue, &id),
380 DIAGNOSTIC << "Invalid result ID '" << textValue << "'.";
381 return SPV_ERROR_INVALID_TEXT);
382 }
383 pInst->words[pInst->wordCount++] = id;
384 if (*pBound <= id) {
385 *pBound = id + 1;
386 }
387 } break;
388 case SPV_OPERAND_TYPE_LITERAL_NUMBER: {
389 // NOTE: Special case for extension instruction lookup
390 if (OpExtInst == pInst->opcode) {
391 spv_ext_inst_desc extInst;
392 spvCheck(spvExtInstTableNameLookup(extInstTable, pInst->extInstType,
393 textValue, &extInst),
394 DIAGNOSTIC << "Invalid extended instruction name '"
395 << textValue << "'.";
396 return SPV_ERROR_INVALID_TEXT);
397 pInst->words[pInst->wordCount++] = extInst->ext_inst;
398 *ppExtraOperands = extInst->operandTypes;
399 return SPV_SUCCESS;
400 }
401
402 // TODO: Literal numbers can be any number up to 64 bits wide. This
403 // includes integers and floating point numbers.
404 spvCheck(spvTextToUInt32(textValue, &pInst->words[pInst->wordCount++]),
405 DIAGNOSTIC << "Invalid literal number '" << textValue << "'.";
406 return SPV_ERROR_INVALID_TEXT);
407 } break;
408 case SPV_OPERAND_TYPE_LITERAL: {
409 spv_literal_t literal = {};
410 spvCheck(spvTextToLiteral(textValue, &literal),
411 DIAGNOSTIC << "Invalid literal '" << textValue << "'.";
412 return SPV_ERROR_INVALID_TEXT);
413 switch (literal.type) {
414 case SPV_LITERAL_TYPE_INT_32:
415 spvCheck(spvBinaryEncodeU32((uint32_t)literal.value.i32, pInst,
416 position, pDiagnostic),
417 return SPV_ERROR_INVALID_TEXT);
418 break;
419 case SPV_LITERAL_TYPE_INT_64: {
420 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.i64, pInst,
421 position, pDiagnostic),
422 return SPV_ERROR_INVALID_TEXT);
423 } break;
424 case SPV_LITERAL_TYPE_UINT_32: {
425 spvCheck(spvBinaryEncodeU32(literal.value.u32, pInst, position,
426 pDiagnostic),
427 return SPV_ERROR_INVALID_TEXT);
428 } break;
429 case SPV_LITERAL_TYPE_UINT_64: {
430 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.u64, pInst,
431 position, pDiagnostic),
432 return SPV_ERROR_INVALID_TEXT);
433 } break;
434 case SPV_LITERAL_TYPE_FLOAT_32: {
435 spvCheck(spvBinaryEncodeU32((uint32_t)literal.value.f, pInst,
436 position, pDiagnostic),
437 return SPV_ERROR_INVALID_TEXT);
438 } break;
439 case SPV_LITERAL_TYPE_FLOAT_64: {
440 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.d, pInst,
441 position, pDiagnostic),
442 return SPV_ERROR_INVALID_TEXT);
443 } break;
444 case SPV_LITERAL_TYPE_STRING: {
445 spvCheck(spvBinaryEncodeString(literal.value.str, pInst, position,
446 pDiagnostic),
447 return SPV_ERROR_INVALID_TEXT);
448 } break;
449 default:
450 DIAGNOSTIC << "Invalid literal '" << textValue << "'";
451 return SPV_ERROR_INVALID_TEXT;
452 }
453 } break;
454 case SPV_OPERAND_TYPE_LITERAL_STRING: {
455 size_t len = strlen(textValue);
456 spvCheck('"' != textValue[0] && '"' != textValue[len - 1],
457 DIAGNOSTIC << "Invalid literal string '" << textValue
458 << "', expected quotes.";
459 return SPV_ERROR_INVALID_TEXT);
460 // NOTE: Strip quotes
461 std::string text(textValue + 1, len - 2);
462
463 // NOTE: Special case for extended instruction library import
464 if (OpExtInstImport == pInst->opcode) {
465 pInst->extInstType = spvExtInstImportTypeGet(text.c_str());
466 }
467
468 spvCheck(
469 spvBinaryEncodeString(text.c_str(), pInst, position, pDiagnostic),
470 return SPV_ERROR_INVALID_TEXT);
471 } break;
472 default: {
473 // NOTE: All non literal operands are handled here using the operand
474 // table.
475 spv_operand_desc entry;
476 spvCheck(spvOperandTableNameLookup(operandTable, type, textValue, &entry),
477 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '"
478 << textValue << "'.";
479 return SPV_ERROR_INVALID_TEXT;);
480 spvCheck(spvBinaryEncodeU32(entry->value, pInst, position, pDiagnostic),
481 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '"
482 << textValue << "'.";
483 return SPV_ERROR_INVALID_TEXT;);
484 if (ppExtraOperands && entry->operandTypes[0] != SPV_OPERAND_TYPE_NONE) {
485 *ppExtraOperands = entry->operandTypes;
486 }
487 } break;
488 }
489 return SPV_SUCCESS;
490}
491
492spv_result_t spvTextEncodeOpcode(
493 const spv_text text, const spv_opcode_table opcodeTable,
494 const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
495 spv_named_id_table namedIdTable, uint32_t *pBound, spv_instruction_t *pInst,
496 spv_position position, spv_diagnostic *pDiagnostic) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400497 // An assembly instruction has two possible formats:
498 // 1. <opcode> <operand>.., e.g., "OpMemoryModel Physical64 OpenCL".
499 // 2. <result-id> = <opcode> <operand>.., e.g., "%void = OpTypeVoid".
500
501 // Assume it's the first format at the beginning.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100502 std::string opcodeName;
503 spv_position_t nextPosition = {};
Lei Zhangdfc50082015-08-21 11:50:55 -0400504 spv_result_t error =
505 spvTextWordGet(text, position, opcodeName, &nextPosition);
506 spvCheck(error, return error);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100507
508 // NOTE: Handle insertion of an immediate integer into the binary stream
Lei Zhangdfc50082015-08-21 11:50:55 -0400509 bool immediate = false;
510 spvCheck('!' == text->str[position->index], immediate = true);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100511 if (immediate) {
512 const char *begin = opcodeName.data() + 1;
513 char *end = nullptr;
514 uint32_t immediateInt = strtoul(begin, &end, 0);
515 size_t size = opcodeName.size() - 1;
516 spvCheck(size != (size_t)(end - begin),
517 DIAGNOSTIC << "Invalid immediate integer '" << opcodeName << "'.";
518 return SPV_ERROR_INVALID_TEXT);
519 position->column += opcodeName.size();
520 position->index += opcodeName.size();
521 pInst->words[0] = immediateInt;
522 pInst->wordCount = 1;
523 return SPV_SUCCESS;
524 }
525
Lei Zhangdfc50082015-08-21 11:50:55 -0400526 // Handle value generating instructions (the second format above) here.
527 std::string result_id;
528 spv_position_t result_id_position = {};
529 // If the word we get doesn't start with "Op", assume it's an <result-id>
530 // from now.
531 spvCheck(!spvStartsWithOp(text, position), result_id = opcodeName);
532 if (!result_id.empty()) {
533 spvCheck('%' != result_id.front(),
534 DIAGNOSTIC << "Expected <opcode> or <result-id> at the beginning "
535 "of an instruction, found '"
536 << result_id << "'.";
537 return SPV_ERROR_INVALID_TEXT);
538 result_id_position = *position;
539 *position = nextPosition;
540 spvCheck(spvTextAdvance(text, position),
541 DIAGNOSTIC << "Expected '=', found end of stream.";
542 return SPV_ERROR_INVALID_TEXT);
543 // The '=' sign.
544 std::string equal_sign;
545 error = spvTextWordGet(text, position, equal_sign, &nextPosition);
546 spvCheck("=" != equal_sign, DIAGNOSTIC << "'=' expected after result id.";
547 return SPV_ERROR_INVALID_TEXT);
548
549 // The <opcode> after the '=' sign.
550 *position = nextPosition;
551 spvCheck(spvTextAdvance(text, position),
552 DIAGNOSTIC << "Expected opcode, found end of stream.";
553 return SPV_ERROR_INVALID_TEXT);
Lei Zhang977e9bc2015-08-21 11:52:03 -0400554 error = spvTextWordGet(text, position, opcodeName, &nextPosition);
555 spvCheck(error, return error);
Lei Zhangdfc50082015-08-21 11:50:55 -0400556 spvCheck(!spvStartsWithOp(text, position),
557 DIAGNOSTIC << "Invalid Opcode prefix '" << opcodeName << "'.";
558 return SPV_ERROR_INVALID_TEXT);
Lei Zhangdfc50082015-08-21 11:50:55 -0400559 }
560
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100561 // NOTE: The table contains Opcode names without the "Op" prefix.
562 const char *pInstName = opcodeName.data() + 2;
563
564 spv_opcode_desc opcodeEntry;
Lei Zhangdfc50082015-08-21 11:50:55 -0400565 error = spvOpcodeTableNameLookup(opcodeTable, pInstName, &opcodeEntry);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100566 spvCheck(error, DIAGNOSTIC << "Invalid Opcode name '"
567 << getWord(text->str + position->index) << "'";
568 return error);
569 pInst->opcode = opcodeEntry->opcode;
570 *position = nextPosition;
571 pInst->wordCount++;
572
Lei Zhangdfc50082015-08-21 11:50:55 -0400573 // Get the arugment index for <result-id>. Used for handling the <result-id>
574 // for value generating instructions below.
575 const int16_t result_id_index = spvOpcodeResultIdIndex(opcodeEntry);
576
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100577 // NOTE: Process the fixed size operands
578 const spv_operand_type_t *extraOperandTypes = nullptr;
579 for (int32_t operandIndex = 0; operandIndex < (opcodeEntry->wordCount - 1);
580 ++operandIndex) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400581 if (operandIndex == result_id_index && !result_id.empty()) {
582 // Handling the <result-id> for value generating instructions.
583 error = spvTextEncodeOperand(
584 SPV_OPERAND_TYPE_RESULT_ID, result_id.c_str(), operandTable,
585 extInstTable, namedIdTable, pInst, &extraOperandTypes, pBound,
586 &result_id_position, pDiagnostic);
587 spvCheck(error, return error);
588 continue;
589 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100590 spvCheck(spvTextAdvance(text, position),
591 DIAGNOSTIC << "Expected operand, found end of stream.";
592 return SPV_ERROR_INVALID_TEXT);
593
594 std::string operandValue;
595 error = spvTextWordGet(text, position, operandValue, &nextPosition);
596 spvCheck(error, return error);
597
598 error = spvTextEncodeOperand(
599 opcodeEntry->operandTypes[operandIndex], operandValue.c_str(),
600 operandTable, extInstTable, namedIdTable, pInst, &extraOperandTypes,
601 pBound, position, pDiagnostic);
602 spvCheck(error, return error);
603
604 *position = nextPosition;
605 }
606
607 if (spvOpcodeIsVariable(opcodeEntry)) {
608 if (!extraOperandTypes) {
609 // NOTE: Handle variable length not defined by an immediate previously
610 // encountered in the Opcode.
611 spv_operand_type_t type =
612 opcodeEntry->operandTypes[opcodeEntry->wordCount - 1];
613
614 while (!spvTextAdvance(text, position)) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400615 // NOTE: If this is the end of the current instruction stream and we
616 // break out of this loop.
617 if (spvTextIsStartOfNewInst(text, position)) break;
618
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100619 std::string textValue;
620 spvTextWordGet(text, position, textValue, &nextPosition);
621
Lei Zhangdfc50082015-08-21 11:50:55 -0400622 if (SPV_OPERAND_TYPE_LITERAL_STRING == type) {
623 spvCheck(spvTextAdvance(text, position),
624 DIAGNOSTIC << "Invalid string, found end of stream.";
625 return SPV_ERROR_INVALID_TEXT);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100626
Lei Zhangdfc50082015-08-21 11:50:55 -0400627 std::string string;
628 spvCheck(spvTextStringGet(text, position, string, &nextPosition),
629 DIAGNOSTIC << "Invalid string, new line or end of stream.";
630 return SPV_ERROR_INVALID_TEXT);
631 spvCheck(spvTextEncodeOperand(type, string.c_str(), operandTable,
632 extInstTable, namedIdTable, pInst,
633 nullptr, pBound, position, pDiagnostic),
634 return SPV_ERROR_INVALID_TEXT);
635 } else {
636 spvCheck(spvTextEncodeOperand(type, textValue.c_str(), operandTable,
637 extInstTable, namedIdTable, pInst,
638 nullptr, pBound, position, pDiagnostic),
639 return SPV_ERROR_INVALID_TEXT);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100640 }
Lei Zhangdfc50082015-08-21 11:50:55 -0400641 *position = nextPosition;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100642 }
643 } else {
644 // NOTE: Process the variable size operands defined by an immediate
645 // previously encountered in the Opcode.
646 uint64_t extraOperandsIndex = 0;
647 while (extraOperandTypes[extraOperandsIndex]) {
648 spvCheck(spvTextAdvance(text, position),
649 DIAGNOSTIC << "Expected operand, found end of stream.";
650 return SPV_ERROR_INVALID_TEXT);
651
652 std::string operandValue;
653 error = spvTextWordGet(text, position, operandValue, &nextPosition);
654
655 error = spvTextEncodeOperand(extraOperandTypes[extraOperandsIndex],
656 operandValue.c_str(), operandTable,
657 extInstTable, namedIdTable, pInst, nullptr,
658 pBound, position, pDiagnostic);
659 spvCheck(error, return error);
660
661 *position = nextPosition;
662
663 extraOperandsIndex++;
664 }
665 }
666 }
667
668 pInst->words[0] = spvOpcodeMake(pInst->wordCount, opcodeEntry->opcode);
669
670 return SPV_SUCCESS;
671}
672
673spv_result_t spvTextToBinary(const spv_text text,
674 const spv_opcode_table opcodeTable,
675 const spv_operand_table operandTable,
676 const spv_ext_inst_table extInstTable,
677 spv_binary *pBinary, spv_diagnostic *pDiagnostic) {
678 spv_position_t position = {};
679 spvCheck(!text->str || !text->length, DIAGNOSTIC << "Text stream is empty.";
680 return SPV_ERROR_INVALID_TEXT);
681 spvCheck(!opcodeTable || !operandTable || !extInstTable,
682 return SPV_ERROR_INVALID_TABLE);
683 spvCheck(!pBinary, return SPV_ERROR_INVALID_POINTER);
684 spvCheck(!pDiagnostic, return SPV_ERROR_INVALID_DIAGNOSTIC);
685
686 // NOTE: Ensure diagnostic is zero initialised
687 *pDiagnostic = {};
688
689 uint32_t bound = 1;
690
691 std::vector<spv_instruction_t> instructions;
692
693 spvCheck(spvTextAdvance(text, &position), DIAGNOSTIC
694 << "Text stream is empty.";
695 return SPV_ERROR_INVALID_TEXT);
696
697 spv_named_id_table namedIdTable = spvNamedIdTableCreate();
698 spvCheck(!namedIdTable, return SPV_ERROR_OUT_OF_MEMORY);
699
700 spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
701 while (text->length > position.index) {
702 spv_instruction_t inst = {};
703 inst.extInstType = extInstType;
704
705 spvCheck(spvTextEncodeOpcode(text, opcodeTable, operandTable, extInstTable,
706 namedIdTable, &bound, &inst, &position,
707 pDiagnostic),
708 spvNamedIdTableDestory(namedIdTable);
709 return SPV_ERROR_INVALID_TEXT);
710 extInstType = inst.extInstType;
711
712 instructions.push_back(inst);
713
714 spvCheck(spvTextAdvance(text, &position), break);
715 }
716
717 spvNamedIdTableDestory(namedIdTable);
718
719 size_t totalSize = SPV_INDEX_INSTRUCTION;
720 for (auto &inst : instructions) {
721 totalSize += inst.wordCount;
722 }
723
724 uint32_t *data = new uint32_t[totalSize];
725 spvCheck(!data, return SPV_ERROR_OUT_OF_MEMORY);
726 uint64_t currentIndex = SPV_INDEX_INSTRUCTION;
727 for (auto &inst : instructions) {
728 memcpy(data + currentIndex, inst.words, sizeof(uint32_t) * inst.wordCount);
729 currentIndex += inst.wordCount;
730 }
731
732 spv_binary binary = new spv_binary_t();
733 spvCheck(!binary, delete[] data; return SPV_ERROR_OUT_OF_MEMORY);
734 binary->code = data;
735 binary->wordCount = totalSize;
736
737 spv_result_t error = spvBinaryHeaderSet(binary, bound);
738 spvCheck(error, spvBinaryDestroy(binary); return error);
739
740 *pBinary = binary;
741
742 return SPV_SUCCESS;
743}
744
745void spvTextDestroy(spv_text text) {
746 spvCheck(!text, return );
747 if (text->str) {
748 delete[] text->str;
749 }
750 delete text;
751}