blob: 1c9127636c859643067653bf481bfd2f048b4730 [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>
37#include <stdlib.h>
38#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;
250 bool isFloat = false;
251 bool isString = false;
252
253 if ('-' == textValue[0]) {
254 isSigned = true;
255 }
256
257 for (uint64_t index = 0; index < strlen(textValue); ++index) {
258 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 '.':
271 isFloat = true;
272 break;
273 default:
274 isString = true;
275 break;
276 }
277 }
278
279 if (isString) {
280 pLiteral->type = SPV_LITERAL_TYPE_STRING;
281 strncpy(pLiteral->value.str, textValue, strlen(textValue));
282 } else if (isFloat) {
283 double d = strtod(textValue, nullptr);
284 float f = (float)d;
285 if (d == (double)f) {
286 pLiteral->type = SPV_LITERAL_TYPE_FLOAT_32;
287 pLiteral->value.f = f;
288 } else {
289 pLiteral->type = SPV_LITERAL_TYPE_FLOAT_64;
290 pLiteral->value.d = d;
291 }
292 } else if (isSigned) {
293 int64_t i64 = strtoll(textValue, nullptr, 10);
294 int32_t i32 = (int32_t)i64;
295 if (i64 == (int64_t)i32) {
296 pLiteral->type = SPV_LITERAL_TYPE_INT_32;
297 pLiteral->value.i32 = i32;
298 } else {
299 pLiteral->type = SPV_LITERAL_TYPE_INT_64;
300 pLiteral->value.i64 = i64;
301 }
302 } else {
303 uint64_t u64 = strtoull(textValue, nullptr, 10);
304 uint32_t u32 = (uint32_t)u64;
305 if (u64 == (uint64_t)u32) {
306 pLiteral->type = SPV_LITERAL_TYPE_UINT_32;
307 pLiteral->value.u32 = u32;
308 } else {
309 pLiteral->type = SPV_LITERAL_TYPE_UINT_64;
310 pLiteral->value.u64 = u64;
311 }
312 }
313
314 return SPV_SUCCESS;
315}
316
317spv_result_t spvTextEncodeOperand(
318 const spv_operand_type_t type, const char *textValue,
319 const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
320 spv_named_id_table namedIdTable, spv_instruction_t *pInst,
321 const spv_operand_type_t **ppExtraOperands, uint32_t *pBound,
322 const spv_position position, spv_diagnostic *pDiagnostic) {
323 // NOTE: Handle immediate int in the stream
324 if ('!' == textValue[0]) {
325 const char *begin = textValue + 1;
326 char *end = nullptr;
327 uint32_t immediateInt = strtoul(begin, &end, 0);
328 size_t size = strlen(textValue);
329 size_t length = (end - begin);
330 spvCheck(size - 1 != length, DIAGNOSTIC << "Invalid immediate integer '"
331 << textValue << "'.";
332 return SPV_ERROR_INVALID_TEXT);
333 position->column += size;
334 position->index += size;
335 pInst->words[pInst->wordCount] = immediateInt;
336 pInst->wordCount += 1;
337 return SPV_SUCCESS;
338 }
339
340 switch (type) {
341 case SPV_OPERAND_TYPE_ID: {
342 if ('$' == textValue[0]) {
343 textValue++;
344 }
345 // TODO: Force all ID's to be prefixed with '$'.
346 uint32_t id = 0;
347 if (spvTextIsNamedId(textValue)) {
348 id = spvNamedIdAssignOrGet(namedIdTable, textValue, pBound);
349 } else {
350 spvCheck(spvTextToUInt32(textValue, &id),
351 DIAGNOSTIC << "Invalid ID '" << textValue << "'.";
352 return SPV_ERROR_INVALID_TEXT);
353 }
354 pInst->words[pInst->wordCount++] = id;
355 if (*pBound <= id) {
356 *pBound = id + 1;
357 }
358 } break;
359 case SPV_OPERAND_TYPE_RESULT_ID: {
360 if ('%' == textValue[0]) {
361 textValue++;
362 }
363 // TODO: Force all Result ID's to be prefixed with '%'.
364 uint32_t id = 0;
365 if (spvTextIsNamedId(textValue)) {
366 id = spvNamedIdAssignOrGet(namedIdTable, textValue, pBound);
367 } else {
368 spvCheck(spvTextToUInt32(textValue, &id),
369 DIAGNOSTIC << "Invalid result ID '" << textValue << "'.";
370 return SPV_ERROR_INVALID_TEXT);
371 }
372 pInst->words[pInst->wordCount++] = id;
373 if (*pBound <= id) {
374 *pBound = id + 1;
375 }
376 } break;
377 case SPV_OPERAND_TYPE_LITERAL_NUMBER: {
378 // NOTE: Special case for extension instruction lookup
379 if (OpExtInst == pInst->opcode) {
380 spv_ext_inst_desc extInst;
381 spvCheck(spvExtInstTableNameLookup(extInstTable, pInst->extInstType,
382 textValue, &extInst),
383 DIAGNOSTIC << "Invalid extended instruction name '"
384 << textValue << "'.";
385 return SPV_ERROR_INVALID_TEXT);
386 pInst->words[pInst->wordCount++] = extInst->ext_inst;
387 *ppExtraOperands = extInst->operandTypes;
388 return SPV_SUCCESS;
389 }
390
391 // TODO: Literal numbers can be any number up to 64 bits wide. This
392 // includes integers and floating point numbers.
393 spvCheck(spvTextToUInt32(textValue, &pInst->words[pInst->wordCount++]),
394 DIAGNOSTIC << "Invalid literal number '" << textValue << "'.";
395 return SPV_ERROR_INVALID_TEXT);
396 } break;
397 case SPV_OPERAND_TYPE_LITERAL: {
398 spv_literal_t literal = {};
399 spvCheck(spvTextToLiteral(textValue, &literal),
400 DIAGNOSTIC << "Invalid literal '" << textValue << "'.";
401 return SPV_ERROR_INVALID_TEXT);
402 switch (literal.type) {
403 case SPV_LITERAL_TYPE_INT_32:
404 spvCheck(spvBinaryEncodeU32((uint32_t)literal.value.i32, pInst,
405 position, pDiagnostic),
406 return SPV_ERROR_INVALID_TEXT);
407 break;
408 case SPV_LITERAL_TYPE_INT_64: {
409 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.i64, pInst,
410 position, pDiagnostic),
411 return SPV_ERROR_INVALID_TEXT);
412 } break;
413 case SPV_LITERAL_TYPE_UINT_32: {
414 spvCheck(spvBinaryEncodeU32(literal.value.u32, pInst, position,
415 pDiagnostic),
416 return SPV_ERROR_INVALID_TEXT);
417 } break;
418 case SPV_LITERAL_TYPE_UINT_64: {
419 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.u64, pInst,
420 position, pDiagnostic),
421 return SPV_ERROR_INVALID_TEXT);
422 } break;
423 case SPV_LITERAL_TYPE_FLOAT_32: {
424 spvCheck(spvBinaryEncodeU32((uint32_t)literal.value.f, pInst,
425 position, pDiagnostic),
426 return SPV_ERROR_INVALID_TEXT);
427 } break;
428 case SPV_LITERAL_TYPE_FLOAT_64: {
429 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.d, pInst,
430 position, pDiagnostic),
431 return SPV_ERROR_INVALID_TEXT);
432 } break;
433 case SPV_LITERAL_TYPE_STRING: {
434 spvCheck(spvBinaryEncodeString(literal.value.str, pInst, position,
435 pDiagnostic),
436 return SPV_ERROR_INVALID_TEXT);
437 } break;
438 default:
439 DIAGNOSTIC << "Invalid literal '" << textValue << "'";
440 return SPV_ERROR_INVALID_TEXT;
441 }
442 } break;
443 case SPV_OPERAND_TYPE_LITERAL_STRING: {
444 size_t len = strlen(textValue);
445 spvCheck('"' != textValue[0] && '"' != textValue[len - 1],
446 DIAGNOSTIC << "Invalid literal string '" << textValue
447 << "', expected quotes.";
448 return SPV_ERROR_INVALID_TEXT);
449 // NOTE: Strip quotes
450 std::string text(textValue + 1, len - 2);
451
452 // NOTE: Special case for extended instruction library import
453 if (OpExtInstImport == pInst->opcode) {
454 pInst->extInstType = spvExtInstImportTypeGet(text.c_str());
455 }
456
457 spvCheck(
458 spvBinaryEncodeString(text.c_str(), pInst, position, pDiagnostic),
459 return SPV_ERROR_INVALID_TEXT);
460 } break;
461 default: {
462 // NOTE: All non literal operands are handled here using the operand
463 // table.
464 spv_operand_desc entry;
465 spvCheck(spvOperandTableNameLookup(operandTable, type, textValue, &entry),
466 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '"
467 << textValue << "'.";
468 return SPV_ERROR_INVALID_TEXT;);
469 spvCheck(spvBinaryEncodeU32(entry->value, pInst, position, pDiagnostic),
470 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '"
471 << textValue << "'.";
472 return SPV_ERROR_INVALID_TEXT;);
473 if (ppExtraOperands && entry->operandTypes[0] != SPV_OPERAND_TYPE_NONE) {
474 *ppExtraOperands = entry->operandTypes;
475 }
476 } break;
477 }
478 return SPV_SUCCESS;
479}
480
481spv_result_t spvTextEncodeOpcode(
482 const spv_text text, const spv_opcode_table opcodeTable,
483 const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
484 spv_named_id_table namedIdTable, uint32_t *pBound, spv_instruction_t *pInst,
485 spv_position position, spv_diagnostic *pDiagnostic) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400486 // An assembly instruction has two possible formats:
487 // 1. <opcode> <operand>.., e.g., "OpMemoryModel Physical64 OpenCL".
488 // 2. <result-id> = <opcode> <operand>.., e.g., "%void = OpTypeVoid".
489
490 // Assume it's the first format at the beginning.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100491 std::string opcodeName;
492 spv_position_t nextPosition = {};
Lei Zhangdfc50082015-08-21 11:50:55 -0400493 spv_result_t error =
494 spvTextWordGet(text, position, opcodeName, &nextPosition);
495 spvCheck(error, return error);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100496
497 // NOTE: Handle insertion of an immediate integer into the binary stream
Lei Zhangdfc50082015-08-21 11:50:55 -0400498 bool immediate = false;
499 spvCheck('!' == text->str[position->index], immediate = true);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100500 if (immediate) {
501 const char *begin = opcodeName.data() + 1;
502 char *end = nullptr;
503 uint32_t immediateInt = strtoul(begin, &end, 0);
504 size_t size = opcodeName.size() - 1;
505 spvCheck(size != (size_t)(end - begin),
506 DIAGNOSTIC << "Invalid immediate integer '" << opcodeName << "'.";
507 return SPV_ERROR_INVALID_TEXT);
508 position->column += opcodeName.size();
509 position->index += opcodeName.size();
510 pInst->words[0] = immediateInt;
511 pInst->wordCount = 1;
512 return SPV_SUCCESS;
513 }
514
Lei Zhangdfc50082015-08-21 11:50:55 -0400515 // Handle value generating instructions (the second format above) here.
516 std::string result_id;
517 spv_position_t result_id_position = {};
518 // If the word we get doesn't start with "Op", assume it's an <result-id>
519 // from now.
520 spvCheck(!spvStartsWithOp(text, position), result_id = opcodeName);
521 if (!result_id.empty()) {
522 spvCheck('%' != result_id.front(),
523 DIAGNOSTIC << "Expected <opcode> or <result-id> at the beginning "
524 "of an instruction, found '"
525 << result_id << "'.";
526 return SPV_ERROR_INVALID_TEXT);
527 result_id_position = *position;
528 *position = nextPosition;
529 spvCheck(spvTextAdvance(text, position),
530 DIAGNOSTIC << "Expected '=', found end of stream.";
531 return SPV_ERROR_INVALID_TEXT);
532 // The '=' sign.
533 std::string equal_sign;
534 error = spvTextWordGet(text, position, equal_sign, &nextPosition);
535 spvCheck("=" != equal_sign, DIAGNOSTIC << "'=' expected after result id.";
536 return SPV_ERROR_INVALID_TEXT);
537
538 // The <opcode> after the '=' sign.
539 *position = nextPosition;
540 spvCheck(spvTextAdvance(text, position),
541 DIAGNOSTIC << "Expected opcode, found end of stream.";
542 return SPV_ERROR_INVALID_TEXT);
Lei Zhang977e9bc2015-08-21 11:52:03 -0400543 error = spvTextWordGet(text, position, opcodeName, &nextPosition);
544 spvCheck(error, return error);
Lei Zhangdfc50082015-08-21 11:50:55 -0400545 spvCheck(!spvStartsWithOp(text, position),
546 DIAGNOSTIC << "Invalid Opcode prefix '" << opcodeName << "'.";
547 return SPV_ERROR_INVALID_TEXT);
Lei Zhangdfc50082015-08-21 11:50:55 -0400548 }
549
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100550 // NOTE: The table contains Opcode names without the "Op" prefix.
551 const char *pInstName = opcodeName.data() + 2;
552
553 spv_opcode_desc opcodeEntry;
Lei Zhangdfc50082015-08-21 11:50:55 -0400554 error = spvOpcodeTableNameLookup(opcodeTable, pInstName, &opcodeEntry);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100555 spvCheck(error, DIAGNOSTIC << "Invalid Opcode name '"
556 << getWord(text->str + position->index) << "'";
557 return error);
558 pInst->opcode = opcodeEntry->opcode;
559 *position = nextPosition;
560 pInst->wordCount++;
561
Lei Zhangdfc50082015-08-21 11:50:55 -0400562 // Get the arugment index for <result-id>. Used for handling the <result-id>
563 // for value generating instructions below.
564 const int16_t result_id_index = spvOpcodeResultIdIndex(opcodeEntry);
565
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100566 // NOTE: Process the fixed size operands
567 const spv_operand_type_t *extraOperandTypes = nullptr;
568 for (int32_t operandIndex = 0; operandIndex < (opcodeEntry->wordCount - 1);
569 ++operandIndex) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400570 if (operandIndex == result_id_index && !result_id.empty()) {
571 // Handling the <result-id> for value generating instructions.
572 error = spvTextEncodeOperand(
573 SPV_OPERAND_TYPE_RESULT_ID, result_id.c_str(), operandTable,
574 extInstTable, namedIdTable, pInst, &extraOperandTypes, pBound,
575 &result_id_position, pDiagnostic);
576 spvCheck(error, return error);
577 continue;
578 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100579 spvCheck(spvTextAdvance(text, position),
580 DIAGNOSTIC << "Expected operand, found end of stream.";
581 return SPV_ERROR_INVALID_TEXT);
582
583 std::string operandValue;
584 error = spvTextWordGet(text, position, operandValue, &nextPosition);
585 spvCheck(error, return error);
586
587 error = spvTextEncodeOperand(
588 opcodeEntry->operandTypes[operandIndex], operandValue.c_str(),
589 operandTable, extInstTable, namedIdTable, pInst, &extraOperandTypes,
590 pBound, position, pDiagnostic);
591 spvCheck(error, return error);
592
593 *position = nextPosition;
594 }
595
596 if (spvOpcodeIsVariable(opcodeEntry)) {
597 if (!extraOperandTypes) {
598 // NOTE: Handle variable length not defined by an immediate previously
599 // encountered in the Opcode.
600 spv_operand_type_t type =
601 opcodeEntry->operandTypes[opcodeEntry->wordCount - 1];
602
603 while (!spvTextAdvance(text, position)) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400604 // NOTE: If this is the end of the current instruction stream and we
605 // break out of this loop.
606 if (spvTextIsStartOfNewInst(text, position)) break;
607
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100608 std::string textValue;
609 spvTextWordGet(text, position, textValue, &nextPosition);
610
Lei Zhangdfc50082015-08-21 11:50:55 -0400611 if (SPV_OPERAND_TYPE_LITERAL_STRING == type) {
612 spvCheck(spvTextAdvance(text, position),
613 DIAGNOSTIC << "Invalid string, found end of stream.";
614 return SPV_ERROR_INVALID_TEXT);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100615
Lei Zhangdfc50082015-08-21 11:50:55 -0400616 std::string string;
617 spvCheck(spvTextStringGet(text, position, string, &nextPosition),
618 DIAGNOSTIC << "Invalid string, new line or end of stream.";
619 return SPV_ERROR_INVALID_TEXT);
620 spvCheck(spvTextEncodeOperand(type, string.c_str(), operandTable,
621 extInstTable, namedIdTable, pInst,
622 nullptr, pBound, position, pDiagnostic),
623 return SPV_ERROR_INVALID_TEXT);
624 } else {
625 spvCheck(spvTextEncodeOperand(type, textValue.c_str(), operandTable,
626 extInstTable, namedIdTable, pInst,
627 nullptr, pBound, position, pDiagnostic),
628 return SPV_ERROR_INVALID_TEXT);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100629 }
Lei Zhangdfc50082015-08-21 11:50:55 -0400630 *position = nextPosition;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100631 }
632 } else {
633 // NOTE: Process the variable size operands defined by an immediate
634 // previously encountered in the Opcode.
635 uint64_t extraOperandsIndex = 0;
636 while (extraOperandTypes[extraOperandsIndex]) {
637 spvCheck(spvTextAdvance(text, position),
638 DIAGNOSTIC << "Expected operand, found end of stream.";
639 return SPV_ERROR_INVALID_TEXT);
640
641 std::string operandValue;
642 error = spvTextWordGet(text, position, operandValue, &nextPosition);
643
644 error = spvTextEncodeOperand(extraOperandTypes[extraOperandsIndex],
645 operandValue.c_str(), operandTable,
646 extInstTable, namedIdTable, pInst, nullptr,
647 pBound, position, pDiagnostic);
648 spvCheck(error, return error);
649
650 *position = nextPosition;
651
652 extraOperandsIndex++;
653 }
654 }
655 }
656
657 pInst->words[0] = spvOpcodeMake(pInst->wordCount, opcodeEntry->opcode);
658
659 return SPV_SUCCESS;
660}
661
662spv_result_t spvTextToBinary(const spv_text text,
663 const spv_opcode_table opcodeTable,
664 const spv_operand_table operandTable,
665 const spv_ext_inst_table extInstTable,
666 spv_binary *pBinary, spv_diagnostic *pDiagnostic) {
667 spv_position_t position = {};
668 spvCheck(!text->str || !text->length, DIAGNOSTIC << "Text stream is empty.";
669 return SPV_ERROR_INVALID_TEXT);
670 spvCheck(!opcodeTable || !operandTable || !extInstTable,
671 return SPV_ERROR_INVALID_TABLE);
672 spvCheck(!pBinary, return SPV_ERROR_INVALID_POINTER);
673 spvCheck(!pDiagnostic, return SPV_ERROR_INVALID_DIAGNOSTIC);
674
675 // NOTE: Ensure diagnostic is zero initialised
676 *pDiagnostic = {};
677
678 uint32_t bound = 1;
679
680 std::vector<spv_instruction_t> instructions;
681
682 spvCheck(spvTextAdvance(text, &position), DIAGNOSTIC
683 << "Text stream is empty.";
684 return SPV_ERROR_INVALID_TEXT);
685
686 spv_named_id_table namedIdTable = spvNamedIdTableCreate();
687 spvCheck(!namedIdTable, return SPV_ERROR_OUT_OF_MEMORY);
688
689 spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
690 while (text->length > position.index) {
691 spv_instruction_t inst = {};
692 inst.extInstType = extInstType;
693
694 spvCheck(spvTextEncodeOpcode(text, opcodeTable, operandTable, extInstTable,
695 namedIdTable, &bound, &inst, &position,
696 pDiagnostic),
697 spvNamedIdTableDestory(namedIdTable);
698 return SPV_ERROR_INVALID_TEXT);
699 extInstType = inst.extInstType;
700
701 instructions.push_back(inst);
702
703 spvCheck(spvTextAdvance(text, &position), break);
704 }
705
706 spvNamedIdTableDestory(namedIdTable);
707
708 size_t totalSize = SPV_INDEX_INSTRUCTION;
709 for (auto &inst : instructions) {
710 totalSize += inst.wordCount;
711 }
712
713 uint32_t *data = new uint32_t[totalSize];
714 spvCheck(!data, return SPV_ERROR_OUT_OF_MEMORY);
715 uint64_t currentIndex = SPV_INDEX_INSTRUCTION;
716 for (auto &inst : instructions) {
717 memcpy(data + currentIndex, inst.words, sizeof(uint32_t) * inst.wordCount);
718 currentIndex += inst.wordCount;
719 }
720
721 spv_binary binary = new spv_binary_t();
722 spvCheck(!binary, delete[] data; return SPV_ERROR_OUT_OF_MEMORY);
723 binary->code = data;
724 binary->wordCount = totalSize;
725
726 spv_result_t error = spvBinaryHeaderSet(binary, bound);
727 spvCheck(error, spvBinaryDestroy(binary); return error);
728
729 *pBinary = binary;
730
731 return SPV_SUCCESS;
732}
733
734void spvTextDestroy(spv_text text) {
735 spvCheck(!text, return );
736 if (text->str) {
737 delete[] text->str;
738 }
739 delete text;
740}