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