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:
John Kessenich 2016-12-09 19:22:20 -07:00
parent bf6d7f43fd
commit 906cc21816
68 changed files with 101 additions and 7783 deletions

View file

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

View file

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

View file

@ -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());
}
}
//

View file

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

View file

@ -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
};
//