blob: 3f932a4543281a15b1abd5f9b2ebf9fbd81fcbaa [file] [log] [blame]
//
// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef _SYMBOL_TABLE_INCLUDED_
#define _SYMBOL_TABLE_INCLUDED_
//
// Symbol table for parsing. Has these design characteristics:
//
// * Same symbol table can be used to compile many shaders, to preserve
// effort of creating and loading with the large numbers of built-in
// symbols.
//
// * Name mangling will be used to give each function a unique name
// so that symbol table lookups are never ambiguous. This allows
// a simpler symbol table structure.
//
// * Pushing and popping of scope, so symbol table will really be a stack
// of symbol tables. Searched from the top, with new inserts going into
// the top.
//
// * Constants: Compile time constant symbols will keep their values
// in the symbol table. The parser can substitute constants at parse
// time, including doing constant folding and constant propagation.
//
// * No temporaries: Temporaries made from operations (+, --, .xy, etc.)
// are tracked in the intermediate representation, not the symbol table.
//
#include <assert.h>
#include "common/angleutils.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/intermediate.h"
// Symbol base class. (Can build functions or variables out of these...)
class TSymbol
{
public:
POOL_ALLOCATOR_NEW_DELETE();
TSymbol(const TString *n)
: uniqueId(0),
name(n)
{
}
virtual ~TSymbol()
{
// don't delete name, it's from the pool
}
const TString &getName() const
{
return *name;
}
virtual const TString &getMangledName() const
{
return getName();
}
virtual bool isFunction() const
{
return false;
}
virtual bool isVariable() const
{
return false;
}
void setUniqueId(int id)
{
uniqueId = id;
}
int getUniqueId() const
{
return uniqueId;
}
void relateToExtension(const TString &ext)
{
extension = ext;
}
const TString &getExtension() const
{
return extension;
}
private:
DISALLOW_COPY_AND_ASSIGN(TSymbol);
int uniqueId; // For real comparing during code generation
const TString *name;
TString extension;
};
// Variable class, meaning a symbol that's not a function.
//
// There could be a separate class heirarchy for Constant variables;
// Only one of int, bool, or float, (or none) is correct for
// any particular use, but it's easy to do this way, and doesn't
// seem worth having separate classes, and "getConst" can't simply return
// different values for different types polymorphically, so this is
// just simple and pragmatic.
class TVariable : public TSymbol
{
public:
TVariable(const TString *name, const TType &t, bool uT = false)
: TSymbol(name),
type(t),
userType(uT),
unionArray(0)
{
}
virtual ~TVariable()
{
}
virtual bool isVariable() const
{
return true;
}
TType &getType()
{
return type;
}
const TType &getType() const
{
return type;
}
bool isUserType() const
{
return userType;
}
void setQualifier(TQualifier qualifier)
{
type.setQualifier(qualifier);
}
ConstantUnion *getConstPointer()
{
if (!unionArray)
unionArray = new ConstantUnion[type.getObjectSize()];
return unionArray;
}
ConstantUnion *getConstPointer() const
{
return unionArray;
}
void shareConstPointer(ConstantUnion *constArray)
{
if (unionArray == constArray)
return;
delete[] unionArray;
unionArray = constArray;
}
private:
DISALLOW_COPY_AND_ASSIGN(TVariable);
TType type;
bool userType;
// we are assuming that Pool Allocator will free the memory
// allocated to unionArray when this object is destroyed.
ConstantUnion *unionArray;
};
// The function sub-class of symbols and the parser will need to
// share this definition of a function parameter.
struct TParameter
{
TString *name;
TType *type;
};
// The function sub-class of a symbol.
class TFunction : public TSymbol
{
public:
TFunction(TOperator o)
: TSymbol(0),
returnType(TType(EbtVoid, EbpUndefined)),
op(o),
defined(false)
{
}
TFunction(const TString *name, TType &retType, TOperator tOp = EOpNull)
: TSymbol(name),
returnType(retType),
mangledName(TFunction::mangleName(*name)),
op(tOp),
defined(false)
{
}
virtual ~TFunction();
virtual bool isFunction() const
{
return true;
}
static TString mangleName(const TString &name)
{
return name + '(';
}
static TString unmangleName(const TString &mangledName)
{
return TString(mangledName.c_str(), mangledName.find_first_of('('));
}
void addParameter(TParameter &p)
{
parameters.push_back(p);
mangledName = mangledName + p.type->getMangledName();
}
const TString &getMangledName() const
{
return mangledName;
}
const TType &getReturnType() const
{
return returnType;
}
void relateToOperator(TOperator o)
{
op = o;
}
TOperator getBuiltInOp() const
{
return op;
}
void setDefined()
{
defined = true;
}
bool isDefined()
{
return defined;
}
size_t getParamCount() const
{
return parameters.size();
}
const TParameter &getParam(size_t i) const
{
return parameters[i];
}
private:
DISALLOW_COPY_AND_ASSIGN(TFunction);
typedef TVector<TParameter> TParamList;
TParamList parameters;
TType returnType;
TString mangledName;
TOperator op;
bool defined;
};
// Interface block name sub-symbol
class TInterfaceBlockName : public TSymbol
{
public:
TInterfaceBlockName(const TString *name)
: TSymbol(name)
{
}
virtual ~TInterfaceBlockName()
{
}
};
class TSymbolTableLevel
{
public:
typedef TMap<TString, TSymbol *> tLevel;
typedef tLevel::const_iterator const_iterator;
typedef const tLevel::value_type tLevelPair;
typedef std::pair<tLevel::iterator, bool> tInsertResult;
TSymbolTableLevel()
{
}
~TSymbolTableLevel();
bool insert(const TString &name, TSymbol &symbol);
bool insert(TSymbol &symbol);
TSymbol *find(const TString &name) const;
void relateToOperator(const char *name, TOperator op);
void relateToExtension(const char *name, const TString &ext);
protected:
tLevel level;
static int uniqueId; // for unique identification in code generation
};
enum ESymbolLevel
{
COMMON_BUILTINS = 0,
ESSL1_BUILTINS = 1,
ESSL3_BUILTINS = 2,
LAST_BUILTIN_LEVEL = ESSL3_BUILTINS,
GLOBAL_LEVEL = 3
};
class TSymbolTable
{
public:
TSymbolTable()
{
// The symbol table cannot be used until push() is called, but
// the lack of an initial call to push() can be used to detect
// that the symbol table has not been preloaded with built-ins.
}
~TSymbolTable();
// When the symbol table is initialized with the built-ins, there should
// 'push' calls, so that built-ins are at level 0 and the shader
// globals are at level 1.
bool isEmpty()
{
return table.empty();
}
bool atBuiltInLevel()
{
return currentLevel() <= LAST_BUILTIN_LEVEL;
}
bool atGlobalLevel()
{
return currentLevel() <= GLOBAL_LEVEL;
}
void push()
{
table.push_back(new TSymbolTableLevel);
precisionStack.push_back(new PrecisionStackLevel);
}
void pop()
{
delete table.back();
table.pop_back();
delete precisionStack.back();
precisionStack.pop_back();
}
bool declare(TSymbol &symbol)
{
return insert(currentLevel(), symbol);
}
bool insert(ESymbolLevel level, TSymbol &symbol)
{
return table[level]->insert(symbol);
}
bool insertConstInt(ESymbolLevel level, const char *name, int value)
{
TVariable *constant = new TVariable(
NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1));
constant->getConstPointer()->setIConst(value);
return insert(level, *constant);
}
void insertBuiltIn(ESymbolLevel level, TType *rvalue, const char *name,
TType *ptype1, TType *ptype2 = 0, TType *ptype3 = 0,
TType *ptype4 = 0, TType *ptype5 = 0);
TSymbol *find(const TString &name, int shaderVersion,
bool *builtIn = NULL, bool *sameScope = NULL);
TSymbol *findBuiltIn(const TString &name, int shaderVersion);
TSymbolTableLevel *getOuterLevel()
{
assert(currentLevel() >= 1);
return table[currentLevel() - 1];
}
void relateToOperator(ESymbolLevel level, const char *name, TOperator op)
{
table[level]->relateToOperator(name, op);
}
void relateToExtension(ESymbolLevel level, const char *name, const TString &ext)
{
table[level]->relateToExtension(name, ext);
}
void dump(TInfoSink &infoSink) const;
bool setDefaultPrecision(const TPublicType &type, TPrecision prec)
{
if (!SupportsPrecision(type.type))
return false;
if (type.isAggregate())
return false; // Not allowed to set for aggregate types
int indexOfLastElement = static_cast<int>(precisionStack.size()) - 1;
// Uses map operator [], overwrites the current value
(*precisionStack[indexOfLastElement])[type.type] = prec;
return true;
}
// Searches down the precisionStack for a precision qualifier
// for the specified TBasicType
TPrecision getDefaultPrecision(TBasicType type);
static int nextUniqueId()
{
return ++uniqueIdCounter;
}
private:
ESymbolLevel currentLevel() const
{
return static_cast<ESymbolLevel>(table.size() - 1);
}
std::vector<TSymbolTableLevel *> table;
typedef TMap<TBasicType, TPrecision> PrecisionStackLevel;
std::vector< PrecisionStackLevel *> precisionStack;
static int uniqueIdCounter;
};
#endif // _SYMBOL_TABLE_INCLUDED_