blob: 521d514449c8f806a0fc0bd212ffcc4d0da1fc91 [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//
David Neto9fc86582016-09-01 15:33:59 -04003// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
Andrew Woloszyn71fc0552015-09-24 10:26:51 -04006//
David Neto9fc86582016-09-01 15:33:59 -04007// http://www.apache.org/licenses/LICENSE-2.0
Andrew Woloszyn71fc0552015-09-24 10:26:51 -04008//
David Neto9fc86582016-09-01 15:33:59 -04009// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040014
David Neto9f79d782015-10-27 16:27:05 -040015#ifndef LIBSPIRV_TEXT_HANDLER_H_
16#define LIBSPIRV_TEXT_HANDLER_H_
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040017
David Neto62741202015-10-13 15:51:12 -040018#include <iomanip>
David Neto62741202015-10-13 15:51:12 -040019#include <sstream>
20#include <type_traits>
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040021#include <unordered_map>
22
23#include "diagnostic.h"
David Netob5dc8fc2015-10-06 16:22:00 -040024#include "instruction.h"
David Neto5a703352016-02-17 14:44:00 -050025#include "spirv-tools/libspirv.h"
David Neto78e677b2015-10-05 13:28:46 -040026#include "text.h"
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040027
28namespace libspirv {
29// Structures
30
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040031// This is a lattice for tracking types.
32enum class IdTypeClass {
Lei Zhang1a0334e2015-11-02 09:41:20 -050033 kBottom = 0, // We have no information yet.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040034 kScalarIntegerType,
35 kScalarFloatType,
36 kOtherType
37};
38
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040039// Contains ID type information that needs to be tracked across all Ids.
40// Bitwidth is only valid when type_class is kScalarIntegerType or
41// kScalarFloatType.
42struct IdType {
43 uint32_t bitwidth; // Safe to assume that we will not have > 2^32 bits.
Lei Zhang1a0334e2015-11-02 09:41:20 -050044 bool isSigned; // This is only significant if type_class is integral.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -040045 IdTypeClass type_class;
46};
47
Andrew Woloszyn157e41b2015-10-16 15:11:00 -040048// Default equality operator for IdType. Tests if all members are the same.
Lei Zhang1a0334e2015-11-02 09:41:20 -050049inline bool operator==(const IdType& first, const IdType& second) {
Andrew Woloszyn157e41b2015-10-16 15:11:00 -040050 return (first.bitwidth == second.bitwidth) &&
51 (first.isSigned == second.isSigned) &&
52 (first.type_class == second.type_class);
53}
54
55// Tests whether any member of the IdTypes do not match.
Lei Zhang1a0334e2015-11-02 09:41:20 -050056inline bool operator!=(const IdType& first, const IdType& second) {
Andrew Woloszyn157e41b2015-10-16 15:11:00 -040057 return !(first == second);
58}
59
David Neto78e677b2015-10-05 13:28:46 -040060// A value representing an unknown type.
61extern const IdType kUnknownType;
62
Andrew Woloszyn537e7762015-09-29 11:28:34 -040063// Returns true if the type is a scalar integer type.
64inline bool isScalarIntegral(const IdType& type) {
65 return type.type_class == IdTypeClass::kScalarIntegerType;
66}
67
68// Returns true if the type is a scalar floating point type.
69inline bool isScalarFloating(const IdType& type) {
70 return type.type_class == IdTypeClass::kScalarFloatType;
71}
72
David Neto78e677b2015-10-05 13:28:46 -040073// Returns the number of bits in the type.
74// This is only valid for bottom, scalar integer, and scalar floating
75// classes. For bottom, assume 32 bits.
76inline int assumedBitWidth(const IdType& type) {
Lei Zhang1a0334e2015-11-02 09:41:20 -050077 switch (type.type_class) {
David Neto78e677b2015-10-05 13:28:46 -040078 case IdTypeClass::kBottom:
79 return 32;
80 case IdTypeClass::kScalarIntegerType:
81 case IdTypeClass::kScalarFloatType:
82 return type.bitwidth;
83 default:
84 break;
85 }
86 // We don't care about this case.
87 return 0;
88}
89
David Neto9e545d72015-11-06 18:08:49 -050090// A templated class with a static member function Clamp, where Clamp
91// sets a referenced value of type T to 0 if T is an unsigned
92// integer type, and returns true if it modified the referenced
93// value.
94template <typename T, typename = void>
95class ClampToZeroIfUnsignedType {
96 public:
97 // The default specialization does not clamp the value.
98 static bool Clamp(T*) { return false; }
99};
100
101// The specialization of ClampToZeroIfUnsignedType for unsigned integer
102// types.
103template <typename T>
104class ClampToZeroIfUnsignedType<
105 T, typename std::enable_if<std::is_unsigned<T>::value>::type> {
106 public:
107 static bool Clamp(T* value_pointer) {
108 if (*value_pointer) {
109 *value_pointer = 0;
110 return true;
111 }
112 return false;
113 }
114};
115
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400116// Encapsulates the data used during the assembly of a SPIR-V module.
117class AssemblyContext {
118 public:
David Netoae7d7072016-01-06 14:43:55 -0500119 AssemblyContext(spv_text text, spv_diagnostic* diagnostic_arg)
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400120 : current_position_({}),
David Netoae7d7072016-01-06 14:43:55 -0500121 pDiagnostic_(diagnostic_arg),
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400122 text_(text),
123 bound_(1) {}
124
125 // Assigns a new integer value to the given text ID, or returns the previously
126 // assigned integer value if the ID has been seen before.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500127 uint32_t spvNamedIdAssignOrGet(const char* textValue);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400128
129 // Returns the largest largest numeric ID that has been assigned.
130 uint32_t getBound() const;
131
132 // Advances position to point to the next word in the input stream.
133 // Returns SPV_SUCCESS on success.
134 spv_result_t advance();
135
Lei Zhang6032b982016-03-15 16:52:40 -0400136 // Sets word to the next word in the input text. Fills next_position with
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400137 // the next location past the end of the word.
Lei Zhang6032b982016-03-15 16:52:40 -0400138 spv_result_t getWord(std::string* word, spv_position next_position);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400139
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400140 // Returns true if the next word in the input is the start of a new Opcode.
141 bool startsWithOp();
142
143 // Returns true if the next word in the input is the start of a new
144 // instruction.
145 bool isStartOfNewInst();
146
147 // Returns a diagnostic object initialized with current position in the input
David Netoac508b02015-10-09 15:48:09 -0400148 // stream, and for the given error code. Any data written to this object will
149 // show up in pDiagnsotic on destruction.
150 DiagnosticStream diagnostic(spv_result_t error) {
David Netobae88512015-10-28 13:16:56 -0400151 return DiagnosticStream(current_position_, pDiagnostic_, error);
David Netoac508b02015-10-09 15:48:09 -0400152 }
153
154 // Returns a diagnostic object with the default assembly error code.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400155 DiagnosticStream diagnostic() {
David Netoac508b02015-10-09 15:48:09 -0400156 // The default failure for assembly is invalid text.
157 return diagnostic(SPV_ERROR_INVALID_TEXT);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400158 }
159
Lei Zhangacf72872015-11-13 11:00:10 -0500160 // Returns then next character in the input stream.
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400161 char peek() const;
162
163 // Returns true if there is more text in the input stream.
164 bool hasText() const;
165
166 // Seeks the input stream forward by 'size' characters.
167 void seekForward(uint32_t size);
168
169 // Sets the current position in the input stream to the given position.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500170 void setPosition(const spv_position_t& newPosition) {
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400171 current_position_ = newPosition;
172 }
173
174 // Returns the current position in the input stream.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500175 const spv_position_t& position() const { return current_position_; }
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400176
177 // Appends the given 32-bit value to the given instruction.
David Neto78e677b2015-10-05 13:28:46 -0400178 // Returns SPV_SUCCESS if the value could be correctly inserted in the
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400179 // instruction.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500180 spv_result_t binaryEncodeU32(const uint32_t value, spv_instruction_t* pInst);
David Neto78e677b2015-10-05 13:28:46 -0400181
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400182 // Appends the given string to the given instruction.
David Neto78e677b2015-10-05 13:28:46 -0400183 // Returns SPV_SUCCESS if the value could be correctly inserted in the
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400184 // instruction.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500185 spv_result_t binaryEncodeString(const char* value, spv_instruction_t* pInst);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400186
David Neto78e677b2015-10-05 13:28:46 -0400187 // Appends the given numeric literal to the given instruction.
188 // Validates and respects the bitwidth supplied in the IdType argument.
189 // If the type is of class kBottom the value will be encoded as a
190 // 32-bit integer.
191 // Returns SPV_SUCCESS if the value could be correctly added to the
David Neto51013d12015-10-14 11:31:51 -0400192 // instruction. Returns the given error code on failure, and emits
Lei Zhangacf72872015-11-13 11:00:10 -0500193 // a diagnostic if that error code is not SPV_FAILED_MATCH.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500194 spv_result_t binaryEncodeNumericLiteral(const char* numeric_literal,
David Neto51013d12015-10-14 11:31:51 -0400195 spv_result_t error_code,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500196 const IdType& type,
197 spv_instruction_t* pInst);
David Neto78e677b2015-10-05 13:28:46 -0400198
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400199 // Returns the IdType associated with this type-generating value.
200 // If the type has not been previously recorded with recordTypeDefinition,
David Neto78e677b2015-10-05 13:28:46 -0400201 // kUnknownType will be returned.
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400202 IdType getTypeOfTypeGeneratingValue(uint32_t value) const;
203
204 // Returns the IdType that represents the return value of this Value
205 // generating instruction.
206 // If the value has not been recorded with recordTypeIdForValue, or the type
David Neto78e677b2015-10-05 13:28:46 -0400207 // could not be determined kUnknownType will be returned.
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400208 IdType getTypeOfValueInstruction(uint32_t value) const;
209
210 // Tracks the type-defining instruction. The result of the tracking can
211 // later be queried using getValueType.
212 // pInst is expected to be completely filled in by the time this instruction
213 // is called.
214 // Returns SPV_SUCCESS on success, or SPV_ERROR_INVALID_VALUE on error.
215 spv_result_t recordTypeDefinition(const spv_instruction_t* pInst);
216
217 // Tracks the relationship between the value and its type.
218 spv_result_t recordTypeIdForValue(uint32_t value, uint32_t type);
219
David Neto2ae4a682015-11-09 18:55:42 -0500220 // Records the given Id as being the import of the given extended instruction
221 // type.
222 spv_result_t recordIdAsExtInstImport(uint32_t id, spv_ext_inst_type_t type);
223
224 // Returns the extended instruction type corresponding to the import with
225 // the given Id, if it exists. Returns SPV_EXT_INST_TYPE_NONE if the
226 // id is not the id for an extended instruction type.
227 spv_ext_inst_type_t getExtInstTypeForId(uint32_t id) const;
228
David Neto62741202015-10-13 15:51:12 -0400229 // Parses a numeric value of a given type from the given text. The number
230 // should take up the entire string, and should be within bounds for the
231 // target type. On success, returns SPV_SUCCESS and populates the object
David Neto51013d12015-10-14 11:31:51 -0400232 // referenced by value_pointer. On failure, returns the given error code,
233 // and emits a diagnostic if that error code is not SPV_FAILED_MATCH.
David Neto62741202015-10-13 15:51:12 -0400234 template <typename T>
Lei Zhang1a0334e2015-11-02 09:41:20 -0500235 spv_result_t parseNumber(const char* text, spv_result_t error_code,
236 T* value_pointer,
237 const char* error_message_fragment) {
David Neto62741202015-10-13 15:51:12 -0400238 // C++11 doesn't define std::istringstream(int8_t&), so calling this method
239 // with a single-byte type leads to implementation-defined behaviour.
240 // Similarly for uint8_t.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500241 static_assert(sizeof(T) > 1,
242 "Don't use a single-byte type this parse method");
David Neto62741202015-10-13 15:51:12 -0400243
244 std::istringstream text_stream(text);
245 // Allow both decimal and hex input for integers.
246 // It also allows octal input, but we don't care about that case.
247 text_stream >> std::setbase(0);
248 text_stream >> *value_pointer;
David Neto62741202015-10-13 15:51:12 -0400249
250 // We should have read something.
Lei Zhang712bed02016-02-25 16:11:16 -0500251 bool ok = (text[0] != 0) && !text_stream.bad();
David Neto62741202015-10-13 15:51:12 -0400252 // It should have been all the text.
253 ok = ok && text_stream.eof();
254 // It should have been in range.
255 ok = ok && !text_stream.fail();
David Neto9e545d72015-11-06 18:08:49 -0500256
David Neto62741202015-10-13 15:51:12 -0400257 // Work around a bug in the GNU C++11 library. It will happily parse
258 // "-1" for uint16_t as 65535.
David Neto9e545d72015-11-06 18:08:49 -0500259 if (ok && text[0] == '-')
260 ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer);
David Neto62741202015-10-13 15:51:12 -0400261
262 if (ok) return SPV_SUCCESS;
David Neto51013d12015-10-14 11:31:51 -0400263 return diagnostic(error_code) << error_message_fragment << text;
David Neto62741202015-10-13 15:51:12 -0400264 }
265
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400266 private:
Andrew Woloszyn4ddb4312016-02-17 13:00:23 -0500267
David Neto78e677b2015-10-05 13:28:46 -0400268 // Appends the given floating point literal to the given instruction.
269 // Returns SPV_SUCCESS if the value was correctly parsed. Otherwise
David Neto51013d12015-10-14 11:31:51 -0400270 // returns the given error code, and emits a diagnostic if that error
271 // code is not SPV_FAILED_MATCH.
272 // Only 32 and 64 bit floating point numbers are supported.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500273 spv_result_t binaryEncodeFloatingPointLiteral(const char* numeric_literal,
David Neto51013d12015-10-14 11:31:51 -0400274 spv_result_t error_code,
David Neto78e677b2015-10-05 13:28:46 -0400275 const IdType& type,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500276 spv_instruction_t* pInst);
David Neto78e677b2015-10-05 13:28:46 -0400277
278 // Appends the given integer literal to the given instruction.
279 // Returns SPV_SUCCESS if the value was correctly parsed. Otherwise
David Neto51013d12015-10-14 11:31:51 -0400280 // returns the given error code, and emits a diagnostic if that error
281 // code is not SPV_FAILED_MATCH.
282 // Integers up to 64 bits are supported.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500283 spv_result_t binaryEncodeIntegerLiteral(const char* numeric_literal,
David Neto51013d12015-10-14 11:31:51 -0400284 spv_result_t error_code,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500285 const IdType& type,
286 spv_instruction_t* pInst);
David Neto78e677b2015-10-05 13:28:46 -0400287
David Neto78e677b2015-10-05 13:28:46 -0400288 // Writes the given 64-bit literal value into the instruction.
289 // return SPV_SUCCESS if the value could be written in the instruction.
Lei Zhang1a0334e2015-11-02 09:41:20 -0500290 spv_result_t binaryEncodeU64(const uint64_t value, spv_instruction_t* pInst);
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400291 // Maps ID names to their corresponding numerical ids.
292 using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
293 // Maps type-defining IDs to their IdType.
294 using spv_id_to_type_map = std::unordered_map<uint32_t, IdType>;
295 // Maps Ids to the id of their type.
296 using spv_id_to_type_id = std::unordered_map<uint32_t, uint32_t>;
297
298 spv_named_id_table named_ids_;
299 spv_id_to_type_map types_;
300 spv_id_to_type_id value_types_;
David Neto2ae4a682015-11-09 18:55:42 -0500301 // Maps an extended instruction import Id to the extended instruction type.
302 std::unordered_map<uint32_t, spv_ext_inst_type_t> import_id_to_ext_inst_type_;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400303 spv_position_t current_position_;
Lei Zhang1a0334e2015-11-02 09:41:20 -0500304 spv_diagnostic* pDiagnostic_;
Andrew Woloszyn71fc0552015-09-24 10:26:51 -0400305 spv_text text_;
306 uint32_t bound_;
307};
308}
David Neto9f79d782015-10-27 16:27:05 -0400309#endif // _LIBSPIRV_TEXT_HANDLER_H_