blob: a0e2512ab2e1e039e2414aa2270e2aa0e2b440f0 [file] [log] [blame]
Andrew Woloszyn71fc0552015-09-24 10:26:51 -04001// 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 "text_handler.h"
28
29#include <algorithm>
30#include <cassert>
31#include <cstring>
32
33#include "binary.h"
34#include "ext_inst.h"
David Netob5dc8fc2015-10-06 16:22:00 -040035#include "instruction.h"
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040036#include "opcode.h"
37#include "text.h"
38
39namespace {
40
41/// @brief Advance text to the start of the next line
42///
43/// @param[in] text to be parsed
44/// @param[in,out] position position text has been advanced to
45///
46/// @return result code
47spv_result_t advanceLine(spv_text text, spv_position position) {
48 while (true) {
49 switch (text->str[position->index]) {
50 case '\0':
51 return SPV_END_OF_STREAM;
52 case '\n':
53 position->column = 0;
54 position->line++;
55 position->index++;
56 return SPV_SUCCESS;
57 default:
58 position->column++;
59 position->index++;
60 break;
61 }
62 }
63}
64
65/// @brief Advance text to first non white space character
66/// If a null terminator is found during the text advance SPV_END_OF_STREAM is
67/// returned, SPV_SUCCESS otherwise. No error checking is performed on the
68/// parameters, its the users responsibility to ensure these are non null.
69///
70/// @param[in] text to be parsed
71/// @param[in,out] position text has been advanced to
72///
73/// @return result code
74spv_result_t advance(spv_text text, spv_position position) {
75 // NOTE: Consume white space, otherwise don't advance.
76 switch (text->str[position->index]) {
77 case '\0':
78 return SPV_END_OF_STREAM;
79 case ';':
80 if (spv_result_t error = advanceLine(text, position)) return error;
81 return advance(text, position);
82 case ' ':
83 case '\t':
84 position->column++;
85 position->index++;
86 return advance(text, position);
87 case '\n':
88 position->column = 0;
89 position->line++;
90 position->index++;
91 return advance(text, position);
92 default:
93 break;
94 }
95 return SPV_SUCCESS;
96}
97
98/// @brief Fetch the next word from the text stream.
99///
100/// A word ends at the next comment or whitespace. However, double-quoted
101/// strings remain intact, and a backslash always escapes the next character.
102///
103/// @param[in] text stream to read from
104/// @param[in] position current position in text stream
105/// @param[out] word returned word
106/// @param[out] endPosition one past the end of the returned word
107///
108/// @return result code
109spv_result_t getWord(spv_text text, spv_position position, std::string &word,
110 spv_position endPosition) {
111 if (!text->str || !text->length) return SPV_ERROR_INVALID_TEXT;
112 if (!position || !endPosition) return SPV_ERROR_INVALID_POINTER;
113
114 *endPosition = *position;
115
116 bool quoting = false;
117 bool escaping = false;
118
119 // NOTE: Assumes first character is not white space!
120 while (true) {
121 const char ch = text->str[endPosition->index];
122 if (ch == '\\')
123 escaping = !escaping;
124 else {
125 switch (ch) {
126 case '"':
127 if (!escaping) quoting = !quoting;
128 break;
129 case ' ':
130 case ';':
131 case '\t':
132 case '\n':
133 if (escaping || quoting) break;
134 // Fall through.
135 case '\0': { // NOTE: End of word found!
136 word.assign(text->str + position->index,
137 (size_t)(endPosition->index - position->index));
138 return SPV_SUCCESS;
139 }
140 default:
141 break;
142 }
143 escaping = false;
144 }
145
146 endPosition->column++;
147 endPosition->index++;
148 }
149}
150
151// Returns true if the characters in the text as position represent
152// the start of an Opcode.
153bool startsWithOp(spv_text text, spv_position position) {
154 if (text->length < position->index + 3) return false;
155 char ch0 = text->str[position->index];
156 char ch1 = text->str[position->index + 1];
157 char ch2 = text->str[position->index + 2];
158 return ('O' == ch0 && 'p' == ch1 && ('A' <= ch2 && ch2 <= 'Z'));
159}
160
161/// @brief Parses a mask expression string for the given operand type.
162///
163/// A mask expression is a sequence of one or more terms separated by '|',
164/// where each term a named enum value for the given type. No whitespace
165/// is permitted.
166///
167/// On success, the value is written to pValue.
168///
169/// @param[in] operandTable operand lookup table
170/// @param[in] type of the operand
171/// @param[in] textValue word of text to be parsed
172/// @param[out] pValue where the resulting value is written
173///
174/// @return result code
175spv_result_t spvTextParseMaskOperand(const spv_operand_table operandTable,
176 const spv_operand_type_t type,
177 const char *textValue, uint32_t *pValue) {
178 if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT;
179 size_t text_length = strlen(textValue);
180 if (text_length == 0) return SPV_ERROR_INVALID_TEXT;
181 const char *text_end = textValue + text_length;
182
183 // We only support mask expressions in ASCII, so the separator value is a
184 // char.
185 const char separator = '|';
186
187 // Accumulate the result by interpreting one word at a time, scanning
188 // from left to right.
189 uint32_t value = 0;
190 const char *begin = textValue; // The left end of the current word.
191 const char *end = nullptr; // One character past the end of the current word.
192 do {
193 end = std::find(begin, text_end, separator);
194
195 spv_operand_desc entry = nullptr;
196 if (spvOperandTableNameLookup(operandTable, type, begin, end - begin,
197 &entry)) {
198 return SPV_ERROR_INVALID_TEXT;
199 }
200 value |= entry->value;
201
202 // Advance to the next word by skipping over the separator.
203 begin = end + 1;
204 } while (end != text_end);
205
206 *pValue = value;
207 return SPV_SUCCESS;
208}
209
210} // anonymous namespace
211
212namespace libspirv {
213
214bool AssemblyGrammar::isValid() const {
215 return operandTable_ && opcodeTable_ && extInstTable_;
216}
217
218spv_result_t AssemblyGrammar::lookupOpcode(const char *name,
219 spv_opcode_desc *desc) const {
220 return spvOpcodeTableNameLookup(opcodeTable_, name, desc);
221}
222spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type,
223 const char *name, size_t name_len,
224 spv_operand_desc *desc) const {
225 return spvOperandTableNameLookup(operandTable_, type, name, name_len, desc);
226}
227
228spv_result_t AssemblyGrammar::parseMaskOperand(const spv_operand_type_t type,
229 const char *textValue,
230 uint32_t *pValue) const {
231 return spvTextParseMaskOperand(operandTable_, type, textValue, pValue);
232}
233spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type,
234 const char *textValue,
235 spv_ext_inst_desc *extInst) const {
236 return spvExtInstTableNameLookup(extInstTable_, type, textValue, extInst);
237}
238
239void AssemblyGrammar::prependOperandTypesForMask(
240 const spv_operand_type_t type, const uint32_t mask,
241 spv_operand_pattern_t *pattern) const {
242 spvPrependOperandTypesForMask(operandTable_, type, mask, pattern);
243}
244
245// This represents all of the data that is only valid for the duration of
246// a single compilation.
247uint32_t AssemblyContext::spvNamedIdAssignOrGet(const char *textValue) {
248 if (named_ids_.end() == named_ids_.find(textValue)) {
249 named_ids_[std::string(textValue)] = bound_++;
250 }
251 return named_ids_[textValue];
252}
253uint32_t AssemblyContext::getBound() const { return bound_; }
254
255spv_result_t AssemblyContext::advance() {
256 return ::advance(text_, &current_position_);
257}
258
259spv_result_t AssemblyContext::getWord(std::string &word,
260 spv_position endPosition) {
261 return ::getWord(text_, &current_position_, word, endPosition);
262}
263
264bool AssemblyContext::startsWithOp() {
265 return ::startsWithOp(text_, &current_position_);
266}
267
268bool AssemblyContext::isStartOfNewInst() {
269 spv_position_t nextPosition = current_position_;
270 if (::advance(text_, &nextPosition)) return false;
271 if (::startsWithOp(text_, &nextPosition)) return true;
272
273 std::string word;
274 spv_position_t startPosition = current_position_;
275 if (::getWord(text_, &startPosition, word, &nextPosition)) return false;
276 if ('%' != word.front()) return false;
277
278 if (::advance(text_, &nextPosition)) return false;
279 startPosition = nextPosition;
280 if (::getWord(text_, &startPosition, word, &nextPosition)) return false;
281 if ("=" != word) return false;
282
283 if (::advance(text_, &nextPosition)) return false;
284 startPosition = nextPosition;
285 if (::startsWithOp(text_, &startPosition)) return true;
286 return false;
287}
288char AssemblyContext::peek() const {
289 return text_->str[current_position_.index];
290}
291
292bool AssemblyContext::hasText() const {
293 return text_->length > current_position_.index;
294}
295std::string AssemblyContext::getWord() const {
296 size_t index = current_position_.index;
297 while (true) {
298 switch (text_->str[index]) {
299 case '\0':
300 case '\t':
301 case '\v':
302 case '\r':
303 case '\n':
304 case ' ':
305 return std::string(text_->str, text_->str + index);
306 default:
307 index++;
308 }
309 }
310 assert(0 && "Unreachable");
311 return ""; // Make certain compilers happy.
312}
313
314void AssemblyContext::seekForward(uint32_t size) {
315 current_position_.index += size;
316 current_position_.column += size;
317}
318
319spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value,
320 spv_instruction_t *pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400321 spvInstructionAddWord(pInst, value);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400322 return SPV_SUCCESS;
323}
324
325spv_result_t AssemblyContext::binaryEncodeU64(const uint64_t value,
326 spv_instruction_t *pInst) {
327 uint32_t low = (uint32_t)(0x00000000ffffffff & value);
328 uint32_t high = (uint32_t)((0xffffffff00000000 & value) >> 32);
329 spv_result_t err = binaryEncodeU32(low, pInst);
330 if (err != SPV_SUCCESS) {
331 return err;
332 }
333 return binaryEncodeU32(high, pInst);
334}
335
336spv_result_t AssemblyContext::binaryEncodeString(
337 const char *value, spv_instruction_t *pInst) {
David Netob5dc8fc2015-10-06 16:22:00 -0400338 const size_t length = strlen(value);
339 const size_t wordCount = (length / 4) + 1;
340 const size_t oldWordCount = pInst->words.size();
341 const size_t newWordCount = oldWordCount + wordCount;
342
343 // TODO(dneto): We can just defer this check until later.
344 if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) {
David Netof6b86512015-10-08 15:20:25 -0400345 diagnostic() << "Instruction too long: more than "
346 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words.";
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400347 return SPV_ERROR_INVALID_TEXT;
348 }
349
David Netob5dc8fc2015-10-06 16:22:00 -0400350 pInst->words.resize(newWordCount);
351
352 // Make sure all the bytes in the last word are 0, in case we only
353 // write a partial word at the end.
354 pInst->words.back() = 0;
355
356 char *dest = (char *)&pInst->words[oldWordCount];
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400357 strncpy(dest, value, length);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400358
359 return SPV_SUCCESS;
360}
361}
362