blob: adbde16fa3fe18f020bce652cce28e94c8966612 [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
Andrew Woloszyn157e41b2015-10-16 15:11:00 -040063// Default equality operator for IdType. Tests if all members are the same.
64inline bool operator==(const IdType &first, const IdType &second) {
65 return (first.bitwidth == second.bitwidth) &&
66 (first.isSigned == second.isSigned) &&
67 (first.type_class == second.type_class);
68}
69
70// Tests whether any member of the IdTypes do not match.
71inline bool operator!=(const IdType &first, const IdType &second) {
72 return !(first == second);
73}
74
David Neto78e677b2015-10-05 13:28:46 -040075// A value representing an unknown type.
76extern const IdType kUnknownType;
77
Andrew Woloszyn537e7762015-09-29 11:28:34 -040078// Returns true if the type is a scalar integer type.
79inline bool isScalarIntegral(const IdType& type) {
80 return type.type_class == IdTypeClass::kScalarIntegerType;
81}
82
83// Returns true if the type is a scalar floating point type.
84inline bool isScalarFloating(const IdType& type) {
85 return type.type_class == IdTypeClass::kScalarFloatType;
86}
87
David Neto78e677b2015-10-05 13:28:46 -040088// Returns the number of bits in the type.
89// This is only valid for bottom, scalar integer, and scalar floating
90// classes. For bottom, assume 32 bits.
91inline int assumedBitWidth(const IdType& type) {
92 switch(type.type_class) {
93 case IdTypeClass::kBottom:
94 return 32;
95 case IdTypeClass::kScalarIntegerType:
96 case IdTypeClass::kScalarFloatType:
97 return type.bitwidth;
98 default:
99 break;
100 }
101 // We don't care about this case.
102 return 0;
103}
104
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400105// Encapsulates the grammar to use for SPIR-V assembly.
106// Contains methods to query for valid instructions and operands.
107class AssemblyGrammar {
108 public:
109 AssemblyGrammar(const spv_operand_table operand_table,
110 const spv_opcode_table opcode_table,
111 const spv_ext_inst_table ext_inst_table)
112 : operandTable_(operand_table),
113 opcodeTable_(opcode_table),
114 extInstTable_(ext_inst_table) {}
115
116 // Returns true if the compilation_data has been initialized with valid data.
117 bool isValid() const;
118
119 // Fills in the desc parameter with the information about the opcode
120 // of the given name. Returns SPV_SUCCESS if the opcode was found, and
121 // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist.
122 spv_result_t lookupOpcode(const char *name, spv_opcode_desc *desc) const;
123
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400124 // Fills in the desc parameter with the information about the opcode
125 // of the valid. Returns SPV_SUCCESS if the opcode was found, and
126 // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist.
127 spv_result_t lookupOpcode(Op opcode, spv_opcode_desc *desc) const;
128
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400129 // Fills in the desc parameter with the information about the given
130 // operand. Returns SPV_SUCCESS if the operand was found, and
131 // SPV_ERROR_INVALID_LOOKUP otherwise.
132 spv_result_t lookupOperand(spv_operand_type_t type, const char *name,
133 size_t name_len, spv_operand_desc *desc) const;
134
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400135 // Fills in the desc parameter with the information about the given
136 // operand. Returns SPV_SUCCESS if the operand was found, and
137 // SPV_ERROR_INVALID_LOOKUP otherwise.
138 spv_result_t lookupOperand(spv_operand_type_t type, uint32_t operand,
139 spv_operand_desc *desc) const;
140
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400141 // Parses a mask expression string for the given operand type.
142 //
143 // A mask expression is a sequence of one or more terms separated by '|',
144 // where each term is a named enum value for a given type. No whitespace
145 // is permitted.
146 //
147 // On success, the value is written to pValue, and SPV_SUCCESS is returend.
148 // The operand type is defined by the type parameter, and the text to be
149 // parsed is defined by the textValue parameter.
150 spv_result_t parseMaskOperand(const spv_operand_type_t type,
151 const char *textValue, uint32_t *pValue) const;
152
153
154 // Writes the extended operand with the given type and text to the *extInst
155 // parameter.
156 // Returns SPV_SUCCESS if the value could be found.
157 spv_result_t lookupExtInst(spv_ext_inst_type_t type, const char *textValue,
158 spv_ext_inst_desc *extInst) const;
159
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400160 // Writes the extended operand with the given type and first encoded word
161 // to the *extInst parameter.
162 // Returns SPV_SUCCESS if the value could be found.
163 spv_result_t lookupExtInst(spv_ext_inst_type_t type,
164 uint32_t firstWord,
165 spv_ext_inst_desc *extInst) const;
166
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400167 // Inserts the operands expected after the given typed mask onto the front
168 // of the given pattern.
169 //
170 // Each set bit in the mask represents zero or more operand types that should
171 // be prepended onto the pattern. Opearnds for a less significant bit always
172 // appear before operands for a more significatn bit.
173 //
174 // If a set bit is unknown, then we assume it has no operands.
175 void prependOperandTypesForMask(const spv_operand_type_t type,
176 const uint32_t mask,
177 spv_operand_pattern_t *pattern) const;
178
179 private:
180 const spv_operand_table operandTable_;
181 const spv_opcode_table opcodeTable_;
182 const spv_ext_inst_table extInstTable_;
183};
184
185// Encapsulates the data used during the assembly of a SPIR-V module.
186class AssemblyContext {
187 public:
188 AssemblyContext(spv_text text, spv_diagnostic *diagnostic)
189 : current_position_({}),
190 pDiagnostic_(diagnostic),
191 text_(text),
192 bound_(1) {}
193
194 // Assigns a new integer value to the given text ID, or returns the previously
195 // assigned integer value if the ID has been seen before.
196 uint32_t spvNamedIdAssignOrGet(const char *textValue);
197
198 // Returns the largest largest numeric ID that has been assigned.
199 uint32_t getBound() const;
200
201 // Advances position to point to the next word in the input stream.
202 // Returns SPV_SUCCESS on success.
203 spv_result_t advance();
204
205 // Sets word to the next word in the input text. Fills endPosition with
206 // the next location past the end of the word.
207 spv_result_t getWord(std::string &word, spv_position endPosition);
208
209 // Returns the next word in the input stream. It is invalid to call this
210 // method if position has been set to a location in the stream that does not
211 // exist. If there are no subsequent words, the empty string will be returend.
212 std::string getWord() const;
213
214 // Returns true if the next word in the input is the start of a new Opcode.
215 bool startsWithOp();
216
217 // Returns true if the next word in the input is the start of a new
218 // instruction.
219 bool isStartOfNewInst();
220
221 // Returns a diagnostic object initialized with current position in the input
David Netoac508b02015-10-09 15:48:09 -0400222 // stream, and for the given error code. Any data written to this object will
223 // show up in pDiagnsotic on destruction.
224 DiagnosticStream diagnostic(spv_result_t error) {
225 return DiagnosticStream(&current_position_, pDiagnostic_, error);
226 }
227
228 // Returns a diagnostic object with the default assembly error code.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400229 DiagnosticStream diagnostic() {
David Netoac508b02015-10-09 15:48:09 -0400230 // The default failure for assembly is invalid text.
231 return diagnostic(SPV_ERROR_INVALID_TEXT);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400232 }
233
234 // Returns then next characted in the input stream.
235 char peek() const;
236
237 // Returns true if there is more text in the input stream.
238 bool hasText() const;
239
240 // Seeks the input stream forward by 'size' characters.
241 void seekForward(uint32_t size);
242
243 // Sets the current position in the input stream to the given position.
244 void setPosition(const spv_position_t &newPosition) {
245 current_position_ = newPosition;
246 }
247
248 // Returns the current position in the input stream.
249 const spv_position_t &position() const { return current_position_; }
250
251 // Appends the given 32-bit value to the given instruction.
David Neto78e677b2015-10-05 13:28:46 -0400252 // Returns SPV_SUCCESS if the value could be correctly inserted in the
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400253 // instruction.
254 spv_result_t binaryEncodeU32(const uint32_t value, spv_instruction_t *pInst);
David Neto78e677b2015-10-05 13:28:46 -0400255
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400256 // Appends the given string to the given instruction.
David Neto78e677b2015-10-05 13:28:46 -0400257 // Returns SPV_SUCCESS if the value could be correctly inserted in the
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400258 // instruction.
259 spv_result_t binaryEncodeString(const char *value, spv_instruction_t *pInst);
260
David Neto78e677b2015-10-05 13:28:46 -0400261 // Appends the given numeric literal to the given instruction.
262 // Validates and respects the bitwidth supplied in the IdType argument.
263 // If the type is of class kBottom the value will be encoded as a
264 // 32-bit integer.
265 // Returns SPV_SUCCESS if the value could be correctly added to the
David Neto51013d12015-10-14 11:31:51 -0400266 // instruction. Returns the given error code on failure, and emits
267 // a diagnotic if that error code is not SPV_FAILED_MATCH.
David Neto78e677b2015-10-05 13:28:46 -0400268 spv_result_t binaryEncodeNumericLiteral(const char *numeric_literal,
David Neto51013d12015-10-14 11:31:51 -0400269 spv_result_t error_code,
David Neto78e677b2015-10-05 13:28:46 -0400270 const IdType &type,
271 spv_instruction_t *pInst);
272
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400273 // Returns the IdType associated with this type-generating value.
274 // If the type has not been previously recorded with recordTypeDefinition,
David Neto78e677b2015-10-05 13:28:46 -0400275 // kUnknownType will be returned.
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400276 IdType getTypeOfTypeGeneratingValue(uint32_t value) const;
277
278 // Returns the IdType that represents the return value of this Value
279 // generating instruction.
280 // If the value has not been recorded with recordTypeIdForValue, or the type
David Neto78e677b2015-10-05 13:28:46 -0400281 // could not be determined kUnknownType will be returned.
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400282 IdType getTypeOfValueInstruction(uint32_t value) const;
283
284 // Tracks the type-defining instruction. The result of the tracking can
285 // later be queried using getValueType.
286 // pInst is expected to be completely filled in by the time this instruction
287 // is called.
288 // Returns SPV_SUCCESS on success, or SPV_ERROR_INVALID_VALUE on error.
289 spv_result_t recordTypeDefinition(const spv_instruction_t* pInst);
290
291 // Tracks the relationship between the value and its type.
292 spv_result_t recordTypeIdForValue(uint32_t value, uint32_t type);
293
David Neto62741202015-10-13 15:51:12 -0400294 // Parses a numeric value of a given type from the given text. The number
295 // should take up the entire string, and should be within bounds for the
296 // target type. On success, returns SPV_SUCCESS and populates the object
David Neto51013d12015-10-14 11:31:51 -0400297 // referenced by value_pointer. On failure, returns the given error code,
298 // and emits a diagnostic if that error code is not SPV_FAILED_MATCH.
David Neto62741202015-10-13 15:51:12 -0400299 template <typename T>
David Neto51013d12015-10-14 11:31:51 -0400300 spv_result_t parseNumber(const char *text, spv_result_t error_code,
301 T *value_pointer,
David Neto62741202015-10-13 15:51:12 -0400302 const char *error_message_fragment) {
303 // C++11 doesn't define std::istringstream(int8_t&), so calling this method
304 // with a single-byte type leads to implementation-defined behaviour.
305 // Similarly for uint8_t.
306 static_assert(sizeof(T) > 1, "Don't use a single-byte type this parse method");
307
308 std::istringstream text_stream(text);
309 // Allow both decimal and hex input for integers.
310 // It also allows octal input, but we don't care about that case.
311 text_stream >> std::setbase(0);
312 text_stream >> *value_pointer;
313 bool ok = true;
314
315 // We should have read something.
316 ok = (text[0] != 0) && !text_stream.bad();
317 // It should have been all the text.
318 ok = ok && text_stream.eof();
319 // It should have been in range.
320 ok = ok && !text_stream.fail();
321 // Work around a bug in the GNU C++11 library. It will happily parse
322 // "-1" for uint16_t as 65535.
323 if (ok && !std::is_signed<T>::value && (text[0] == '-') &&
324 *value_pointer != 0) {
325 ok = false;
326 // Match expected error behaviour of std::istringstream::operator>>
327 // on failure to parse.
328 *value_pointer = 0;
329 }
330
331 if (ok) return SPV_SUCCESS;
David Neto51013d12015-10-14 11:31:51 -0400332 return diagnostic(error_code) << error_message_fragment << text;
David Neto62741202015-10-13 15:51:12 -0400333 }
334
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400335 private:
David Neto78e677b2015-10-05 13:28:46 -0400336 // Appends the given floating point literal to the given instruction.
337 // Returns SPV_SUCCESS if the value was correctly parsed. Otherwise
David Neto51013d12015-10-14 11:31:51 -0400338 // returns the given error code, and emits a diagnostic if that error
339 // code is not SPV_FAILED_MATCH.
340 // Only 32 and 64 bit floating point numbers are supported.
David Neto78e677b2015-10-05 13:28:46 -0400341 spv_result_t binaryEncodeFloatingPointLiteral(const char *numeric_literal,
David Neto51013d12015-10-14 11:31:51 -0400342 spv_result_t error_code,
David Neto78e677b2015-10-05 13:28:46 -0400343 const IdType& type,
344 spv_instruction_t *pInst);
345
346 // Appends the given integer literal to the given instruction.
347 // Returns SPV_SUCCESS if the value was correctly parsed. Otherwise
David Neto51013d12015-10-14 11:31:51 -0400348 // returns the given error code, and emits a diagnostic if that error
349 // code is not SPV_FAILED_MATCH.
350 // Integers up to 64 bits are supported.
David Neto78e677b2015-10-05 13:28:46 -0400351 spv_result_t binaryEncodeIntegerLiteral(const char *numeric_literal,
David Neto51013d12015-10-14 11:31:51 -0400352 spv_result_t error_code,
David Neto78e677b2015-10-05 13:28:46 -0400353 const IdType &type,
354 spv_instruction_t *pInst);
355
356 // Returns SPV_SUCCESS if the given value fits within the target scalar
357 // integral type. The target type may have an unusual bit width.
358 // If the value was originally specified as a hexadecimal number, then
359 // the overflow bits should be zero. If it was hex and the target type is
360 // signed, then return the sign-extended value through the
361 // updated_value_for_hex pointer argument.
David Neto51013d12015-10-14 11:31:51 -0400362 // On failure, return the given error code and emit a diagnostic if that error
363 // code is not SPV_FAILED_MATCH.
David Neto78e677b2015-10-05 13:28:46 -0400364 template <typename T>
David Neto51013d12015-10-14 11:31:51 -0400365 spv_result_t checkRangeAndIfHexThenSignExtend(T value, spv_result_t error_code,
David Neto78e677b2015-10-05 13:28:46 -0400366 const IdType &type, bool is_hex,
367 T *updated_value_for_hex);
368
369 // Writes the given 64-bit literal value into the instruction.
370 // return SPV_SUCCESS if the value could be written in the instruction.
371 spv_result_t binaryEncodeU64(const uint64_t value, spv_instruction_t *pInst);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400372 // Maps ID names to their corresponding numerical ids.
373 using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
374 // Maps type-defining IDs to their IdType.
375 using spv_id_to_type_map = std::unordered_map<uint32_t, IdType>;
376 // Maps Ids to the id of their type.
377 using spv_id_to_type_id = std::unordered_map<uint32_t, uint32_t>;
378
379 spv_named_id_table named_ids_;
380 spv_id_to_type_map types_;
381 spv_id_to_type_id value_types_;
382 spv_position_t current_position_;
383 spv_diagnostic *pDiagnostic_;
384 spv_text text_;
385 uint32_t bound_;
386};
387}
388#endif // _LIBSPIRV_UTIL_TEXT_HANDLER_H_
389