blob: 6d4355d8aced77871e9b3fccfa7dcc1c1656bda3 [file] [log] [blame]
John Bauman66b8ab22014-05-06 15:57:45 -04001//
John Baumand4ae8632014-05-06 16:18:33 -04002// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
John Bauman66b8ab22014-05-06 15:57:45 -04003// 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 "compiler/InfoSink.h"
36#include "compiler/intermediate.h"
37
38//
39// Symbol base class. (Can build functions or variables out of these...)
40//
41class TSymbol {
42public:
43 POOL_ALLOCATOR_NEW_DELETE(GlobalPoolAllocator)
44 TSymbol(const TString *n) : name(n) { }
45 virtual ~TSymbol() { /* don't delete name, it's from the pool */ }
46 const TString& getName() const { return *name; }
47 virtual const TString& getMangledName() const { return getName(); }
48 virtual bool isFunction() const { return false; }
49 virtual bool isVariable() const { return false; }
50 void setUniqueId(int id) { uniqueId = id; }
51 int getUniqueId() const { return uniqueId; }
John Bauman66b8ab22014-05-06 15:57:45 -040052 TSymbol(const TSymbol&);
John Bauman66b8ab22014-05-06 15:57:45 -040053
54protected:
55 const TString *name;
56 unsigned int uniqueId; // For real comparing during code generation
57};
58
59//
60// Variable class, meaning a symbol that's not a function.
61//
62// There could be a separate class heirarchy for Constant variables;
63// Only one of int, bool, or float, (or none) is correct for
64// any particular use, but it's easy to do this way, and doesn't
65// seem worth having separate classes, and "getConst" can't simply return
66// different values for different types polymorphically, so this is
67// just simple and pragmatic.
68//
69class TVariable : public TSymbol {
70public:
71 TVariable(const TString *name, const TType& t, bool uT = false ) : TSymbol(name), type(t), userType(uT), unionArray(0), arrayInformationType(0) { }
72 virtual ~TVariable() { }
73 virtual bool isVariable() const { return true; }
74 TType& getType() { return type; }
75 const TType& getType() const { return type; }
76 bool isUserType() const { return userType; }
77 void setQualifier(TQualifier qualifier) { type.setQualifier(qualifier); }
78 void updateArrayInformationType(TType *t) { arrayInformationType = t; }
79 TType* getArrayInformationType() { return arrayInformationType; }
80
John Bauman66b8ab22014-05-06 15:57:45 -040081 ConstantUnion* getConstPointer()
82 {
83 if (!unionArray)
84 unionArray = new ConstantUnion[type.getObjectSize()];
85
86 return unionArray;
87 }
88
89 ConstantUnion* getConstPointer() const { return unionArray; }
90
91 void shareConstPointer( ConstantUnion *constArray)
92 {
93 if (unionArray == constArray)
94 return;
95
96 delete[] unionArray;
97 unionArray = constArray;
98 }
John Bauman66b8ab22014-05-06 15:57:45 -040099
100protected:
101 TType type;
102 bool userType;
103 // we are assuming that Pool Allocator will free the memory allocated to unionArray
104 // when this object is destroyed
105 ConstantUnion *unionArray;
106 TType *arrayInformationType; // this is used for updating maxArraySize in all the references to a given symbol
107};
108
109//
110// The function sub-class of symbols and the parser will need to
111// share this definition of a function parameter.
112//
113struct TParameter {
114 TString *name;
John Baumand4ae8632014-05-06 16:18:33 -0400115 TType *type;
John Bauman66b8ab22014-05-06 15:57:45 -0400116};
117
118//
119// The function sub-class of a symbol.
120//
121class TFunction : public TSymbol {
122public:
123 TFunction(TOperator o) :
124 TSymbol(0),
125 returnType(TType(EbtVoid, EbpUndefined)),
126 op(o),
127 defined(false) { }
128 TFunction(const TString *name, TType& retType, TOperator tOp = EOpNull) :
129 TSymbol(name),
130 returnType(retType),
131 mangledName(TFunction::mangleName(*name)),
132 op(tOp),
133 defined(false) { }
134 virtual ~TFunction();
135 virtual bool isFunction() const { return true; }
136
137 static TString mangleName(const TString& name) { return name + '('; }
138 static TString unmangleName(const TString& mangledName)
139 {
140 return TString(mangledName.c_str(), mangledName.find_first_of('('));
141 }
142
143 void addParameter(TParameter& p)
144 {
145 parameters.push_back(p);
146 mangledName = mangledName + p.type->getMangledName();
147 }
148
149 const TString& getMangledName() const { return mangledName; }
150 const TType& getReturnType() const { return returnType; }
151
152 void relateToOperator(TOperator o) { op = o; }
153 TOperator getBuiltInOp() const { return op; }
154
155 void relateToExtension(const TString& ext) { extension = ext; }
156 const TString& getExtension() const { return extension; }
157
158 void setDefined() { defined = true; }
159 bool isDefined() { return defined; }
160
161 int getParamCount() const { return static_cast<int>(parameters.size()); }
162 const TParameter& getParam(int i) const { return parameters[i]; }
163
John Bauman66b8ab22014-05-06 15:57:45 -0400164protected:
165 typedef TVector<TParameter> TParamList;
166 TParamList parameters;
167 TType returnType;
168 TString mangledName;
169 TOperator op;
170 TString extension;
171 bool defined;
172};
173
174
175class TSymbolTableLevel {
176public:
177 typedef TMap<TString, TSymbol*> tLevel;
178 typedef tLevel::const_iterator const_iterator;
179 typedef const tLevel::value_type tLevelPair;
180 typedef std::pair<tLevel::iterator, bool> tInsertResult;
181
182 POOL_ALLOCATOR_NEW_DELETE(GlobalPoolAllocator)
183 TSymbolTableLevel() { }
184 ~TSymbolTableLevel();
185
John Baumand4ae8632014-05-06 16:18:33 -0400186 bool insert(TSymbol &symbol)
John Bauman66b8ab22014-05-06 15:57:45 -0400187 {
John Baumand4ae8632014-05-06 16:18:33 -0400188 symbol.setUniqueId(++uniqueId);
189
John Bauman66b8ab22014-05-06 15:57:45 -0400190 //
191 // returning true means symbol was added to the table
192 //
193 tInsertResult result;
194 result = level.insert(tLevelPair(symbol.getMangledName(), &symbol));
195
196 return result.second;
197 }
198
199 TSymbol* find(const TString& name) const
200 {
201 tLevel::const_iterator it = level.find(name);
202 if (it == level.end())
203 return 0;
204 else
205 return (*it).second;
206 }
207
John Bauman66b8ab22014-05-06 15:57:45 -0400208 void relateToOperator(const char* name, TOperator op);
209 void relateToExtension(const char* name, const TString& ext);
John Bauman66b8ab22014-05-06 15:57:45 -0400210
211protected:
212 tLevel level;
John Baumand4ae8632014-05-06 16:18:33 -0400213 static int uniqueId; // for unique identification in code generation
John Bauman66b8ab22014-05-06 15:57:45 -0400214};
215
216class TSymbolTable {
217public:
John Baumand4ae8632014-05-06 16:18:33 -0400218 TSymbolTable()
John Bauman66b8ab22014-05-06 15:57:45 -0400219 {
220 //
221 // The symbol table cannot be used until push() is called, but
222 // the lack of an initial call to push() can be used to detect
223 // that the symbol table has not been preloaded with built-ins.
224 //
225 }
226
227 ~TSymbolTable()
228 {
229 // level 0 is always built In symbols, so we never pop that out
230 while (table.size() > 1)
231 pop();
232 }
233
234 //
235 // When the symbol table is initialized with the built-ins, there should
236 // 'push' calls, so that built-ins are at level 0 and the shader
237 // globals are at level 1.
238 //
239 bool isEmpty() { return table.size() == 0; }
240 bool atBuiltInLevel() { return table.size() == 1; }
241 bool atGlobalLevel() { return table.size() <= 2; }
242 void push()
243 {
244 table.push_back(new TSymbolTableLevel);
245 precisionStack.push_back( PrecisionStackLevel() );
246 }
247
248 void pop()
249 {
250 delete table[currentLevel()];
251 table.pop_back();
252 precisionStack.pop_back();
253 }
254
255 bool insert(TSymbol& symbol)
256 {
John Bauman66b8ab22014-05-06 15:57:45 -0400257 return table[currentLevel()]->insert(symbol);
258 }
259
John Baumand4ae8632014-05-06 16:18:33 -0400260 bool insertConstInt(const char *name, int value)
261 {
262 TVariable *constant = new TVariable(NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1));
263 constant->getConstPointer()->setIConst(value);
264 return insert(*constant);
265 }
266
Nicolas Capens434311c2014-05-28 19:23:55 -0400267 bool insertBuiltIn(TType *rvalue, const char *name, TType *ptype1, TType *ptype2 = 0, TType *ptype3 = 0)
John Baumand4ae8632014-05-06 16:18:33 -0400268 {
269 TFunction *function = new TFunction(NewPoolTString(name), *rvalue);
270
Nicolas Capens434311c2014-05-28 19:23:55 -0400271 TParameter param1 = {0, ptype1};
John Baumand4ae8632014-05-06 16:18:33 -0400272 function->addParameter(param1);
273
Nicolas Capens434311c2014-05-28 19:23:55 -0400274 if(ptype2)
John Baumand4ae8632014-05-06 16:18:33 -0400275 {
Nicolas Capens434311c2014-05-28 19:23:55 -0400276 TParameter param2 = {0, ptype2};
John Baumand4ae8632014-05-06 16:18:33 -0400277 function->addParameter(param2);
278 }
279
Nicolas Capens434311c2014-05-28 19:23:55 -0400280 if(ptype3)
John Baumand4ae8632014-05-06 16:18:33 -0400281 {
Nicolas Capens434311c2014-05-28 19:23:55 -0400282 TParameter param3 = {0, ptype3};
John Baumand4ae8632014-05-06 16:18:33 -0400283 function->addParameter(param3);
284 }
285
286 return insert(*function);
287 }
288
John Bauman66b8ab22014-05-06 15:57:45 -0400289 TSymbol* find(const TString& name, bool* builtIn = 0, bool *sameScope = 0)
290 {
291 int level = currentLevel();
292 TSymbol* symbol;
293 do {
294 symbol = table[level]->find(name);
295 --level;
296 } while (symbol == 0 && level >= 0);
297 level++;
298 if (builtIn)
299 *builtIn = level == 0;
300 if (sameScope)
301 *sameScope = level == currentLevel();
302 return symbol;
303 }
304
305 TSymbol *findBuiltIn(const TString &name)
306 {
307 return table[0]->find(name);
308 }
309
310 TSymbolTableLevel* getGlobalLevel() {
311 assert(table.size() >= 2);
312 return table[1];
313 }
314
315 TSymbolTableLevel* getOuterLevel() {
316 assert(table.size() >= 2);
317 return table[currentLevel() - 1];
318 }
319
320 void relateToOperator(const char* name, TOperator op) {
321 table[0]->relateToOperator(name, op);
322 }
323 void relateToExtension(const char* name, const TString& ext) {
324 table[0]->relateToExtension(name, ext);
325 }
John Bauman66b8ab22014-05-06 15:57:45 -0400326
John Baumand4ae8632014-05-06 16:18:33 -0400327 bool setDefaultPrecision( const TPublicType& type, TPrecision prec ){
328 if (IsSampler(type.type))
329 return true; // Skip sampler types for the time being
330 if (type.type != EbtFloat && type.type != EbtInt)
331 return false; // Only set default precision for int/float
332 if (type.size != 1 || type.matrix || type.array)
333 return false; // Not allowed to set for aggregate types
John Bauman66b8ab22014-05-06 15:57:45 -0400334 int indexOfLastElement = static_cast<int>(precisionStack.size()) - 1;
John Baumand4ae8632014-05-06 16:18:33 -0400335 precisionStack[indexOfLastElement][type.type] = prec; // Uses map operator [], overwrites the current value
336 return true;
John Bauman66b8ab22014-05-06 15:57:45 -0400337 }
338
339 // Searches down the precisionStack for a precision qualifier for the specified TBasicType
340 TPrecision getDefaultPrecision( TBasicType type){
341 if( type != EbtFloat && type != EbtInt ) return EbpUndefined;
342 int level = static_cast<int>(precisionStack.size()) - 1;
343 assert( level >= 0); // Just to be safe. Should not happen.
344 PrecisionStackLevel::iterator it;
345 TPrecision prec = EbpUndefined; // If we dont find anything we return this. Should we error check this?
346 while( level >= 0 ){
347 it = precisionStack[level].find( type );
348 if( it != precisionStack[level].end() ){
349 prec = (*it).second;
350 break;
351 }
352 level--;
353 }
354 return prec;
355 }
356
357protected:
358 int currentLevel() const { return static_cast<int>(table.size()) - 1; }
359
360 std::vector<TSymbolTableLevel*> table;
361 typedef std::map< TBasicType, TPrecision > PrecisionStackLevel;
362 std::vector< PrecisionStackLevel > precisionStack;
John Bauman66b8ab22014-05-06 15:57:45 -0400363};
364
365#endif // _SYMBOL_TABLE_INCLUDED_