|
1 // |
|
2 // Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved. |
|
3 // Use of this source code is governed by a BSD-style license that can be |
|
4 // found in the LICENSE file. |
|
5 // |
|
6 |
|
7 #ifndef _SYMBOL_TABLE_INCLUDED_ |
|
8 #define _SYMBOL_TABLE_INCLUDED_ |
|
9 |
|
10 // |
|
11 // Symbol table for parsing. Has these design characteristics: |
|
12 // |
|
13 // * Same symbol table can be used to compile many shaders, to preserve |
|
14 // effort of creating and loading with the large numbers of built-in |
|
15 // symbols. |
|
16 // |
|
17 // * Name mangling will be used to give each function a unique name |
|
18 // so that symbol table lookups are never ambiguous. This allows |
|
19 // a simpler symbol table structure. |
|
20 // |
|
21 // * Pushing and popping of scope, so symbol table will really be a stack |
|
22 // of symbol tables. Searched from the top, with new inserts going into |
|
23 // the top. |
|
24 // |
|
25 // * Constants: Compile time constant symbols will keep their values |
|
26 // in the symbol table. The parser can substitute constants at parse |
|
27 // time, including doing constant folding and constant propagation. |
|
28 // |
|
29 // * No temporaries: Temporaries made from operations (+, --, .xy, etc.) |
|
30 // are tracked in the intermediate representation, not the symbol table. |
|
31 // |
|
32 |
|
33 #include <assert.h> |
|
34 |
|
35 #include "common/angleutils.h" |
|
36 #include "compiler/InfoSink.h" |
|
37 #include "compiler/intermediate.h" |
|
38 |
|
39 // |
|
40 // Symbol base class. (Can build functions or variables out of these...) |
|
41 // |
|
42 class TSymbol { |
|
43 public: |
|
44 POOL_ALLOCATOR_NEW_DELETE(); |
|
45 TSymbol(const TString *n) : name(n) { } |
|
46 virtual ~TSymbol() { /* don't delete name, it's from the pool */ } |
|
47 |
|
48 const TString& getName() const { return *name; } |
|
49 virtual const TString& getMangledName() const { return getName(); } |
|
50 virtual bool isFunction() const { return false; } |
|
51 virtual bool isVariable() const { return false; } |
|
52 void setUniqueId(int id) { uniqueId = id; } |
|
53 int getUniqueId() const { return uniqueId; } |
|
54 virtual void dump(TInfoSink &infoSink) const = 0; |
|
55 void relateToExtension(const TString& ext) { extension = ext; } |
|
56 const TString& getExtension() const { return extension; } |
|
57 |
|
58 private: |
|
59 DISALLOW_COPY_AND_ASSIGN(TSymbol); |
|
60 |
|
61 const TString *name; |
|
62 unsigned int uniqueId; // For real comparing during code generation |
|
63 TString extension; |
|
64 }; |
|
65 |
|
66 // |
|
67 // Variable class, meaning a symbol that's not a function. |
|
68 // |
|
69 // There could be a separate class heirarchy for Constant variables; |
|
70 // Only one of int, bool, or float, (or none) is correct for |
|
71 // any particular use, but it's easy to do this way, and doesn't |
|
72 // seem worth having separate classes, and "getConst" can't simply return |
|
73 // different values for different types polymorphically, so this is |
|
74 // just simple and pragmatic. |
|
75 // |
|
76 class TVariable : public TSymbol { |
|
77 public: |
|
78 TVariable(const TString *name, const TType& t, bool uT = false ) : TSymbol(name), type(t), userType(uT), unionArray(0) { } |
|
79 virtual ~TVariable() { } |
|
80 virtual bool isVariable() const { return true; } |
|
81 TType& getType() { return type; } |
|
82 const TType& getType() const { return type; } |
|
83 bool isUserType() const { return userType; } |
|
84 void setQualifier(TQualifier qualifier) { type.setQualifier(qualifier); } |
|
85 |
|
86 virtual void dump(TInfoSink &infoSink) const; |
|
87 |
|
88 ConstantUnion* getConstPointer() |
|
89 { |
|
90 if (!unionArray) |
|
91 unionArray = new ConstantUnion[type.getObjectSize()]; |
|
92 |
|
93 return unionArray; |
|
94 } |
|
95 |
|
96 ConstantUnion* getConstPointer() const { return unionArray; } |
|
97 |
|
98 void shareConstPointer( ConstantUnion *constArray) |
|
99 { |
|
100 if (unionArray == constArray) |
|
101 return; |
|
102 |
|
103 delete[] unionArray; |
|
104 unionArray = constArray; |
|
105 } |
|
106 |
|
107 private: |
|
108 DISALLOW_COPY_AND_ASSIGN(TVariable); |
|
109 |
|
110 TType type; |
|
111 bool userType; |
|
112 // we are assuming that Pool Allocator will free the memory allocated to unionArray |
|
113 // when this object is destroyed |
|
114 ConstantUnion *unionArray; |
|
115 }; |
|
116 |
|
117 // |
|
118 // The function sub-class of symbols and the parser will need to |
|
119 // share this definition of a function parameter. |
|
120 // |
|
121 struct TParameter { |
|
122 TString *name; |
|
123 TType* type; |
|
124 }; |
|
125 |
|
126 // |
|
127 // The function sub-class of a symbol. |
|
128 // |
|
129 class TFunction : public TSymbol { |
|
130 public: |
|
131 TFunction(TOperator o) : |
|
132 TSymbol(0), |
|
133 returnType(TType(EbtVoid, EbpUndefined)), |
|
134 op(o), |
|
135 defined(false) { } |
|
136 TFunction(const TString *name, TType& retType, TOperator tOp = EOpNull) : |
|
137 TSymbol(name), |
|
138 returnType(retType), |
|
139 mangledName(TFunction::mangleName(*name)), |
|
140 op(tOp), |
|
141 defined(false) { } |
|
142 virtual ~TFunction(); |
|
143 virtual bool isFunction() const { return true; } |
|
144 |
|
145 static TString mangleName(const TString& name) { return name + '('; } |
|
146 static TString unmangleName(const TString& mangledName) |
|
147 { |
|
148 return TString(mangledName.c_str(), mangledName.find_first_of('(')); |
|
149 } |
|
150 |
|
151 void addParameter(TParameter& p) |
|
152 { |
|
153 parameters.push_back(p); |
|
154 mangledName = mangledName + p.type->getMangledName(); |
|
155 } |
|
156 |
|
157 const TString& getMangledName() const { return mangledName; } |
|
158 const TType& getReturnType() const { return returnType; } |
|
159 |
|
160 void relateToOperator(TOperator o) { op = o; } |
|
161 TOperator getBuiltInOp() const { return op; } |
|
162 |
|
163 void setDefined() { defined = true; } |
|
164 bool isDefined() { return defined; } |
|
165 |
|
166 size_t getParamCount() const { return parameters.size(); } |
|
167 const TParameter& getParam(size_t i) const { return parameters[i]; } |
|
168 |
|
169 virtual void dump(TInfoSink &infoSink) const; |
|
170 |
|
171 private: |
|
172 DISALLOW_COPY_AND_ASSIGN(TFunction); |
|
173 |
|
174 typedef TVector<TParameter> TParamList; |
|
175 TParamList parameters; |
|
176 TType returnType; |
|
177 TString mangledName; |
|
178 TOperator op; |
|
179 bool defined; |
|
180 }; |
|
181 |
|
182 |
|
183 class TSymbolTableLevel { |
|
184 public: |
|
185 typedef TMap<TString, TSymbol*> tLevel; |
|
186 typedef tLevel::const_iterator const_iterator; |
|
187 typedef const tLevel::value_type tLevelPair; |
|
188 typedef std::pair<tLevel::iterator, bool> tInsertResult; |
|
189 |
|
190 POOL_ALLOCATOR_NEW_DELETE(); |
|
191 TSymbolTableLevel() { } |
|
192 ~TSymbolTableLevel(); |
|
193 |
|
194 bool insert(const TString &name, TSymbol &symbol) |
|
195 { |
|
196 // |
|
197 // returning true means symbol was added to the table |
|
198 // |
|
199 tInsertResult result; |
|
200 result = level.insert(tLevelPair(name, &symbol)); |
|
201 |
|
202 return result.second; |
|
203 } |
|
204 |
|
205 bool insert(TSymbol &symbol) |
|
206 { |
|
207 return insert(symbol.getMangledName(), symbol); |
|
208 } |
|
209 |
|
210 TSymbol* find(const TString& name) const |
|
211 { |
|
212 tLevel::const_iterator it = level.find(name); |
|
213 if (it == level.end()) |
|
214 return 0; |
|
215 else |
|
216 return (*it).second; |
|
217 } |
|
218 |
|
219 const_iterator begin() const |
|
220 { |
|
221 return level.begin(); |
|
222 } |
|
223 |
|
224 const_iterator end() const |
|
225 { |
|
226 return level.end(); |
|
227 } |
|
228 |
|
229 void relateToOperator(const char* name, TOperator op); |
|
230 void relateToExtension(const char* name, const TString& ext); |
|
231 void dump(TInfoSink &infoSink) const; |
|
232 |
|
233 protected: |
|
234 tLevel level; |
|
235 }; |
|
236 |
|
237 class TSymbolTable { |
|
238 public: |
|
239 TSymbolTable() : uniqueId(0) |
|
240 { |
|
241 // |
|
242 // The symbol table cannot be used until push() is called, but |
|
243 // the lack of an initial call to push() can be used to detect |
|
244 // that the symbol table has not been preloaded with built-ins. |
|
245 // |
|
246 } |
|
247 |
|
248 ~TSymbolTable() |
|
249 { |
|
250 // level 0 is always built In symbols, so we never pop that out |
|
251 while (table.size() > 1) |
|
252 pop(); |
|
253 } |
|
254 |
|
255 // |
|
256 // When the symbol table is initialized with the built-ins, there should |
|
257 // 'push' calls, so that built-ins are at level 0 and the shader |
|
258 // globals are at level 1. |
|
259 // |
|
260 bool isEmpty() { return table.size() == 0; } |
|
261 bool atBuiltInLevel() { return table.size() == 1; } |
|
262 bool atGlobalLevel() { return table.size() <= 2; } |
|
263 void push() |
|
264 { |
|
265 table.push_back(new TSymbolTableLevel); |
|
266 precisionStack.push_back( PrecisionStackLevel() ); |
|
267 } |
|
268 |
|
269 void pop() |
|
270 { |
|
271 delete table[currentLevel()]; |
|
272 table.pop_back(); |
|
273 precisionStack.pop_back(); |
|
274 } |
|
275 |
|
276 bool insert(TSymbol& symbol) |
|
277 { |
|
278 symbol.setUniqueId(++uniqueId); |
|
279 return table[currentLevel()]->insert(symbol); |
|
280 } |
|
281 |
|
282 bool insertConstInt(const char *name, int value) |
|
283 { |
|
284 TVariable *constant = new TVariable(NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1)); |
|
285 constant->getConstPointer()->setIConst(value); |
|
286 return insert(*constant); |
|
287 } |
|
288 |
|
289 bool insertBuiltIn(TType *rvalue, const char *name, TType *ptype1, TType *ptype2 = 0, TType *ptype3 = 0) |
|
290 { |
|
291 TFunction *function = new TFunction(NewPoolTString(name), *rvalue); |
|
292 |
|
293 TParameter param1 = {NULL, ptype1}; |
|
294 function->addParameter(param1); |
|
295 |
|
296 if(ptype2) |
|
297 { |
|
298 TParameter param2 = {NULL, ptype2}; |
|
299 function->addParameter(param2); |
|
300 } |
|
301 |
|
302 if(ptype3) |
|
303 { |
|
304 TParameter param3 = {NULL, ptype3}; |
|
305 function->addParameter(param3); |
|
306 } |
|
307 |
|
308 return insert(*function); |
|
309 } |
|
310 |
|
311 TSymbol* find(const TString& name, bool* builtIn = 0, bool *sameScope = 0) |
|
312 { |
|
313 int level = currentLevel(); |
|
314 TSymbol* symbol; |
|
315 do { |
|
316 symbol = table[level]->find(name); |
|
317 --level; |
|
318 } while (symbol == 0 && level >= 0); |
|
319 level++; |
|
320 if (builtIn) |
|
321 *builtIn = level == 0; |
|
322 if (sameScope) |
|
323 *sameScope = level == currentLevel(); |
|
324 return symbol; |
|
325 } |
|
326 |
|
327 TSymbol *findBuiltIn(const TString &name) |
|
328 { |
|
329 return table[0]->find(name); |
|
330 } |
|
331 |
|
332 TSymbolTableLevel* getGlobalLevel() { |
|
333 assert(table.size() >= 2); |
|
334 return table[1]; |
|
335 } |
|
336 |
|
337 TSymbolTableLevel* getOuterLevel() { |
|
338 assert(table.size() >= 2); |
|
339 return table[currentLevel() - 1]; |
|
340 } |
|
341 |
|
342 void relateToOperator(const char* name, TOperator op) { |
|
343 table[0]->relateToOperator(name, op); |
|
344 } |
|
345 void relateToExtension(const char* name, const TString& ext) { |
|
346 table[0]->relateToExtension(name, ext); |
|
347 } |
|
348 int getMaxSymbolId() { return uniqueId; } |
|
349 void dump(TInfoSink &infoSink) const; |
|
350 |
|
351 bool setDefaultPrecision( const TPublicType& type, TPrecision prec ){ |
|
352 if (IsSampler(type.type)) |
|
353 return true; // Skip sampler types for the time being |
|
354 if (type.type != EbtFloat && type.type != EbtInt) |
|
355 return false; // Only set default precision for int/float |
|
356 if (type.size != 1 || type.matrix || type.array) |
|
357 return false; // Not allowed to set for aggregate types |
|
358 int indexOfLastElement = static_cast<int>(precisionStack.size()) - 1; |
|
359 precisionStack[indexOfLastElement][type.type] = prec; // Uses map operator [], overwrites the current value |
|
360 return true; |
|
361 } |
|
362 |
|
363 // Searches down the precisionStack for a precision qualifier for the specified TBasicType |
|
364 TPrecision getDefaultPrecision( TBasicType type){ |
|
365 if( type != EbtFloat && type != EbtInt ) return EbpUndefined; |
|
366 int level = static_cast<int>(precisionStack.size()) - 1; |
|
367 assert( level >= 0); // Just to be safe. Should not happen. |
|
368 PrecisionStackLevel::iterator it; |
|
369 TPrecision prec = EbpUndefined; // If we dont find anything we return this. Should we error check this? |
|
370 while( level >= 0 ){ |
|
371 it = precisionStack[level].find( type ); |
|
372 if( it != precisionStack[level].end() ){ |
|
373 prec = (*it).second; |
|
374 break; |
|
375 } |
|
376 level--; |
|
377 } |
|
378 return prec; |
|
379 } |
|
380 |
|
381 protected: |
|
382 int currentLevel() const { return static_cast<int>(table.size()) - 1; } |
|
383 |
|
384 std::vector<TSymbolTableLevel*> table; |
|
385 typedef std::map< TBasicType, TPrecision > PrecisionStackLevel; |
|
386 std::vector< PrecisionStackLevel > precisionStack; |
|
387 int uniqueId; // for unique identification in code generation |
|
388 }; |
|
389 |
|
390 #endif // _SYMBOL_TABLE_INCLUDED_ |