• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 SOURCE_TEXT_HANDLER_H_
16 #define SOURCE_TEXT_HANDLER_H_
17 
18 #include <iomanip>
19 #include <set>
20 #include <sstream>
21 #include <string>
22 #include <type_traits>
23 #include <unordered_map>
24 #include <utility>
25 
26 #include "source/diagnostic.h"
27 #include "source/instruction.h"
28 #include "source/text.h"
29 #include "spirv-tools/libspirv.h"
30 
31 namespace spvtools {
32 
33 // Structures
34 
35 // This is a lattice for tracking types.
36 enum class IdTypeClass {
37   kBottom = 0,  // We have no information yet.
38   kScalarIntegerType,
39   kScalarFloatType,
40   kOtherType
41 };
42 
43 // Contains ID type information that needs to be tracked across all Ids.
44 // Bitwidth is only valid when type_class is kScalarIntegerType or
45 // kScalarFloatType.
46 struct IdType {
47   uint32_t bitwidth;  // Safe to assume that we will not have > 2^32 bits.
48   bool isSigned;      // This is only significant if type_class is integral.
49   IdTypeClass type_class;
50 };
51 
52 // Default equality operator for IdType. Tests if all members are the same.
53 inline bool operator==(const IdType& first, const IdType& second) {
54   return (first.bitwidth == second.bitwidth) &&
55          (first.isSigned == second.isSigned) &&
56          (first.type_class == second.type_class);
57 }
58 
59 // Tests whether any member of the IdTypes do not match.
60 inline bool operator!=(const IdType& first, const IdType& second) {
61   return !(first == second);
62 }
63 
64 // A value representing an unknown type.
65 extern const IdType kUnknownType;
66 
67 // Returns true if the type is a scalar integer type.
isScalarIntegral(const IdType & type)68 inline bool isScalarIntegral(const IdType& type) {
69   return type.type_class == IdTypeClass::kScalarIntegerType;
70 }
71 
72 // Returns true if the type is a scalar floating point type.
isScalarFloating(const IdType & type)73 inline bool isScalarFloating(const IdType& type) {
74   return type.type_class == IdTypeClass::kScalarFloatType;
75 }
76 
77 // Returns the number of bits in the type.
78 // This is only valid for bottom, scalar integer, and scalar floating
79 // classes.  For bottom, assume 32 bits.
assumedBitWidth(const IdType & type)80 inline int assumedBitWidth(const IdType& type) {
81   switch (type.type_class) {
82     case IdTypeClass::kBottom:
83       return 32;
84     case IdTypeClass::kScalarIntegerType:
85     case IdTypeClass::kScalarFloatType:
86       return type.bitwidth;
87     default:
88       break;
89   }
90   // We don't care about this case.
91   return 0;
92 }
93 
94 // A templated class with a static member function Clamp, where Clamp
95 // sets a referenced value of type T to 0 if T is an unsigned
96 // integer type, and returns true if it modified the referenced
97 // value.
98 template <typename T, typename = void>
99 class ClampToZeroIfUnsignedType {
100  public:
101   // The default specialization does not clamp the value.
Clamp(T *)102   static bool Clamp(T*) { return false; }
103 };
104 
105 // The specialization of ClampToZeroIfUnsignedType for unsigned integer
106 // types.
107 template <typename T>
108 class ClampToZeroIfUnsignedType<
109     T, typename std::enable_if<std::is_unsigned<T>::value>::type> {
110  public:
Clamp(T * value_pointer)111   static bool Clamp(T* value_pointer) {
112     if (*value_pointer) {
113       *value_pointer = 0;
114       return true;
115     }
116     return false;
117   }
118 };
119 
120 // Encapsulates the data used during the assembly of a SPIR-V module.
121 class AssemblyContext {
122  public:
123   AssemblyContext(spv_text text, const MessageConsumer& consumer,
124                   std::set<uint32_t>&& ids_to_preserve = std::set<uint32_t>())
125       : current_position_({}),
126         consumer_(consumer),
127         text_(text),
128         bound_(1),
129         next_id_(1),
130         ids_to_preserve_(std::move(ids_to_preserve)) {}
131 
132   // Assigns a new integer value to the given text ID, or returns the previously
133   // assigned integer value if the ID has been seen before.
134   uint32_t spvNamedIdAssignOrGet(const char* textValue);
135 
136   // Returns the largest largest numeric ID that has been assigned.
137   uint32_t getBound() const;
138 
139   // Advances position to point to the next word in the input stream.
140   // Returns SPV_SUCCESS on success.
141   spv_result_t advance();
142 
143   // Sets word to the next word in the input text. Fills next_position with
144   // the next location past the end of the word.
145   spv_result_t getWord(std::string* word, spv_position next_position);
146 
147   // Returns true if the next word in the input is the start of a new Opcode.
148   bool startsWithOp();
149 
150   // Returns true if the next word in the input is the start of a new
151   // instruction.
152   bool isStartOfNewInst();
153 
154   // Returns a diagnostic object initialized with current position in the input
155   // stream, and for the given error code. Any data written to this object will
156   // show up in pDiagnsotic on destruction.
diagnostic(spv_result_t error)157   DiagnosticStream diagnostic(spv_result_t error) {
158     return DiagnosticStream(current_position_, consumer_, "", error);
159   }
160 
161   // Returns a diagnostic object with the default assembly error code.
diagnostic()162   DiagnosticStream diagnostic() {
163     // The default failure for assembly is invalid text.
164     return diagnostic(SPV_ERROR_INVALID_TEXT);
165   }
166 
167   // Returns then next character in the input stream.
168   char peek() const;
169 
170   // Returns true if there is more text in the input stream.
171   bool hasText() const;
172 
173   // Seeks the input stream forward by 'size' characters.
174   void seekForward(uint32_t size);
175 
176   // Sets the current position in the input stream to the given position.
setPosition(const spv_position_t & newPosition)177   void setPosition(const spv_position_t& newPosition) {
178     current_position_ = newPosition;
179   }
180 
181   // Returns the current position in the input stream.
position()182   const spv_position_t& position() const { return current_position_; }
183 
184   // Appends the given 32-bit value to the given instruction.
185   // Returns SPV_SUCCESS if the value could be correctly inserted in the
186   // instruction.
187   spv_result_t binaryEncodeU32(const uint32_t value, spv_instruction_t* pInst);
188 
189   // Appends the given string to the given instruction.
190   // Returns SPV_SUCCESS if the value could be correctly inserted in the
191   // instruction.
192   spv_result_t binaryEncodeString(const char* value, spv_instruction_t* pInst);
193 
194   // Appends the given numeric literal to the given instruction.
195   // Validates and respects the bitwidth supplied in the IdType argument.
196   // If the type is of class kBottom the value will be encoded as a
197   // 32-bit integer.
198   // Returns SPV_SUCCESS if the value could be correctly added to the
199   // instruction.  Returns the given error code on failure, and emits
200   // a diagnostic if that error code is not SPV_FAILED_MATCH.
201   spv_result_t binaryEncodeNumericLiteral(const char* numeric_literal,
202                                           spv_result_t error_code,
203                                           const IdType& type,
204                                           spv_instruction_t* pInst);
205 
206   // Returns the IdType associated with this type-generating value.
207   // If the type has not been previously recorded with recordTypeDefinition,
208   // kUnknownType  will be returned.
209   IdType getTypeOfTypeGeneratingValue(uint32_t value) const;
210 
211   // Returns the IdType that represents the return value of this Value
212   // generating instruction.
213   // If the value has not been recorded with recordTypeIdForValue, or the type
214   // could not be determined kUnknownType will be returned.
215   IdType getTypeOfValueInstruction(uint32_t value) const;
216 
217   // Tracks the type-defining instruction. The result of the tracking can
218   // later be queried using getValueType.
219   // pInst is expected to be completely filled in by the time this instruction
220   // is called.
221   // Returns SPV_SUCCESS on success, or SPV_ERROR_INVALID_VALUE on error.
222   spv_result_t recordTypeDefinition(const spv_instruction_t* pInst);
223 
224   // Tracks the relationship between the value and its type.
225   spv_result_t recordTypeIdForValue(uint32_t value, uint32_t type);
226 
227   // Records the given Id as being the import of the given extended instruction
228   // type.
229   spv_result_t recordIdAsExtInstImport(uint32_t id, spv_ext_inst_type_t type);
230 
231   // Returns the extended instruction type corresponding to the import with
232   // the given Id, if it exists.  Returns SPV_EXT_INST_TYPE_NONE if the
233   // id is not the id for an extended instruction type.
234   spv_ext_inst_type_t getExtInstTypeForId(uint32_t id) const;
235 
236   // Returns a set consisting of each ID generated by spvNamedIdAssignOrGet from
237   // a numeric ID text representation. For example, generated from "%12" but not
238   // from "%foo".
239   std::set<uint32_t> GetNumericIds() const;
240 
241  private:
242   // Maps ID names to their corresponding numerical ids.
243   using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
244   // Maps type-defining IDs to their IdType.
245   using spv_id_to_type_map = std::unordered_map<uint32_t, IdType>;
246   // Maps Ids to the id of their type.
247   using spv_id_to_type_id = std::unordered_map<uint32_t, uint32_t>;
248 
249   spv_named_id_table named_ids_;
250   spv_id_to_type_map types_;
251   spv_id_to_type_id value_types_;
252   // Maps an extended instruction import Id to the extended instruction type.
253   std::unordered_map<uint32_t, spv_ext_inst_type_t> import_id_to_ext_inst_type_;
254   spv_position_t current_position_;
255   MessageConsumer consumer_;
256   spv_text text_;
257   uint32_t bound_;
258   uint32_t next_id_;
259   std::set<uint32_t> ids_to_preserve_;
260 };
261 
262 }  // namespace spvtools
263 
264 #endif  // SOURCE_TEXT_HANDLER_H_
265