diff --git a/Install/Windows/glslangValidator.exe b/Install/Windows/glslangValidator.exe index 9fd73c33..8fff1f69 100644 Binary files a/Install/Windows/glslangValidator.exe and b/Install/Windows/glslangValidator.exe differ diff --git a/Test/150.vert b/Test/150.vert new file mode 100644 index 00000000..cc7642c0 --- /dev/null +++ b/Test/150.vert @@ -0,0 +1,21 @@ +#version 150 core + +in vec4 iv4; + +uniform float ps; + +invariant gl_Position; + +void main() +{ + gl_Position = iv4; + gl_PointSize = ps; + gl_ClipDistance[2] = iv4.x; +} + +float gl_ClipDistance[4]; + +uniform foob { + int a[]; +}; +int a[5]; // ERROR, resizing user-block member diff --git a/Test/300link.frag b/Test/300link.frag new file mode 100644 index 00000000..5d39cc0f --- /dev/null +++ b/Test/300link.frag @@ -0,0 +1,8 @@ +#version 300 es + +precision highp float; + +out vec4 color1; +out vec4 color2; + +void main() {} diff --git a/Test/300link2.frag b/Test/300link2.frag new file mode 100644 index 00000000..7800ac9b --- /dev/null +++ b/Test/300link2.frag @@ -0,0 +1,11 @@ +#version 300 es +precision mediump float; +in vec4 pos; + +layout(location = 1) out vec4 c; +layout(location = 5) out vec4 p; +layout(location = 9) out vec4 q[2]; + +void main() +{ +} diff --git a/Test/300link3.frag b/Test/300link3.frag new file mode 100644 index 00000000..410592c6 --- /dev/null +++ b/Test/300link3.frag @@ -0,0 +1,7 @@ +#version 300 es + +precision highp float; + +out vec4 color1; + +void main() {} diff --git a/Test/410.geom b/Test/410.geom new file mode 100644 index 00000000..2e5eeeb7 --- /dev/null +++ b/Test/410.geom @@ -0,0 +1,6 @@ +#version 410 core + +void main() +{ + gl_ViewportIndex = 7; +} diff --git a/Test/baseResults/150.vert.out b/Test/baseResults/150.vert.out new file mode 100644 index 00000000..5d58342e --- /dev/null +++ b/Test/baseResults/150.vert.out @@ -0,0 +1,33 @@ +Warning, version 150 is not yet complete; some version-specific features are present, but many are missing. +ERROR: 0:13: 'gl_ClipDistance' : undeclared identifier +ERROR: 0:13: 'gl_ClipDistance' : left of '[' is not of type array, matrix, or vector +ERROR: 0:13: 'assign' : l-value required (can't modify a const) +ERROR: 0:16: 'gl_' : reserved built-in name +ERROR: 0:21: 'a' : cannot redeclare a user-block member array +ERROR: 5 compilation errors. No code generated. + +ERROR: node is still EOpNull! +0:9 Function Definition: main( (void) +0:9 Function Parameters: +0:11 Sequence +0:11 move second child to first child (4-component vector of float) +0:11 'gl_Position' (invariant gl_Position 4-component vector of float) +0:11 'iv4' (in 4-component vector of float) +0:12 move second child to first child (float) +0:12 'gl_PointSize' (gl_PointSize float) +0:12 'ps' (uniform float) +0:13 move second child to first child (float) +0:13 Constant: +0:13 0.000000 +0:13 direct index (float) +0:13 'iv4' (in 4-component vector of float) +0:13 Constant: +0:13 0 (const int) +0:? Linker Objects +0:? 'iv4' (in 4-component vector of float) +0:? 'ps' (uniform float) +0:? 'gl_ClipDistance' (4-element array of float) +0:? '__anon__0' (layout(shared ) uniform block) +0:? 'gl_VertexID' (gl_VertexId int) +0:? 'gl_InstanceID' (gl_InstanceId int) + diff --git a/Test/baseResults/300link.frag.out b/Test/baseResults/300link.frag.out new file mode 100644 index 00000000..7ce563a4 --- /dev/null +++ b/Test/baseResults/300link.frag.out @@ -0,0 +1,15 @@ +300link.frag + +0:? Sequence +0:8 Function Definition: main( (void) +0:8 Function Parameters: +0:? Linker Objects +0:? 'color1' (out highp 4-component vector of float) +0:? 'color2' (out highp 4-component vector of float) + + +Linked fragment stage: + +ERROR: Linking fragment stage: when more than one fragment shader output, all must have location qualifiers + + diff --git a/Test/baseResults/300link2.frag.out b/Test/baseResults/300link2.frag.out new file mode 100644 index 00000000..bf264ecb --- /dev/null +++ b/Test/baseResults/300link2.frag.out @@ -0,0 +1,16 @@ +300link2.frag + +0:? Sequence +0:9 Function Definition: main( (void) +0:9 Function Parameters: +0:? Linker Objects +0:? 'pos' (smooth in mediump 4-component vector of float) +0:? 'c' (layout(location=1 ) out mediump 4-component vector of float) +0:? 'p' (layout(location=5 ) out mediump 4-component vector of float) +0:? 'q' (layout(location=9 ) out 2-element array of mediump 4-component vector of float) + + +Linked fragment stage: + + + diff --git a/Test/baseResults/300link3.frag.out b/Test/baseResults/300link3.frag.out new file mode 100644 index 00000000..1317a2d6 --- /dev/null +++ b/Test/baseResults/300link3.frag.out @@ -0,0 +1,13 @@ +300link3.frag + +0:? Sequence +0:7 Function Definition: main( (void) +0:7 Function Parameters: +0:? Linker Objects +0:? 'color1' (out highp 4-component vector of float) + + +Linked fragment stage: + + + diff --git a/Test/baseResults/410.geom.out b/Test/baseResults/410.geom.out new file mode 100644 index 00000000..a937b844 --- /dev/null +++ b/Test/baseResults/410.geom.out @@ -0,0 +1,14 @@ +Warning, version 410 is not yet complete; some version-specific features are present, but many are missing. +ERROR: 0:5: 'gl_ViewportIndex' : undeclared identifier +ERROR: 1 compilation errors. No code generated. + +ERROR: node is still EOpNull! +0:3 Function Definition: main( (void) +0:3 Function Parameters: +0:5 Sequence +0:5 move second child to first child (float) +0:5 'gl_ViewportIndex' (float) +0:5 Constant: +0:5 7.000000 +0:? Linker Objects + diff --git a/Test/runtests b/Test/runtests index b9e9f714..0bde19ee 100644 --- a/Test/runtests +++ b/Test/runtests @@ -38,6 +38,9 @@ runLinkTest mains1.frag mains2.frag noMain1.geom noMain2.geom runLinkTest noMain.vert mains.frag runLinkTest link1.frag link2.frag link3.frag runLinkTest recurse1.vert recurse1.frag recurse2.frag +runLinkTest 300link.frag +runLinkTest 300link2.frag +runLinkTest 300link3.frag # # multi-threaded test diff --git a/Test/testlist b/Test/testlist index 26e7b00e..b6b3ad73 100644 --- a/Test/testlist +++ b/Test/testlist @@ -12,6 +12,7 @@ versionsErrors.vert 120.frag 130.frag 140.frag +150.vert 150.geom precision.frag precision.vert @@ -52,6 +53,7 @@ numeral.frag 400.geom 400.tesc 400.tese +410.geom 420.tese 430.comp dce.frag diff --git a/Todo.txt b/Todo.txt index a989f64f..6c8cb692 100644 --- a/Todo.txt +++ b/Todo.txt @@ -8,25 +8,25 @@ Link Validation Cross-stage linking - type consistency check of uniform and ins <-> outs, both variables and blocks, stage-specific arrayness matching - location/binding/index check - - matching initializers for uniforms - mixed es/non-es profiles - statically consumed input not produced by previous stage - - matching between gl_PerVertex blocks and gl_PerFragment blocks - - compute shader not combined with any other stages - give error for sharing a packed block + - 1.2: matching initializers for uniforms + - 1.5: matching between gl_PerVertex blocks and gl_PerFragment blocks - 1.3: deprecated mixing fixed vertex/fragment stage with programmable fragment/vertex stage. + - 4.3: compute shader not combined with any other stages - 4.3: remove cross-version linking restrictions. - 4.3: Allow mismatches in interpolation and auxiliary qualification across stages. - 4.4: A stage contains two different blocks, each with no instance name, where the blocks contain a member with the same name. Intra-stage linking + - ES 3.0: location aliasing/overlap (except desktop vertex shader inputs) + + ES 3.0: fragment outputs all have locations, if more than one + exactly one main + Non ES: type consistency check of uniforms, globals, ins, and outs + Non ES: value checking of global const initializers + Non ES: value checking of uniform initializers + Non ES: location match - - gl_TexCoord can only have a max array size of up to gl_MaxTextureCoords - - location aliasing/overlap (except desktop vertex shader inputs) - - 1.0: count the number of uniforms and varyings, compare against limits + - Non ES: gl_TexCoord can only have a max array size of up to gl_MaxTextureCoords + recursion for functions - Non ES: block matching - Non ES: component/binding/index/offset match check @@ -46,8 +46,8 @@ Link Validation Shader Functionality to Implement/Finish ESSL 2.0 (#version 100) - - implement non-inductive loop limitation detection - - implement non-inductive array accesses limitation detection + + implement non-inductive loop limitation detection + + implement non-inductive array accesses limitation detection ESSL 3.0 - "const" compile-time constant propagation in the front-end has to be complete, for all built-in functions GLSL 1.2 @@ -72,7 +72,7 @@ Shader Functionality to Implement/Finish - Built-in uniforms except for depth range parameters - Built-in interface between vertex and fragment: gl_TexCoord, gl_FogFragCoord, and all the color values. - Built-in two-sided coloring. - - Fixed functionality for a programmable stage. Supply shaders for all stages currently being used. + - Fixed functionality for a programmable stage. - ftransform(). Use invariant outputs instead. GLSL 1.5 (Non-ES) - Deprecated gl_MaxVaryingComponents diff --git a/glslang.vcxproj b/glslang.vcxproj index f54d85da..47ba6ee1 100644 --- a/glslang.vcxproj +++ b/glslang.vcxproj @@ -157,6 +157,7 @@ xcopy /y $(IntDir)$(TargetName)$(TargetExt) Test + @@ -171,7 +172,6 @@ xcopy /y $(IntDir)$(TargetName)$(TargetExt) Test - @@ -192,7 +192,6 @@ xcopy /y $(IntDir)$(TargetName)$(TargetExt) Test - diff --git a/glslang.vcxproj.filters b/glslang.vcxproj.filters index 9f127417..62fb9ae2 100644 --- a/glslang.vcxproj.filters +++ b/glslang.vcxproj.filters @@ -49,9 +49,6 @@ Machine Independent - - Machine Independent - Machine Independent @@ -112,6 +109,9 @@ Machine Independent + + Machine Independent + @@ -120,9 +120,6 @@ Machine Independent - - Machine Independent - Machine Independent diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index 0088dcde..d2179451 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -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 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. // diff --git a/glslang/MachineIndependent/Makefile b/glslang/MachineIndependent/Makefile index 1b6c4045..fe3b8bd8 100644 --- a/glslang/MachineIndependent/Makefile +++ b/glslang/MachineIndependent/Makefile @@ -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 diff --git a/glslang/MachineIndependent/QualifierAlive.cpp b/glslang/MachineIndependent/QualifierAlive.cpp deleted file mode 100644 index e6c632f6..00000000 --- a/glslang/MachineIndependent/QualifierAlive.cpp +++ /dev/null @@ -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(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(it); - - if (lit->wasFound()) - return false; - - return true; -} - -} // end namespace glslang - -#endif diff --git a/glslang/MachineIndependent/QualifierAlive.h b/glslang/MachineIndependent/QualifierAlive.h deleted file mode 100644 index ac172da6..00000000 --- a/glslang/MachineIndependent/QualifierAlive.h +++ /dev/null @@ -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 diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index eae5a80f..fe5c5a3c 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -145,7 +145,7 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil if (! parseContext.parseShaderStrings(ppContext, const_cast(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; } diff --git a/glslang/MachineIndependent/linkValidate.cpp b/glslang/MachineIndependent/linkValidate.cpp new file mode 100644 index 00000000..d37b078e --- /dev/null +++ b/glslang/MachineIndependent/linkValidate.cpp @@ -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 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 diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index f038211a..769167e4 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -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;