blob: 065a71048725d03e406e46b4b7ab2a001dc9e123 [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#ifndef _LIBSPIRV_UTIL_TEXT_HANDLER_H_
28#define _LIBSPIRV_UTIL_TEXT_HANDLER_H_
29
David Neto62741202015-10-13 15:51:12 -040030#include <iomanip>
31#include <limits>
32#include <sstream>
33#include <type_traits>
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040034#include <unordered_map>
35
David Neto62741202015-10-13 15:51:12 -040036#include <libspirv/libspirv.h>
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040037#include "diagnostic.h"
David Netob5dc8fc2015-10-06 16:22:00 -040038#include "instruction.h"
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040039#include "operand.h"
David Neto78e677b2015-10-05 13:28:46 -040040#include "text.h"
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040041
42namespace libspirv {
43// Structures
44
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040045// This is a lattice for tracking types.
46enum class IdTypeClass {
David Neto78e677b2015-10-05 13:28:46 -040047 kBottom = 0, // We have no information yet.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040048 kScalarIntegerType,
49 kScalarFloatType,
50 kOtherType
51};
52
53
54// Contains ID type information that needs to be tracked across all Ids.
55// Bitwidth is only valid when type_class is kScalarIntegerType or
56// kScalarFloatType.
57struct IdType {
58 uint32_t bitwidth; // Safe to assume that we will not have > 2^32 bits.
David Neto78e677b2015-10-05 13:28:46 -040059 bool isSigned; // This is only significant if type_class is integral.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040060 IdTypeClass type_class;
61};
62
David Neto78e677b2015-10-05 13:28:46 -040063// A value representing an unknown type.
64extern const IdType kUnknownType;
65
Andrew Woloszyn537e7762015-09-29 11:28:34 -040066// Returns true if the type is a scalar integer type.
67inline bool isScalarIntegral(const IdType& type) {
68 return type.type_class == IdTypeClass::kScalarIntegerType;
69}
70
71// Returns true if the type is a scalar floating point type.
72inline bool isScalarFloating(const IdType& type) {
73 return type.type_class == IdTypeClass::kScalarFloatType;
74}
75
David Neto78e677b2015-10-05 13:28:46 -040076// Returns the number of bits in the type.
77// This is only valid for bottom, scalar integer, and scalar floating
78// classes. For bottom, assume 32 bits.
79inline int assumedBitWidth(const IdType& type) {
80 switch(type.type_class) {
81 case IdTypeClass::kBottom:
82 return 32;
83 case IdTypeClass::kScalarIntegerType:
84 case IdTypeClass::kScalarFloatType:
85 return type.bitwidth;
86 default:
87 break;
88 }
89 // We don't care about this case.
90 return 0;
91}
92
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040093// Encapsulates the grammar to use for SPIR-V assembly.
94// Contains methods to query for valid instructions and operands.
95class AssemblyGrammar {
96 public:
97 AssemblyGrammar(const spv_operand_table operand_table,
98 const spv_opcode_table opcode_table,
99 const spv_ext_inst_table ext_inst_table)
100 : operandTable_(operand_table),
101 opcodeTable_(opcode_table),
102 extInstTable_(ext_inst_table) {}
103
104 // Returns true if the compilation_data has been initialized with valid data.
105 bool isValid() const;
106
107 // Fills in the desc parameter with the information about the opcode
108 // of the given name. Returns SPV_SUCCESS if the opcode was found, and
109 // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist.
110 spv_result_t lookupOpcode(const char *name, spv_opcode_desc *desc) const;
111
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400112 // Fills in the desc parameter with the information about the opcode
113 // of the valid. Returns SPV_SUCCESS if the opcode was found, and
114 // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist.
115 spv_result_t lookupOpcode(Op opcode, spv_opcode_desc *desc) const;
116
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400117 // Fills in the desc parameter with the information about the given
118 // operand. Returns SPV_SUCCESS if the operand was found, and
119 // SPV_ERROR_INVALID_LOOKUP otherwise.
120 spv_result_t lookupOperand(spv_operand_type_t type, const char *name,
121 size_t name_len, spv_operand_desc *desc) const;
122
123 // Parses a mask expression string for the given operand type.
124 //
125 // A mask expression is a sequence of one or more terms separated by '|',
126 // where each term is a named enum value for a given type. No whitespace
127 // is permitted.
128 //
129 // On success, the value is written to pValue, and SPV_SUCCESS is returend.
130 // The operand type is defined by the type parameter, and the text to be
131 // parsed is defined by the textValue parameter.
132 spv_result_t parseMaskOperand(const spv_operand_type_t type,
133 const char *textValue, uint32_t *pValue) const;
134
135
136 // Writes the extended operand with the given type and text to the *extInst
137 // parameter.
138 // Returns SPV_SUCCESS if the value could be found.
139 spv_result_t lookupExtInst(spv_ext_inst_type_t type, const char *textValue,
140 spv_ext_inst_desc *extInst) const;
141
142 // Inserts the operands expected after the given typed mask onto the front
143 // of the given pattern.
144 //
145 // Each set bit in the mask represents zero or more operand types that should
146 // be prepended onto the pattern. Opearnds for a less significant bit always
147 // appear before operands for a more significatn bit.
148 //
149 // If a set bit is unknown, then we assume it has no operands.
150 void prependOperandTypesForMask(const spv_operand_type_t type,
151 const uint32_t mask,
152 spv_operand_pattern_t *pattern) const;
153
154 private:
155 const spv_operand_table operandTable_;
156 const spv_opcode_table opcodeTable_;
157 const spv_ext_inst_table extInstTable_;
158};
159
160// Encapsulates the data used during the assembly of a SPIR-V module.
161class AssemblyContext {
162 public:
163 AssemblyContext(spv_text text, spv_diagnostic *diagnostic)
164 : current_position_({}),
165 pDiagnostic_(diagnostic),
166 text_(text),
167 bound_(1) {}
168
169 // Assigns a new integer value to the given text ID, or returns the previously
170 // assigned integer value if the ID has been seen before.
171 uint32_t spvNamedIdAssignOrGet(const char *textValue);
172
173 // Returns the largest largest numeric ID that has been assigned.
174 uint32_t getBound() const;
175
176 // Advances position to point to the next word in the input stream.
177 // Returns SPV_SUCCESS on success.
178 spv_result_t advance();
179
180 // Sets word to the next word in the input text. Fills endPosition with
181 // the next location past the end of the word.
182 spv_result_t getWord(std::string &word, spv_position endPosition);
183
184 // Returns the next word in the input stream. It is invalid to call this
185 // method if position has been set to a location in the stream that does not
186 // exist. If there are no subsequent words, the empty string will be returend.
187 std::string getWord() const;
188
189 // Returns true if the next word in the input is the start of a new Opcode.
190 bool startsWithOp();
191
192 // Returns true if the next word in the input is the start of a new
193 // instruction.
194 bool isStartOfNewInst();
195
196 // Returns a diagnostic object initialized with current position in the input
David Netoac508b02015-10-09 15:48:09 -0400197 // stream, and for the given error code. Any data written to this object will
198 // show up in pDiagnsotic on destruction.
199 DiagnosticStream diagnostic(spv_result_t error) {
200 return DiagnosticStream(&current_position_, pDiagnostic_, error);
201 }
202
203 // Returns a diagnostic object with the default assembly error code.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400204 DiagnosticStream diagnostic() {
David Netoac508b02015-10-09 15:48:09 -0400205 // The default failure for assembly is invalid text.
206 return diagnostic(SPV_ERROR_INVALID_TEXT);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400207 }
208
209 // Returns then next characted in the input stream.
210 char peek() const;
211
212 // Returns true if there is more text in the input stream.
213 bool hasText() const;
214
215 // Seeks the input stream forward by 'size' characters.
216 void seekForward(uint32_t size);
217
218 // Sets the current position in the input stream to the given position.
219 void setPosition(const spv_position_t &newPosition) {
220 current_position_ = newPosition;
221 }
222
223 // Returns the current position in the input stream.
224 const spv_position_t &position() const { return current_position_; }
225
226 // Appends the given 32-bit value to the given instruction.
David Neto78e677b2015-10-05 13:28:46 -0400227 // Returns SPV_SUCCESS if the value could be correctly inserted in the
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400228 // instruction.
229 spv_result_t binaryEncodeU32(const uint32_t value, spv_instruction_t *pInst);
David Neto78e677b2015-10-05 13:28:46 -0400230
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400231 // Appends the given string to the given instruction.
David Neto78e677b2015-10-05 13:28:46 -0400232 // Returns SPV_SUCCESS if the value could be correctly inserted in the
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400233 // instruction.
234 spv_result_t binaryEncodeString(const char *value, spv_instruction_t *pInst);
235
David Neto78e677b2015-10-05 13:28:46 -0400236 // Appends the given numeric literal to the given instruction.
237 // Validates and respects the bitwidth supplied in the IdType argument.
238 // If the type is of class kBottom the value will be encoded as a
239 // 32-bit integer.
240 // Returns SPV_SUCCESS if the value could be correctly added to the
241 // instruction.
242 spv_result_t binaryEncodeNumericLiteral(const char *numeric_literal,
243 bool optional,
244 const IdType &type,
245 spv_instruction_t *pInst);
246
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400247 // Returns the IdType associated with this type-generating value.
248 // If the type has not been previously recorded with recordTypeDefinition,
David Neto78e677b2015-10-05 13:28:46 -0400249 // kUnknownType will be returned.
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400250 IdType getTypeOfTypeGeneratingValue(uint32_t value) const;
251
252 // Returns the IdType that represents the return value of this Value
253 // generating instruction.
254 // If the value has not been recorded with recordTypeIdForValue, or the type
David Neto78e677b2015-10-05 13:28:46 -0400255 // could not be determined kUnknownType will be returned.
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400256 IdType getTypeOfValueInstruction(uint32_t value) const;
257
258 // Tracks the type-defining instruction. The result of the tracking can
259 // later be queried using getValueType.
260 // pInst is expected to be completely filled in by the time this instruction
261 // is called.
262 // Returns SPV_SUCCESS on success, or SPV_ERROR_INVALID_VALUE on error.
263 spv_result_t recordTypeDefinition(const spv_instruction_t* pInst);
264
265 // Tracks the relationship between the value and its type.
266 spv_result_t recordTypeIdForValue(uint32_t value, uint32_t type);
267
David Neto62741202015-10-13 15:51:12 -0400268 // Parses a numeric value of a given type from the given text. The number
269 // should take up the entire string, and should be within bounds for the
270 // target type. On success, returns SPV_SUCCESS and populates the object
271 // referenced by value_pointer. On failure, returns SPV_FAILED_MATCH if
272 // is_optional is true, and returns SPV_ERROR_INVALID_TEXT and emits a
273 // diagnostic otherwise.
274 template <typename T>
275 spv_result_t parseNumber(const char *text, bool is_optional, T *value_pointer,
276 const char *error_message_fragment) {
277 // C++11 doesn't define std::istringstream(int8_t&), so calling this method
278 // with a single-byte type leads to implementation-defined behaviour.
279 // Similarly for uint8_t.
280 static_assert(sizeof(T) > 1, "Don't use a single-byte type this parse method");
281
282 std::istringstream text_stream(text);
283 // Allow both decimal and hex input for integers.
284 // It also allows octal input, but we don't care about that case.
285 text_stream >> std::setbase(0);
286 text_stream >> *value_pointer;
287 bool ok = true;
288
289 // We should have read something.
290 ok = (text[0] != 0) && !text_stream.bad();
291 // It should have been all the text.
292 ok = ok && text_stream.eof();
293 // It should have been in range.
294 ok = ok && !text_stream.fail();
295 // Work around a bug in the GNU C++11 library. It will happily parse
296 // "-1" for uint16_t as 65535.
297 if (ok && !std::is_signed<T>::value && (text[0] == '-') &&
298 *value_pointer != 0) {
299 ok = false;
300 // Match expected error behaviour of std::istringstream::operator>>
301 // on failure to parse.
302 *value_pointer = 0;
303 }
304
305 if (ok) return SPV_SUCCESS;
306 if (is_optional) return SPV_FAILED_MATCH;
307 return diagnostic() << error_message_fragment << text;
308 }
309
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400310 private:
David Neto78e677b2015-10-05 13:28:46 -0400311 // Appends the given floating point literal to the given instruction.
312 // Returns SPV_SUCCESS if the value was correctly parsed. Otherwise
313 // an error code is returned, and a message is emitted if is_optional
314 // is false. Only 32 and 64 bit floating point numbers are supported.
315 spv_result_t binaryEncodeFloatingPointLiteral(const char *numeric_literal,
316 bool optional,
317 const IdType& type,
318 spv_instruction_t *pInst);
319
320 // Appends the given integer literal to the given instruction.
321 // Returns SPV_SUCCESS if the value was correctly parsed. Otherwise
322 // an error code is returned, and a message is emitted if is_optional
323 // is false. Integers up to 64 bits are supported.
324 spv_result_t binaryEncodeIntegerLiteral(const char *numeric_literal,
325 bool optional,
326 const IdType &type,
327 spv_instruction_t *pInst);
328
329 // Returns SPV_SUCCESS if the given value fits within the target scalar
330 // integral type. The target type may have an unusual bit width.
331 // If the value was originally specified as a hexadecimal number, then
332 // the overflow bits should be zero. If it was hex and the target type is
333 // signed, then return the sign-extended value through the
334 // updated_value_for_hex pointer argument.
335 // On failure, if is_optional is true then return SPV_FAILED_MATCH, otherwise
336 // emit a diagnostic and return SPV_ERROR_INVALID_TEXT.
337 template <typename T>
338 spv_result_t checkRangeAndIfHexThenSignExtend(T value, bool is_optional,
339 const IdType &type, bool is_hex,
340 T *updated_value_for_hex);
341
342 // Writes the given 64-bit literal value into the instruction.
343 // return SPV_SUCCESS if the value could be written in the instruction.
344 spv_result_t binaryEncodeU64(const uint64_t value, spv_instruction_t *pInst);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400345 // Maps ID names to their corresponding numerical ids.
346 using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
347 // Maps type-defining IDs to their IdType.
348 using spv_id_to_type_map = std::unordered_map<uint32_t, IdType>;
349 // Maps Ids to the id of their type.
350 using spv_id_to_type_id = std::unordered_map<uint32_t, uint32_t>;
351
352 spv_named_id_table named_ids_;
353 spv_id_to_type_map types_;
354 spv_id_to_type_id value_types_;
355 spv_position_t current_position_;
356 spv_diagnostic *pDiagnostic_;
357 spv_text text_;
358 uint32_t bound_;
359};
360}
361#endif // _LIBSPIRV_UTIL_TEXT_HANDLER_H_
362