Merge pull request #512 from steve-lunarg/liveness-traverser

Refactor TLiveTraverser from the former reflection traverser, for fut…
This commit is contained in:
John Kessenich 2016-09-19 17:19:25 -06:00 committed by GitHub
commit 632f575ecc
4 changed files with 150 additions and 67 deletions

View File

@ -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

View File

@ -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 <list>
#include <unordered_set>
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<TIntermAggregate*> 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<TString> TLiveFunctions;
TLiveFunctions liveFunctions;
bool traverseAll;
private:
// prevent copy & copy construct
TLiveTraverser(TLiveTraverser&);
TLiveTraverser& operator=(TLiveTraverser&);
};
} // namespace glslang

View File

@ -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<TIntermAggregate*> TFunctionStack;
TFunctionStack functions;
const TIntermediate& intermediate;
TReflection& reflection;
std::set<const TIntermNode*> 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(");

View File

@ -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<TString, int> TNameToIndex;
typedef std::map<TString, int> TNameToIndex;