Merge pull request #512 from steve-lunarg/liveness-traverser
Refactor TLiveTraverser from the former reflection traverser, for fut…
This commit is contained in:
commit
632f575ecc
4 changed files with 150 additions and 67 deletions
|
|
@ -55,6 +55,7 @@ set(HEADERS
|
||||||
MachineIndependent/glslang_tab.cpp.h
|
MachineIndependent/glslang_tab.cpp.h
|
||||||
MachineIndependent/gl_types.h
|
MachineIndependent/gl_types.h
|
||||||
MachineIndependent/Initialize.h
|
MachineIndependent/Initialize.h
|
||||||
|
MachineIndependent/LiveTraverser.h
|
||||||
MachineIndependent/localintermediate.h
|
MachineIndependent/localintermediate.h
|
||||||
MachineIndependent/ParseHelper.h
|
MachineIndependent/ParseHelper.h
|
||||||
MachineIndependent/reflection.h
|
MachineIndependent/reflection.h
|
||||||
|
|
|
||||||
134
glslang/MachineIndependent/LiveTraverser.h
Normal file
134
glslang/MachineIndependent/LiveTraverser.h
Normal 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
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
//Copyright (C) 2013 LunarG, Inc.
|
//Copyright (C) 2013-2016 LunarG, Inc.
|
||||||
//
|
//
|
||||||
//All rights reserved.
|
//All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "../Include/Common.h"
|
#include "../Include/Common.h"
|
||||||
#include "reflection.h"
|
#include "reflection.h"
|
||||||
|
#include "LiveTraverser.h"
|
||||||
#include "localintermediate.h"
|
#include "localintermediate.h"
|
||||||
|
|
||||||
#include "gl_types.h"
|
#include "gl_types.h"
|
||||||
|
|
@ -62,37 +63,26 @@
|
||||||
// there wasn't exactly one entry point.
|
// there wasn't exactly one entry point.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
namespace glslang {
|
namespace glslang {
|
||||||
|
|
||||||
//
|
//
|
||||||
// The traverser: mostly pass through, except
|
// 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 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 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.
|
// This is in the glslang namespace directly so it can be a friend of TReflection.
|
||||||
//
|
//
|
||||||
|
|
||||||
class TLiveTraverser : public TIntermTraverser {
|
class TReflectionTraverser : public TLiveTraverser {
|
||||||
public:
|
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 bool visitBinary(TVisit, TIntermBinary* node);
|
||||||
virtual void visitSymbol(TIntermSymbol* base);
|
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.
|
// 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.
|
// However, no dereference doesn't mean simple... it could be a complex aggregate.
|
||||||
|
|
@ -358,21 +348,6 @@ public:
|
||||||
return blockIndex;
|
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?
|
// Are we at a level in a dereference chain at which individual active uniform queries are made?
|
||||||
bool isReflectionGranularity(const TType& type)
|
bool isReflectionGranularity(const TType& type)
|
||||||
|
|
@ -639,33 +614,21 @@ public:
|
||||||
return type.isArray() ? type.getOuterArraySize() : 1;
|
return type.isArray() ? type.getOuterArraySize() : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::list<TIntermAggregate*> TFunctionStack;
|
|
||||||
TFunctionStack functions;
|
|
||||||
const TIntermediate& intermediate;
|
|
||||||
TReflection& reflection;
|
TReflection& reflection;
|
||||||
std::set<const TIntermNode*> processedDerefs;
|
std::set<const TIntermNode*> processedDerefs;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TLiveTraverser(TLiveTraverser&);
|
TReflectionTraverser(TReflectionTraverser&);
|
||||||
TLiveTraverser& operator=(TLiveTraverser&);
|
TReflectionTraverser& operator=(TReflectionTraverser&);
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Implement the traversal functions of interest.
|
// 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.
|
// To catch dereferenced aggregates that must be reflected.
|
||||||
// This catches them at the highest level possible in the tree.
|
// 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()) {
|
switch (node->getOp()) {
|
||||||
case EOpIndexDirect:
|
case EOpIndexDirect:
|
||||||
|
|
@ -683,7 +646,7 @@ bool TLiveTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// To reflect non-dereferenced objects.
|
// To reflect non-dereferenced objects.
|
||||||
void TLiveTraverser::visitSymbol(TIntermSymbol* base)
|
void TReflectionTraverser::visitSymbol(TIntermSymbol* base)
|
||||||
{
|
{
|
||||||
if (base->getQualifier().storage == EvqUniform)
|
if (base->getQualifier().storage == EvqUniform)
|
||||||
addUniform(*base);
|
addUniform(*base);
|
||||||
|
|
@ -692,21 +655,6 @@ void TLiveTraverser::visitSymbol(TIntermSymbol* base)
|
||||||
addAttribute(*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.
|
// Implement TReflection methods.
|
||||||
|
|
@ -720,7 +668,7 @@ bool TReflection::addStage(EShLanguage, const TIntermediate& intermediate)
|
||||||
if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
|
if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
TLiveTraverser it(intermediate, *this);
|
TReflectionTraverser it(intermediate, *this);
|
||||||
|
|
||||||
// put the entry point on functions to process
|
// put the entry point on functions to process
|
||||||
it.pushFunction("main(");
|
it.pushFunction("main(");
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ namespace glslang {
|
||||||
|
|
||||||
class TIntermediate;
|
class TIntermediate;
|
||||||
class TIntermAggregate;
|
class TIntermAggregate;
|
||||||
class TLiveTraverser;
|
class TReflectionTraverser;
|
||||||
|
|
||||||
// Data needed for just a single object at the granularity exchanged by the reflection API
|
// Data needed for just a single object at the granularity exchanged by the reflection API
|
||||||
class TObjectReflection {
|
class TObjectReflection {
|
||||||
|
|
@ -116,7 +116,7 @@ public:
|
||||||
void dump();
|
void dump();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class glslang::TLiveTraverser;
|
friend class glslang::TReflectionTraverser;
|
||||||
|
|
||||||
// Need a TString hash: typedef std::unordered_map<TString, int> TNameToIndex;
|
// Need a TString hash: typedef std::unordered_map<TString, int> TNameToIndex;
|
||||||
typedef std::map<TString, int> TNameToIndex;
|
typedef std::map<TString, int> TNameToIndex;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue