Linker: Eliminate uncalled functions, because they can be ill-defined.
Fixes issue #610. Also provides a testing option to keep uncalled functions.
This commit is contained in:
parent
bf6d7f43fd
commit
906cc21816
68 changed files with 101 additions and 7783 deletions
|
|
@ -2,5 +2,5 @@
|
|||
// For the version, it uses the latest git tag followed by the number of commits.
|
||||
// For the date, it uses the current date (when then script is run).
|
||||
|
||||
#define GLSLANG_REVISION "Overload400-PrecQual.1687"
|
||||
#define GLSLANG_REVISION "Overload400-PrecQual.1688"
|
||||
#define GLSLANG_DATE "09-Dec-2016"
|
||||
|
|
|
|||
|
|
@ -1696,7 +1696,7 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
|
|||
intermediate[stage]->merge(*infoSink, *(*it)->intermediate);
|
||||
}
|
||||
|
||||
intermediate[stage]->finalCheck(*infoSink);
|
||||
intermediate[stage]->finalCheck(*infoSink, (messages & EShMsgKeepUncalled) != 0);
|
||||
|
||||
if (messages & EShMsgAST)
|
||||
intermediate[stage]->output(*infoSink, true);
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& sy
|
|||
//
|
||||
// Also, lock in defaults of things not set, including array sizes.
|
||||
//
|
||||
void TIntermediate::finalCheck(TInfoSink& infoSink)
|
||||
void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled)
|
||||
{
|
||||
if (getTreeRoot() == nullptr)
|
||||
return;
|
||||
|
|
@ -394,7 +394,7 @@ void TIntermediate::finalCheck(TInfoSink& infoSink)
|
|||
|
||||
// recursion and missing body checking
|
||||
checkCallGraphCycles(infoSink);
|
||||
checkCallGraphBodies(infoSink);
|
||||
checkCallGraphBodies(infoSink, keepUncalled);
|
||||
|
||||
// overlap/alias/missing I/O, etc.
|
||||
inOutLocationCheck(infoSink);
|
||||
|
|
@ -583,23 +583,27 @@ void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink)
|
|||
//
|
||||
// See which functions are reachable from the entry point and which have bodies.
|
||||
// Reachable ones with missing bodies are errors.
|
||||
// Unreachable bodies are dead code.
|
||||
//
|
||||
void TIntermediate::checkCallGraphBodies(TInfoSink& infoSink)
|
||||
void TIntermediate::checkCallGraphBodies(TInfoSink& infoSink, bool keepUncalled)
|
||||
{
|
||||
// Clear fields we'll use for this.
|
||||
for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
|
||||
call->visited = false;
|
||||
call->calleeBodyPosition = -1;
|
||||
}
|
||||
|
||||
|
||||
// The top level of the AST includes function definitions (bodies).
|
||||
// Compare these to function calls in the call graph.
|
||||
// We'll end up knowing which have bodies, and if so,
|
||||
// how to map the call-graph node to the location in the AST.
|
||||
TIntermSequence &functionSequence = getTreeRoot()->getAsAggregate()->getSequence();
|
||||
std::vector<bool> reachable(functionSequence.size(), true); // so that non-functions are reachable
|
||||
for (int f = 0; f < (int)functionSequence.size(); ++f) {
|
||||
glslang::TIntermAggregate* node = functionSequence[f]->getAsAggregate();
|
||||
if (node && (node->getOp() == glslang::EOpFunction)) {
|
||||
if (node->getName().compare(getEntryPointMangledName().c_str()) != 0)
|
||||
reachable[f] = false; // so that function bodies are unreachable, until proven otherwise
|
||||
for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
|
||||
if (call->callee == node->getName())
|
||||
call->calleeBodyPosition = f;
|
||||
|
|
@ -638,9 +642,21 @@ void TIntermediate::checkCallGraphBodies(TInfoSink& infoSink)
|
|||
if (call->calleeBodyPosition == -1) {
|
||||
error(infoSink, "No function definition (body) found: ");
|
||||
infoSink.info << " " << call->callee << "\n";
|
||||
}
|
||||
} else
|
||||
reachable[call->calleeBodyPosition] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Bodies in the AST not reached by the call graph are dead;
|
||||
// clear them out, since they can't be reached and also can't
|
||||
// be translated further due to possibility of being ill defined.
|
||||
if (! keepUncalled) {
|
||||
for (int f = 0; f < (int)functionSequence.size(); ++f) {
|
||||
if (! reachable[f])
|
||||
functionSequence[f] = nullptr;
|
||||
}
|
||||
functionSequence.erase(std::remove(functionSequence.begin(), functionSequence.end(), nullptr), functionSequence.end());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ public:
|
|||
|
||||
void addToCallGraph(TInfoSink&, const TString& caller, const TString& callee);
|
||||
void merge(TInfoSink&, TIntermediate&);
|
||||
void finalCheck(TInfoSink&);
|
||||
void finalCheck(TInfoSink&, bool keepUncalled);
|
||||
|
||||
void addIoAccessed(const TString& name) { ioAccessed.insert(name); }
|
||||
bool inIoAccessed(const TString& name) const { return ioAccessed.find(name) != ioAccessed.end(); }
|
||||
|
|
@ -396,7 +396,7 @@ protected:
|
|||
void mergeImplicitArraySizes(TType&, const TType&);
|
||||
void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, bool crossStage);
|
||||
void checkCallGraphCycles(TInfoSink&);
|
||||
void checkCallGraphBodies(TInfoSink&);
|
||||
void checkCallGraphBodies(TInfoSink&, bool keepUncalled);
|
||||
void inOutLocationCheck(TInfoSink&);
|
||||
TIntermSequence& findLinkerObjects() const;
|
||||
bool userOutputUsed() const;
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ enum EShMessages {
|
|||
EShMsgOnlyPreprocessor = (1 << 5), // only print out errors produced by the preprocessor
|
||||
EShMsgReadHlsl = (1 << 6), // use HLSL parsing rules and semantics
|
||||
EShMsgCascadingErrors = (1 << 7), // get cascading errors; risks error-recovery issues, instead of an early exit
|
||||
EShMsgKeepUncalled = (1 << 8), // for testing, don't eliminate uncalled functions
|
||||
};
|
||||
|
||||
//
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue