EOptionRelaxedErrors = 0x008,
EOptionGiveWarnings = 0x010,
EOptionsLinkProgram = 0x020,
+ EOptionMultiThreaded = 0x040,
};
//
resources.maxProgramTexelOffset = 7;
}
+// thread-safe list of shaders to asynchronously grab and compile
glslang::TWorklist Worklist;
+
+// array of unique places to leave the shader names and infologs for the asynchronous compiles
+glslang::TWorkItem **Work = 0;
+int NumWorkItems = 0;
+
int Options = 0;
bool Delay = false;
+const char* ExecutableName;
bool ProcessArguments(int argc, char* argv[])
{
+ ExecutableName = argv[0];
+ NumWorkItems = argc; // will include some empties where the '-' options were, but it doesn't matter, they'll be 0
+ Work = new glslang::TWorkItem*[NumWorkItems];
+ Work[0] = 0;
+
argc--;
argv++;
for (; argc >= 1; argc--, argv++) {
if (argv[0][0] == '-') {
+ Work[argc] = 0;
switch (argv[0][1]) {
case 'd':
Delay = true;
case 's':
Options |= EOptionSuppressInfolog;
break;
+ case 't':
+ #ifdef _WIN32
+ Options |= EOptionMultiThreaded;
+ #endif
+ break;
default:
return false;
}
- } else
- Worklist.add(std::string(argv[0]));
+ } else {
+ Work[argc] = new glslang::TWorkItem(std::string(argv[0]));
+ Worklist.add(Work[argc]);
+ }
}
if (Worklist.empty())
#endif
CompileShaders(void*)
{
- std::string shaderName;
- while (Worklist.remove(shaderName)) {
- ShHandle compiler = ShConstructCompiler(FindLanguage(shaderName), Options);
+ glslang::TWorkItem* workItem;
+ while (Worklist.remove(workItem)) {
+ ShHandle compiler = ShConstructCompiler(FindLanguage(workItem->name), Options);
if (compiler == 0)
return false;
TBuiltInResource resources;
GenerateResources(resources);
- CompileFile(shaderName.c_str(), compiler, Options, &resources);
+ CompileFile(workItem->name.c_str(), compiler, Options, &resources);
if (! (Options & EOptionSuppressInfolog))
- puts(ShGetInfoLog(compiler));
+ workItem->results = ShGetInfoLog(compiler);
ShDestruct(compiler);
}
//
glslang::TProgram program;
- std::string shaderName;
- while (Worklist.remove(shaderName)) {
- EShLanguage stage = FindLanguage(shaderName);
+ glslang::TWorkItem* workItem;
+ while (Worklist.remove(workItem)) {
+ EShLanguage stage = FindLanguage(workItem->name);
glslang::TShader* shader = new glslang::TShader(stage);
shaders.push_back(shader);
- char** shaderStrings = ReadFileData(shaderName.c_str());
+ char** shaderStrings = ReadFileData(workItem->name.c_str());
if (! shaderStrings) {
usage();
return;
program.addShader(shader);
if (! (Options & EOptionSuppressInfolog)) {
- puts(shaderName.c_str());
+ puts(workItem->name.c_str());
puts(shader->getInfoLog());
puts(shader->getInfoDebugLog());
}
// Init for front-end proper
ShInitialize();
- // Init for for standalone
+ // Init for standalone
glslang::InitGlobalLock();
if (! ProcessArguments(argc, argv)) {
//
// Two modes:
- // 1) linking all arguments together, single-threaded
- // 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety
+ // 1) linking all arguments together, single-threaded, new C++ interface
+ // 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety, using the old handle interface
//
-
- // TODO: finish threading, allow external control over number of threads
- const int NumThreads = 1;
- if (NumThreads > 1) {
- void* threads[NumThreads];
- for (int t = 0; t < NumThreads; ++t) {
- threads[t] = glslang::OS_CreateThread(&CompileShaders);
- if (! threads[t]) {
- printf("Failed to create thread\n");
- return EFailThreadCreate;
+ if (Options & EOptionsLinkProgram)
+ CompileAndLinkShaders();
+ else {
+ bool printShaderNames = Worklist.size() > 1;
+
+ if (Options & EOptionMultiThreaded) {
+ const int NumThreads = 16;
+ void* threads[NumThreads];
+ for (int t = 0; t < NumThreads; ++t) {
+ threads[t] = glslang::OS_CreateThread(&CompileShaders);
+ if (! threads[t]) {
+ printf("Failed to create thread\n");
+ return EFailThreadCreate;
+ }
}
- }
- glslang::OS_WaitForAllThreads(threads, NumThreads);
- } else {
- if (Options & EOptionsLinkProgram) {
- CompileAndLinkShaders();
+ glslang::OS_WaitForAllThreads(threads, NumThreads);
} else {
if (! CompileShaders(0))
compileFailed = true;
}
+
+ // Print out all the resulting infologs
+ for (int w = 0; w < NumWorkItems; ++w) {
+ if (Work[w]) {
+ if (printShaderNames)
+ puts(Work[w]->name.c_str());
+ puts(Work[w]->results.c_str());
+ delete Work[w];
+ }
+ }
}
if (Delay)
//
void usage()
{
- printf("Usage: standalone [ options ] filename\n"
+ printf("Usage: glslangValidator [ options ] filename\n"
"Where: filename is a name ending in\n"
" .vert for a vertex shader\n"
" .tesc for a tessellation control shader\n"
"-d: delay exit\n"
"-l: link validation of all input files\n"
"-m: memory leak mode\n"
+ "-r: relaxed semantic error-checking mode\n"
"-s: silent mode\n"
- "-r: relaxed semantic error-checking mode\n");
+ "-t: multi-threaded mode\n");
}
#ifndef _WIN32
namespace glslang {
+ class TWorkItem {
+ public:
+ TWorkItem() { }
+ explicit TWorkItem(const std::string& s) :
+ name(s) { }
+ std::string name;
+ std::string results;
+ };
+
class TWorklist {
public:
TWorklist() { }
virtual ~TWorklist() { }
- void add(const std::string& s)
+ void add(TWorkItem* item)
{
GetGlobalLock();
- worklist.push_back(s);
+ worklist.push_back(item);
ReleaseGlobalLock();
}
- bool remove(std::string& s)
+ bool remove(TWorkItem*& item)
{
GetGlobalLock();
if (worklist.empty())
return false;
- s = worklist.front();
+ item = worklist.front();
worklist.pop_front();
ReleaseGlobalLock();
return true;
}
+ int size()
+ {
+ return worklist.size();
+ }
+
bool empty()
{
return worklist.empty();
}
protected:
- std::list<std::string> worklist;
+ std::list<TWorkItem*> worklist;
};
} // end namespace glslang
runLinkTest mains1.frag mains2.frag noMain1.geom noMain2.geom
runLinkTest noMain.vert mains.frag
+
+#
+# multi-threaded test
+#
+
+echo Comparing single thread to multithread for all tests in current directory...
+$EXE -i *.vert *.geom *.frag *.tes* *.comp > singleThread.out
+$EXE -i *.vert *.geom *.frag *.tes* *.comp -t > multiThread.out
+diff singleThread.out multiThread.out
Current functionality level: ESSL 3.0
-Performance
-
-Testing
- - thread safety
-
Link Validation
- ensure no static references thrown away
Cross-stage linking
Intra-stage linking
- exactly one main
- type consistency check of uniforms, globals, ins, and outs, both variables and blocks
+ - value checking of global const initializers
+ - value checking of uniform initializers
- location/component/binding/index/offset match check
- location/component aliasing (except desktop vertex shader inputs)
- location layout range/overlap semantics
virtual bool isMatrix() const { return matrixCols ? true : false; }
virtual bool isArray() const { return arraySizes != 0; }
int getArraySize() const { return arraySizes->front(); }
- void setArraySizes(TArraySizes s) {
+ void setArraySizes(TArraySizes s)
+ {
// copy; we don't want distinct types sharing the same descriptor
if (! arraySizes)
arraySizes = NewPoolTArraySizes();
*arraySizes = *s;
}
+
void changeArraySize(int s) { arraySizes->front() = s; }
void setMaxArraySize (int s) { maxArraySize = s; }
int getMaxArraySize () const { return maxArraySize; }
TType* getArrayInformationType() { return arrayInformationType; }
virtual bool isVector() const { return vectorSize > 1; }
virtual bool isScalar() const { return vectorSize == 1; }
- const char* getBasicString() const {
+ const char* getBasicString() const
+ {
return TType::getBasicString(basicType);
}
- static const char* getBasicString(TBasicType t) {
+
+ static const char* getBasicString(TBasicType t)
+ {
switch (t) {
case EbtVoid: return "void";
case EbtFloat: return "float";
TArraySizes arraySizes = NewPoolTArraySizes();
arraySizes->push_back(resources.maxDrawBuffers);
fragData.setArraySizes(arraySizes);
- symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData"), fragData));
+ symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData"), fragData));
}
break;
- default:
+ default:
break;
}
}
{
TIntermTyped* node = 0;
- TAnonMember* anon = symbol ? symbol->getAsAnonMember() : 0;
+ const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : 0;
if (anon) {
// it was a member of an anonymous container, have to insert its dereference
- TVariable* variable = anon->getAnonContainer().getAsVariable();
+ const TVariable* variable = anon->getAnonContainer().getAsVariable();
TIntermTyped* container = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), variable->getType(), loc);
TConstUnion* unionArray = new TConstUnion[1];
unionArray->setUConst(anon->getMemberNumber());
}
return errorReturn;
- default:
+ default:
break;
}
error(loc, " l-value required", op, "", "");
case EbtVoid:
message = "can't modify void";
break;
- default:
+ default:
break;
}
}
case EOpConstructDMat4x4:
constructingMatrix = true;
break;
- default:
+ default:
break;
}
return false;
}
- TVariable* variable = symbol->getAsVariable();
+ const TVariable* variable = symbol->getAsVariable();
if (! variable) {
infoSink.info.message(EPrefixInternalError, "variable expected");
// Don't check for reserved word use until after we know it's not in the symbol table,
// because reserved arrays can be redeclared.
//
+ // However, reserved arrays cannot be modified in a shared symbol table, so add a new
+ // one at a non-shared level in the table.
+ //
- bool sameScope = false;
- TSymbol* symbol = symbolTable.find(identifier, 0, &sameScope);
- if (symbol == 0 || !sameScope) {
+ bool currentScope;
+ TSymbol* symbol = symbolTable.find(identifier, 0, ¤tScope);
+ if (symbol == 0 || ! currentScope) {
if (reservedErrorCheck(loc, identifier))
return;
-
- variable = new TVariable(&identifier, TType(type));
-
- if (! symbolTable.insert(*variable)) {
- delete variable;
- error(loc, "INTERNAL ERROR inserting new symbol", identifier.c_str(), "");
- return;
- }
+ variable = new TVariable(&identifier, TType(type));
+ symbolTable.insert(*variable);
} else {
variable = symbol->getAsVariable();
t = t->getArrayInformationType();
}
- variable->getType().setArraySizes(type.arraySizes);
+ variable->getWritableType().setArraySizes(type.arraySizes);
}
voidErrorCheck(loc, identifier, type);
return true;
}
+ // There are multiple copies of the array type tagging results of operations.
+ // Chain these together, so they can all reflect the final size.
type->setArrayInformationType(variable->getArrayInformationType());
variable->updateArrayInformationType(type);
}
}
- // we dont want to update the maxArraySize when this flag is not set, we just want to include this
- // node type in the chain of node types so that its updated when a higher maxArraySize comes in.
- if (!updateFlag)
+ // We don't want to update the maxArraySize when this flag is not set, we just want to include this
+ // node type in the chain of node types so that it's updated when a higher maxArraySize comes in.
+ if (! updateFlag)
return false;
size++;
- variable->getType().setMaxArraySize(size);
+ variable->getWritableType().setMaxArraySize(size);
type->setMaxArraySize(size);
TType* tt = type;
}
//
-// Initializers show up in several places in the grammar. Have one set of
-// code to handle them here.
+// Handle all types of initializers from the grammar.
//
bool TParseContext::executeInitializerError(TSourceLoc loc, TString& identifier, TPublicType& pType,
TIntermTyped* initializer, TIntermNode*& intermNode, TVariable* variable)
if (qualifier == EvqConst) {
if (qualifier != initializer->getType().getQualifier().storage) {
error(loc, " assigning non-constant to", "=", "'%s'", variable->getType().getCompleteString().c_str());
- variable->getType().getQualifier().storage = EvqTemporary;
+ variable->getWritableType().getQualifier().storage = EvqTemporary;
return true;
}
if (type != initializer->getType()) {
error(loc, " non-matching types for const initializer ",
variable->getType().getStorageQualifierString(), "");
- variable->getType().getQualifier().storage = EvqTemporary;
+ variable->getWritableType().getQualifier().storage = EvqTemporary;
return true;
}
if (initializer->getAsConstantUnion()) {
}
} else if (initializer->getAsSymbolNode()) {
TSymbol* symbol = symbolTable.find(initializer->getAsSymbolNode()->getName());
- if (TVariable* tVar = symbol->getAsVariable()) {
+ if (const TVariable* tVar = symbol->getAsVariable()) {
TConstUnion* constArray = tVar->getConstUnionPointer();
variable->shareConstPointer(constArray);
} else {
}
} else {
error(loc, " cannot assign to", "=", "'%s'", variable->getType().getCompleteString().c_str());
- variable->getType().getQualifier().storage = EvqTemporary;
+ variable->getWritableType().getQualifier().storage = EvqTemporary;
return true;
}
}
// For an identifier that is already declared, add more qualification to it.
void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier, const TString& identifier)
{
- TSymbol* existing = symbolTable.find(identifier);
+ bool sharedLevel;
+ TSymbol* existing = symbolTable.find(identifier, 0, 0, &sharedLevel);
TVariable* variable = existing ? existing->getAsVariable() : 0;
if (! variable) {
error(loc, "identifier not previously declared", identifier.c_str(), "");
return;
}
+ //
+ // Don't change a shared variable; rather add a new one at the current scope.
+ //
+ if (sharedLevel) {
+ variable = new TVariable(&variable->getName(), variable->getType());
+ symbolTable.insert(*variable);
+ }
+
if (qualifier.invariant)
- variable->getType().getQualifier().invariant = true;
+ variable->getWritableType().getQualifier().invariant = true;
}
void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier, TIdentifierList& identifiers)
return identifierOrReserved(reserved);
}
- default:
+ default:
parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc);
return 0;
}
parserToken->sType.lex.symbol = parseContext.symbolTable.find(*parserToken->sType.lex.string);
if (afterType == false && parserToken->sType.lex.symbol) {
- if (TVariable* variable = parserToken->sType.lex.symbol->getAsVariable()) {
+ if (const TVariable* variable = parserToken->sType.lex.symbol->getAsVariable()) {
if (variable->isUserType()) {
afterType = true;
TPoolAllocator* PerProcessGPA = 0;
+//
+// Parse and add to the given symbol table the content of the given shader string.
+//
bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink,
TSymbolTable& symbolTable)
{
parseContext.setPpContext(&ppContext);
//
- // Parse the built-ins. This should only happen once per
- // language symbol table when no 'resources' are passed in.
- //
// Push the symbol table to give it an initial scope. This
// push should not have a corresponding pop, so that built-ins
// are preserved, and the test for an empty table fails.
return true;
}
+int CommonIndex(EProfile profile, EShLanguage language)
+{
+ return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral;
+}
+
//
-// To call for per-stage initialization, with the common table already complete.
+// To initialize per-stage shared tables, with the common table already complete.
//
-void InitializeSymbolTable(TBuiltIns& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables)
+void InitializeStageSymbolTable(TBuiltIns& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables)
{
- int commonIndex = EPcGeneral;
- if (profile == EEsProfile && language == EShLangFragment)
- commonIndex = EPcFragment;
-
- (*symbolTables[language]).adoptLevels(*commonTable[commonIndex]);
+ (*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]);
InitializeSymbolTable(builtIns.getStageString(language), version, profile, language, infoSink, *symbolTables[language]);
IdentifyBuiltIns(version, profile, language, *symbolTables[language]);
if (profile == EEsProfile)
(*symbolTables[language]).setNoBuiltInRedeclarations();
}
-bool GenerateBuiltInSymbolTable(TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables, int version, EProfile profile)
+//
+// Initialize the full set of shareable symbol tables;
+// The common (cross-stage) and those sharable per-stage.
+//
+bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables, int version, EProfile profile)
{
TBuiltIns builtIns;
-
builtIns.initialize(version, profile);
- // do the common table
+ // do the common tables
InitializeSymbolTable(builtIns.getCommonString(), version, profile, EShLangVertex, infoSink, *commonTable[EPcGeneral]);
if (profile == EEsProfile)
InitializeSymbolTable(builtIns.getCommonString(), version, profile, EShLangFragment, infoSink, *commonTable[EPcFragment]);
// do the per-stage tables
- InitializeSymbolTable(builtIns, version, profile, EShLangVertex, infoSink, commonTable, symbolTables);
- InitializeSymbolTable(builtIns, version, profile, EShLangFragment, infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(builtIns, version, profile, EShLangVertex, infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(builtIns, version, profile, EShLangFragment, infoSink, commonTable, symbolTables);
if (profile != EEsProfile && version >= 400) {
- InitializeSymbolTable(builtIns, version, profile, EShLangTessControl, infoSink, commonTable, symbolTables);
- InitializeSymbolTable(builtIns, version, profile, EShLangTessEvaluation, infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(builtIns, version, profile, EShLangTessControl, infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(builtIns, version, profile, EShLangTessEvaluation, infoSink, commonTable, symbolTables);
}
if (profile != EEsProfile && version >= 150)
- InitializeSymbolTable(builtIns, version, profile, EShLangGeometry, infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(builtIns, version, profile, EShLangGeometry, infoSink, commonTable, symbolTables);
if (profile != EEsProfile && version >= 430)
- InitializeSymbolTable(builtIns, version, profile, EShLangCompute, infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(builtIns, version, profile, EShLangCompute, infoSink, commonTable, symbolTables);
return true;
}
TPoolAllocator* builtInPoolAllocator = new TPoolAllocator();
SetThreadPoolAllocator(*builtInPoolAllocator);
- // Dynamically allocate the symbol tables so we can control when they are deallocated WRT the pool.
+ // Dynamically allocate the local symbol tables so we can control when they are deallocated WRT when the pool is popped.
TSymbolTable* commonTable[EPcCount];
TSymbolTable* stageTables[EShLangCount];
for (int precClass = 0; precClass < EPcCount; ++precClass)
stageTables[stage] = new TSymbolTable;
// Generate the local symbol tables using the new pool
- GenerateBuiltInSymbolTable(infoSink, commonTable, stageTables, version, profile);
+ InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile);
// Switch to the process-global pool
SetThreadPoolAllocator(*PerProcessGPA);
if (! commonTable[precClass]->isEmpty()) {
CommonSymbolTable[versionIndex][profile][precClass] = new TSymbolTable;
CommonSymbolTable[versionIndex][profile][precClass]->copyTable(*commonTable[precClass]);
+ CommonSymbolTable[versionIndex][profile][precClass]->readOnly();
}
}
for (int stage = 0; stage < EShLangCount; ++stage) {
if (! stageTables[stage]->isEmpty()) {
SharedSymbolTables[versionIndex][profile][stage] = new TSymbolTable;
+ SharedSymbolTables[versionIndex][profile][stage]->adoptLevels(*CommonSymbolTable[versionIndex][profile][CommonIndex(profile, (EShLanguage)stage)]);
SharedSymbolTables[versionIndex][profile][stage]->copyTable(*stageTables[stage]);
+ SharedSymbolTables[versionIndex][profile][stage]->readOnly();
}
}
}
}
+//
+// Make all symbols in this table level read only.
+//
+void TSymbolTableLevel::readOnly()
+{
+ for (tLevel::iterator it = level.begin(); it != level.end(); ++it)
+ (*it).second->readOnly();
+}
+
TSymbol::TSymbol(const TSymbol& copyOf)
{
name = NewPoolTString(copyOf.name->c_str());
void TSymbolTable::copyTable(const TSymbolTable& copyOf)
{
+ assert(adoptedLevels == copyOf.adoptedLevels);
+
TStructureMap remapper;
uniqueId = copyOf.uniqueId;
noBuiltInRedeclarations = copyOf.noBuiltInRedeclarations;
- for (unsigned int i = 0; i < copyOf.table.size(); ++i)
+ for (unsigned int i = copyOf.adoptedLevels; i < copyOf.table.size(); ++i)
table.push_back(copyOf.table[i]->clone(remapper));
}
class TSymbol {
public:
POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
- explicit TSymbol(const TString *n) : name(n) { }
+ explicit TSymbol(const TString *n) : name(n), writable(true) { }
virtual TSymbol* clone(TStructureMap& remapper) = 0;
virtual ~TSymbol() { }
void changeName(const char* buf) { name = new TString(buf); }
virtual const TString& getMangledName() const { return getName(); }
virtual TFunction* getAsFunction() { return 0; }
+ virtual const TFunction* getAsFunction() const { return 0; }
virtual TVariable* getAsVariable() { return 0; }
- virtual TAnonMember* getAsAnonMember() { return 0; }
+ virtual const TVariable* getAsVariable() const { return 0; }
+ virtual const TAnonMember* getAsAnonMember() const { return 0; }
void setUniqueId(int id) { uniqueId = id; }
int getUniqueId() const { return uniqueId; }
virtual void dump(TInfoSink &infoSink) const = 0;
+ void readOnly() { writable = false; }
+
protected:
explicit TSymbol(const TSymbol&);
TSymbol& operator=(const TSymbol&);
const TString *name;
unsigned int uniqueId; // For cross-scope comparing during code generation
+
+ //
+ // N.B.: Non-const functions that will be generally used should assert an this,
+ // to avoid overwriting shared symbol-table information.
+ //
+ bool writable;
};
//
virtual ~TVariable() { }
virtual TVariable* getAsVariable() { return this; }
- TType& getType() { return type; }
+ virtual const TVariable* getAsVariable() const { return this; }
+ TType& getWritableType() { assert(writable); return type; }
const TType& getType() const { return type; }
bool isUserType() const { return userType; }
- void setStorageQualifier(TStorageQualifier qualifier) { type.getQualifier().storage = qualifier; }
- void updateArrayInformationType(TType *t) { arrayInformationType = t; }
- TType* getArrayInformationType() { return arrayInformationType; }
+ void updateArrayInformationType(TType *t) { assert(writable); arrayInformationType = t; }
+ TType* getArrayInformationType() { assert(writable); return arrayInformationType; }
virtual void dump(TInfoSink &infoSink) const;
virtual ~TFunction();
virtual TFunction* getAsFunction() { return this; }
+ virtual const TFunction* getAsFunction() const { return this; }
void addParameter(TParameter& p)
{
+ assert(writable);
parameters.push_back(p);
p.type->appendMangledName(mangledName);
}
const TString& getMangledName() const { return mangledName; }
const TType& getReturnType() const { return returnType; }
- void relateToOperator(TOperator o) { op = o; }
+ void relateToOperator(TOperator o) { assert(writable); op = o; }
TOperator getBuiltInOp() const { return op; }
- void setDefined() { defined = true; }
- bool isDefined() { return defined; }
+ void setDefined() { assert(writable); defined = true; }
+ bool isDefined() const { return defined; }
int getParamCount() const { return static_cast<int>(parameters.size()); }
- TParameter& operator [](int i) { return parameters[i]; }
+ TParameter& operator [](int i) { assert(writable); return parameters[i]; }
const TParameter& operator [](int i) const { return parameters[i]; }
virtual void dump(TInfoSink &infoSink) const;
virtual TAnonMember* clone(TStructureMap& remapper);
virtual ~TAnonMember() { }
- TAnonMember* getAsAnonMember() { return this; }
+ const TAnonMember* getAsAnonMember() const { return this; }
TSymbol& getAnonContainer() const { return anonContainer; }
unsigned int getMemberNumber() const { return memberNumber; }
virtual void dump(TInfoSink &infoSink) const;
symbol.changeName(buf);
bool isOkay = true;
- TTypeList& types = *symbol.getAsVariable()->getType().getStruct();
+ const TTypeList& types = *symbol.getAsVariable()->getType().getStruct();
for (unsigned int m = 0; m < types.size(); ++m) {
TAnonMember* member = new TAnonMember(&types[m].type->getFieldName(), m, symbol);
result = level.insert(tLevelPair(member->getMangledName(), member));
void relateToOperator(const char* name, TOperator op);
void dump(TInfoSink &infoSink) const;
TSymbolTableLevel* clone(TStructureMap& remapper);
+ void readOnly();
protected:
explicit TSymbolTableLevel(TSymbolTableLevel&);
// convention for levels:
// 0: common built-ins shared across all stages, all compiles, only one copy for all symbol tables
// 1: per-stage built-ins, shared across all compiles, but a different copy per stage
- // 2: built-ins specific to a compile, like resources that are context-dependent
+ // 2: built-ins specific to a compile, like resources that are context-dependent, or redeclared built-ins
// 3: user-shader globals
//
protected:
- static const int globalLevel = 3;
+ bool isSharedLevel(int level) { return level <= 1; } // exclude all per-compile levels
+ bool isBuiltInLevel(int level) { return level <= 2; } // exclude user globals
+ bool isGlobalLevel(int level) { return level <= 3; } // include user globals
public:
bool isEmpty() { return table.size() == 0; }
- bool atBuiltInLevel() { return table.size() <= globalLevel; } // exclude user globals
- bool atGlobalLevel() { return table.size() <= globalLevel + 1; } // include user globals
+ bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); }
+ bool atGlobalLevel() { return isGlobalLevel(currentLevel()); }
void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; }
return table[currentLevel()]->insert(symbol);
}
- TSymbol* find(const TString& name, bool* builtIn = 0, bool *sameScope = 0)
+ TSymbol* find(const TString& name, bool* builtIn = 0, bool *currentScope = 0, bool *sharedLevel = 0)
{
int level = currentLevel();
TSymbol* symbol;
} while (symbol == 0 && level >= 0);
level++;
if (builtIn)
- *builtIn = level < 2;
- if (sameScope)
- *sameScope = level == currentLevel();
+ *builtIn = isBuiltInLevel(level);
+ if (currentScope)
+ *currentScope = level == currentLevel();
+ if (sharedLevel)
+ *sharedLevel = isSharedLevel(level);
return symbol;
}
void setPreviousDefaultPrecisions(TPrecisionQualifier *p) { table[currentLevel()]->setPreviousDefaultPrecisions(p); }
+ void readOnly()
+ {
+ for (unsigned int level = 0; level < table.size(); ++level)
+ table[level]->readOnly();
+ }
+
protected:
TSymbolTable(TSymbolTable&);
TSymbolTable& operator=(TSymbolTableLevel&);
TSymbol* symbol = parseContext.symbolTable.find($1->getMangledName(), &builtIn);\r
if (symbol && symbol->getAsFunction() && builtIn)\r
parseContext.requireProfile($2.loc, static_cast<EProfileMask>(~EEsProfileMask), "redeclaration of built-in function");\r
- TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;\r
+ const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;\r
if (prevDec) {\r
if (prevDec->getReturnType() != $1->getReturnType()) {\r
parseContext.error($2.loc, "overloaded functions must have the same return type", $1->getReturnType().getCompleteTypeString().c_str(), "");\r
// This is for user defined type names. The lexical phase looked up the\r
// type.\r
//\r
- if (TVariable* variable = ($1.symbol)->getAsVariable()) {\r
+ if (const TVariable* variable = ($1.symbol)->getAsVariable()) {\r
const TType& structure = variable->getType();\r
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());\r
$$.basicType = EbtStruct;\r
friend class TProgram;
private:
- void operator=(TShader&);
+ TShader& operator=(TShader&);
};
class TProgram {
TInfoSink* infoSink;
private:
- void operator=(TProgram&);
+ TProgram& operator=(TProgram&);
};
} // end namespace glslang