blob: 793bb5827bd7fc56ba99f4b311bb04e20652efad [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 ' ':
David Neto574884c2015-08-25 13:53:19 -0400158 case ';':
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100159 case '\t':
160 case '\n':
161 case '\0': { // NOTE: End of word found!
162 word.assign(text->str + startPosition->index,
163 (size_t)(endPosition->index - startPosition->index));
164 return SPV_SUCCESS;
165 }
166 default:
167 break;
168 }
169
170 endPosition->column++;
171 endPosition->index++;
172 }
173}
174
Lei Zhangdfc50082015-08-21 11:50:55 -0400175// Returns true if the string at the given position in text starts with "Op".
176static bool spvStartsWithOp(const spv_text text, const spv_position position) {
177 if (text->length < position->index + 2) return false;
178 return ('O' == text->str[position->index] &&
179 'p' == text->str[position->index + 1]);
180}
181
182// Returns true if a new instruction begins at the given position in text.
183static bool spvTextIsStartOfNewInst(const spv_text text,
184 const spv_position position) {
185 spv_position_t nextPosition = *position;
186 if (spvTextAdvance(text, &nextPosition)) return false;
187 if (spvStartsWithOp(text, position)) return true;
188
189 std::string word;
190 spv_position_t startPosition = nextPosition;
191 if (spvTextWordGet(text, &startPosition, word, &nextPosition)) return false;
192 if ('%' != word.front()) return false;
193
194 if (spvTextAdvance(text, &nextPosition)) return false;
195 startPosition = nextPosition;
196 if (spvTextWordGet(text, &startPosition, word, &nextPosition)) return false;
197 if ("=" != word) return false;
198
199 if (spvTextAdvance(text, &nextPosition)) return false;
200 startPosition = nextPosition;
201 if (spvStartsWithOp(text, &startPosition)) return true;
202 return false;
203}
204
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100205spv_result_t spvTextStringGet(const spv_text text,
206 const spv_position startPosition,
207 std::string &string, spv_position endPosition) {
208 spvCheck(!text->str || !text->length, return SPV_ERROR_INVALID_TEXT);
209 spvCheck(!startPosition || !endPosition, return SPV_ERROR_INVALID_POINTER);
210
211 spvCheck('"' != text->str[startPosition->index],
212 return SPV_ERROR_INVALID_TEXT);
213
214 *endPosition = *startPosition;
215
216 // NOTE: Assumes first character is not white space
217 while (true) {
218 endPosition->column++;
219 endPosition->index++;
220
221 switch (text->str[endPosition->index]) {
222 case '"': {
223 endPosition->column++;
224 endPosition->index++;
225
226 string.assign(text->str + startPosition->index,
227 (size_t)(endPosition->index - startPosition->index));
228
229 return SPV_SUCCESS;
230 }
231 case '\n':
232 case '\0':
233 return SPV_ERROR_INVALID_TEXT;
234 default:
235 break;
236 }
237 }
238}
239
240spv_result_t spvTextToUInt32(const char *textValue, uint32_t *pValue) {
241 char *endPtr = nullptr;
242 *pValue = strtoul(textValue, &endPtr, 0);
243 if (0 == *pValue && textValue == endPtr) {
244 return SPV_ERROR_INVALID_TEXT;
245 }
246 return SPV_SUCCESS;
247}
248
249spv_result_t spvTextToLiteral(const char *textValue, spv_literal_t *pLiteral) {
250 bool isSigned = false;
David Netoaffa6962015-08-24 15:33:14 -0400251 int numPeriods = 0;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100252 bool isString = false;
253
David Netoaffa6962015-08-24 15:33:14 -0400254 const size_t len = strlen(textValue);
David Neto98290a22015-08-24 16:27:02 -0400255 if (len == 0) return SPV_FAILED_MATCH;
256
David Netoaffa6962015-08-24 15:33:14 -0400257 for (uint64_t index = 0; index < len; ++index) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100258 switch (textValue[index]) {
259 case '0':
260 case '1':
261 case '2':
262 case '3':
263 case '4':
264 case '5':
265 case '6':
266 case '7':
267 case '8':
268 case '9':
269 break;
270 case '.':
David Netoaffa6962015-08-24 15:33:14 -0400271 numPeriods++;
272 break;
273 case '-':
274 if (index == 0) {
275 isSigned = true;
276 } else {
277 isString = true;
278 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100279 break;
280 default:
281 isString = true;
David Netoaffa6962015-08-24 15:33:14 -0400282 index = len; // break out of the loop too.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100283 break;
284 }
285 }
286
David Netoaffa6962015-08-24 15:33:14 -0400287 pLiteral->type = spv_literal_type_t(99);
288
289 if (isString || numPeriods > 1 || (isSigned && len==1)) {
David Netoaffa6962015-08-24 15:33:14 -0400290 // TODO(dneto): Allow escaping.
David Neto98290a22015-08-24 16:27:02 -0400291 if (len < 2 || textValue[0] != '"' || textValue[len - 1] != '"')
292 return SPV_FAILED_MATCH;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100293 pLiteral->type = SPV_LITERAL_TYPE_STRING;
David Netoaffa6962015-08-24 15:33:14 -0400294 // Need room for the null-terminator.
David Neto98290a22015-08-24 16:27:02 -0400295 if (len >= sizeof(pLiteral->value.str)) return SPV_ERROR_OUT_OF_MEMORY;
296 strncpy(pLiteral->value.str, textValue+1, len-2);
297 pLiteral->value.str[len-2] = 0;
David Netoaffa6962015-08-24 15:33:14 -0400298 } else if (numPeriods == 1) {
299 double d = std::strtod(textValue, nullptr);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100300 float f = (float)d;
301 if (d == (double)f) {
302 pLiteral->type = SPV_LITERAL_TYPE_FLOAT_32;
303 pLiteral->value.f = f;
304 } else {
305 pLiteral->type = SPV_LITERAL_TYPE_FLOAT_64;
306 pLiteral->value.d = d;
307 }
308 } else if (isSigned) {
309 int64_t i64 = strtoll(textValue, nullptr, 10);
310 int32_t i32 = (int32_t)i64;
311 if (i64 == (int64_t)i32) {
312 pLiteral->type = SPV_LITERAL_TYPE_INT_32;
313 pLiteral->value.i32 = i32;
314 } else {
315 pLiteral->type = SPV_LITERAL_TYPE_INT_64;
316 pLiteral->value.i64 = i64;
317 }
318 } else {
319 uint64_t u64 = strtoull(textValue, nullptr, 10);
320 uint32_t u32 = (uint32_t)u64;
321 if (u64 == (uint64_t)u32) {
322 pLiteral->type = SPV_LITERAL_TYPE_UINT_32;
323 pLiteral->value.u32 = u32;
324 } else {
325 pLiteral->type = SPV_LITERAL_TYPE_UINT_64;
326 pLiteral->value.u64 = u64;
327 }
328 }
329
330 return SPV_SUCCESS;
331}
332
333spv_result_t spvTextEncodeOperand(
334 const spv_operand_type_t type, const char *textValue,
335 const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
336 spv_named_id_table namedIdTable, spv_instruction_t *pInst,
337 const spv_operand_type_t **ppExtraOperands, uint32_t *pBound,
338 const spv_position position, spv_diagnostic *pDiagnostic) {
339 // NOTE: Handle immediate int in the stream
340 if ('!' == textValue[0]) {
341 const char *begin = textValue + 1;
342 char *end = nullptr;
343 uint32_t immediateInt = strtoul(begin, &end, 0);
344 size_t size = strlen(textValue);
345 size_t length = (end - begin);
346 spvCheck(size - 1 != length, DIAGNOSTIC << "Invalid immediate integer '"
347 << textValue << "'.";
348 return SPV_ERROR_INVALID_TEXT);
349 position->column += size;
350 position->index += size;
351 pInst->words[pInst->wordCount] = immediateInt;
352 pInst->wordCount += 1;
353 return SPV_SUCCESS;
354 }
355
356 switch (type) {
David Netoe3f70b92015-08-27 13:50:05 -0400357 case SPV_OPERAND_TYPE_ID:
358 case SPV_OPERAND_TYPE_RESULT_ID: {
Lei Zhangabafd5e2015-08-21 11:52:29 -0400359 if ('%' == textValue[0]) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100360 textValue++;
361 }
Lei Zhangabafd5e2015-08-21 11:52:29 -0400362 // TODO: Force all ID's to be prefixed with '%'.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100363 uint32_t id = 0;
364 if (spvTextIsNamedId(textValue)) {
365 id = spvNamedIdAssignOrGet(namedIdTable, textValue, pBound);
366 } else {
367 spvCheck(spvTextToUInt32(textValue, &id),
David Netoe3f70b92015-08-27 13:50:05 -0400368 DIAGNOSTIC << "Invalid "
369 << ((type == SPV_OPERAND_TYPE_RESULT_ID) ? "result " : "")
370 << "ID '" << textValue << "'.";
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100371 return SPV_ERROR_INVALID_TEXT);
372 }
373 pInst->words[pInst->wordCount++] = id;
374 if (*pBound <= id) {
375 *pBound = id + 1;
376 }
377 } break;
378 case SPV_OPERAND_TYPE_LITERAL_NUMBER: {
379 // NOTE: Special case for extension instruction lookup
380 if (OpExtInst == pInst->opcode) {
381 spv_ext_inst_desc extInst;
382 spvCheck(spvExtInstTableNameLookup(extInstTable, pInst->extInstType,
383 textValue, &extInst),
384 DIAGNOSTIC << "Invalid extended instruction name '"
385 << textValue << "'.";
386 return SPV_ERROR_INVALID_TEXT);
387 pInst->words[pInst->wordCount++] = extInst->ext_inst;
388 *ppExtraOperands = extInst->operandTypes;
389 return SPV_SUCCESS;
390 }
391
392 // TODO: Literal numbers can be any number up to 64 bits wide. This
393 // includes integers and floating point numbers.
394 spvCheck(spvTextToUInt32(textValue, &pInst->words[pInst->wordCount++]),
395 DIAGNOSTIC << "Invalid literal number '" << textValue << "'.";
396 return SPV_ERROR_INVALID_TEXT);
397 } break;
398 case SPV_OPERAND_TYPE_LITERAL: {
399 spv_literal_t literal = {};
400 spvCheck(spvTextToLiteral(textValue, &literal),
401 DIAGNOSTIC << "Invalid literal '" << textValue << "'.";
402 return SPV_ERROR_INVALID_TEXT);
403 switch (literal.type) {
404 case SPV_LITERAL_TYPE_INT_32:
405 spvCheck(spvBinaryEncodeU32((uint32_t)literal.value.i32, pInst,
406 position, pDiagnostic),
407 return SPV_ERROR_INVALID_TEXT);
408 break;
409 case SPV_LITERAL_TYPE_INT_64: {
410 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.i64, pInst,
411 position, pDiagnostic),
412 return SPV_ERROR_INVALID_TEXT);
413 } break;
414 case SPV_LITERAL_TYPE_UINT_32: {
415 spvCheck(spvBinaryEncodeU32(literal.value.u32, pInst, position,
416 pDiagnostic),
417 return SPV_ERROR_INVALID_TEXT);
418 } break;
419 case SPV_LITERAL_TYPE_UINT_64: {
420 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.u64, pInst,
421 position, pDiagnostic),
422 return SPV_ERROR_INVALID_TEXT);
423 } break;
424 case SPV_LITERAL_TYPE_FLOAT_32: {
425 spvCheck(spvBinaryEncodeU32((uint32_t)literal.value.f, pInst,
426 position, pDiagnostic),
427 return SPV_ERROR_INVALID_TEXT);
428 } break;
429 case SPV_LITERAL_TYPE_FLOAT_64: {
430 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.d, pInst,
431 position, pDiagnostic),
432 return SPV_ERROR_INVALID_TEXT);
433 } break;
434 case SPV_LITERAL_TYPE_STRING: {
435 spvCheck(spvBinaryEncodeString(literal.value.str, pInst, position,
436 pDiagnostic),
437 return SPV_ERROR_INVALID_TEXT);
438 } break;
439 default:
440 DIAGNOSTIC << "Invalid literal '" << textValue << "'";
441 return SPV_ERROR_INVALID_TEXT;
442 }
443 } break;
444 case SPV_OPERAND_TYPE_LITERAL_STRING: {
445 size_t len = strlen(textValue);
446 spvCheck('"' != textValue[0] && '"' != textValue[len - 1],
447 DIAGNOSTIC << "Invalid literal string '" << textValue
448 << "', expected quotes.";
449 return SPV_ERROR_INVALID_TEXT);
450 // NOTE: Strip quotes
451 std::string text(textValue + 1, len - 2);
452
453 // NOTE: Special case for extended instruction library import
454 if (OpExtInstImport == pInst->opcode) {
455 pInst->extInstType = spvExtInstImportTypeGet(text.c_str());
456 }
457
458 spvCheck(
459 spvBinaryEncodeString(text.c_str(), pInst, position, pDiagnostic),
460 return SPV_ERROR_INVALID_TEXT);
461 } break;
462 default: {
463 // NOTE: All non literal operands are handled here using the operand
464 // table.
465 spv_operand_desc entry;
466 spvCheck(spvOperandTableNameLookup(operandTable, type, textValue, &entry),
467 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '"
468 << textValue << "'.";
469 return SPV_ERROR_INVALID_TEXT;);
470 spvCheck(spvBinaryEncodeU32(entry->value, pInst, position, pDiagnostic),
471 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '"
472 << textValue << "'.";
473 return SPV_ERROR_INVALID_TEXT;);
474 if (ppExtraOperands && entry->operandTypes[0] != SPV_OPERAND_TYPE_NONE) {
475 *ppExtraOperands = entry->operandTypes;
476 }
477 } break;
478 }
479 return SPV_SUCCESS;
480}
481
482spv_result_t spvTextEncodeOpcode(
483 const spv_text text, const spv_opcode_table opcodeTable,
484 const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
485 spv_named_id_table namedIdTable, uint32_t *pBound, spv_instruction_t *pInst,
486 spv_position position, spv_diagnostic *pDiagnostic) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400487 // An assembly instruction has two possible formats:
488 // 1. <opcode> <operand>.., e.g., "OpMemoryModel Physical64 OpenCL".
489 // 2. <result-id> = <opcode> <operand>.., e.g., "%void = OpTypeVoid".
490
491 // Assume it's the first format at the beginning.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100492 std::string opcodeName;
493 spv_position_t nextPosition = {};
Lei Zhangdfc50082015-08-21 11:50:55 -0400494 spv_result_t error =
495 spvTextWordGet(text, position, opcodeName, &nextPosition);
496 spvCheck(error, return error);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100497
498 // NOTE: Handle insertion of an immediate integer into the binary stream
Lei Zhangdfc50082015-08-21 11:50:55 -0400499 bool immediate = false;
500 spvCheck('!' == text->str[position->index], immediate = true);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100501 if (immediate) {
502 const char *begin = opcodeName.data() + 1;
503 char *end = nullptr;
504 uint32_t immediateInt = strtoul(begin, &end, 0);
505 size_t size = opcodeName.size() - 1;
506 spvCheck(size != (size_t)(end - begin),
507 DIAGNOSTIC << "Invalid immediate integer '" << opcodeName << "'.";
508 return SPV_ERROR_INVALID_TEXT);
509 position->column += opcodeName.size();
510 position->index += opcodeName.size();
511 pInst->words[0] = immediateInt;
512 pInst->wordCount = 1;
513 return SPV_SUCCESS;
514 }
515
Lei Zhangdfc50082015-08-21 11:50:55 -0400516 // Handle value generating instructions (the second format above) here.
517 std::string result_id;
518 spv_position_t result_id_position = {};
519 // If the word we get doesn't start with "Op", assume it's an <result-id>
520 // from now.
521 spvCheck(!spvStartsWithOp(text, position), result_id = opcodeName);
522 if (!result_id.empty()) {
523 spvCheck('%' != result_id.front(),
524 DIAGNOSTIC << "Expected <opcode> or <result-id> at the beginning "
525 "of an instruction, found '"
526 << result_id << "'.";
527 return SPV_ERROR_INVALID_TEXT);
528 result_id_position = *position;
529 *position = nextPosition;
530 spvCheck(spvTextAdvance(text, position),
531 DIAGNOSTIC << "Expected '=', found end of stream.";
532 return SPV_ERROR_INVALID_TEXT);
533 // The '=' sign.
534 std::string equal_sign;
535 error = spvTextWordGet(text, position, equal_sign, &nextPosition);
536 spvCheck("=" != equal_sign, DIAGNOSTIC << "'=' expected after result id.";
537 return SPV_ERROR_INVALID_TEXT);
538
539 // The <opcode> after the '=' sign.
540 *position = nextPosition;
541 spvCheck(spvTextAdvance(text, position),
542 DIAGNOSTIC << "Expected opcode, found end of stream.";
543 return SPV_ERROR_INVALID_TEXT);
Lei Zhang977e9bc2015-08-21 11:52:03 -0400544 error = spvTextWordGet(text, position, opcodeName, &nextPosition);
545 spvCheck(error, return error);
Lei Zhangdfc50082015-08-21 11:50:55 -0400546 spvCheck(!spvStartsWithOp(text, position),
547 DIAGNOSTIC << "Invalid Opcode prefix '" << opcodeName << "'.";
548 return SPV_ERROR_INVALID_TEXT);
Lei Zhangdfc50082015-08-21 11:50:55 -0400549 }
550
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100551 // NOTE: The table contains Opcode names without the "Op" prefix.
552 const char *pInstName = opcodeName.data() + 2;
553
554 spv_opcode_desc opcodeEntry;
Lei Zhangdfc50082015-08-21 11:50:55 -0400555 error = spvOpcodeTableNameLookup(opcodeTable, pInstName, &opcodeEntry);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100556 spvCheck(error, DIAGNOSTIC << "Invalid Opcode name '"
557 << getWord(text->str + position->index) << "'";
558 return error);
559 pInst->opcode = opcodeEntry->opcode;
560 *position = nextPosition;
561 pInst->wordCount++;
562
Lei Zhangdfc50082015-08-21 11:50:55 -0400563 // Get the arugment index for <result-id>. Used for handling the <result-id>
564 // for value generating instructions below.
565 const int16_t result_id_index = spvOpcodeResultIdIndex(opcodeEntry);
566
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100567 // NOTE: Process the fixed size operands
568 const spv_operand_type_t *extraOperandTypes = nullptr;
569 for (int32_t operandIndex = 0; operandIndex < (opcodeEntry->wordCount - 1);
570 ++operandIndex) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400571 if (operandIndex == result_id_index && !result_id.empty()) {
572 // Handling the <result-id> for value generating instructions.
573 error = spvTextEncodeOperand(
574 SPV_OPERAND_TYPE_RESULT_ID, result_id.c_str(), operandTable,
575 extInstTable, namedIdTable, pInst, &extraOperandTypes, pBound,
576 &result_id_position, pDiagnostic);
577 spvCheck(error, return error);
578 continue;
579 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100580 spvCheck(spvTextAdvance(text, position),
581 DIAGNOSTIC << "Expected operand, found end of stream.";
582 return SPV_ERROR_INVALID_TEXT);
583
584 std::string operandValue;
585 error = spvTextWordGet(text, position, operandValue, &nextPosition);
586 spvCheck(error, return error);
587
588 error = spvTextEncodeOperand(
589 opcodeEntry->operandTypes[operandIndex], operandValue.c_str(),
590 operandTable, extInstTable, namedIdTable, pInst, &extraOperandTypes,
591 pBound, position, pDiagnostic);
592 spvCheck(error, return error);
593
594 *position = nextPosition;
595 }
596
597 if (spvOpcodeIsVariable(opcodeEntry)) {
598 if (!extraOperandTypes) {
599 // NOTE: Handle variable length not defined by an immediate previously
600 // encountered in the Opcode.
601 spv_operand_type_t type =
602 opcodeEntry->operandTypes[opcodeEntry->wordCount - 1];
603
604 while (!spvTextAdvance(text, position)) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400605 // NOTE: If this is the end of the current instruction stream and we
606 // break out of this loop.
607 if (spvTextIsStartOfNewInst(text, position)) break;
608
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100609 std::string textValue;
610 spvTextWordGet(text, position, textValue, &nextPosition);
611
Lei Zhangdfc50082015-08-21 11:50:55 -0400612 if (SPV_OPERAND_TYPE_LITERAL_STRING == type) {
613 spvCheck(spvTextAdvance(text, position),
614 DIAGNOSTIC << "Invalid string, found end of stream.";
615 return SPV_ERROR_INVALID_TEXT);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100616
Lei Zhangdfc50082015-08-21 11:50:55 -0400617 std::string string;
618 spvCheck(spvTextStringGet(text, position, string, &nextPosition),
619 DIAGNOSTIC << "Invalid string, new line or end of stream.";
620 return SPV_ERROR_INVALID_TEXT);
621 spvCheck(spvTextEncodeOperand(type, string.c_str(), operandTable,
622 extInstTable, namedIdTable, pInst,
623 nullptr, pBound, position, pDiagnostic),
624 return SPV_ERROR_INVALID_TEXT);
625 } else {
626 spvCheck(spvTextEncodeOperand(type, textValue.c_str(), operandTable,
627 extInstTable, namedIdTable, pInst,
628 nullptr, pBound, position, pDiagnostic),
629 return SPV_ERROR_INVALID_TEXT);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100630 }
Lei Zhangdfc50082015-08-21 11:50:55 -0400631 *position = nextPosition;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100632 }
633 } else {
634 // NOTE: Process the variable size operands defined by an immediate
635 // previously encountered in the Opcode.
636 uint64_t extraOperandsIndex = 0;
637 while (extraOperandTypes[extraOperandsIndex]) {
638 spvCheck(spvTextAdvance(text, position),
639 DIAGNOSTIC << "Expected operand, found end of stream.";
640 return SPV_ERROR_INVALID_TEXT);
641
642 std::string operandValue;
643 error = spvTextWordGet(text, position, operandValue, &nextPosition);
644
645 error = spvTextEncodeOperand(extraOperandTypes[extraOperandsIndex],
646 operandValue.c_str(), operandTable,
647 extInstTable, namedIdTable, pInst, nullptr,
648 pBound, position, pDiagnostic);
649 spvCheck(error, return error);
650
651 *position = nextPosition;
652
653 extraOperandsIndex++;
654 }
655 }
656 }
657
658 pInst->words[0] = spvOpcodeMake(pInst->wordCount, opcodeEntry->opcode);
659
660 return SPV_SUCCESS;
661}
662
663spv_result_t spvTextToBinary(const spv_text text,
664 const spv_opcode_table opcodeTable,
665 const spv_operand_table operandTable,
666 const spv_ext_inst_table extInstTable,
667 spv_binary *pBinary, spv_diagnostic *pDiagnostic) {
668 spv_position_t position = {};
669 spvCheck(!text->str || !text->length, DIAGNOSTIC << "Text stream is empty.";
670 return SPV_ERROR_INVALID_TEXT);
671 spvCheck(!opcodeTable || !operandTable || !extInstTable,
672 return SPV_ERROR_INVALID_TABLE);
673 spvCheck(!pBinary, return SPV_ERROR_INVALID_POINTER);
674 spvCheck(!pDiagnostic, return SPV_ERROR_INVALID_DIAGNOSTIC);
675
676 // NOTE: Ensure diagnostic is zero initialised
677 *pDiagnostic = {};
678
679 uint32_t bound = 1;
680
681 std::vector<spv_instruction_t> instructions;
682
683 spvCheck(spvTextAdvance(text, &position), DIAGNOSTIC
684 << "Text stream is empty.";
685 return SPV_ERROR_INVALID_TEXT);
686
687 spv_named_id_table namedIdTable = spvNamedIdTableCreate();
688 spvCheck(!namedIdTable, return SPV_ERROR_OUT_OF_MEMORY);
689
690 spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
691 while (text->length > position.index) {
692 spv_instruction_t inst = {};
693 inst.extInstType = extInstType;
694
695 spvCheck(spvTextEncodeOpcode(text, opcodeTable, operandTable, extInstTable,
696 namedIdTable, &bound, &inst, &position,
697 pDiagnostic),
698 spvNamedIdTableDestory(namedIdTable);
699 return SPV_ERROR_INVALID_TEXT);
700 extInstType = inst.extInstType;
701
702 instructions.push_back(inst);
703
704 spvCheck(spvTextAdvance(text, &position), break);
705 }
706
707 spvNamedIdTableDestory(namedIdTable);
708
709 size_t totalSize = SPV_INDEX_INSTRUCTION;
710 for (auto &inst : instructions) {
711 totalSize += inst.wordCount;
712 }
713
714 uint32_t *data = new uint32_t[totalSize];
715 spvCheck(!data, return SPV_ERROR_OUT_OF_MEMORY);
716 uint64_t currentIndex = SPV_INDEX_INSTRUCTION;
717 for (auto &inst : instructions) {
718 memcpy(data + currentIndex, inst.words, sizeof(uint32_t) * inst.wordCount);
719 currentIndex += inst.wordCount;
720 }
721
722 spv_binary binary = new spv_binary_t();
723 spvCheck(!binary, delete[] data; return SPV_ERROR_OUT_OF_MEMORY);
724 binary->code = data;
725 binary->wordCount = totalSize;
726
727 spv_result_t error = spvBinaryHeaderSet(binary, bound);
728 spvCheck(error, spvBinaryDestroy(binary); return error);
729
730 *pBinary = binary;
731
732 return SPV_SUCCESS;
733}
734
735void spvTextDestroy(spv_text text) {
736 spvCheck(!text, return );
737 if (text->str) {
738 delete[] text->str;
739 }
740 delete text;
741}