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