1 // Copyright (c) 2015-2016 The Khronos Group Inc.
2 //
3 // 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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // 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.
14
15 #ifndef LIBSPIRV_TEXT_HANDLER_H_
16 #define LIBSPIRV_TEXT_HANDLER_H_
17
18 #include <iomanip>
19 #include <sstream>
20 #include <type_traits>
21 #include <unordered_map>
22
23 #include "diagnostic.h"
24 #include "instruction.h"
25 #include "message.h"
26 #include "spirv-tools/libspirv.h"
27 #include "text.h"
28
29 namespace libspirv {
30 // Structures
31
32 // This is a lattice for tracking types.
33 enum class IdTypeClass {
34 kBottom = 0, // We have no information yet.
35 kScalarIntegerType,
36 kScalarFloatType,
37 kOtherType
38 };
39
40 // Contains ID type information that needs to be tracked across all Ids.
41 // Bitwidth is only valid when type_class is kScalarIntegerType or
42 // kScalarFloatType.
43 struct IdType {
44 uint32_t bitwidth; // Safe to assume that we will not have > 2^32 bits.
45 bool isSigned; // This is only significant if type_class is integral.
46 IdTypeClass type_class;
47 };
48
49 // Default equality operator for IdType. Tests if all members are the same.
50 inline bool operator==(const IdType& first, const IdType& second) {
51 return (first.bitwidth == second.bitwidth) &&
52 (first.isSigned == second.isSigned) &&
53 (first.type_class == second.type_class);
54 }
55
56 // Tests whether any member of the IdTypes do not match.
57 inline bool operator!=(const IdType& first, const IdType& second) {
58 return !(first == second);
59 }
60
61 // A value representing an unknown type.
62 extern const IdType kUnknownType;
63
64 // Returns true if the type is a scalar integer type.
isScalarIntegral(const IdType & type)65 inline bool isScalarIntegral(const IdType& type) {
66 return type.type_class == IdTypeClass::kScalarIntegerType;
67 }
68
69 // Returns true if the type is a scalar floating point type.
isScalarFloating(const IdType & type)70 inline bool isScalarFloating(const IdType& type) {
71 return type.type_class == IdTypeClass::kScalarFloatType;
72 }
73
74 // Returns the number of bits in the type.
75 // This is only valid for bottom, scalar integer, and scalar floating
76 // classes. For bottom, assume 32 bits.
assumedBitWidth(const IdType & type)77 inline int assumedBitWidth(const IdType& type) {
78 switch (type.type_class) {
79 case IdTypeClass::kBottom:
80 return 32;
81 case IdTypeClass::kScalarIntegerType:
82 case IdTypeClass::kScalarFloatType:
83 return type.bitwidth;
84 default:
85 break;
86 }
87 // We don't care about this case.
88 return 0;
89 }
90
91 // A templated class with a static member function Clamp, where Clamp
92 // sets a referenced value of type T to 0 if T is an unsigned
93 // integer type, and returns true if it modified the referenced
94 // value.
95 template <typename T, typename = void>
96 class ClampToZeroIfUnsignedType {
97 public:
98 // The default specialization does not clamp the value.
Clamp(T *)99 static bool Clamp(T*) { return false; }
100 };
101
102 // The specialization of ClampToZeroIfUnsignedType for unsigned integer
103 // types.
104 template <typename T>
105 class ClampToZeroIfUnsignedType<
106 T, typename std::enable_if<std::is_unsigned<T>::value>::type> {
107 public:
Clamp(T * value_pointer)108 static bool Clamp(T* value_pointer) {
109 if (*value_pointer) {
110 *value_pointer = 0;
111 return true;
112 }
113 return false;
114 }
115 };
116
117 // Encapsulates the data used during the assembly of a SPIR-V module.
118 class AssemblyContext {
119 public:
120 AssemblyContext(spv_text text, const spvtools::MessageConsumer& consumer,
121 std::set<uint32_t>&& ids_to_preserve = std::set<uint32_t>())
122 : current_position_({}), consumer_(consumer), text_(text), bound_(1),
123 next_id_(1), ids_to_preserve_(std::move(ids_to_preserve)) {}
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.
127 uint32_t spvNamedIdAssignOrGet(const char* textValue);
128
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
136 // Sets word to the next word in the input text. Fills next_position with
137 // the next location past the end of the word.
138 spv_result_t getWord(std::string* word, spv_position next_position);
139
140 // 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
148 // stream, and for the given error code. Any data written to this object will
149 // show up in pDiagnsotic on destruction.
diagnostic(spv_result_t error)150 DiagnosticStream diagnostic(spv_result_t error) {
151 return DiagnosticStream(current_position_, consumer_, error);
152 }
153
154 // Returns a diagnostic object with the default assembly error code.
diagnostic()155 DiagnosticStream diagnostic() {
156 // The default failure for assembly is invalid text.
157 return diagnostic(SPV_ERROR_INVALID_TEXT);
158 }
159
160 // Returns then next character in the input stream.
161 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.
setPosition(const spv_position_t & newPosition)170 void setPosition(const spv_position_t& newPosition) {
171 current_position_ = newPosition;
172 }
173
174 // Returns the current position in the input stream.
position()175 const spv_position_t& position() const { return current_position_; }
176
177 // Appends the given 32-bit value to the given instruction.
178 // Returns SPV_SUCCESS if the value could be correctly inserted in the
179 // instruction.
180 spv_result_t binaryEncodeU32(const uint32_t value, spv_instruction_t* pInst);
181
182 // Appends the given string to the given instruction.
183 // Returns SPV_SUCCESS if the value could be correctly inserted in the
184 // instruction.
185 spv_result_t binaryEncodeString(const char* value, spv_instruction_t* pInst);
186
187 // 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
192 // instruction. Returns the given error code on failure, and emits
193 // a diagnostic if that error code is not SPV_FAILED_MATCH.
194 spv_result_t binaryEncodeNumericLiteral(const char* numeric_literal,
195 spv_result_t error_code,
196 const IdType& type,
197 spv_instruction_t* pInst);
198
199 // Returns the IdType associated with this type-generating value.
200 // If the type has not been previously recorded with recordTypeDefinition,
201 // kUnknownType will be returned.
202 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
207 // could not be determined kUnknownType will be returned.
208 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
220 // 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
229 // Returns a set consisting of each ID generated by spvNamedIdAssignOrGet from
230 // a numeric ID text representation. For example, generated from "%12" but not
231 // from "%foo".
232 std::set<uint32_t> GetNumericIds() const;
233
234 private:
235 // Maps ID names to their corresponding numerical ids.
236 using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
237 // Maps type-defining IDs to their IdType.
238 using spv_id_to_type_map = std::unordered_map<uint32_t, IdType>;
239 // Maps Ids to the id of their type.
240 using spv_id_to_type_id = std::unordered_map<uint32_t, uint32_t>;
241
242 spv_named_id_table named_ids_;
243 spv_id_to_type_map types_;
244 spv_id_to_type_id value_types_;
245 // Maps an extended instruction import Id to the extended instruction type.
246 std::unordered_map<uint32_t, spv_ext_inst_type_t> import_id_to_ext_inst_type_;
247 spv_position_t current_position_;
248 spvtools::MessageConsumer consumer_;
249 spv_text text_;
250 uint32_t bound_;
251 uint32_t next_id_;
252 std::set<uint32_t> ids_to_preserve_;
253 };
254 }
255 #endif // _LIBSPIRV_TEXT_HANDLER_H_
256