/src/spirv-tools/source/text.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2015-2016 The Khronos Group Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include "source/text.h" |
16 | | |
17 | | #include <algorithm> |
18 | | #include <cassert> |
19 | | #include <cctype> |
20 | | #include <cstdio> |
21 | | #include <cstdlib> |
22 | | #include <cstring> |
23 | | #include <memory> |
24 | | #include <set> |
25 | | #include <sstream> |
26 | | #include <string> |
27 | | #include <unordered_map> |
28 | | #include <utility> |
29 | | #include <vector> |
30 | | |
31 | | #include "source/assembly_grammar.h" |
32 | | #include "source/binary.h" |
33 | | #include "source/diagnostic.h" |
34 | | #include "source/ext_inst.h" |
35 | | #include "source/instruction.h" |
36 | | #include "source/opcode.h" |
37 | | #include "source/operand.h" |
38 | | #include "source/spirv_constant.h" |
39 | | #include "source/spirv_target_env.h" |
40 | | #include "source/table.h" |
41 | | #include "source/table2.h" |
42 | | #include "source/text_handler.h" |
43 | | #include "source/util/bitutils.h" |
44 | | #include "source/util/parse_number.h" |
45 | | #include "spirv-tools/libspirv.h" |
46 | | |
47 | 26.3M | bool spvIsValidIDCharacter(const char value) { |
48 | 26.3M | return value == '_' || 0 != ::isalnum(value); |
49 | 26.3M | } |
50 | | |
51 | | // Returns true if the given string represents a valid ID name. |
52 | 1.44M | bool spvIsValidID(const char* textValue) { |
53 | 1.44M | const char* c = textValue; |
54 | 27.7M | for (; *c != '\0'; ++c) { |
55 | 26.3M | if (!spvIsValidIDCharacter(*c)) { |
56 | 192 | return false; |
57 | 192 | } |
58 | 26.3M | } |
59 | | // If the string was empty, then the ID also is not valid. |
60 | 1.44M | return c != textValue; |
61 | 1.44M | } |
62 | | |
63 | | // Text API |
64 | | |
65 | 274k | spv_result_t spvTextToLiteral(const char* textValue, spv_literal_t* pLiteral) { |
66 | 274k | bool isSigned = false; |
67 | 274k | int numPeriods = 0; |
68 | 274k | bool isString = false; |
69 | | |
70 | 274k | const size_t len = strlen(textValue); |
71 | 274k | if (len == 0) return SPV_FAILED_MATCH; |
72 | | |
73 | 6.21M | for (uint64_t index = 0; index < len; ++index) { |
74 | 5.93M | switch (textValue[index]) { |
75 | 5.17M | case '0': |
76 | 5.24M | case '1': |
77 | 5.28M | case '2': |
78 | 5.32M | case '3': |
79 | 5.35M | case '4': |
80 | 5.35M | case '5': |
81 | 5.37M | case '6': |
82 | 5.38M | case '7': |
83 | 5.40M | case '8': |
84 | 5.41M | case '9': |
85 | 5.41M | break; |
86 | 1.51k | case '.': |
87 | 1.51k | numPeriods++; |
88 | 1.51k | break; |
89 | 258k | case '-': |
90 | 258k | if (index == 0) { |
91 | 510 | isSigned = true; |
92 | 257k | } else { |
93 | 257k | isString = true; |
94 | 257k | } |
95 | 258k | break; |
96 | 259k | default: |
97 | 259k | isString = true; |
98 | 259k | index = len; // break out of the loop too. |
99 | 259k | break; |
100 | 5.93M | } |
101 | 5.93M | } |
102 | | |
103 | 274k | pLiteral->type = spv_literal_type_t(99); |
104 | | |
105 | 274k | if (isString || numPeriods > 1 || (isSigned && len == 1)) { |
106 | 259k | if (len < 2 || textValue[0] != '"' || textValue[len - 1] != '"') |
107 | 197k | return SPV_FAILED_MATCH; |
108 | 62.4k | bool escaping = false; |
109 | 28.7M | for (const char* val = textValue + 1; val != textValue + len - 1; ++val) { |
110 | 28.7M | if ((*val == '\\') && (!escaping)) { |
111 | 5.32k | escaping = true; |
112 | 28.7M | } else { |
113 | | // Have to save space for the null-terminator |
114 | 28.7M | if (pLiteral->str.size() >= SPV_LIMIT_LITERAL_STRING_BYTES_MAX) |
115 | 8 | return SPV_ERROR_OUT_OF_MEMORY; |
116 | 28.7M | pLiteral->str.push_back(*val); |
117 | 28.7M | escaping = false; |
118 | 28.7M | } |
119 | 28.7M | } |
120 | | |
121 | 62.4k | pLiteral->type = SPV_LITERAL_TYPE_STRING; |
122 | 62.4k | } else if (numPeriods == 1) { |
123 | 16 | double d = std::strtod(textValue, nullptr); |
124 | 16 | float f = (float)d; |
125 | 16 | if (d == (double)f) { |
126 | 12 | pLiteral->type = SPV_LITERAL_TYPE_FLOAT_32; |
127 | 12 | pLiteral->value.f = f; |
128 | 12 | } else { |
129 | 4 | pLiteral->type = SPV_LITERAL_TYPE_FLOAT_64; |
130 | 4 | pLiteral->value.d = d; |
131 | 4 | } |
132 | 14.8k | } else if (isSigned) { |
133 | 226 | int64_t i64 = strtoll(textValue, nullptr, 10); |
134 | 226 | int32_t i32 = (int32_t)i64; |
135 | 226 | if (i64 == (int64_t)i32) { |
136 | 66 | pLiteral->type = SPV_LITERAL_TYPE_INT_32; |
137 | 66 | pLiteral->value.i32 = i32; |
138 | 160 | } else { |
139 | 160 | pLiteral->type = SPV_LITERAL_TYPE_INT_64; |
140 | 160 | pLiteral->value.i64 = i64; |
141 | 160 | } |
142 | 14.6k | } else { |
143 | 14.6k | uint64_t u64 = strtoull(textValue, nullptr, 10); |
144 | 14.6k | uint32_t u32 = (uint32_t)u64; |
145 | 14.6k | if (u64 == (uint64_t)u32) { |
146 | 14.4k | pLiteral->type = SPV_LITERAL_TYPE_UINT_32; |
147 | 14.4k | pLiteral->value.u32 = u32; |
148 | 14.4k | } else { |
149 | 194 | pLiteral->type = SPV_LITERAL_TYPE_UINT_64; |
150 | 194 | pLiteral->value.u64 = u64; |
151 | 194 | } |
152 | 14.6k | } |
153 | | |
154 | 77.3k | return SPV_SUCCESS; |
155 | 274k | } |
156 | | |
157 | | namespace { |
158 | | |
159 | | /// Parses an immediate integer from text, guarding against overflow. If |
160 | | /// successful, adds the parsed value to pInst, advances the context past it, |
161 | | /// and returns SPV_SUCCESS. Otherwise, leaves pInst alone, emits diagnostics, |
162 | | /// and returns SPV_ERROR_INVALID_TEXT. |
163 | | spv_result_t encodeImmediate(spvtools::AssemblyContext* context, |
164 | 190k | const char* text, spv_instruction_t* pInst) { |
165 | 190k | assert(*text == '!'); |
166 | 190k | uint32_t parse_result; |
167 | 190k | if (!spvtools::utils::ParseNumber(text + 1, &parse_result)) { |
168 | 266 | return context->diagnostic(SPV_ERROR_INVALID_TEXT) |
169 | 266 | << "Invalid immediate integer: !" << text + 1; |
170 | 266 | } |
171 | 190k | context->binaryEncodeU32(parse_result, pInst); |
172 | 190k | context->seekForward(static_cast<uint32_t>(strlen(text))); |
173 | 190k | return SPV_SUCCESS; |
174 | 190k | } |
175 | | |
176 | | } // anonymous namespace |
177 | | |
178 | | /// @brief Translate an Opcode operand to binary form |
179 | | /// |
180 | | /// @param[in] grammar the grammar to use for compilation |
181 | | /// @param[in, out] context the dynamic compilation info |
182 | | /// @param[in] type of the operand |
183 | | /// @param[in] textValue word of text to be parsed |
184 | | /// @param[out] pInst return binary Opcode |
185 | | /// @param[in,out] pExpectedOperands the operand types expected |
186 | | /// |
187 | | /// @return result code |
188 | | spv_result_t spvTextEncodeOperand(const spvtools::AssemblyGrammar& grammar, |
189 | | spvtools::AssemblyContext* context, |
190 | | const spv_operand_type_t type, |
191 | | const char* textValue, |
192 | | spv_instruction_t* pInst, |
193 | 23.2M | spv_operand_pattern_t* pExpectedOperands) { |
194 | | // NOTE: Handle immediate int in the stream |
195 | 23.2M | if ('!' == textValue[0]) { |
196 | 49.9k | if (auto error = encodeImmediate(context, textValue, pInst)) { |
197 | 10 | return error; |
198 | 10 | } |
199 | 49.9k | *pExpectedOperands = |
200 | 49.9k | spvAlternatePatternFollowingImmediate(*pExpectedOperands); |
201 | 49.9k | return SPV_SUCCESS; |
202 | 49.9k | } |
203 | | |
204 | | // Optional literal operands can fail to parse. In that case use |
205 | | // SPV_FAILED_MATCH to avoid emitting a diagnostic. Use the following |
206 | | // for those situations. |
207 | 23.2M | spv_result_t error_code_for_literals = |
208 | 23.2M | spvOperandIsOptional(type) ? SPV_FAILED_MATCH : SPV_ERROR_INVALID_TEXT; |
209 | | |
210 | 23.2M | switch (type) { |
211 | 423k | case SPV_OPERAND_TYPE_ID: |
212 | 780k | case SPV_OPERAND_TYPE_TYPE_ID: |
213 | 1.18M | case SPV_OPERAND_TYPE_RESULT_ID: |
214 | 1.19M | case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: |
215 | 1.19M | case SPV_OPERAND_TYPE_SCOPE_ID: |
216 | 1.44M | case SPV_OPERAND_TYPE_OPTIONAL_ID: { |
217 | 1.44M | if ('%' == textValue[0]) { |
218 | 1.44M | textValue++; |
219 | 1.44M | } else { |
220 | 1.62k | return context->diagnostic() << "Expected id to start with %."; |
221 | 1.62k | } |
222 | 1.44M | if (!spvIsValidID(textValue)) { |
223 | 306 | return context->diagnostic() << "Invalid ID " << textValue; |
224 | 306 | } |
225 | 1.44M | const uint32_t id = context->spvNamedIdAssignOrGet(textValue); |
226 | 1.44M | if (type == SPV_OPERAND_TYPE_TYPE_ID) pInst->resultTypeId = id; |
227 | 1.44M | spvInstructionAddWord(pInst, id); |
228 | | |
229 | | // Set the extended instruction type. |
230 | | // The import set id is the 3rd operand of OpExtInst. |
231 | 1.44M | if (spvIsExtendedInstruction(pInst->opcode) && pInst->words.size() == 4) { |
232 | 20.1k | auto ext_inst_type = context->getExtInstTypeForId(pInst->words[3]); |
233 | 20.1k | if (ext_inst_type == SPV_EXT_INST_TYPE_NONE) { |
234 | 38 | return context->diagnostic() |
235 | 38 | << "Invalid extended instruction import Id " |
236 | 38 | << pInst->words[2]; |
237 | 38 | } |
238 | 20.1k | pInst->extInstType = ext_inst_type; |
239 | 20.1k | } |
240 | 1.44M | } break; |
241 | | |
242 | 1.44M | case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { |
243 | | // The assembler accepts the symbolic name for an extended instruction, |
244 | | // and emits its corresponding number. |
245 | 19.6k | const spvtools::ExtInstDesc* desc = nullptr; |
246 | 19.6k | if (spvtools::LookupExtInst(pInst->extInstType, textValue, &desc) == |
247 | 19.6k | SPV_SUCCESS) { |
248 | | // if we know about this extended instruction, push the numeric value |
249 | 5.05k | spvInstructionAddWord(pInst, desc->value); |
250 | | |
251 | | // Prepare to parse the operands for the extended instructions. |
252 | 5.05k | spvPushOperandTypes(desc->operands(), pExpectedOperands); |
253 | 14.5k | } else { |
254 | | // if we don't know this extended instruction and the set isn't |
255 | | // non-semantic, we cannot process further |
256 | 14.5k | if (!spvExtInstIsNonSemantic(pInst->extInstType)) { |
257 | 258 | return context->diagnostic() |
258 | 258 | << "Invalid extended instruction name '" << textValue << "'."; |
259 | 14.3k | } else { |
260 | | // for non-semantic instruction sets, as long as the text name is an |
261 | | // integer value we can encode it since we know the form of all such |
262 | | // extended instructions |
263 | 14.3k | spv_literal_t extInstValue; |
264 | 14.3k | if (spvTextToLiteral(textValue, &extInstValue) || |
265 | 14.3k | extInstValue.type != SPV_LITERAL_TYPE_UINT_32) { |
266 | 16 | return context->diagnostic() |
267 | 16 | << "Couldn't translate unknown extended instruction name '" |
268 | 16 | << textValue << "' to unsigned integer."; |
269 | 16 | } |
270 | | |
271 | 14.3k | spvInstructionAddWord(pInst, extInstValue.value.u32); |
272 | | |
273 | | // opcode contains an unknown number of IDs. |
274 | 14.3k | pExpectedOperands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID); |
275 | 14.3k | } |
276 | 14.5k | } |
277 | 19.6k | } break; |
278 | | |
279 | 19.3k | case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { |
280 | | // The assembler accepts the symbolic name for the opcode, but without |
281 | | // the "Op" prefix. For example, "IAdd" is accepted. The number |
282 | | // of the opcode is emitted. |
283 | 2.80k | spv::Op opcode; |
284 | 2.80k | if (grammar.lookupSpecConstantOpcode(textValue, &opcode)) { |
285 | 204 | return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) |
286 | 204 | << " '" << textValue << "'."; |
287 | 204 | } |
288 | 2.59k | const spvtools::InstructionDesc* opcodeEntry = nullptr; |
289 | 2.59k | if (LookupOpcodeForEnv(grammar.target_env(), opcode, &opcodeEntry)) { |
290 | 0 | return context->diagnostic(SPV_ERROR_INTERNAL) |
291 | 0 | << "OpSpecConstant opcode table out of sync"; |
292 | 0 | } |
293 | 2.59k | spvInstructionAddWord(pInst, uint32_t(opcodeEntry->opcode)); |
294 | | |
295 | | // Prepare to parse the operands for the opcode. Except skip the |
296 | | // type Id and result Id, since they've already been processed. |
297 | 2.59k | assert(opcodeEntry->hasType); |
298 | 2.59k | assert(opcodeEntry->hasResult); |
299 | 2.59k | assert(opcodeEntry->operands().size() >= 2); |
300 | 2.59k | spvPushOperandTypes(opcodeEntry->operands().subspan(2), |
301 | 2.59k | pExpectedOperands); |
302 | 2.59k | } break; |
303 | | |
304 | 26.9k | case SPV_OPERAND_TYPE_LITERAL_INTEGER: |
305 | 30.0k | case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: { |
306 | | // The current operand is an *unsigned* 32-bit integer. |
307 | | // That's just how the grammar works. |
308 | 30.0k | spvtools::IdType expected_type = { |
309 | 30.0k | 32, false, spvtools::IdTypeClass::kScalarIntegerType}; |
310 | 30.0k | if (auto error = context->binaryEncodeNumericLiteral( |
311 | 30.0k | textValue, error_code_for_literals, expected_type, pInst)) { |
312 | 16 | return error; |
313 | 16 | } |
314 | 30.0k | } break; |
315 | | |
316 | 30.0k | case SPV_OPERAND_TYPE_LITERAL_FLOAT: { |
317 | | // The current operand is a 32-bit float. |
318 | | // That's just how the grammar works. |
319 | 202 | spvtools::IdType expected_type = { |
320 | 202 | 32, false, spvtools::IdTypeClass::kScalarFloatType}; |
321 | 202 | if (auto error = context->binaryEncodeNumericLiteral( |
322 | 202 | textValue, error_code_for_literals, expected_type, pInst)) { |
323 | 2 | return error; |
324 | 2 | } |
325 | 202 | } break; |
326 | | |
327 | 10.5M | case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER: |
328 | | // This is a context-independent literal number which can be a 32-bit |
329 | | // number of floating point value. |
330 | 10.5M | if (auto error = context->binaryEncodeNumericLiteral( |
331 | 10.5M | textValue, error_code_for_literals, spvtools::kUnknownType, |
332 | 10.5M | pInst)) { |
333 | 240k | return error; |
334 | 240k | } |
335 | 10.3M | break; |
336 | | |
337 | 10.3M | case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: |
338 | 129k | case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: { |
339 | 129k | spvtools::IdType expected_type = spvtools::kUnknownType; |
340 | | // The encoding for OpConstant, OpSpecConstant and OpSwitch all |
341 | | // depend on either their own result-id or the result-id of |
342 | | // one of their parameters. |
343 | 129k | if (spv::Op::OpConstant == pInst->opcode || |
344 | 129k | spv::Op::OpSpecConstant == pInst->opcode) { |
345 | | // The type of the literal is determined by the type Id of the |
346 | | // instruction. |
347 | 124k | expected_type = |
348 | 124k | context->getTypeOfTypeGeneratingValue(pInst->resultTypeId); |
349 | 124k | if (!spvtools::isScalarFloating(expected_type) && |
350 | 124k | !spvtools::isScalarIntegral(expected_type)) { |
351 | 42 | const spvtools::InstructionDesc* opcodeEntry = nullptr; |
352 | 42 | const char* opcode_name = "opcode"; |
353 | 42 | if (SPV_SUCCESS == LookupOpcode(pInst->opcode, &opcodeEntry)) { |
354 | 42 | opcode_name = |
355 | 42 | opcodeEntry->name().data(); // assumes it's null-terminated |
356 | 42 | } |
357 | 42 | return context->diagnostic() |
358 | 42 | << "Type for " << opcode_name |
359 | 42 | << " must be a scalar floating point or integer type"; |
360 | 42 | } |
361 | 124k | } else if (pInst->opcode == spv::Op::OpSwitch) { |
362 | | // The type of the literal is the same as the type of the selector. |
363 | 5.53k | expected_type = context->getTypeOfValueInstruction(pInst->words[1]); |
364 | 5.53k | if (!spvtools::isScalarIntegral(expected_type)) { |
365 | 318 | return context->diagnostic() |
366 | 318 | << "The selector operand for OpSwitch must be the result" |
367 | 318 | " of an instruction that generates an integer scalar"; |
368 | 318 | } |
369 | 5.53k | } |
370 | 129k | if (auto error = context->binaryEncodeNumericLiteral( |
371 | 129k | textValue, error_code_for_literals, expected_type, pInst)) { |
372 | 2.47k | return error; |
373 | 2.47k | } |
374 | 129k | } break; |
375 | | |
376 | 126k | case SPV_OPERAND_TYPE_LITERAL_STRING: |
377 | 260k | case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: { |
378 | 260k | spv_literal_t literal = {}; |
379 | 260k | spv_result_t error = spvTextToLiteral(textValue, &literal); |
380 | 260k | if (error != SPV_SUCCESS) { |
381 | 197k | if (error == SPV_ERROR_OUT_OF_MEMORY) return error; |
382 | 197k | return context->diagnostic(error_code_for_literals) |
383 | 197k | << "Invalid literal string '" << textValue << "'."; |
384 | 197k | } |
385 | 62.9k | if (literal.type != SPV_LITERAL_TYPE_STRING) { |
386 | 544 | return context->diagnostic() |
387 | 544 | << "Expected literal string, found literal number '" << textValue |
388 | 544 | << "'."; |
389 | 544 | } |
390 | | |
391 | | // NOTE: Special case for extended instruction library import |
392 | 62.4k | if (spv::Op::OpExtInstImport == pInst->opcode) { |
393 | 7.67k | const spv_ext_inst_type_t ext_inst_type = |
394 | 7.67k | spvExtInstImportTypeGet(literal.str.c_str()); |
395 | 7.67k | if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) { |
396 | 1.13k | return context->diagnostic() |
397 | 1.13k | << "Invalid extended instruction import '" << literal.str |
398 | 1.13k | << "'"; |
399 | 1.13k | } |
400 | 6.54k | if ((error = context->recordIdAsExtInstImport(pInst->words[1], |
401 | 6.54k | ext_inst_type))) |
402 | 29 | return error; |
403 | 6.54k | } |
404 | | |
405 | 61.2k | if (context->binaryEncodeString(literal.str.c_str(), pInst)) |
406 | 14 | return SPV_ERROR_INVALID_TEXT; |
407 | 61.2k | } break; |
408 | | |
409 | | // Masks. |
410 | 61.2k | case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: |
411 | 2.22k | case SPV_OPERAND_TYPE_FUNCTION_CONTROL: |
412 | 3.95k | case SPV_OPERAND_TYPE_LOOP_CONTROL: |
413 | 4.69k | case SPV_OPERAND_TYPE_IMAGE: |
414 | 5.19k | case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: |
415 | 5.19k | case SPV_OPERAND_TYPE_TENSOR_OPERANDS: |
416 | 5.19k | case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS: |
417 | 109k | case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: |
418 | 109k | case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS: |
419 | 111k | case SPV_OPERAND_TYPE_SELECTION_CONTROL: |
420 | 111k | case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: |
421 | 112k | case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: |
422 | 112k | case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: |
423 | 112k | case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: |
424 | 127k | case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE: |
425 | 127k | case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: { |
426 | 127k | uint32_t value; |
427 | 127k | if (auto error = grammar.parseMaskOperand(type, textValue, &value)) { |
428 | 146 | return context->diagnostic(error) |
429 | 146 | << "Invalid " << spvOperandTypeStr(type) << " operand '" |
430 | 146 | << textValue << "'."; |
431 | 146 | } |
432 | 127k | if (auto error = context->binaryEncodeU32(value, pInst)) return error; |
433 | | // Prepare to parse the operands for this logical operand. |
434 | 127k | grammar.pushOperandTypesForMask(type, value, pExpectedOperands); |
435 | 127k | } break; |
436 | 10.5M | case SPV_OPERAND_TYPE_OPTIONAL_CIV: { |
437 | 10.5M | auto error = spvTextEncodeOperand( |
438 | 10.5M | grammar, context, SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER, textValue, |
439 | 10.5M | pInst, pExpectedOperands); |
440 | 10.5M | if (error == SPV_FAILED_MATCH) { |
441 | | // It's not a literal number -- is it a literal string? |
442 | 240k | error = spvTextEncodeOperand(grammar, context, |
443 | 240k | SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING, |
444 | 240k | textValue, pInst, pExpectedOperands); |
445 | 240k | } |
446 | 10.5M | if (error == SPV_FAILED_MATCH) { |
447 | | // It's not a literal -- is it an ID? |
448 | 197k | error = |
449 | 197k | spvTextEncodeOperand(grammar, context, SPV_OPERAND_TYPE_OPTIONAL_ID, |
450 | 197k | textValue, pInst, pExpectedOperands); |
451 | 197k | } |
452 | 10.5M | if (error) { |
453 | 2.52k | return context->diagnostic(error) |
454 | 2.52k | << "Invalid word following !<integer>: " << textValue; |
455 | 2.52k | } |
456 | 10.5M | if (pExpectedOperands->empty()) { |
457 | 10.5M | pExpectedOperands->push_back(SPV_OPERAND_TYPE_OPTIONAL_CIV); |
458 | 10.5M | } |
459 | 10.5M | } break; |
460 | 39.1k | default: { |
461 | | // NOTE: All non literal operands are handled here using the operand |
462 | | // table. |
463 | 39.1k | const spvtools::OperandDesc* entry = nullptr; |
464 | 39.1k | if (spvtools::LookupOperand(type, textValue, strlen(textValue), &entry)) { |
465 | 526 | return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) |
466 | 526 | << " '" << textValue << "'."; |
467 | 526 | } |
468 | 38.5k | if (context->binaryEncodeU32(entry->value, pInst)) { |
469 | 0 | return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) |
470 | 0 | << " '" << textValue << "'."; |
471 | 0 | } |
472 | | |
473 | | // Prepare to parse the operands for this logical operand. |
474 | 38.5k | spvPushOperandTypes(entry->operands(), pExpectedOperands); |
475 | 38.5k | } break; |
476 | 23.2M | } |
477 | 22.7M | return SPV_SUCCESS; |
478 | 23.2M | } |
479 | | |
480 | | namespace { |
481 | | |
482 | | /// Encodes an instruction started by !<integer> at the given position in text. |
483 | | /// |
484 | | /// Puts the encoded words into *pInst. If successful, moves position past the |
485 | | /// instruction and returns SPV_SUCCESS. Otherwise, returns an error code and |
486 | | /// leaves position pointing to the error in text. |
487 | | spv_result_t encodeInstructionStartingWithImmediate( |
488 | | const spvtools::AssemblyGrammar& grammar, |
489 | 140k | spvtools::AssemblyContext* context, spv_instruction_t* pInst) { |
490 | 140k | std::string firstWord; |
491 | 140k | spv_position_t nextPosition = {}; |
492 | 140k | auto error = context->getWord(&firstWord, &nextPosition); |
493 | 140k | if (error) return context->diagnostic(error) << "Internal Error"; |
494 | | |
495 | 140k | if ((error = encodeImmediate(context, firstWord.c_str(), pInst))) { |
496 | 256 | return error; |
497 | 256 | } |
498 | 3.40M | while (context->advance() != SPV_END_OF_STREAM) { |
499 | | // A beginning of a new instruction means we're done. |
500 | 3.40M | if (context->isStartOfNewInst()) return SPV_SUCCESS; |
501 | | |
502 | | // Otherwise, there must be an operand that's either a literal, an ID, or |
503 | | // an immediate. |
504 | 3.27M | std::string operandValue; |
505 | 3.27M | if ((error = context->getWord(&operandValue, &nextPosition))) |
506 | 0 | return context->diagnostic(error) << "Internal Error"; |
507 | | |
508 | 3.27M | if (operandValue == "=") |
509 | 6 | return context->diagnostic() << firstWord << " not allowed before =."; |
510 | | |
511 | | // Needed to pass to spvTextEncodeOperand(), but it shouldn't ever be |
512 | | // expanded. |
513 | 3.27M | spv_operand_pattern_t dummyExpectedOperands; |
514 | 3.27M | error = spvTextEncodeOperand( |
515 | 3.27M | grammar, context, SPV_OPERAND_TYPE_OPTIONAL_CIV, operandValue.c_str(), |
516 | 3.27M | pInst, &dummyExpectedOperands); |
517 | 3.27M | if (error) return error; |
518 | 3.26M | context->setPosition(nextPosition); |
519 | 3.26M | } |
520 | 6.56k | return SPV_SUCCESS; |
521 | 140k | } |
522 | | |
523 | | /// @brief Translate an instruction started by OpUnknown and the following |
524 | | /// operands to binary form |
525 | | /// |
526 | | /// @param[in] grammar the grammar to use for compilation |
527 | | /// @param[in, out] context the dynamic compilation info |
528 | | /// @param[out] pInst returned binary Opcode |
529 | | /// |
530 | | /// @return result code |
531 | | spv_result_t encodeInstructionStartingWithOpUnknown( |
532 | | const spvtools::AssemblyGrammar& grammar, |
533 | 6.68k | spvtools::AssemblyContext* context, spv_instruction_t* pInst) { |
534 | 6.68k | spv_position_t nextPosition = {}; |
535 | | |
536 | 6.68k | uint16_t opcode; |
537 | 6.68k | uint16_t wordCount; |
538 | | |
539 | | // The '(' character. |
540 | 6.68k | if (context->advance()) |
541 | 4 | return context->diagnostic() << "Expected '(', found end of stream."; |
542 | 6.67k | if ('(' != context->peek()) { |
543 | 20 | return context->diagnostic() << "'(' expected after OpUnknown but found '" |
544 | 20 | << context->peek() << "'."; |
545 | 20 | } |
546 | 6.65k | context->seekForward(1); |
547 | | |
548 | | // The opcode enumerant. |
549 | 6.65k | if (context->advance()) |
550 | 2 | return context->diagnostic() |
551 | 2 | << "Expected opcode enumerant, found end of stream."; |
552 | 6.65k | std::string opcodeString; |
553 | 6.65k | spv_result_t error = context->getWord(&opcodeString, &nextPosition); |
554 | 6.65k | if (error) return context->diagnostic(error) << "Internal Error"; |
555 | | |
556 | 6.65k | if (!spvtools::utils::ParseNumber(opcodeString.c_str(), &opcode)) { |
557 | 150 | return context->diagnostic() |
558 | 150 | << "Invalid opcode enumerant: \"" << opcodeString << "\"."; |
559 | 150 | } |
560 | | |
561 | 6.50k | context->setPosition(nextPosition); |
562 | | |
563 | | // The ',' character. |
564 | 6.50k | if (context->advance()) |
565 | 16 | return context->diagnostic() << "Expected ',', found end of stream."; |
566 | 6.49k | if (',' != context->peek()) { |
567 | 20 | return context->diagnostic() |
568 | 20 | << "',' expected after opcode enumerant but found '" |
569 | 20 | << context->peek() << "'."; |
570 | 20 | } |
571 | 6.47k | context->seekForward(1); |
572 | | |
573 | | // The number of words. |
574 | 6.47k | if (context->advance()) |
575 | 2 | return context->diagnostic() |
576 | 2 | << "Expected number of words, found end of stream."; |
577 | 6.46k | std::string wordCountString; |
578 | 6.46k | error = context->getWord(&wordCountString, &nextPosition); |
579 | 6.46k | if (error) return context->diagnostic(error) << "Internal Error"; |
580 | | |
581 | 6.46k | if (!spvtools::utils::ParseNumber(wordCountString.c_str(), &wordCount)) { |
582 | 12 | return context->diagnostic() |
583 | 12 | << "Invalid number of words: \"" << wordCountString << "\"."; |
584 | 12 | } |
585 | | |
586 | 6.45k | if (wordCount == 0) { |
587 | 6 | return context->diagnostic() << "Number of words (which includes the " |
588 | 6 | "opcode) must be greater than zero."; |
589 | 6 | } |
590 | | |
591 | 6.45k | context->setPosition(nextPosition); |
592 | | |
593 | | // The ')' character. |
594 | 6.45k | if (context->advance()) |
595 | 28 | return context->diagnostic() << "Expected ')', found end of stream."; |
596 | 6.42k | if (')' != context->peek()) { |
597 | 18 | return context->diagnostic() |
598 | 18 | << "')' expected after number of words but found '" |
599 | 18 | << context->peek() << "'."; |
600 | 18 | } |
601 | 6.40k | context->seekForward(1); |
602 | | |
603 | 6.40k | pInst->opcode = static_cast<spv::Op>(opcode); |
604 | 6.40k | context->binaryEncodeU32(spvOpcodeMake(wordCount, pInst->opcode), pInst); |
605 | | |
606 | 6.40k | wordCount--; // Subtract the opcode from the number of words left to read. |
607 | | |
608 | 15.7k | while (wordCount-- > 0) { |
609 | 9.56k | if (context->advance() == SPV_END_OF_STREAM) { |
610 | 68 | return context->diagnostic() << "Expected " << wordCount + 1 |
611 | 68 | << " more operands, found end of stream."; |
612 | 68 | } |
613 | 9.49k | if (context->isStartOfNewInst()) { |
614 | 4 | std::string invalid; |
615 | 4 | context->getWord(&invalid, &nextPosition); |
616 | 4 | return context->diagnostic() |
617 | 4 | << "Unexpected start of new instruction: \"" << invalid |
618 | 4 | << "\". Expected " << wordCount + 1 << " more operands"; |
619 | 4 | } |
620 | | |
621 | 9.48k | std::string operandValue; |
622 | 9.48k | if ((error = context->getWord(&operandValue, &nextPosition))) |
623 | 0 | return context->diagnostic(error) << "Internal Error"; |
624 | | |
625 | 9.48k | if (operandValue == "=") |
626 | 2 | return context->diagnostic() << "OpUnknown not allowed before =."; |
627 | | |
628 | | // Needed to pass to spvTextEncodeOperand(), but it shouldn't ever be |
629 | | // expanded. |
630 | 9.48k | spv_operand_pattern_t dummyExpectedOperands; |
631 | 9.48k | error = spvTextEncodeOperand( |
632 | 9.48k | grammar, context, SPV_OPERAND_TYPE_OPTIONAL_CIV, operandValue.c_str(), |
633 | 9.48k | pInst, &dummyExpectedOperands); |
634 | 9.48k | if (error) return error; |
635 | 9.33k | context->setPosition(nextPosition); |
636 | 9.33k | } |
637 | | |
638 | 6.18k | return SPV_SUCCESS; |
639 | 6.40k | } |
640 | | |
641 | | /// @brief Translate single Opcode and operands to binary form |
642 | | /// |
643 | | /// @param[in] grammar the grammar to use for compilation |
644 | | /// @param[in, out] context the dynamic compilation info |
645 | | /// @param[in] text stream to translate |
646 | | /// @param[out] pInst returned binary Opcode |
647 | | /// @param[in,out] pPosition in the text stream |
648 | | /// |
649 | | /// @return result code |
650 | | spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar, |
651 | | spvtools::AssemblyContext* context, |
652 | 776k | spv_instruction_t* pInst) { |
653 | | // Check for !<integer> first. |
654 | 776k | if ('!' == context->peek()) { |
655 | 140k | return encodeInstructionStartingWithImmediate(grammar, context, pInst); |
656 | 140k | } |
657 | | |
658 | 635k | std::string firstWord; |
659 | 635k | spv_position_t nextPosition = {}; |
660 | 635k | spv_result_t error = context->getWord(&firstWord, &nextPosition); |
661 | 635k | if (error) return context->diagnostic() << "Internal Error"; |
662 | | |
663 | 635k | std::string opcodeName; |
664 | 635k | std::string result_id; |
665 | 635k | spv_position_t result_id_position = {}; |
666 | 635k | if (context->startsWithOp()) { |
667 | 226k | opcodeName = firstWord; |
668 | 408k | } else { |
669 | 408k | result_id = firstWord; |
670 | 408k | if ('%' != result_id.front()) { |
671 | 406 | return context->diagnostic() |
672 | 406 | << "Expected <opcode> or <result-id> at the beginning " |
673 | 406 | "of an instruction, found '" |
674 | 406 | << result_id << "'."; |
675 | 406 | } |
676 | 408k | result_id_position = context->position(); |
677 | | |
678 | | // The '=' sign. |
679 | 408k | context->setPosition(nextPosition); |
680 | 408k | if (context->advance()) |
681 | 68 | return context->diagnostic() << "Expected '=', found end of stream."; |
682 | 408k | std::string equal_sign; |
683 | 408k | error = context->getWord(&equal_sign, &nextPosition); |
684 | 408k | if ("=" != equal_sign) |
685 | 84 | return context->diagnostic() << "'=' expected after result id but found '" |
686 | 84 | << equal_sign << "'."; |
687 | | |
688 | | // The <opcode> after the '=' sign. |
689 | 408k | context->setPosition(nextPosition); |
690 | 408k | if (context->advance()) |
691 | 4 | return context->diagnostic() << "Expected opcode, found end of stream."; |
692 | 408k | error = context->getWord(&opcodeName, &nextPosition); |
693 | 408k | if (error) return context->diagnostic(error) << "Internal Error"; |
694 | 408k | if (!context->startsWithOp()) { |
695 | 22 | return context->diagnostic() |
696 | 22 | << "Invalid Opcode prefix '" << opcodeName << "'."; |
697 | 22 | } |
698 | 408k | } |
699 | | |
700 | 634k | if (opcodeName == "OpUnknown") { |
701 | 6.70k | if (!result_id.empty()) { |
702 | 22 | return context->diagnostic() |
703 | 22 | << "OpUnknown not allowed in assignment. Use an explicit result " |
704 | 22 | "id operand instead."; |
705 | 22 | } |
706 | 6.68k | context->setPosition(nextPosition); |
707 | 6.68k | return encodeInstructionStartingWithOpUnknown(grammar, context, pInst); |
708 | 6.70k | } |
709 | | |
710 | | // NOTE: The table contains Opcode names without the "Op" prefix. |
711 | 628k | const char* pInstName = opcodeName.data() + 2; |
712 | | |
713 | 628k | const spvtools::InstructionDesc* opcodeEntry = nullptr; |
714 | 628k | error = LookupOpcodeForEnv(grammar.target_env(), pInstName, &opcodeEntry); |
715 | 628k | if (error) { |
716 | 610 | return context->diagnostic(error) |
717 | 610 | << "Invalid Opcode name '" << opcodeName << "'"; |
718 | 610 | } |
719 | 627k | if (opcodeEntry->hasResult && result_id.empty()) { |
720 | 22 | return context->diagnostic() |
721 | 22 | << "Expected <result-id> at the beginning of an instruction, found '" |
722 | 22 | << firstWord << "'."; |
723 | 22 | } |
724 | 627k | if (!opcodeEntry->hasResult && !result_id.empty()) { |
725 | 36 | return context->diagnostic() |
726 | 36 | << "Cannot set ID " << result_id << " because " << opcodeName |
727 | 36 | << " does not produce a result ID."; |
728 | 36 | } |
729 | 627k | pInst->opcode = opcodeEntry->opcode; |
730 | 627k | context->setPosition(nextPosition); |
731 | | // Reserve the first word for the instruction. |
732 | 627k | spvInstructionAddWord(pInst, 0); |
733 | | |
734 | | // Maintains the ordered list of expected operand types. |
735 | | // For many instructions we only need the {numTypes, operandTypes} |
736 | | // entries in opcodeEntry. However, sometimes we need to modify |
737 | | // the list as we parse the operands. This occurs when an operand |
738 | | // has its own logical operands (such as the LocalSize operand for |
739 | | // ExecutionMode), or for extended instructions that may have their |
740 | | // own operands depending on the selected extended instruction. |
741 | 627k | spv_operand_pattern_t expectedOperands; |
742 | 627k | { |
743 | 627k | const auto operands = opcodeEntry->operands(); |
744 | 627k | const auto n = operands.size(); |
745 | 627k | expectedOperands.reserve(n); |
746 | 2.31M | for (auto i = 0u; i < n; i++) { |
747 | 1.68M | auto ty = operands[n - i - 1]; |
748 | 1.68M | expectedOperands.push_back(ty); |
749 | 1.68M | } |
750 | 627k | } |
751 | | |
752 | 9.68M | while (!expectedOperands.empty()) { |
753 | 9.19M | const spv_operand_type_t type = expectedOperands.back(); |
754 | 9.19M | expectedOperands.pop_back(); |
755 | | |
756 | | // Expand optional tuples lazily. |
757 | 9.19M | if (spvExpandOperandSequenceOnce(type, &expectedOperands)) continue; |
758 | | |
759 | 9.11M | if (type == SPV_OPERAND_TYPE_RESULT_ID && !result_id.empty()) { |
760 | | // Handle the <result-id> for value generating instructions. |
761 | | // We've already consumed it from the text stream. Here |
762 | | // we inject its words into the instruction. |
763 | 408k | spv_position_t temp_pos = context->position(); |
764 | 408k | error = spvTextEncodeOperand(grammar, context, SPV_OPERAND_TYPE_RESULT_ID, |
765 | 408k | result_id.c_str(), pInst, nullptr); |
766 | 408k | result_id_position = context->position(); |
767 | | // Because we are injecting we have to reset the position afterwards. |
768 | 408k | context->setPosition(temp_pos); |
769 | 408k | if (error) return error; |
770 | 8.70M | } else { |
771 | | // Find the next word. |
772 | 8.70M | error = context->advance(); |
773 | 8.70M | if (error == SPV_END_OF_STREAM) { |
774 | 4.11k | if (spvOperandIsOptional(type)) { |
775 | | // This would have been the last potential operand for the |
776 | | // instruction, |
777 | | // and we didn't find one. We're finished parsing this instruction. |
778 | 3.96k | break; |
779 | 3.96k | } else { |
780 | 150 | return context->diagnostic() |
781 | 150 | << "Expected operand for " << opcodeName |
782 | 150 | << " instruction, but found the end of the stream."; |
783 | 150 | } |
784 | 4.11k | } |
785 | 8.70M | assert(error == SPV_SUCCESS && "Somebody added another way to fail"); |
786 | | |
787 | 8.70M | if (context->isStartOfNewInst()) { |
788 | 128k | if (spvOperandIsOptional(type)) { |
789 | 128k | break; |
790 | 128k | } else { |
791 | 16 | return context->diagnostic() |
792 | 16 | << "Expected operand for " << opcodeName |
793 | 16 | << " instruction, but found the next instruction instead."; |
794 | 16 | } |
795 | 128k | } |
796 | | |
797 | 8.57M | std::string operandValue; |
798 | 8.57M | error = context->getWord(&operandValue, &nextPosition); |
799 | 8.57M | if (error) return context->diagnostic(error) << "Internal Error"; |
800 | | |
801 | 8.57M | error = spvTextEncodeOperand(grammar, context, type, operandValue.c_str(), |
802 | 8.57M | pInst, &expectedOperands); |
803 | | |
804 | 8.57M | if (error == SPV_FAILED_MATCH && spvOperandIsOptional(type)) |
805 | 24 | return SPV_SUCCESS; |
806 | | |
807 | 8.57M | if (error) return error; |
808 | | |
809 | 8.56M | context->setPosition(nextPosition); |
810 | 8.56M | } |
811 | 9.11M | } |
812 | | |
813 | 621k | if (spvOpcodeGeneratesType(pInst->opcode)) { |
814 | 25.1k | if (context->recordTypeDefinition(pInst) != SPV_SUCCESS) { |
815 | 216 | return SPV_ERROR_INVALID_TEXT; |
816 | 216 | } |
817 | 596k | } else if (opcodeEntry->hasType) { |
818 | | // SPIR-V dictates that if an instruction has both a return value and a |
819 | | // type ID then the type id is first, and the return value is second. |
820 | 363k | assert(opcodeEntry->hasResult && |
821 | 363k | "Unknown opcode: has a type but no result."); |
822 | 363k | context->recordTypeIdForValue(pInst->words[2], pInst->words[1]); |
823 | 363k | } |
824 | | |
825 | 621k | if (pInst->words.size() > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) { |
826 | 18 | return context->diagnostic() |
827 | 18 | << opcodeName << " Instruction too long: " << pInst->words.size() |
828 | 18 | << " words, but the limit is " |
829 | 18 | << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX; |
830 | 18 | } |
831 | | |
832 | 621k | pInst->words[0] = |
833 | 621k | spvOpcodeMake(uint16_t(pInst->words.size()), opcodeEntry->opcode); |
834 | | |
835 | 621k | return SPV_SUCCESS; |
836 | 621k | } |
837 | | |
838 | | enum { kAssemblerVersion = 0 }; |
839 | | |
840 | | // Populates a binary stream's |header|. The target environment is specified via |
841 | | // |env| and Id bound is via |bound|. |
842 | | spv_result_t SetHeader(spv_target_env env, const uint32_t bound, |
843 | 13.1k | uint32_t* header) { |
844 | 13.1k | if (!header) return SPV_ERROR_INVALID_BINARY; |
845 | | |
846 | 13.1k | header[SPV_INDEX_MAGIC_NUMBER] = spv::MagicNumber; |
847 | 13.1k | header[SPV_INDEX_VERSION_NUMBER] = spvVersionForTargetEnv(env); |
848 | 13.1k | header[SPV_INDEX_GENERATOR_NUMBER] = |
849 | 13.1k | SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, kAssemblerVersion); |
850 | 13.1k | header[SPV_INDEX_BOUND] = bound; |
851 | 13.1k | header[SPV_INDEX_SCHEMA] = 0; // NOTE: Reserved |
852 | | |
853 | 13.1k | return SPV_SUCCESS; |
854 | 13.1k | } |
855 | | |
856 | | // Collects all numeric ids in the module source into |numeric_ids|. |
857 | | // This function is essentially a dry-run of spvTextToBinary. |
858 | | spv_result_t GetNumericIds(const spvtools::AssemblyGrammar& grammar, |
859 | | const spvtools::MessageConsumer& consumer, |
860 | | const spv_text text, |
861 | 11.5k | std::set<uint32_t>* numeric_ids) { |
862 | 11.5k | spvtools::AssemblyContext context(text, consumer); |
863 | | |
864 | 11.5k | if (!text->str) return context.diagnostic() << "Missing assembly text."; |
865 | | |
866 | | // Skip past whitespace and comments. |
867 | 11.5k | context.advance(); |
868 | | |
869 | 279k | while (context.hasText()) { |
870 | 279k | spv_instruction_t inst; |
871 | | |
872 | | // Operand parsing sometimes involves knowing the opcode of the instruction |
873 | | // being parsed. A malformed input might feature such an operand *before* |
874 | | // the opcode is known. To guard against accessing an uninitialized opcode, |
875 | | // the instruction's opcode is initialized to a default value. |
876 | 279k | inst.opcode = spv::Op::Max; |
877 | | |
878 | 279k | if (spvTextEncodeOpcode(grammar, &context, &inst)) { |
879 | 4.87k | return SPV_ERROR_INVALID_TEXT; |
880 | 4.87k | } |
881 | | |
882 | 274k | if (context.advance()) break; |
883 | 274k | } |
884 | | |
885 | 6.71k | *numeric_ids = context.GetNumericIds(); |
886 | 6.71k | return SPV_SUCCESS; |
887 | 11.5k | } |
888 | | |
889 | | // Translates a given assembly language module into binary form. |
890 | | // If a diagnostic is generated, it is not yet marked as being |
891 | | // for a text-based input. |
892 | | spv_result_t spvTextToBinaryInternal(const spvtools::AssemblyGrammar& grammar, |
893 | | const spvtools::MessageConsumer& consumer, |
894 | | const spv_text text, |
895 | | const uint32_t options, |
896 | 23.1k | spv_binary* pBinary) { |
897 | | // The ids in this set will have the same values both in source and binary. |
898 | | // All other ids will be generated by filling in the gaps. |
899 | 23.1k | std::set<uint32_t> ids_to_preserve; |
900 | | |
901 | 23.1k | if (options & SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS) { |
902 | | // Collect all numeric ids from the source into ids_to_preserve. |
903 | 11.5k | const spv_result_t result = |
904 | 11.5k | GetNumericIds(grammar, consumer, text, &ids_to_preserve); |
905 | 11.5k | if (result != SPV_SUCCESS) return result; |
906 | 11.5k | } |
907 | | |
908 | 18.2k | spvtools::AssemblyContext context(text, consumer, std::move(ids_to_preserve)); |
909 | | |
910 | 18.2k | if (!text->str) return context.diagnostic() << "Missing assembly text."; |
911 | 18.2k | if (!pBinary) return SPV_ERROR_INVALID_POINTER; |
912 | | |
913 | 18.2k | std::vector<spv_instruction_t> instructions; |
914 | | |
915 | | // Skip past whitespace and comments. |
916 | 18.2k | context.advance(); |
917 | | |
918 | 496k | while (context.hasText()) { |
919 | 496k | instructions.push_back({}); |
920 | 496k | spv_instruction_t& inst = instructions.back(); |
921 | | |
922 | 496k | if (auto error = spvTextEncodeOpcode(grammar, &context, &inst)) { |
923 | 5.11k | return error; |
924 | 5.11k | } |
925 | | |
926 | 491k | if (context.advance()) break; |
927 | 491k | } |
928 | | |
929 | 13.1k | size_t totalSize = SPV_INDEX_INSTRUCTION; |
930 | 433k | for (auto& inst : instructions) { |
931 | 433k | totalSize += inst.words.size(); |
932 | 433k | } |
933 | | |
934 | 13.1k | uint32_t* data = new uint32_t[totalSize]; |
935 | 13.1k | if (!data) return SPV_ERROR_OUT_OF_MEMORY; |
936 | 13.1k | uint64_t currentIndex = SPV_INDEX_INSTRUCTION; |
937 | 433k | for (auto& inst : instructions) { |
938 | 433k | memcpy(data + currentIndex, inst.words.data(), |
939 | 433k | sizeof(uint32_t) * inst.words.size()); |
940 | 433k | currentIndex += inst.words.size(); |
941 | 433k | } |
942 | | |
943 | 13.1k | if (auto error = SetHeader(grammar.target_env(), context.getBound(), data)) |
944 | 0 | return error; |
945 | | |
946 | 13.1k | spv_binary binary = new spv_binary_t(); |
947 | 13.1k | if (!binary) { |
948 | 0 | delete[] data; |
949 | 0 | return SPV_ERROR_OUT_OF_MEMORY; |
950 | 0 | } |
951 | 13.1k | binary->code = data; |
952 | 13.1k | binary->wordCount = totalSize; |
953 | | |
954 | 13.1k | *pBinary = binary; |
955 | | |
956 | 13.1k | return SPV_SUCCESS; |
957 | 13.1k | } |
958 | | |
959 | | } // anonymous namespace |
960 | | |
961 | | spv_result_t spvTextToBinary(const spv_const_context context, |
962 | | const char* input_text, |
963 | | const size_t input_text_size, spv_binary* pBinary, |
964 | 0 | spv_diagnostic* pDiagnostic) { |
965 | 0 | return spvTextToBinaryWithOptions(context, input_text, input_text_size, |
966 | 0 | SPV_TEXT_TO_BINARY_OPTION_NONE, pBinary, |
967 | 0 | pDiagnostic); |
968 | 0 | } |
969 | | |
970 | | spv_result_t spvTextToBinaryWithOptions(const spv_const_context context, |
971 | | const char* input_text, |
972 | | const size_t input_text_size, |
973 | | const uint32_t options, |
974 | | spv_binary* pBinary, |
975 | 23.1k | spv_diagnostic* pDiagnostic) { |
976 | 23.1k | spv_context_t hijack_context = *context; |
977 | 23.1k | if (pDiagnostic) { |
978 | 23.1k | *pDiagnostic = nullptr; |
979 | 23.1k | spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); |
980 | 23.1k | } |
981 | | |
982 | 23.1k | spv_text_t text = {input_text, input_text_size}; |
983 | 23.1k | spvtools::AssemblyGrammar grammar(&hijack_context); |
984 | | |
985 | 23.1k | spv_result_t result = spvTextToBinaryInternal( |
986 | 23.1k | grammar, hijack_context.consumer, &text, options, pBinary); |
987 | 23.1k | if (pDiagnostic && *pDiagnostic) (*pDiagnostic)->isTextSource = true; |
988 | | |
989 | 23.1k | return result; |
990 | 23.1k | } |
991 | | |
992 | 0 | void spvTextDestroy(spv_text text) { |
993 | 0 | if (text) { |
994 | 0 | if (text->str) delete[] text->str; |
995 | 0 | delete text; |
996 | 0 | } |
997 | 0 | } |