blob: f33d8287c876294d8aef46cfbcc83dd1efdc1823 [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:
114 position->line++;
115 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 ';':
127 return spvTextAdvanceLine(text, position);
128 case ' ':
129 case '\t':
130 position->column++;
131 position->index++;
132 return spvTextAdvance(text, position);
133 case '\n':
134 position->column = 0;
135 position->line++;
136 position->index++;
137 return spvTextAdvance(text, position);
138 default:
139 break;
140 }
141
142 return SPV_SUCCESS;
143}
144
145spv_result_t spvTextWordGet(const spv_text text,
146 const spv_position startPosition, std::string &word,
147 spv_position endPosition) {
148 spvCheck(!text->str || !text->length, return SPV_ERROR_INVALID_TEXT);
149 spvCheck(!startPosition || !endPosition, return SPV_ERROR_INVALID_POINTER);
150
151 *endPosition = *startPosition;
152
153 // NOTE: Assumes first character is not white space!
154 while (true) {
155 switch (text->str[endPosition->index]) {
156 case ' ':
157 case '\t':
158 case '\n':
159 case '\0': { // NOTE: End of word found!
160 word.assign(text->str + startPosition->index,
161 (size_t)(endPosition->index - startPosition->index));
162 return SPV_SUCCESS;
163 }
164 default:
165 break;
166 }
167
168 endPosition->column++;
169 endPosition->index++;
170 }
171}
172
Lei Zhangdfc50082015-08-21 11:50:55 -0400173// Returns true if the string at the given position in text starts with "Op".
174static bool spvStartsWithOp(const spv_text text, const spv_position position) {
175 if (text->length < position->index + 2) return false;
176 return ('O' == text->str[position->index] &&
177 'p' == text->str[position->index + 1]);
178}
179
180// Returns true if a new instruction begins at the given position in text.
181static bool spvTextIsStartOfNewInst(const spv_text text,
182 const spv_position position) {
183 spv_position_t nextPosition = *position;
184 if (spvTextAdvance(text, &nextPosition)) return false;
185 if (spvStartsWithOp(text, position)) return true;
186
187 std::string word;
188 spv_position_t startPosition = nextPosition;
189 if (spvTextWordGet(text, &startPosition, word, &nextPosition)) return false;
190 if ('%' != word.front()) return false;
191
192 if (spvTextAdvance(text, &nextPosition)) return false;
193 startPosition = nextPosition;
194 if (spvTextWordGet(text, &startPosition, word, &nextPosition)) return false;
195 if ("=" != word) return false;
196
197 if (spvTextAdvance(text, &nextPosition)) return false;
198 startPosition = nextPosition;
199 if (spvStartsWithOp(text, &startPosition)) return true;
200 return false;
201}
202
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100203spv_result_t spvTextStringGet(const spv_text text,
204 const spv_position startPosition,
205 std::string &string, spv_position endPosition) {
206 spvCheck(!text->str || !text->length, return SPV_ERROR_INVALID_TEXT);
207 spvCheck(!startPosition || !endPosition, return SPV_ERROR_INVALID_POINTER);
208
209 spvCheck('"' != text->str[startPosition->index],
210 return SPV_ERROR_INVALID_TEXT);
211
212 *endPosition = *startPosition;
213
214 // NOTE: Assumes first character is not white space
215 while (true) {
216 endPosition->column++;
217 endPosition->index++;
218
219 switch (text->str[endPosition->index]) {
220 case '"': {
221 endPosition->column++;
222 endPosition->index++;
223
224 string.assign(text->str + startPosition->index,
225 (size_t)(endPosition->index - startPosition->index));
226
227 return SPV_SUCCESS;
228 }
229 case '\n':
230 case '\0':
231 return SPV_ERROR_INVALID_TEXT;
232 default:
233 break;
234 }
235 }
236}
237
238spv_result_t spvTextToUInt32(const char *textValue, uint32_t *pValue) {
239 char *endPtr = nullptr;
240 *pValue = strtoul(textValue, &endPtr, 0);
241 if (0 == *pValue && textValue == endPtr) {
242 return SPV_ERROR_INVALID_TEXT;
243 }
244 return SPV_SUCCESS;
245}
246
247spv_result_t spvTextToLiteral(const char *textValue, spv_literal_t *pLiteral) {
248 bool isSigned = false;
249 bool isFloat = false;
250 bool isString = false;
251
252 if ('-' == textValue[0]) {
253 isSigned = true;
254 }
255
256 for (uint64_t index = 0; index < strlen(textValue); ++index) {
257 switch (textValue[index]) {
258 case '0':
259 case '1':
260 case '2':
261 case '3':
262 case '4':
263 case '5':
264 case '6':
265 case '7':
266 case '8':
267 case '9':
268 break;
269 case '.':
270 isFloat = true;
271 break;
272 default:
273 isString = true;
274 break;
275 }
276 }
277
278 if (isString) {
279 pLiteral->type = SPV_LITERAL_TYPE_STRING;
280 strncpy(pLiteral->value.str, textValue, strlen(textValue));
281 } else if (isFloat) {
282 double d = strtod(textValue, nullptr);
283 float f = (float)d;
284 if (d == (double)f) {
285 pLiteral->type = SPV_LITERAL_TYPE_FLOAT_32;
286 pLiteral->value.f = f;
287 } else {
288 pLiteral->type = SPV_LITERAL_TYPE_FLOAT_64;
289 pLiteral->value.d = d;
290 }
291 } else if (isSigned) {
292 int64_t i64 = strtoll(textValue, nullptr, 10);
293 int32_t i32 = (int32_t)i64;
294 if (i64 == (int64_t)i32) {
295 pLiteral->type = SPV_LITERAL_TYPE_INT_32;
296 pLiteral->value.i32 = i32;
297 } else {
298 pLiteral->type = SPV_LITERAL_TYPE_INT_64;
299 pLiteral->value.i64 = i64;
300 }
301 } else {
302 uint64_t u64 = strtoull(textValue, nullptr, 10);
303 uint32_t u32 = (uint32_t)u64;
304 if (u64 == (uint64_t)u32) {
305 pLiteral->type = SPV_LITERAL_TYPE_UINT_32;
306 pLiteral->value.u32 = u32;
307 } else {
308 pLiteral->type = SPV_LITERAL_TYPE_UINT_64;
309 pLiteral->value.u64 = u64;
310 }
311 }
312
313 return SPV_SUCCESS;
314}
315
316spv_result_t spvTextEncodeOperand(
317 const spv_operand_type_t type, const char *textValue,
318 const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
319 spv_named_id_table namedIdTable, spv_instruction_t *pInst,
320 const spv_operand_type_t **ppExtraOperands, uint32_t *pBound,
321 const spv_position position, spv_diagnostic *pDiagnostic) {
322 // NOTE: Handle immediate int in the stream
323 if ('!' == textValue[0]) {
324 const char *begin = textValue + 1;
325 char *end = nullptr;
326 uint32_t immediateInt = strtoul(begin, &end, 0);
327 size_t size = strlen(textValue);
328 size_t length = (end - begin);
329 spvCheck(size - 1 != length, DIAGNOSTIC << "Invalid immediate integer '"
330 << textValue << "'.";
331 return SPV_ERROR_INVALID_TEXT);
332 position->column += size;
333 position->index += size;
334 pInst->words[pInst->wordCount] = immediateInt;
335 pInst->wordCount += 1;
336 return SPV_SUCCESS;
337 }
338
339 switch (type) {
340 case SPV_OPERAND_TYPE_ID: {
341 if ('$' == textValue[0]) {
342 textValue++;
343 }
344 // TODO: Force all ID's to be prefixed with '$'.
345 uint32_t id = 0;
346 if (spvTextIsNamedId(textValue)) {
347 id = spvNamedIdAssignOrGet(namedIdTable, textValue, pBound);
348 } else {
349 spvCheck(spvTextToUInt32(textValue, &id),
350 DIAGNOSTIC << "Invalid ID '" << textValue << "'.";
351 return SPV_ERROR_INVALID_TEXT);
352 }
353 pInst->words[pInst->wordCount++] = id;
354 if (*pBound <= id) {
355 *pBound = id + 1;
356 }
357 } break;
358 case SPV_OPERAND_TYPE_RESULT_ID: {
359 if ('%' == textValue[0]) {
360 textValue++;
361 }
362 // TODO: Force all Result ID's to be prefixed with '%'.
363 uint32_t id = 0;
364 if (spvTextIsNamedId(textValue)) {
365 id = spvNamedIdAssignOrGet(namedIdTable, textValue, pBound);
366 } else {
367 spvCheck(spvTextToUInt32(textValue, &id),
368 DIAGNOSTIC << "Invalid result ID '" << textValue << "'.";
369 return SPV_ERROR_INVALID_TEXT);
370 }
371 pInst->words[pInst->wordCount++] = id;
372 if (*pBound <= id) {
373 *pBound = id + 1;
374 }
375 } break;
376 case SPV_OPERAND_TYPE_LITERAL_NUMBER: {
377 // NOTE: Special case for extension instruction lookup
378 if (OpExtInst == pInst->opcode) {
379 spv_ext_inst_desc extInst;
380 spvCheck(spvExtInstTableNameLookup(extInstTable, pInst->extInstType,
381 textValue, &extInst),
382 DIAGNOSTIC << "Invalid extended instruction name '"
383 << textValue << "'.";
384 return SPV_ERROR_INVALID_TEXT);
385 pInst->words[pInst->wordCount++] = extInst->ext_inst;
386 *ppExtraOperands = extInst->operandTypes;
387 return SPV_SUCCESS;
388 }
389
390 // TODO: Literal numbers can be any number up to 64 bits wide. This
391 // includes integers and floating point numbers.
392 spvCheck(spvTextToUInt32(textValue, &pInst->words[pInst->wordCount++]),
393 DIAGNOSTIC << "Invalid literal number '" << textValue << "'.";
394 return SPV_ERROR_INVALID_TEXT);
395 } break;
396 case SPV_OPERAND_TYPE_LITERAL: {
397 spv_literal_t literal = {};
398 spvCheck(spvTextToLiteral(textValue, &literal),
399 DIAGNOSTIC << "Invalid literal '" << textValue << "'.";
400 return SPV_ERROR_INVALID_TEXT);
401 switch (literal.type) {
402 case SPV_LITERAL_TYPE_INT_32:
403 spvCheck(spvBinaryEncodeU32((uint32_t)literal.value.i32, pInst,
404 position, pDiagnostic),
405 return SPV_ERROR_INVALID_TEXT);
406 break;
407 case SPV_LITERAL_TYPE_INT_64: {
408 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.i64, pInst,
409 position, pDiagnostic),
410 return SPV_ERROR_INVALID_TEXT);
411 } break;
412 case SPV_LITERAL_TYPE_UINT_32: {
413 spvCheck(spvBinaryEncodeU32(literal.value.u32, pInst, position,
414 pDiagnostic),
415 return SPV_ERROR_INVALID_TEXT);
416 } break;
417 case SPV_LITERAL_TYPE_UINT_64: {
418 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.u64, pInst,
419 position, pDiagnostic),
420 return SPV_ERROR_INVALID_TEXT);
421 } break;
422 case SPV_LITERAL_TYPE_FLOAT_32: {
423 spvCheck(spvBinaryEncodeU32((uint32_t)literal.value.f, pInst,
424 position, pDiagnostic),
425 return SPV_ERROR_INVALID_TEXT);
426 } break;
427 case SPV_LITERAL_TYPE_FLOAT_64: {
428 spvCheck(spvBinaryEncodeU64((uint64_t)literal.value.d, pInst,
429 position, pDiagnostic),
430 return SPV_ERROR_INVALID_TEXT);
431 } break;
432 case SPV_LITERAL_TYPE_STRING: {
433 spvCheck(spvBinaryEncodeString(literal.value.str, pInst, position,
434 pDiagnostic),
435 return SPV_ERROR_INVALID_TEXT);
436 } break;
437 default:
438 DIAGNOSTIC << "Invalid literal '" << textValue << "'";
439 return SPV_ERROR_INVALID_TEXT;
440 }
441 } break;
442 case SPV_OPERAND_TYPE_LITERAL_STRING: {
443 size_t len = strlen(textValue);
444 spvCheck('"' != textValue[0] && '"' != textValue[len - 1],
445 DIAGNOSTIC << "Invalid literal string '" << textValue
446 << "', expected quotes.";
447 return SPV_ERROR_INVALID_TEXT);
448 // NOTE: Strip quotes
449 std::string text(textValue + 1, len - 2);
450
451 // NOTE: Special case for extended instruction library import
452 if (OpExtInstImport == pInst->opcode) {
453 pInst->extInstType = spvExtInstImportTypeGet(text.c_str());
454 }
455
456 spvCheck(
457 spvBinaryEncodeString(text.c_str(), pInst, position, pDiagnostic),
458 return SPV_ERROR_INVALID_TEXT);
459 } break;
460 default: {
461 // NOTE: All non literal operands are handled here using the operand
462 // table.
463 spv_operand_desc entry;
464 spvCheck(spvOperandTableNameLookup(operandTable, type, textValue, &entry),
465 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '"
466 << textValue << "'.";
467 return SPV_ERROR_INVALID_TEXT;);
468 spvCheck(spvBinaryEncodeU32(entry->value, pInst, position, pDiagnostic),
469 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " '"
470 << textValue << "'.";
471 return SPV_ERROR_INVALID_TEXT;);
472 if (ppExtraOperands && entry->operandTypes[0] != SPV_OPERAND_TYPE_NONE) {
473 *ppExtraOperands = entry->operandTypes;
474 }
475 } break;
476 }
477 return SPV_SUCCESS;
478}
479
480spv_result_t spvTextEncodeOpcode(
481 const spv_text text, const spv_opcode_table opcodeTable,
482 const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
483 spv_named_id_table namedIdTable, uint32_t *pBound, spv_instruction_t *pInst,
484 spv_position position, spv_diagnostic *pDiagnostic) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400485 // An assembly instruction has two possible formats:
486 // 1. <opcode> <operand>.., e.g., "OpMemoryModel Physical64 OpenCL".
487 // 2. <result-id> = <opcode> <operand>.., e.g., "%void = OpTypeVoid".
488
489 // Assume it's the first format at the beginning.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100490 std::string opcodeName;
491 spv_position_t nextPosition = {};
Lei Zhangdfc50082015-08-21 11:50:55 -0400492 spv_result_t error =
493 spvTextWordGet(text, position, opcodeName, &nextPosition);
494 spvCheck(error, return error);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100495
496 // NOTE: Handle insertion of an immediate integer into the binary stream
Lei Zhangdfc50082015-08-21 11:50:55 -0400497 bool immediate = false;
498 spvCheck('!' == text->str[position->index], immediate = true);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100499 if (immediate) {
500 const char *begin = opcodeName.data() + 1;
501 char *end = nullptr;
502 uint32_t immediateInt = strtoul(begin, &end, 0);
503 size_t size = opcodeName.size() - 1;
504 spvCheck(size != (size_t)(end - begin),
505 DIAGNOSTIC << "Invalid immediate integer '" << opcodeName << "'.";
506 return SPV_ERROR_INVALID_TEXT);
507 position->column += opcodeName.size();
508 position->index += opcodeName.size();
509 pInst->words[0] = immediateInt;
510 pInst->wordCount = 1;
511 return SPV_SUCCESS;
512 }
513
Lei Zhangdfc50082015-08-21 11:50:55 -0400514 // Handle value generating instructions (the second format above) here.
515 std::string result_id;
516 spv_position_t result_id_position = {};
517 // If the word we get doesn't start with "Op", assume it's an <result-id>
518 // from now.
519 spvCheck(!spvStartsWithOp(text, position), result_id = opcodeName);
520 if (!result_id.empty()) {
521 spvCheck('%' != result_id.front(),
522 DIAGNOSTIC << "Expected <opcode> or <result-id> at the beginning "
523 "of an instruction, found '"
524 << result_id << "'.";
525 return SPV_ERROR_INVALID_TEXT);
526 result_id_position = *position;
527 *position = nextPosition;
528 spvCheck(spvTextAdvance(text, position),
529 DIAGNOSTIC << "Expected '=', found end of stream.";
530 return SPV_ERROR_INVALID_TEXT);
531 // The '=' sign.
532 std::string equal_sign;
533 error = spvTextWordGet(text, position, equal_sign, &nextPosition);
534 spvCheck("=" != equal_sign, DIAGNOSTIC << "'=' expected after result id.";
535 return SPV_ERROR_INVALID_TEXT);
536
537 // The <opcode> after the '=' sign.
538 *position = nextPosition;
539 spvCheck(spvTextAdvance(text, position),
540 DIAGNOSTIC << "Expected opcode, found end of stream.";
541 return SPV_ERROR_INVALID_TEXT);
542 spvCheck(!spvStartsWithOp(text, position),
543 DIAGNOSTIC << "Invalid Opcode prefix '" << opcodeName << "'.";
544 return SPV_ERROR_INVALID_TEXT);
545 error = spvTextWordGet(text, position, opcodeName, &nextPosition);
546 }
547
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100548 // NOTE: The table contains Opcode names without the "Op" prefix.
549 const char *pInstName = opcodeName.data() + 2;
550
551 spv_opcode_desc opcodeEntry;
Lei Zhangdfc50082015-08-21 11:50:55 -0400552 error = spvOpcodeTableNameLookup(opcodeTable, pInstName, &opcodeEntry);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100553 spvCheck(error, DIAGNOSTIC << "Invalid Opcode name '"
554 << getWord(text->str + position->index) << "'";
555 return error);
556 pInst->opcode = opcodeEntry->opcode;
557 *position = nextPosition;
558 pInst->wordCount++;
559
Lei Zhangdfc50082015-08-21 11:50:55 -0400560 // Get the arugment index for <result-id>. Used for handling the <result-id>
561 // for value generating instructions below.
562 const int16_t result_id_index = spvOpcodeResultIdIndex(opcodeEntry);
563
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100564 // NOTE: Process the fixed size operands
565 const spv_operand_type_t *extraOperandTypes = nullptr;
566 for (int32_t operandIndex = 0; operandIndex < (opcodeEntry->wordCount - 1);
567 ++operandIndex) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400568 if (operandIndex == result_id_index && !result_id.empty()) {
569 // Handling the <result-id> for value generating instructions.
570 error = spvTextEncodeOperand(
571 SPV_OPERAND_TYPE_RESULT_ID, result_id.c_str(), operandTable,
572 extInstTable, namedIdTable, pInst, &extraOperandTypes, pBound,
573 &result_id_position, pDiagnostic);
574 spvCheck(error, return error);
575 continue;
576 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100577 spvCheck(spvTextAdvance(text, position),
578 DIAGNOSTIC << "Expected operand, found end of stream.";
579 return SPV_ERROR_INVALID_TEXT);
580
581 std::string operandValue;
582 error = spvTextWordGet(text, position, operandValue, &nextPosition);
583 spvCheck(error, return error);
584
585 error = spvTextEncodeOperand(
586 opcodeEntry->operandTypes[operandIndex], operandValue.c_str(),
587 operandTable, extInstTable, namedIdTable, pInst, &extraOperandTypes,
588 pBound, position, pDiagnostic);
589 spvCheck(error, return error);
590
591 *position = nextPosition;
592 }
593
594 if (spvOpcodeIsVariable(opcodeEntry)) {
595 if (!extraOperandTypes) {
596 // NOTE: Handle variable length not defined by an immediate previously
597 // encountered in the Opcode.
598 spv_operand_type_t type =
599 opcodeEntry->operandTypes[opcodeEntry->wordCount - 1];
600
601 while (!spvTextAdvance(text, position)) {
Lei Zhangdfc50082015-08-21 11:50:55 -0400602 // NOTE: If this is the end of the current instruction stream and we
603 // break out of this loop.
604 if (spvTextIsStartOfNewInst(text, position)) break;
605
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100606 std::string textValue;
607 spvTextWordGet(text, position, textValue, &nextPosition);
608
Lei Zhangdfc50082015-08-21 11:50:55 -0400609 if (SPV_OPERAND_TYPE_LITERAL_STRING == type) {
610 spvCheck(spvTextAdvance(text, position),
611 DIAGNOSTIC << "Invalid string, found end of stream.";
612 return SPV_ERROR_INVALID_TEXT);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100613
Lei Zhangdfc50082015-08-21 11:50:55 -0400614 std::string string;
615 spvCheck(spvTextStringGet(text, position, string, &nextPosition),
616 DIAGNOSTIC << "Invalid string, new line or end of stream.";
617 return SPV_ERROR_INVALID_TEXT);
618 spvCheck(spvTextEncodeOperand(type, string.c_str(), operandTable,
619 extInstTable, namedIdTable, pInst,
620 nullptr, pBound, position, pDiagnostic),
621 return SPV_ERROR_INVALID_TEXT);
622 } else {
623 spvCheck(spvTextEncodeOperand(type, textValue.c_str(), operandTable,
624 extInstTable, namedIdTable, pInst,
625 nullptr, pBound, position, pDiagnostic),
626 return SPV_ERROR_INVALID_TEXT);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100627 }
Lei Zhangdfc50082015-08-21 11:50:55 -0400628 *position = nextPosition;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100629 }
630 } else {
631 // NOTE: Process the variable size operands defined by an immediate
632 // previously encountered in the Opcode.
633 uint64_t extraOperandsIndex = 0;
634 while (extraOperandTypes[extraOperandsIndex]) {
635 spvCheck(spvTextAdvance(text, position),
636 DIAGNOSTIC << "Expected operand, found end of stream.";
637 return SPV_ERROR_INVALID_TEXT);
638
639 std::string operandValue;
640 error = spvTextWordGet(text, position, operandValue, &nextPosition);
641
642 error = spvTextEncodeOperand(extraOperandTypes[extraOperandsIndex],
643 operandValue.c_str(), operandTable,
644 extInstTable, namedIdTable, pInst, nullptr,
645 pBound, position, pDiagnostic);
646 spvCheck(error, return error);
647
648 *position = nextPosition;
649
650 extraOperandsIndex++;
651 }
652 }
653 }
654
655 pInst->words[0] = spvOpcodeMake(pInst->wordCount, opcodeEntry->opcode);
656
657 return SPV_SUCCESS;
658}
659
660spv_result_t spvTextToBinary(const spv_text text,
661 const spv_opcode_table opcodeTable,
662 const spv_operand_table operandTable,
663 const spv_ext_inst_table extInstTable,
664 spv_binary *pBinary, spv_diagnostic *pDiagnostic) {
665 spv_position_t position = {};
666 spvCheck(!text->str || !text->length, DIAGNOSTIC << "Text stream is empty.";
667 return SPV_ERROR_INVALID_TEXT);
668 spvCheck(!opcodeTable || !operandTable || !extInstTable,
669 return SPV_ERROR_INVALID_TABLE);
670 spvCheck(!pBinary, return SPV_ERROR_INVALID_POINTER);
671 spvCheck(!pDiagnostic, return SPV_ERROR_INVALID_DIAGNOSTIC);
672
673 // NOTE: Ensure diagnostic is zero initialised
674 *pDiagnostic = {};
675
676 uint32_t bound = 1;
677
678 std::vector<spv_instruction_t> instructions;
679
680 spvCheck(spvTextAdvance(text, &position), DIAGNOSTIC
681 << "Text stream is empty.";
682 return SPV_ERROR_INVALID_TEXT);
683
684 spv_named_id_table namedIdTable = spvNamedIdTableCreate();
685 spvCheck(!namedIdTable, return SPV_ERROR_OUT_OF_MEMORY);
686
687 spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
688 while (text->length > position.index) {
689 spv_instruction_t inst = {};
690 inst.extInstType = extInstType;
691
692 spvCheck(spvTextEncodeOpcode(text, opcodeTable, operandTable, extInstTable,
693 namedIdTable, &bound, &inst, &position,
694 pDiagnostic),
695 spvNamedIdTableDestory(namedIdTable);
696 return SPV_ERROR_INVALID_TEXT);
697 extInstType = inst.extInstType;
698
699 instructions.push_back(inst);
700
701 spvCheck(spvTextAdvance(text, &position), break);
702 }
703
704 spvNamedIdTableDestory(namedIdTable);
705
706 size_t totalSize = SPV_INDEX_INSTRUCTION;
707 for (auto &inst : instructions) {
708 totalSize += inst.wordCount;
709 }
710
711 uint32_t *data = new uint32_t[totalSize];
712 spvCheck(!data, return SPV_ERROR_OUT_OF_MEMORY);
713 uint64_t currentIndex = SPV_INDEX_INSTRUCTION;
714 for (auto &inst : instructions) {
715 memcpy(data + currentIndex, inst.words, sizeof(uint32_t) * inst.wordCount);
716 currentIndex += inst.wordCount;
717 }
718
719 spv_binary binary = new spv_binary_t();
720 spvCheck(!binary, delete[] data; return SPV_ERROR_OUT_OF_MEMORY);
721 binary->code = data;
722 binary->wordCount = totalSize;
723
724 spv_result_t error = spvBinaryHeaderSet(binary, bound);
725 spvCheck(error, spvBinaryDestroy(binary); return error);
726
727 *pBinary = binary;
728
729 return SPV_SUCCESS;
730}
731
732void spvTextDestroy(spv_text text) {
733 spvCheck(!text, return );
734 if (text->str) {
735 delete[] text->str;
736 }
737 delete text;
738}