| // | |
| // Copyright 2017 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // RunAtTheEndOfShader.cpp: Add code to be run at the end of the shader. In case main() contains a | |
| // return statement, this is done by replacing the main() function with another function that calls | |
| // the old main, like this: | |
| // | |
| // void main() { body } | |
| // => | |
| // void main0() { body } | |
| // void main() | |
| // { | |
| // main0(); | |
| // codeToRun | |
| // } | |
| // | |
| // This way the code will get run even if the return statement inside main is executed. | |
| // | |
| #include "compiler/translator/tree_util/RunAtTheEndOfShader.h" | |
| #include "compiler/translator/Compiler.h" | |
| #include "compiler/translator/IntermNode.h" | |
| #include "compiler/translator/StaticType.h" | |
| #include "compiler/translator/SymbolTable.h" | |
| #include "compiler/translator/tree_util/FindMain.h" | |
| #include "compiler/translator/tree_util/IntermNode_util.h" | |
| #include "compiler/translator/tree_util/IntermTraverse.h" | |
| namespace sh | |
| { | |
| namespace | |
| { | |
| constexpr const ImmutableString kMainString("main"); | |
| class ContainsReturnTraverser : public TIntermTraverser | |
| { | |
| public: | |
| ContainsReturnTraverser() : TIntermTraverser(true, false, false), mContainsReturn(false) {} | |
| bool visitBranch(Visit visit, TIntermBranch *node) override | |
| { | |
| if (node->getFlowOp() == EOpReturn) | |
| { | |
| mContainsReturn = true; | |
| } | |
| return false; | |
| } | |
| bool containsReturn() { return mContainsReturn; } | |
| private: | |
| bool mContainsReturn; | |
| }; | |
| bool ContainsReturn(TIntermNode *node) | |
| { | |
| ContainsReturnTraverser traverser; | |
| node->traverse(&traverser); | |
| return traverser.containsReturn(); | |
| } | |
| void WrapMainAndAppend(TIntermBlock *root, | |
| TIntermFunctionDefinition *main, | |
| TIntermNode *codeToRun, | |
| TSymbolTable *symbolTable) | |
| { | |
| // Replace main() with main0() with the same body. | |
| TFunction *oldMain = | |
| new TFunction(symbolTable, kEmptyImmutableString, SymbolType::AngleInternal, | |
| StaticType::GetBasic<EbtVoid>(), false); | |
| TIntermFunctionDefinition *oldMainDefinition = | |
| CreateInternalFunctionDefinitionNode(*oldMain, main->getBody()); | |
| bool replaced = root->replaceChildNode(main, oldMainDefinition); | |
| ASSERT(replaced); | |
| // void main() | |
| TFunction *newMain = new TFunction(symbolTable, kMainString, SymbolType::UserDefined, | |
| StaticType::GetBasic<EbtVoid>(), false); | |
| TIntermFunctionPrototype *newMainProto = new TIntermFunctionPrototype(newMain); | |
| // { | |
| // main0(); | |
| // codeToRun | |
| // } | |
| TIntermBlock *newMainBody = new TIntermBlock(); | |
| TIntermAggregate *oldMainCall = | |
| TIntermAggregate::CreateFunctionCall(*oldMain, new TIntermSequence()); | |
| newMainBody->appendStatement(oldMainCall); | |
| newMainBody->appendStatement(codeToRun); | |
| // Add the new main() to the root node. | |
| TIntermFunctionDefinition *newMainDefinition = | |
| new TIntermFunctionDefinition(newMainProto, newMainBody); | |
| root->appendStatement(newMainDefinition); | |
| } | |
| } // anonymous namespace | |
| bool RunAtTheEndOfShader(TCompiler *compiler, | |
| TIntermBlock *root, | |
| TIntermNode *codeToRun, | |
| TSymbolTable *symbolTable) | |
| { | |
| TIntermFunctionDefinition *main = FindMain(root); | |
| if (!ContainsReturn(main)) | |
| { | |
| main->getBody()->appendStatement(codeToRun); | |
| } | |
| else | |
| { | |
| WrapMainAndAppend(root, main, codeToRun, symbolTable); | |
| } | |
| return compiler->validateAST(root); | |
| } | |
| } // namespace sh |