blob: 7149e5f5b4e081b315c4e2f529bde2f60ebe59a2 [file] [log] [blame]
Dejan Mircevskib6fe02f2016-01-07 13:44:22 -05001// Copyright (c) 2015-2016 The Khronos Group Inc.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -04002//
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
David Neto9f79d782015-10-27 16:27:05 -040027#ifndef LIBSPIRV_TEXT_HANDLER_H_
28#define LIBSPIRV_TEXT_HANDLER_H_
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040029
David Neto62741202015-10-13 15:51:12 -040030#include <iomanip>
David Neto62741202015-10-13 15:51:12 -040031#include <sstream>
32#include <type_traits>
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040033#include <unordered_map>
34
35#include "diagnostic.h"
David Netob5dc8fc2015-10-06 16:22:00 -040036#include "instruction.h"
Lei Zhang923f6c12015-11-11 12:45:23 -050037#include "libspirv/libspirv.h"
David Neto78e677b2015-10-05 13:28:46 -040038#include "text.h"
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040039
40namespace libspirv {
41// Structures
42
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040043// This is a lattice for tracking types.
44enum class IdTypeClass {
Lei Zhang1a0334e2015-11-02 09:41:20 -050045 kBottom = 0, // We have no information yet.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040046 kScalarIntegerType,
47 kScalarFloatType,
48 kOtherType
49};
50
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040051// Contains ID type information that needs to be tracked across all Ids.
52// Bitwidth is only valid when type_class is kScalarIntegerType or
53// kScalarFloatType.
54struct IdType {
55 uint32_t bitwidth; // Safe to assume that we will not have > 2^32 bits.
Lei Zhang1a0334e2015-11-02 09:41:20 -050056 bool isSigned; // This is only significant if type_class is integral.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040057 IdTypeClass type_class;
58};
59
Andrew Woloszyn157e41b2015-10-16 15:11:00 -040060// Default equality operator for IdType. Tests if all members are the same.
Lei Zhang1a0334e2015-11-02 09:41:20 -050061inline bool operator==(const IdType& first, const IdType& second) {
Andrew Woloszyn157e41b2015-10-16 15:11:00 -040062 return (first.bitwidth == second.bitwidth) &&
63 (first.isSigned == second.isSigned) &&
64 (first.type_class == second.type_class);
65}
66
67// Tests whether any member of the IdTypes do not match.
Lei Zhang1a0334e2015-11-02 09:41:20 -050068inline bool operator!=(const IdType& first, const IdType& second) {
Andrew Woloszyn157e41b2015-10-16 15:11:00 -040069 return !(first == second);
70}
71
David Neto78e677b2015-10-05 13:28:46 -040072// A value representing an unknown type.
73extern const IdType kUnknownType;
74
Andrew Woloszyn537e7762015-09-29 11:28:34 -040075// Returns true if the type is a scalar integer type.
76inline bool isScalarIntegral(const IdType& type) {
77 return type.type_class == IdTypeClass::kScalarIntegerType;
78}
79
80// Returns true if the type is a scalar floating point type.
81inline bool isScalarFloating(const IdType& type) {
82 return type.type_class == IdTypeClass::kScalarFloatType;
83}
84
David Neto78e677b2015-10-05 13:28:46 -040085// Returns the number of bits in the type.
86// This is only valid for bottom, scalar integer, and scalar floating
87// classes. For bottom, assume 32 bits.
88inline int assumedBitWidth(const IdType& type) {
Lei Zhang1a0334e2015-11-02 09:41:20 -050089 switch (type.type_class) {
David Neto78e677b2015-10-05 13:28:46 -040090 case IdTypeClass::kBottom:
91 return 32;
92 case IdTypeClass::kScalarIntegerType:
93 case IdTypeClass::kScalarFloatType:
94 return type.bitwidth;
95 default:
96 break;
97 }
98 // We don't care about this case.
99 return 0;
100}
101
David Neto9e545d72015-11-06 18:08:49 -0500102// A templated class with a static member function Clamp, where Clamp
103// sets a referenced value of type T to 0 if T is an unsigned
104// integer type, and returns true if it modified the referenced
105// value.
106template <typename T, typename = void>
107class ClampToZeroIfUnsignedType {
108 public:
109 // The default specialization does not clamp the value.
110 static bool Clamp(T*) { return false; }
111};
112
113// The specialization of ClampToZeroIfUnsignedType for unsigned integer
114// types.
115template <typename T>
116class ClampToZeroIfUnsignedType<
117 T, typename std::enable_if<std::is_unsigned<T>::value>::type> {
118 public:
119 static bool Clamp(T* value_pointer) {
120 if (*value_pointer) {
121 *value_pointer = 0;
122 return true;
123 }
124 return false;
125 }
126};
127
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400128// Encapsulates the data used during the assembly of a SPIR-V module.
129class AssemblyContext {
130 public:
David Netoae7d7072016-01-06 14:43:55 -0500131 AssemblyContext(spv_text text, spv_diagnostic* diagnostic_arg)
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400132 : current_position_({}),
David Netoae7d7072016-01-06 14:43:55 -0500133 pDiagnostic_(diagnostic_arg),
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400134 text_(text),
135 bound_(1) {}
136
137 // Assigns a new integer value to the given text ID, or returns the previously
138 // assigned integer value if the ID has been seen before.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500139 uint32_t spvNamedIdAssignOrGet(const char* textValue);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400140
141 // Returns the largest largest numeric ID that has been assigned.
142 uint32_t getBound() const;
143
144 // Advances position to point to the next word in the input stream.
145 // Returns SPV_SUCCESS on success.
146 spv_result_t advance();
147
148 // Sets word to the next word in the input text. Fills endPosition with
149 // the next location past the end of the word.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500150 spv_result_t getWord(std::string& word, spv_position endPosition);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400151
152 // Returns the next word in the input stream. It is invalid to call this
153 // method if position has been set to a location in the stream that does not
Lei Zhangacf72872015-11-13 11:00:10 -0500154 // exist. If there are no subsequent words, the empty string will be returned.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400155 std::string getWord() const;
156
157 // Returns true if the next word in the input is the start of a new Opcode.
158 bool startsWithOp();
159
160 // Returns true if the next word in the input is the start of a new
161 // instruction.
162 bool isStartOfNewInst();
163
164 // Returns a diagnostic object initialized with current position in the input
David Netoac508b02015-10-09 15:48:09 -0400165 // stream, and for the given error code. Any data written to this object will
166 // show up in pDiagnsotic on destruction.
167 DiagnosticStream diagnostic(spv_result_t error) {
David Netobae88512015-10-28 13:16:56 -0400168 return DiagnosticStream(current_position_, pDiagnostic_, error);
David Netoac508b02015-10-09 15:48:09 -0400169 }
170
171 // Returns a diagnostic object with the default assembly error code.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400172 DiagnosticStream diagnostic() {
David Netoac508b02015-10-09 15:48:09 -0400173 // The default failure for assembly is invalid text.
174 return diagnostic(SPV_ERROR_INVALID_TEXT);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400175 }
176
Lei Zhangacf72872015-11-13 11:00:10 -0500177 // Returns then next character in the input stream.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400178 char peek() const;
179
180 // Returns true if there is more text in the input stream.
181 bool hasText() const;
182
183 // Seeks the input stream forward by 'size' characters.
184 void seekForward(uint32_t size);
185
186 // Sets the current position in the input stream to the given position.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500187 void setPosition(const spv_position_t& newPosition) {
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400188 current_position_ = newPosition;
189 }
190
191 // Returns the current position in the input stream.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500192 const spv_position_t& position() const { return current_position_; }
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400193
194 // Appends the given 32-bit value to the given instruction.
David Neto78e677b2015-10-05 13:28:46 -0400195 // Returns SPV_SUCCESS if the value could be correctly inserted in the
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400196 // instruction.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500197 spv_result_t binaryEncodeU32(const uint32_t value, spv_instruction_t* pInst);
David Neto78e677b2015-10-05 13:28:46 -0400198
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400199 // Appends the given string to the given instruction.
David Neto78e677b2015-10-05 13:28:46 -0400200 // Returns SPV_SUCCESS if the value could be correctly inserted in the
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400201 // instruction.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500202 spv_result_t binaryEncodeString(const char* value, spv_instruction_t* pInst);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400203
David Neto78e677b2015-10-05 13:28:46 -0400204 // Appends the given numeric literal to the given instruction.
205 // Validates and respects the bitwidth supplied in the IdType argument.
206 // If the type is of class kBottom the value will be encoded as a
207 // 32-bit integer.
208 // Returns SPV_SUCCESS if the value could be correctly added to the
David Neto51013d12015-10-14 11:31:51 -0400209 // instruction. Returns the given error code on failure, and emits
Lei Zhangacf72872015-11-13 11:00:10 -0500210 // a diagnostic if that error code is not SPV_FAILED_MATCH.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500211 spv_result_t binaryEncodeNumericLiteral(const char* numeric_literal,
David Neto51013d12015-10-14 11:31:51 -0400212 spv_result_t error_code,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500213 const IdType& type,
214 spv_instruction_t* pInst);
David Neto78e677b2015-10-05 13:28:46 -0400215
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400216 // Returns the IdType associated with this type-generating value.
217 // If the type has not been previously recorded with recordTypeDefinition,
David Neto78e677b2015-10-05 13:28:46 -0400218 // kUnknownType will be returned.
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400219 IdType getTypeOfTypeGeneratingValue(uint32_t value) const;
220
221 // Returns the IdType that represents the return value of this Value
222 // generating instruction.
223 // If the value has not been recorded with recordTypeIdForValue, or the type
David Neto78e677b2015-10-05 13:28:46 -0400224 // could not be determined kUnknownType will be returned.
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400225 IdType getTypeOfValueInstruction(uint32_t value) const;
226
227 // Tracks the type-defining instruction. The result of the tracking can
228 // later be queried using getValueType.
229 // pInst is expected to be completely filled in by the time this instruction
230 // is called.
231 // Returns SPV_SUCCESS on success, or SPV_ERROR_INVALID_VALUE on error.
232 spv_result_t recordTypeDefinition(const spv_instruction_t* pInst);
233
234 // Tracks the relationship between the value and its type.
235 spv_result_t recordTypeIdForValue(uint32_t value, uint32_t type);
236
David Neto2ae4a682015-11-09 18:55:42 -0500237 // Records the given Id as being the import of the given extended instruction
238 // type.
239 spv_result_t recordIdAsExtInstImport(uint32_t id, spv_ext_inst_type_t type);
240
241 // Returns the extended instruction type corresponding to the import with
242 // the given Id, if it exists. Returns SPV_EXT_INST_TYPE_NONE if the
243 // id is not the id for an extended instruction type.
244 spv_ext_inst_type_t getExtInstTypeForId(uint32_t id) const;
245
David Neto62741202015-10-13 15:51:12 -0400246 // Parses a numeric value of a given type from the given text. The number
247 // should take up the entire string, and should be within bounds for the
248 // target type. On success, returns SPV_SUCCESS and populates the object
David Neto51013d12015-10-14 11:31:51 -0400249 // referenced by value_pointer. On failure, returns the given error code,
250 // and emits a diagnostic if that error code is not SPV_FAILED_MATCH.
David Neto62741202015-10-13 15:51:12 -0400251 template <typename T>
Lei Zhang1a0334e2015-11-02 09:41:20 -0500252 spv_result_t parseNumber(const char* text, spv_result_t error_code,
253 T* value_pointer,
254 const char* error_message_fragment) {
David Neto62741202015-10-13 15:51:12 -0400255 // C++11 doesn't define std::istringstream(int8_t&), so calling this method
256 // with a single-byte type leads to implementation-defined behaviour.
257 // Similarly for uint8_t.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500258 static_assert(sizeof(T) > 1,
259 "Don't use a single-byte type this parse method");
David Neto62741202015-10-13 15:51:12 -0400260
261 std::istringstream text_stream(text);
262 // Allow both decimal and hex input for integers.
263 // It also allows octal input, but we don't care about that case.
264 text_stream >> std::setbase(0);
265 text_stream >> *value_pointer;
266 bool ok = true;
267
268 // We should have read something.
269 ok = (text[0] != 0) && !text_stream.bad();
270 // It should have been all the text.
271 ok = ok && text_stream.eof();
272 // It should have been in range.
273 ok = ok && !text_stream.fail();
David Neto9e545d72015-11-06 18:08:49 -0500274
David Neto62741202015-10-13 15:51:12 -0400275 // Work around a bug in the GNU C++11 library. It will happily parse
276 // "-1" for uint16_t as 65535.
David Neto9e545d72015-11-06 18:08:49 -0500277 if (ok && text[0] == '-')
278 ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer);
David Neto62741202015-10-13 15:51:12 -0400279
280 if (ok) return SPV_SUCCESS;
David Neto51013d12015-10-14 11:31:51 -0400281 return diagnostic(error_code) << error_message_fragment << text;
David Neto62741202015-10-13 15:51:12 -0400282 }
283
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400284 private:
David Neto78e677b2015-10-05 13:28:46 -0400285 // Appends the given floating point literal to the given instruction.
286 // Returns SPV_SUCCESS if the value was correctly parsed. Otherwise
David Neto51013d12015-10-14 11:31:51 -0400287 // returns the given error code, and emits a diagnostic if that error
288 // code is not SPV_FAILED_MATCH.
289 // Only 32 and 64 bit floating point numbers are supported.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500290 spv_result_t binaryEncodeFloatingPointLiteral(const char* numeric_literal,
David Neto51013d12015-10-14 11:31:51 -0400291 spv_result_t error_code,
David Neto78e677b2015-10-05 13:28:46 -0400292 const IdType& type,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500293 spv_instruction_t* pInst);
David Neto78e677b2015-10-05 13:28:46 -0400294
295 // Appends the given integer literal to the given instruction.
296 // Returns SPV_SUCCESS if the value was correctly parsed. Otherwise
David Neto51013d12015-10-14 11:31:51 -0400297 // returns the given error code, and emits a diagnostic if that error
298 // code is not SPV_FAILED_MATCH.
299 // Integers up to 64 bits are supported.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500300 spv_result_t binaryEncodeIntegerLiteral(const char* numeric_literal,
David Neto51013d12015-10-14 11:31:51 -0400301 spv_result_t error_code,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500302 const IdType& type,
303 spv_instruction_t* pInst);
David Neto78e677b2015-10-05 13:28:46 -0400304
305 // Returns SPV_SUCCESS if the given value fits within the target scalar
306 // integral type. The target type may have an unusual bit width.
307 // If the value was originally specified as a hexadecimal number, then
308 // the overflow bits should be zero. If it was hex and the target type is
309 // signed, then return the sign-extended value through the
310 // updated_value_for_hex pointer argument.
David Neto51013d12015-10-14 11:31:51 -0400311 // On failure, return the given error code and emit a diagnostic if that error
312 // code is not SPV_FAILED_MATCH.
David Neto78e677b2015-10-05 13:28:46 -0400313 template <typename T>
Lei Zhang1a0334e2015-11-02 09:41:20 -0500314 spv_result_t checkRangeAndIfHexThenSignExtend(T value,
315 spv_result_t error_code,
316 const IdType& type, bool is_hex,
317 T* updated_value_for_hex);
David Neto78e677b2015-10-05 13:28:46 -0400318
319 // Writes the given 64-bit literal value into the instruction.
320 // return SPV_SUCCESS if the value could be written in the instruction.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500321 spv_result_t binaryEncodeU64(const uint64_t value, spv_instruction_t* pInst);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400322 // Maps ID names to their corresponding numerical ids.
323 using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
324 // Maps type-defining IDs to their IdType.
325 using spv_id_to_type_map = std::unordered_map<uint32_t, IdType>;
326 // Maps Ids to the id of their type.
327 using spv_id_to_type_id = std::unordered_map<uint32_t, uint32_t>;
328
329 spv_named_id_table named_ids_;
330 spv_id_to_type_map types_;
331 spv_id_to_type_id value_types_;
David Neto2ae4a682015-11-09 18:55:42 -0500332 // Maps an extended instruction import Id to the extended instruction type.
333 std::unordered_map<uint32_t, spv_ext_inst_type_t> import_id_to_ext_inst_type_;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400334 spv_position_t current_position_;
Lei Zhang1a0334e2015-11-02 09:41:20 -0500335 spv_diagnostic* pDiagnostic_;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400336 spv_text text_;
337 uint32_t bound_;
338};
339}
David Neto9f79d782015-10-27 16:27:05 -0400340#endif // _LIBSPIRV_TEXT_HANDLER_H_