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