diff --git a/glslang/CMakeLists.txt b/glslang/CMakeLists.txt index ff911355..6325fd4a 100644 --- a/glslang/CMakeLists.txt +++ b/glslang/CMakeLists.txt @@ -55,6 +55,7 @@ set(HEADERS MachineIndependent/glslang_tab.cpp.h MachineIndependent/gl_types.h MachineIndependent/Initialize.h + MachineIndependent/LiveTraverser.h MachineIndependent/localintermediate.h MachineIndependent/ParseHelper.h MachineIndependent/reflection.h diff --git a/glslang/MachineIndependent/LiveTraverser.h b/glslang/MachineIndependent/LiveTraverser.h new file mode 100644 index 00000000..5a902049 --- /dev/null +++ b/glslang/MachineIndependent/LiveTraverser.h @@ -0,0 +1,134 @@ +// +//Copyright (C) 2016 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. +// + +#include "../Include/Common.h" +#include "reflection.h" +#include "localintermediate.h" + +#include "gl_types.h" + +#include +#include + +namespace glslang { + +// +// The traverser: mostly pass through, except +// - processing function-call nodes to push live functions onto the stack of functions to process +// - processing selection nodes to trim semantically dead code +// +// This is in the glslang namespace directly so it can be a friend of TReflection. +// This can be derived from to implement reflection database traversers or +// binding mappers: anything that wants to traverse the live subset of the tree. +// + +class TLiveTraverser : public TIntermTraverser { +public: + TLiveTraverser(const TIntermediate& i, bool traverseAll = false) : + intermediate(i), traverseAll(traverseAll) + { } + + // + // Given a function name, find its subroot in the tree, and push it onto the stack of + // functions left to process. + // + void pushFunction(const TString& name) + { + TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence(); + for (unsigned int f = 0; f < globals.size(); ++f) { + TIntermAggregate* candidate = globals[f]->getAsAggregate(); + if (candidate && candidate->getOp() == EOpFunction && candidate->getName() == name) { + functions.push_back(candidate); + break; + } + } + } + + typedef std::list TFunctionStack; + TFunctionStack functions; + +protected: + // To catch which function calls are not dead, and hence which functions must be visited. + virtual bool visitAggregate(TVisit, TIntermAggregate* node) + { + if (!traverseAll) + if (node->getOp() == EOpFunctionCall) + addFunctionCall(node); + + return true; // traverse this subtree + } + + // To prune semantically dead paths. + virtual bool visitSelection(TVisit /* visit */, TIntermSelection* node) + { + if (traverseAll) + return true; // traverse all code + + TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion(); + if (constant) { + // cull the path that is dead + if (constant->getConstArray()[0].getBConst() == true && node->getTrueBlock()) + node->getTrueBlock()->traverse(this); + if (constant->getConstArray()[0].getBConst() == false && node->getFalseBlock()) + node->getFalseBlock()->traverse(this); + + return false; // don't traverse any more, we did it all above + } else + return true; // traverse the whole subtree + } + + // Track live functions as well as uniforms, so that we don't visit dead functions + // and only visit each function once. + void addFunctionCall(TIntermAggregate* call) + { + // // just use the map to ensure we process each function at most once + if (liveFunctions.find(call->getName()) == liveFunctions.end()) { + liveFunctions.insert(call->getName()); + pushFunction(call->getName()); + } + } + + const TIntermediate& intermediate; + typedef std::unordered_set TLiveFunctions; + TLiveFunctions liveFunctions; + bool traverseAll; + +private: + // prevent copy & copy construct + TLiveTraverser(TLiveTraverser&); + TLiveTraverser& operator=(TLiveTraverser&); +}; + +} // namespace glslang diff --git a/glslang/MachineIndependent/reflection.cpp b/glslang/MachineIndependent/reflection.cpp index 3082647f..a951122a 100644 --- a/glslang/MachineIndependent/reflection.cpp +++ b/glslang/MachineIndependent/reflection.cpp @@ -1,5 +1,5 @@ // -//Copyright (C) 2013 LunarG, Inc. +//Copyright (C) 2013-2016 LunarG, Inc. // //All rights reserved. // @@ -35,6 +35,7 @@ #include "../Include/Common.h" #include "reflection.h" +#include "LiveTraverser.h" #include "localintermediate.h" #include "gl_types.h" @@ -62,37 +63,26 @@ // there wasn't exactly one entry point. // + namespace glslang { // // The traverser: mostly pass through, except -// - processing function-call nodes to push live functions onto the stack of functions to process // - processing binary nodes to see if they are dereferences of an aggregates to track // - processing symbol nodes to see if they are non-aggregate objects to track -// - processing selection nodes to trim semantically dead code +// +// This ignores semantically dead code by using TLiveTraverser. // // This is in the glslang namespace directly so it can be a friend of TReflection. // -class TLiveTraverser : public TIntermTraverser { +class TReflectionTraverser : public TLiveTraverser { public: - TLiveTraverser(const TIntermediate& i, TReflection& r) : intermediate(i), reflection(r) { } + TReflectionTraverser(const TIntermediate& i, TReflection& r) : + TLiveTraverser(i), reflection(r) { } - virtual bool visitAggregate(TVisit, TIntermAggregate* node); virtual bool visitBinary(TVisit, TIntermBinary* node); virtual void visitSymbol(TIntermSymbol* base); - virtual bool visitSelection(TVisit, TIntermSelection* node); - - // Track live functions as well as uniforms, so that we don't visit dead functions - // and only visit each function once. - void addFunctionCall(TIntermAggregate* call) - { - // just use the map to ensure we process each function at most once - if (reflection.nameToIndex.find(call->getName()) == reflection.nameToIndex.end()) { - reflection.nameToIndex[call->getName()] = -1; - pushFunction(call->getName()); - } - } // Add a simple reference to a uniform variable to the uniform database, no dereference involved. // However, no dereference doesn't mean simple... it could be a complex aggregate. @@ -358,21 +348,6 @@ public: return blockIndex; } - // - // Given a function name, find its subroot in the tree, and push it onto the stack of - // functions left to process. - // - void pushFunction(const TString& name) - { - TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence(); - for (unsigned int f = 0; f < globals.size(); ++f) { - TIntermAggregate* candidate = globals[f]->getAsAggregate(); - if (candidate && candidate->getOp() == EOpFunction && candidate->getName() == name) { - functions.push_back(candidate); - break; - } - } - } // Are we at a level in a dereference chain at which individual active uniform queries are made? bool isReflectionGranularity(const TType& type) @@ -639,33 +614,21 @@ public: return type.isArray() ? type.getOuterArraySize() : 1; } - typedef std::list TFunctionStack; - TFunctionStack functions; - const TIntermediate& intermediate; TReflection& reflection; std::set processedDerefs; protected: - TLiveTraverser(TLiveTraverser&); - TLiveTraverser& operator=(TLiveTraverser&); + TReflectionTraverser(TReflectionTraverser&); + TReflectionTraverser& operator=(TReflectionTraverser&); }; // // Implement the traversal functions of interest. // -// To catch which function calls are not dead, and hence which functions must be visited. -bool TLiveTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) -{ - if (node->getOp() == EOpFunctionCall) - addFunctionCall(node); - - return true; // traverse this subtree -} - // To catch dereferenced aggregates that must be reflected. // This catches them at the highest level possible in the tree. -bool TLiveTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) +bool TReflectionTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) { switch (node->getOp()) { case EOpIndexDirect: @@ -683,7 +646,7 @@ bool TLiveTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) } // To reflect non-dereferenced objects. -void TLiveTraverser::visitSymbol(TIntermSymbol* base) +void TReflectionTraverser::visitSymbol(TIntermSymbol* base) { if (base->getQualifier().storage == EvqUniform) addUniform(*base); @@ -692,21 +655,6 @@ void TLiveTraverser::visitSymbol(TIntermSymbol* base) addAttribute(*base); } -// To prune semantically dead paths. -bool TLiveTraverser::visitSelection(TVisit /* visit */, TIntermSelection* node) -{ - TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion(); - if (constant) { - // cull the path that is dead - if (constant->getConstArray()[0].getBConst() == true && node->getTrueBlock()) - node->getTrueBlock()->traverse(this); - if (constant->getConstArray()[0].getBConst() == false && node->getFalseBlock()) - node->getFalseBlock()->traverse(this); - - return false; // don't traverse any more, we did it all above - } else - return true; // traverse the whole subtree -} // // Implement TReflection methods. @@ -720,7 +668,7 @@ bool TReflection::addStage(EShLanguage, const TIntermediate& intermediate) if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) return false; - TLiveTraverser it(intermediate, *this); + TReflectionTraverser it(intermediate, *this); // put the entry point on functions to process it.pushFunction("main("); diff --git a/glslang/MachineIndependent/reflection.h b/glslang/MachineIndependent/reflection.h index 5d930c7a..8880602c 100644 --- a/glslang/MachineIndependent/reflection.h +++ b/glslang/MachineIndependent/reflection.h @@ -49,7 +49,7 @@ namespace glslang { class TIntermediate; class TIntermAggregate; -class TLiveTraverser; +class TReflectionTraverser; // Data needed for just a single object at the granularity exchanged by the reflection API class TObjectReflection { @@ -116,7 +116,7 @@ public: void dump(); protected: - friend class glslang::TLiveTraverser; + friend class glslang::TReflectionTraverser; // Need a TString hash: typedef std::unordered_map TNameToIndex; typedef std::map TNameToIndex;