Add basic intra-stage linking validation for matching types and qualification of uniforms/ins/outs/globals, function body duplication, and mixing ES/non-ES shaders.

Still need to handle arrays and built-in redeclarations, and many more rules, but this puts the basics in place.


git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@23225 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich 2013-09-24 21:18:46 +00:00
parent f5ebfb5f27
commit 337dbc7d8c
37 changed files with 657 additions and 196 deletions

View file

@ -850,7 +850,7 @@ bool TIntermediate::postProcess(TIntermNode* root, EShLanguage language)
return true;
}
void TIntermediate::addSymbolLinkageNodes(TIntermNode* root, TIntermAggregate*& linkage, EShLanguage language, TSymbolTable& symbolTable)
void TIntermediate::addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage language, TSymbolTable& symbolTable)
{
// Add top-level nodes for declarations that must be checked cross
// compilation unit by a linker, yet might not have been referenced
@ -880,13 +880,9 @@ void TIntermediate::addSymbolLinkageNodes(TIntermNode* root, TIntermAggregate*&
addSymbolLinkageNode(linkage, symbolTable, "gl_InstanceID");
}
if (linkage) {
// Finish off linkage sequence
linkage->setOperator(EOpLinkerObjects);
// Add a child to the root node for the linker objects
growAggregate(root, linkage);
}
// Add a child to the root node for the linker objects
linkage->setOperator(EOpLinkerObjects);
treeRoot = growAggregate(treeRoot, linkage);
}
void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable& symbolTable, const TString& name)
@ -903,29 +899,178 @@ void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TVari
}
//
// Merge the information in 'unit' into 'this'
// Merge the information from 'unit' into 'this'
//
void TIntermediate::merge(TIntermediate& unit)
void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit)
{
numMains += unit.numMains;
if (profile != EEsProfile && unit.profile == EEsProfile ||
profile == EEsProfile && unit.profile != EEsProfile)
error(infoSink, "Cannot mix ES profile with non-ES profile shaders\n");
if (unit.treeRoot == 0)
return;
if (treeRoot == 0) {
version = unit.version;
treeRoot = unit.treeRoot;
return;
} else
version = std::max(version, unit.version);
// Get the top-level globals of each level
TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence();
TIntermSequence& unitGlobals = unit.treeRoot->getAsAggregate()->getSequence();
// Get the last members of the sequences, expected to be the linker-object lists
assert(globals.back()->getAsAggregate()->getOp() == EOpLinkerObjects);
assert(unitGlobals.back()->getAsAggregate()->getOp() == EOpLinkerObjects);
TIntermSequence& linkerObjects = globals.back()->getAsAggregate()->getSequence();
TIntermSequence& unitLinkerObjects = unitGlobals.back()->getAsAggregate()->getSequence();
mergeBodies(infoSink, globals, unitGlobals);
mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects);
}
//
// Merge the function bodies and global-level initalizers from unitGlobals into globals.
// Will error check duplication of function bodies for the same signature.
//
void TIntermediate::mergeBodies(TInfoSink& infoSink, TIntermSequence& globals, const TIntermSequence& unitGlobals)
{
// TODO: Performance: Processing in alphabetical order will be faster
// Error check the global objects, not including the linker objects
for (unsigned int child = 0; child < globals.size() - 1; ++child) {
for (unsigned int unitChild = 0; unitChild < unitGlobals.size() - 1; ++unitChild) {
TIntermAggregate* body = globals[child]->getAsAggregate();
TIntermAggregate* unitBody = unitGlobals[unitChild]->getAsAggregate();
if (body && unitBody && body->getOp() == EOpFunction && unitBody->getOp() == EOpFunction && body->getName() == unitBody->getName()) {
error(infoSink, "Multiple function bodies in multiple compilation units for the same signature in the same stage:");
infoSink.info << " " << globals[child]->getAsAggregate()->getName() << "\n";
}
}
}
// Merge the global objects, just in front of the linker objects
globals.insert(globals.end() - 1, unitGlobals.begin(), unitGlobals.end() - 1);
}
//
// Merge the linker objects from unitLinkerObjects into linkerObjects.
// Duplication is expected and filtered out, but contradictions are an error.
//
void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects)
{
// Error check and merge the linker objects (duplicates should not be merged)
unsigned int initialNumLinkerObjects = linkerObjects.size();
for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) {
bool merge = true;
for (unsigned int linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) {
TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode();
TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode();
assert(symbol && unitSymbol);
if (symbol->getName() == unitSymbol->getName()) {
// filter out copy
merge = false;
// Check for consistent types/qualification/etc.
linkErrorCheck(infoSink, *symbol, *unitSymbol, false);
}
}
if (merge)
linkerObjects.push_back(unitLinkerObjects[unitLinkObj]);
}
}
void TIntermediate::errorCheck(TInfoSink& infoSink)
{
if (numMains < 1)
error(infoSink, "Missing entry point: Each stage requires one \"void main()\" entry point");
if (numMains > 1)
error(infoSink, "Too many entry points: Each stage can have at most one \"void main()\" entry point.");
}
void TIntermediate::error(TInfoSink& infoSink, const char* message)
{
infoSink.info.prefix(EPrefixError);
infoSink.info << message << "\n";
infoSink.info << "Linking " << StageName[language] << " stage: " << message << "\n";
++numErrors;
}
//
// Compare two global objects from two compilation units and see if they match
// well enough. Rules can be different for intra- vs. cross-stage matching.
//
// This function only does one of intra- or cross-stage matching per call.
//
// TODO: Linker Functionality: this function is under active development
//
void TIntermediate::linkErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol, bool crossStage)
{
bool writeTypeComparison = false;
// Types have to match
if (symbol.getType() != unitSymbol.getType()) {
error(infoSink, "Types must match:");
writeTypeComparison = true;
}
// Qualifiers have to (almost) match
// Storage...
if (symbol.getQualifier().storage != unitSymbol.getQualifier().storage) {
error(infoSink, "Storage qualifiers must match:");
writeTypeComparison = true;
}
// Precision...
if (symbol.getQualifier().precision != unitSymbol.getQualifier().precision) {
error(infoSink, "Precision qualifiers must match:");
writeTypeComparison = true;
}
// Invariance...
if (! crossStage && symbol.getQualifier().invariant != unitSymbol.getQualifier().invariant) {
error(infoSink, "Presence of invariant qualifier must match:");
writeTypeComparison = true;
}
// Auxiliary and interpolation...
if (symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid ||
symbol.getQualifier().smooth != unitSymbol.getQualifier().smooth ||
symbol.getQualifier().flat != unitSymbol.getQualifier().flat ||
symbol.getQualifier().sample != unitSymbol.getQualifier().sample ||
symbol.getQualifier().patch != unitSymbol.getQualifier().patch ||
symbol.getQualifier().nopersp != unitSymbol.getQualifier().nopersp) {
error(infoSink, "Interpolation and auxiliary storage qualifiers must match:");
writeTypeComparison = true;
}
// Memory...
if (symbol.getQualifier().shared != unitSymbol.getQualifier().shared ||
symbol.getQualifier().coherent != unitSymbol.getQualifier().coherent ||
symbol.getQualifier().volatil != unitSymbol.getQualifier().volatil ||
symbol.getQualifier().restrict != unitSymbol.getQualifier().restrict ||
symbol.getQualifier().readonly != unitSymbol.getQualifier().readonly ||
symbol.getQualifier().writeonly != unitSymbol.getQualifier().writeonly) {
error(infoSink, "Memory qualifiers must match:");
writeTypeComparison = true;
}
// Layouts...
if (symbol.getQualifier().layoutMatrix != unitSymbol.getQualifier().layoutMatrix ||
symbol.getQualifier().layoutPacking != unitSymbol.getQualifier().layoutPacking ||
symbol.getQualifier().layoutSlotLocation != unitSymbol.getQualifier().layoutSlotLocation) {
error(infoSink, "Layout qualification must match:");
writeTypeComparison = true;
}
if (writeTypeComparison)
infoSink.info << " " << symbol.getName() << ": \"" << symbol.getType().getCompleteString() << "\" versus \"" <<
unitSymbol.getType().getCompleteString() << "\"\n";
}
//
// This deletes the tree.
//

View file

@ -51,12 +51,15 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, bool pb,
intermediate(interm), symbolTable(symt), infoSink(is), language(L),
version(v), profile(p), forwardCompatible(fc), messages(m),
contextPragma(true, false), loopNestingLevel(0), structNestingLevel(0),
linkage(0), tokensBeforeEOF(false),
tokensBeforeEOF(false),
parsingBuiltins(pb), numErrors(0), afterEOF(false)
{
currentLoc.line = 1;
currentLoc.string = 0;
// ensure we always have a linkage node, even if empty, to simplify tree topology algorithms
linkage = new TIntermAggregate;
// set all precision defaults to EpqNone, which is correct for all desktop types
// and for ES types that don't have defaults (thus getting an error on use)
for (int type = 0; type < EbtNumTypes; ++type)
@ -1795,7 +1798,7 @@ void TParseContext::nonInitConstCheck(TSourceLoc loc, TString& identifier, TPubl
//
// Make the qualifier make sense.
//
if (type.qualifier.storage == EvqConst) {
if (type.qualifier.storage == EvqConst) {
type.qualifier.storage = EvqTemporary;
error(loc, "variables with qualifier 'const' must be initialized", identifier.c_str(), "");
}
@ -1808,8 +1811,10 @@ void TParseContext::nonInitConstCheck(TSourceLoc loc, TString& identifier, TPubl
void TParseContext::nonInitCheck(TSourceLoc loc, TString& identifier, TPublicType& publicType)
{
TType type(publicType);
bool newDeclaration;
bool newDeclaration; // true if a new entry gets added to the symbol table
TVariable* variable = redeclare(loc, identifier, type, newDeclaration);
if (! variable) {
reservedErrorCheck(loc, identifier);
variable = new TVariable(&identifier, type);
@ -1823,19 +1828,19 @@ void TParseContext::nonInitCheck(TSourceLoc loc, TString& identifier, TPublicTyp
voidErrorCheck(loc, identifier, publicType);
// see if it's a linker-level object to track
if (type.getQualifier().isUniform() || type.getQualifier().isPipeInput() || type.getQualifier().isPipeOutput())
intermediate.addSymbolLinkageNode(linkage, *variable);
if (type.getQualifier().isUniform() || type.getQualifier().isPipeInput() || type.getQualifier().isPipeOutput() || type.getQualifier().storage == EvqGlobal)
intermediate.addSymbolLinkageNode(linkage, *variable);
}
}
//
// See if the identifier is a built-in symbol that can be redeclared,
// and if so, copy of the symbol table's read-only built-in to the current
// globol level, so it can be modified.
// global level, so it can be modified.
//
TVariable* TParseContext::redeclare(TSourceLoc loc, const TString& identifier, const TType& type, bool& newDeclaration)
{
newDeclaration = false;
newDeclaration = false; // true if a new entry gets added to the symbol table
if (profile == EEsProfile || identifier.substr(0, 3) != TString("gl_") || symbolTable.atBuiltInLevel())
return 0;

View file

@ -106,7 +106,7 @@ TPoolAllocator* PerProcessGPA = 0;
bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink,
TSymbolTable& symbolTable)
{
TIntermediate intermediate(version, profile);
TIntermediate intermediate(language, version, profile);
TParseContext parseContext(symbolTable, intermediate, true, version, profile, language, infoSink);
TPpContext ppContext(parseContext);
@ -452,7 +452,7 @@ bool CompileDeferred(
bool ret = parseContext.parseShaderStrings(ppContext, const_cast<char**>(shaderStrings), lengths, numStrings);
if (! ret)
success = false;
intermediate.addSymbolLinkageNodes(intermediate.getTreeRoot(), parseContext.linkage, parseContext.language, symbolTable);
intermediate.addSymbolLinkageNodes(parseContext.linkage, parseContext.language, symbolTable);
// Clean up the symbol table. The AST is self-sufficient now.
delete symbolTableMemory;
@ -595,7 +595,7 @@ int ShCompile(
compiler->infoSink.info.erase();
compiler->infoSink.debug.erase();
TIntermediate intermediate;
TIntermediate intermediate(compiler->getLanguage());
bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, optLevel, resources, defaultVersion, forwardCompatible, messages, intermediate);
//
@ -866,7 +866,7 @@ TShader::TShader(EShLanguage s)
{
infoSink = new TInfoSink;
compiler = new TDeferredCompiler(stage, *infoSink);
intermediate = new TIntermediate;
intermediate = new TIntermediate(s);
}
TShader::~TShader()
@ -941,7 +941,7 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
if (stages[stage].size() == 1)
merged = stages[stage].front()->intermediate;
else {
intermediate[stage] = new TIntermediate();
intermediate[stage] = new TIntermediate(stage);
merged = intermediate[stage];
}
@ -950,7 +950,7 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
if (stages[stage].size() > 1) {
std::list<TShader*>::const_iterator it;
for (it = stages[stage].begin(); it != stages[stage].end(); ++it)
merged->merge(*(*it)->intermediate);
merged->merge(*infoSink, *(*it)->intermediate);
if (messages & EShMsgAST)
merged->outputTree(*infoSink);

View file

@ -56,7 +56,7 @@ class TVariable;
//
class TIntermediate {
public:
explicit TIntermediate(int v = 0, EProfile p = ENoProfile) : treeRoot(0), profile(p), version(v), numMains(0), numErrors(0) { }
explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : language(l), treeRoot(0), profile(p), version(v), numMains(0), numErrors(0) { }
void setVersion(int v) { version = v; }
int getVersion() const { return version; }
@ -95,20 +95,24 @@ public:
TIntermBranch* addBranch(TOperator, TIntermTyped*, TSourceLoc);
TIntermTyped* addSwizzle(TVectorFields&, TSourceLoc);
bool postProcess(TIntermNode*, EShLanguage);
void addSymbolLinkageNodes(TIntermNode* root, TIntermAggregate*& linkage, EShLanguage, TSymbolTable&);
void addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage, TSymbolTable&);
void addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable&, const TString&);
void addSymbolLinkageNode(TIntermAggregate*& linkage, const TVariable&);
void merge(TIntermediate&);
void errorCheck(TInfoSink& infoSink);
void merge(TInfoSink&, TIntermediate&);
void errorCheck(TInfoSink&);
void outputTree(TInfoSink& infoSink);
void outputTree(TInfoSink&);
void removeTree();
protected:
void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals);
void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects);
void error(TInfoSink& infoSink, const char*);
void linkErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, bool crossStage);
protected:
EShLanguage language;
TIntermNode* treeRoot;
EProfile profile;
int version;