Ensure the shared symbol table levels are read-only to make multi-threading safe. Also removed inadvertent extra copies of the symbol table shared across all stages.

git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@22939 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich 2013-09-06 19:52:57 +00:00
parent 69f4b517c2
commit 38f3b890de
14 changed files with 230 additions and 125 deletions

View file

@ -1657,11 +1657,11 @@ void IdentifyBuiltIns(int version, EProfile profile, EShLanguage language, TSymb
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;
}
}

View file

@ -456,10 +456,10 @@ TIntermTyped* TParseContext::handleVariable(TSourceLoc loc, TSymbol* symbol, TSt
{
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());
@ -1084,7 +1084,7 @@ bool TParseContext::lValueErrorCheck(TSourceLoc loc, const char* op, TIntermType
}
return errorReturn;
default:
default:
break;
}
error(loc, " l-value required", op, "", "");
@ -1120,7 +1120,7 @@ bool TParseContext::lValueErrorCheck(TSourceLoc loc, const char* op, TIntermType
case EbtVoid:
message = "can't modify void";
break;
default:
default:
break;
}
}
@ -1237,7 +1237,7 @@ bool TParseContext::constructorError(TSourceLoc loc, TIntermNode* node, TFunctio
case EOpConstructDMat4x4:
constructingMatrix = true;
break;
default:
default:
break;
}
@ -1626,7 +1626,7 @@ bool TParseContext::insertBuiltInArrayAtGlobalLevel()
return false;
}
TVariable* variable = symbol->getAsVariable();
const TVariable* variable = symbol->getAsVariable();
if (! variable) {
infoSink.info.message(EPrefixInternalError, "variable expected");
@ -1730,21 +1730,18 @@ void TParseContext::arrayCheck(TSourceLoc loc, TString& identifier, const TPubli
// 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, &currentScope);
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;
}
symbolTable.insert(*variable);
} else {
variable = symbol->getAsVariable();
@ -1777,7 +1774,7 @@ void TParseContext::arrayCheck(TSourceLoc loc, TString& identifier, const TPubli
t = t->getArrayInformationType();
}
variable->getType().setArraySizes(type.arraySizes);
variable->getWritableType().setArraySizes(type.arraySizes);
}
voidErrorCheck(loc, identifier, type);
@ -1797,6 +1794,8 @@ bool TParseContext::arraySetMaxSize(TIntermSymbol *node, TType* type, int size,
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);
@ -1816,13 +1815,13 @@ bool TParseContext::arraySetMaxSize(TIntermSymbol *node, TType* type, int size,
}
}
// 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;
@ -1997,8 +1996,7 @@ const TFunction* TParseContext::findFunction(TSourceLoc loc, TFunction* call, bo
}
//
// 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)
@ -2044,13 +2042,13 @@ bool TParseContext::executeInitializerError(TSourceLoc loc, TString& identifier,
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()) {
@ -2063,7 +2061,7 @@ bool TParseContext::executeInitializerError(TSourceLoc loc, TString& identifier,
}
} 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 {
@ -2072,7 +2070,7 @@ bool TParseContext::executeInitializerError(TSourceLoc loc, TString& identifier,
}
} else {
error(loc, " cannot assign to", "=", "'%s'", variable->getType().getCompleteString().c_str());
variable->getType().getQualifier().storage = EvqTemporary;
variable->getWritableType().getQualifier().storage = EvqTemporary;
return true;
}
}
@ -2372,7 +2370,8 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString*
// 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(), "");
@ -2390,8 +2389,16 @@ void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier,
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)

View file

@ -851,7 +851,7 @@ int TScanContext::tokenizeIdentifier()
return identifierOrReserved(reserved);
}
default:
default:
parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc);
return 0;
}
@ -868,7 +868,7 @@ int TScanContext::identifierOrType()
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;

View file

@ -100,6 +100,9 @@ TSymbolTable* SharedSymbolTables[VersionCount][EProfileCount][EShLangCount] = {}
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)
{
@ -111,9 +114,6 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil
parseContext.setScanContext(&scanContext);
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
@ -137,44 +137,48 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil
return true;
}
//
// To call for per-stage initialization, with the common table already complete.
//
void InitializeSymbolTable(TBuiltIns& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables)
int CommonIndex(EProfile profile, EShLanguage language)
{
int commonIndex = EPcGeneral;
if (profile == EEsProfile && language == EShLangFragment)
commonIndex = EPcFragment;
return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral;
}
(*symbolTables[language]).adoptLevels(*commonTable[commonIndex]);
//
// To initialize per-stage shared tables, with the common table already complete.
//
void InitializeStageSymbolTable(TBuiltIns& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables)
{
(*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;
}
@ -222,7 +226,7 @@ void SetupBuiltinSymbolTable(int version, EProfile profile)
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)
@ -231,7 +235,7 @@ void SetupBuiltinSymbolTable(int version, EProfile profile)
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);
@ -241,12 +245,15 @@ void SetupBuiltinSymbolTable(int version, EProfile profile)
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();
}
}

View file

@ -207,6 +207,15 @@ void TSymbolTableLevel::relateToOperator(const char* name, TOperator op)
}
}
//
// 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());
@ -279,10 +288,12 @@ TSymbolTableLevel* TSymbolTableLevel::clone(TStructureMap& remapper)
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));
}

View file

@ -80,7 +80,7 @@ class TAnonMember;
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() { }
@ -88,18 +88,28 @@ public:
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;
};
//
@ -119,12 +129,12 @@ public:
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;
@ -193,22 +203,24 @@ public:
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;
@ -232,7 +244,7 @@ public:
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;
@ -266,7 +278,7 @@ public:
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));
@ -351,6 +363,7 @@ public:
void relateToOperator(const char* name, TOperator op);
void dump(TInfoSink &infoSink) const;
TSymbolTableLevel* clone(TStructureMap& remapper);
void readOnly();
protected:
explicit TSymbolTableLevel(TSymbolTableLevel&);
@ -397,15 +410,17 @@ public:
// 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; }
@ -440,7 +455,7 @@ public:
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;
@ -450,9 +465,11 @@ public:
} 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;
}
@ -469,6 +486,12 @@ public:
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&);

View file

@ -807,7 +807,7 @@ function_prototype
TSymbol* symbol = parseContext.symbolTable.find($1->getMangledName(), &builtIn);
if (symbol && symbol->getAsFunction() && builtIn)
parseContext.requireProfile($2.loc, static_cast<EProfileMask>(~EEsProfileMask), "redeclaration of built-in function");
TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;
const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;
if (prevDec) {
if (prevDec->getReturnType() != $1->getReturnType()) {
parseContext.error($2.loc, "overloaded functions must have the same return type", $1->getReturnType().getCompleteTypeString().c_str(), "");
@ -2041,7 +2041,7 @@ type_specifier_nonarray
// This is for user defined type names. The lexical phase looked up the
// type.
//
if (TVariable* variable = ($1.symbol)->getAsVariable()) {
if (const TVariable* variable = ($1.symbol)->getAsVariable()) {
const TType& structure = variable->getType();
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtStruct;