Add ES 3.0 fragment output link-time test for outputs having or not having location qualifiers.

Also split linker validation into its own file, removed dead "QualifierAlive" files, printed errors for parsing problems with built-in symbols,  updated the Windows binary, and added some tests.


git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@23490 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich 2013-10-15 21:46:20 +00:00
parent 27b72e42c3
commit fb57e7cc5e
23 changed files with 543 additions and 434 deletions

View file

@ -39,7 +39,6 @@
//
#include "localintermediate.h"
#include "QualifierAlive.h"
#include "RemoveTree.h"
#include "SymbolTable.h"
@ -928,275 +927,6 @@ void TIntermediate::addToCallGraph(TInfoSink& infoSink, const TString& caller, c
callGraph.push_front(TCall(caller, callee));
}
//
// Merge the information from 'unit' into 'this'
//
void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit)
{
numMains += unit.numMains;
numErrors += unit.numErrors;
callGraph.insert(callGraph.end(), unit.callGraph.begin(), unit.callGraph.end());
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)
std::size_t initialNumLinkerObjects = linkerObjects.size();
for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) {
bool merge = true;
for (std::size_t 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;
// but if one has an initializer and the other does not, update
// the initializer
if (symbol->getConstArray().empty() && ! unitSymbol->getConstArray().empty())
symbol->setConstArray(unitSymbol->getConstArray());
// Check for consistent types/qualification/initializers etc.
linkErrorCheck(infoSink, *symbol, *unitSymbol, false);
}
}
if (merge)
linkerObjects.push_back(unitLinkerObjects[unitLinkObj]);
}
}
//
// Do final link-time error checking of a complete (merged) intermediate representation.
// (Most error checking was done during merging).
//
void TIntermediate::errorCheck(TInfoSink& infoSink)
{
if (numMains < 1)
error(infoSink, "Missing entry point: Each stage requires one \"void main()\" entry point");
checkCallGraphCycles(infoSink);
}
//
// See if the call graph contains any static recursion, which is disallowed
// by the specification.
//
void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink)
{
// Reset everything, once.
for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
call->visited = false;
call->subGraph = false;
call->errorGiven = false;
}
//
// Loop, looking for a new connected subgraph. One subgraph is handled per loop iteration.
//
TCall* newRoot;
do {
// See if we have unvisited parts of the graph.
newRoot = 0;
for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
if (! call->visited) {
newRoot = &(*call);
break;
}
}
// If not, we are done.
if (! newRoot)
break;
// Otherwise, we found a new subgraph, process it:
// See what all can be reached by this new root, and if any of
// that is recursive. This is done by marking processed calls as active,
// and if a new call is found that is already active, we looped,
// thereby detecting recursion.
std::list<TCall*> stack;
stack.push_back(newRoot);
newRoot->subGraph = true;
while (! stack.empty()) {
// get a caller
TCall* call = stack.back();
stack.pop_back();
// Add to the stack all the callees of the last subgraph node popped from the stack.
// This algorithm always terminates, because only subGraph == false causes a push
// and all pushes change subGraph to true, and nothing changes subGraph to false.
for (TGraph::iterator child = callGraph.begin(); child != callGraph.end(); ++child) {
if (call->callee == child->caller) {
if (child->subGraph) {
if (! child->errorGiven) {
error(infoSink, "Recursion detected:");
infoSink.info << " " << call->callee << " calling " << child->callee << "\n";
child->errorGiven = true;
}
} else {
child->subGraph = true;
stack.push_back(&(*child));
}
}
}
} // end while, meaning nothing left to process in this subtree
// Mark all the subGraph nodes as visited, closing out this subgraph.
for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
if (call->subGraph)
call->visited = true;
}
} while (newRoot); // redundant loop check; should always exit via the 'break' above
}
void TIntermediate::error(TInfoSink& infoSink, const char* message)
{
infoSink.info.prefix(EPrefixError);
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;
}
// Initializers have to match, if both are present, and if we don't already know the types don't match
if (! writeTypeComparison) {
if (! symbol.getConstArray().empty() && ! unitSymbol.getConstArray().empty()) {
if (symbol.getConstArray() != unitSymbol.getConstArray()) {
error(infoSink, "Initializers must match:");
infoSink.info << " " << symbol.getName() << "\n";
}
}
}
if (writeTypeComparison)
infoSink.info << " " << symbol.getName() << ": \"" << symbol.getType().getCompleteString() << "\" versus \"" <<
unitSymbol.getType().getCompleteString() << "\"\n";
}
//
// This deletes the tree.
//

View file

@ -9,12 +9,12 @@ LIBOSDEPENDENT=./../OSDependent/Linux/libOssource.a
LIBINITIALISATION=./../../OGLCompilersDLL/libInitializeDll.a
LIBCODEGEN=./../GenericCodeGen/libCodeGen.a
OBJECTS= Initialize.o IntermTraverse.o \
Intermediate.o ParseHelper.o PoolAlloc.o QualifierAlive.o \
Intermediate.o ParseHelper.o PoolAlloc.o \
RemoveTree.o ShaderLang.o intermOut.o parseConst.o SymbolTable.o \
InfoSink.o Versions.o Constant.o Scan.o limits.o
InfoSink.o Versions.o Constant.o Scan.o limits.o linkValidate.o
SRCS= gen_glslang_tab.cpp Initialize.cpp IntermTraverse.cpp \
Intermediate.cpp ParseHelper.cpp PoolAlloc.cp QualifierAlive.cpp \
Intermediate.cpp ParseHelper.cpp PoolAlloc.cp \
RemoveTree.cpp ShaderLang.cpp SymbolTable.cpp intermOut.cpp \
parseConst.cpp InfoSink.cpp Versions.cpp Constant.cpp Scan.cpp
CPPFLAGS=$(DEFINE) $(INCLUDE) -fPIC
@ -98,7 +98,7 @@ Intermediate.o: localintermediate.h ../Include/intermediate.h
Intermediate.o: ../Public/ShaderLang.h SymbolTable.h ../Include/Common.h
Intermediate.o: ../Include/intermediate.h ../Include/Types.h
Intermediate.o: ../Include/BaseTypes.h ../Include/ConstantUnion.h
Intermediate.o: ../Include/InfoSink.h QualifierAlive.h RemoveTree.h
Intermediate.o: ../Include/InfoSink.h RemoveTree.h
ParseHelper.o: ParseHelper.h ../Include/ShHandle.h
ParseHelper.o: ../Public/ShaderLang.h ../Include/InfoSink.h
ParseHelper.o: ../Include/Common.h ../Include/PoolAlloc.h SymbolTable.h
@ -109,7 +109,6 @@ ParseHelper.o: localintermediate.h ../Include/intermediate.h
ParseHelper.o: ../Public/ShaderLang.h
ParseHelper.o: ../OSDependent/Linux/osinclude.h
ParseHelper.o: ../Include/InitializeGlobals.h ../Include/PoolAlloc.h
QualifierAlive.o: ../Include/intermediate.h
Scan.o: Scan.h
Scan.o: ParseHelper.h SymbolTable.h
Scan.o: glslang_tab.cpp.h
@ -147,3 +146,4 @@ InfoSink.o: ../Include/InfoSink.h
Versions.o: ParseHelper.h Versions.h ../Include/ShHandle.h SymbolTable.h localintermediate.h
Constant.o: localintermediate.h ../Include/intermediate.h ../Public/ShaderLang.h SymbolTable.h Versions.h
limits.o: ParseHelper.h
linkValidate.o: localintermediate.h

View file

@ -1,99 +0,0 @@
//
//Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//POSSIBILITY OF SUCH DAMAGE.
//
#ifdef USE_QUALIFIER_ALIVE
#include "../Include/intermediate.h"
namespace glslang {
class TAliveTraverser : public TIntermTraverser {
public:
TAliveTraverser(TStorageQualifier q) : TIntermTraverser(), found(false), qualifier(q)
{
visitSymbol = AliveSymbol;
visitSelection = AliveSelection;
rightToLeft = true;
}
bool wasFound() { return found; }
protected:
bool found;
TStorageQualifier qualifier;
friend void AliveSymbol(TIntermSymbol*, TIntermTraverser*);
friend bool AliveSelection(bool, TIntermSelection*, TIntermTraverser*);
};
//
// Report whether or not a variable of the given qualifier type
// is guaranteed written. Not always possible to determine if
// it is written conditionally.
//
// It does not do this well yet, this is just a place holder
// that simply determines if it was reference at all, anywhere.
//
bool QualifierWritten(TIntermNode* node, TStorageQualifier qualifier)
{
TAliveTraverser it(qualifier);
if (node)
node->traverse(&it);
return it.wasFound();
}
void AliveSymbol(TIntermSymbol* node, TIntermTraverser* it)
{
TAliveTraverser* lit = static_cast<TAliveTraverser*>(it);
//
// If it's what we're looking for, record it.
//
if (node->getQualifier().storage == lit->qualifier)
lit->found = true;
}
bool AliveSelection(bool preVisit, TIntermSelection* node, TIntermTraverser* it)
{
TAliveTraverser* lit = static_cast<TAliveTraverser*>(it);
if (lit->wasFound())
return false;
return true;
}
} // end namespace glslang
#endif

View file

@ -1,39 +0,0 @@
//
//Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//POSSIBILITY OF SUCH DAMAGE.
//
namespace glslang {
bool QualifierWritten(TIntermNode* root, TStorageQualifier);
} // end namespace glslang

View file

@ -145,7 +145,7 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil
if (! parseContext.parseShaderStrings(ppContext, const_cast<char**>(builtInShaders), builtInLengths, 1) != 0) {
infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
printf("Unable to parse built-ins\n");
printf("Unable to parse built-ins\n%s\n", infoSink.info.c_str());
return false;
}

View file

@ -0,0 +1,370 @@
//
//Copyright (C) 2013 LunarG, Inc.
//
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//POSSIBILITY OF SUCH DAMAGE.
//
//
// Do link-time merging and validation of intermediate representations.
//
// Basic model is that during compilation, each compilation unit (shader) is
// compiled into one TIntermediate instance. Then, at link time, multiple
// units for the same stage can be merged together, which can generate errors.
// Then, after all merging, a single instance of TIntermediate represents
// the whole stage. A final error check can be done on the resulting stage,
// even if no merging was done (i.e., the stage was only one compilation unit).
//
#include "localintermediate.h"
#include "../Include/InfoSink.h"
namespace glslang {
//
// Link-time error emitter.
//
void TIntermediate::error(TInfoSink& infoSink, const char* message)
{
infoSink.info.prefix(EPrefixError);
infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n";
++numErrors;
}
//
// Merge the information from 'unit' into 'this'
//
void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit)
{
numMains += unit.numMains;
numErrors += unit.numErrors;
callGraph.insert(callGraph.end(), unit.callGraph.begin(), unit.callGraph.end());
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 unit
TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence();
TIntermSequence& unitGlobals = unit.treeRoot->getAsAggregate()->getSequence();
// Get the linker-object lists
TIntermSequence& linkerObjects = findLinkerObjects();
TIntermSequence& unitLinkerObjects = unit.findLinkerObjects();
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)
std::size_t initialNumLinkerObjects = linkerObjects.size();
for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) {
bool merge = true;
for (std::size_t 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;
// but if one has an initializer and the other does not, update
// the initializer
if (symbol->getConstArray().empty() && ! unitSymbol->getConstArray().empty())
symbol->setConstArray(unitSymbol->getConstArray());
// Check for consistent types/qualification/initializers etc.
mergeErrorCheck(infoSink, *symbol, *unitSymbol, false);
}
}
if (merge)
linkerObjects.push_back(unitLinkerObjects[unitLinkObj]);
}
}
//
// 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::mergeErrorCheck(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;
}
// Initializers have to match, if both are present, and if we don't already know the types don't match
if (! writeTypeComparison) {
if (! symbol.getConstArray().empty() && ! unitSymbol.getConstArray().empty()) {
if (symbol.getConstArray() != unitSymbol.getConstArray()) {
error(infoSink, "Initializers must match:");
infoSink.info << " " << symbol.getName() << "\n";
}
}
}
if (writeTypeComparison)
infoSink.info << " " << symbol.getName() << ": \"" << symbol.getType().getCompleteString() << "\" versus \"" <<
unitSymbol.getType().getCompleteString() << "\"\n";
}
//
// Do final link-time error checking of a complete (merged) intermediate representation.
// (Much error checking was done during merging).
//
void TIntermediate::errorCheck(TInfoSink& infoSink)
{
if (numMains < 1)
error(infoSink, "Missing entry point: Each stage requires one \"void main()\" entry point");
// recursion checking
checkCallGraphCycles(infoSink);
// overlap/alias/missing I/O, etc.
inOutLocationCheck(infoSink);
}
//
// See if the call graph contains any static recursion, which is disallowed
// by the specification.
//
void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink)
{
// Reset everything, once.
for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
call->visited = false;
call->subGraph = false;
call->errorGiven = false;
}
//
// Loop, looking for a new connected subgraph. One subgraph is handled per loop iteration.
//
TCall* newRoot;
do {
// See if we have unvisited parts of the graph.
newRoot = 0;
for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
if (! call->visited) {
newRoot = &(*call);
break;
}
}
// If not, we are done.
if (! newRoot)
break;
// Otherwise, we found a new subgraph, process it:
// See what all can be reached by this new root, and if any of
// that is recursive. This is done by marking processed calls as active,
// and if a new call is found that is already active, we looped,
// thereby detecting recursion.
std::list<TCall*> stack;
stack.push_back(newRoot);
newRoot->subGraph = true;
while (! stack.empty()) {
// get a caller
TCall* call = stack.back();
stack.pop_back();
// Add to the stack all the callees of the last subgraph node popped from the stack.
// This algorithm always terminates, because only subGraph == false causes a push
// and all pushes change subGraph to true, and nothing changes subGraph to false.
for (TGraph::iterator child = callGraph.begin(); child != callGraph.end(); ++child) {
if (call->callee == child->caller) {
if (child->subGraph) {
if (! child->errorGiven) {
error(infoSink, "Recursion detected:");
infoSink.info << " " << call->callee << " calling " << child->callee << "\n";
child->errorGiven = true;
}
} else {
child->subGraph = true;
stack.push_back(&(*child));
}
}
}
} // end while, meaning nothing left to process in this subtree
// Mark all the subGraph nodes as visited, closing out this subgraph.
for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
if (call->subGraph)
call->visited = true;
}
} while (newRoot); // redundant loop check; should always exit via the 'break' above
}
//
// Satisfy rules for location qualifiers on inputs and outputs
//
void TIntermediate::inOutLocationCheck(TInfoSink& infoSink)
{
// ES 3.0 requires all outputs to have location qualifiers if there is more than one output
bool fragOutHasLocation = false;
bool fragOutWithNoLocation = false;
int numFragOut = 0;
// TODO: maps for location collision checking
TIntermSequence& linkObjects = findLinkerObjects();
for (size_t i = 0; i < linkObjects.size(); ++i) {
const TType& type = linkObjects[i]->getAsTyped()->getType();
const TQualifier& qualifier = type.getQualifier();
if (language == EShLangFragment) {
if (qualifier.storage == EvqVaryingOut) {
++numFragOut;
if (qualifier.hasLocation())
fragOutHasLocation = true;
else
fragOutWithNoLocation = true;
}
}
}
if (profile == EEsProfile) {
if (numFragOut > 1 && fragOutWithNoLocation)
error(infoSink, "when more than one fragment shader output, all must have location qualifiers");
}
}
TIntermSequence& TIntermediate::findLinkerObjects()
{
// Get the top-level globals
TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence();
// Get the last member of the sequences, expected to be the linker-object lists
assert(globals.back()->getAsAggregate()->getOp() == EOpLinkerObjects);
return globals.back()->getAsAggregate()->getSequence();
}
} // end namespace glslang

View file

@ -107,14 +107,16 @@ public:
void removeTree();
protected:
void error(TInfoSink& infoSink, const char*);
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);
void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, bool crossStage);
void checkCallGraphCycles(TInfoSink&);
void inOutLocationCheck(TInfoSink&);
TIntermSequence& findLinkerObjects();
protected:
EShLanguage language;
const EShLanguage language;
TIntermNode* treeRoot;
EProfile profile;
int version;