summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceShadowingVariables.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceShadowingVariables.cpp')
-rw-r--r--gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceShadowingVariables.cpp142
1 files changed, 142 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceShadowingVariables.cpp b/gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceShadowingVariables.cpp
new file mode 100644
index 0000000000..47842eaade
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceShadowingVariables.cpp
@@ -0,0 +1,142 @@
+//
+// Copyright 2019 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.
+//
+// ReplaceShadowingVariables.cpp: Replace all references to any variable in the AST that is
+// a redefinition of a variable in a nested scope. This is a useful for ESSL 1.00 shaders
+// where the spec section "4.2.3. Redeclaring Variables" states "However, a nested scope can
+// override an outer scope's declaration of a particular variable name." This is changed in
+// later spec versions, such as ESSL 3.20 spec which states "If [a variable] is declared as
+// a parameter in a function definition, it is scoped until the end of that function
+// definition. A function's parameter declarations and body together form a single scope."
+//
+// So this class is useful when translating from ESSL 1.00 shaders, where function body var
+// redefinition is allowed, to later shader versions where it's not allowed.
+//
+
+#include "compiler/translator/tree_util/ReplaceShadowingVariables.h"
+#include "compiler/translator/tree_util/ReplaceVariable.h"
+
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/Symbol.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/tree_util/IntermNode_util.h"
+#include "compiler/translator/tree_util/IntermTraverse.h"
+
+#include <unordered_set>
+
+namespace sh
+{
+
+namespace
+{
+
+// Custom struct to queue up any replacements until after AST traversal
+struct DeferredReplacementBlock
+{
+ const TVariable *originalVariable; // variable to be replaced
+ TVariable *replacementVariable; // variable to replace originalVar with
+ TIntermBlock *functionBody; // function body where replacement occurs
+};
+
+class ReplaceShadowingVariablesTraverser : public TIntermTraverser
+{
+ public:
+ ReplaceShadowingVariablesTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, true, true, symbolTable), mParameterNames{}, mFunctionBody(nullptr)
+ {}
+
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
+ {
+ // In pre-visit of function, record params
+ if (visit == PreVisit)
+ {
+ ASSERT(mParameterNames.size() == 0);
+ const TFunction *func = node->getFunctionPrototype()->getFunction();
+ // Grab all of the parameter names from the function prototype
+ size_t paramCount = func->getParamCount();
+ for (size_t i = 0; i < paramCount; ++i)
+ {
+ mParameterNames.emplace(std::string(func->getParam(i)->name().data()));
+ }
+ if (mParameterNames.size() > 0)
+ mFunctionBody = node->getBody();
+ }
+ else if (visit == PostVisit)
+ {
+ // Clear data saved from function definition
+ mParameterNames.clear();
+ mFunctionBody = nullptr;
+ }
+ return true;
+ }
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
+ {
+ if (visit == PreVisit && mParameterNames.size() != 0)
+ {
+ TIntermSequence *decls = node->getSequence();
+ for (auto &declVector : *decls)
+ {
+ // no init case
+ TIntermSymbol *symNode = declVector->getAsSymbolNode();
+ if (symNode == nullptr)
+ {
+ // init case
+ TIntermBinary *binaryNode = declVector->getAsBinaryNode();
+ ASSERT(binaryNode->getOp() == EOpInitialize);
+ symNode = binaryNode->getLeft()->getAsSymbolNode();
+ }
+ ASSERT(symNode != nullptr);
+ std::string varName = std::string(symNode->variable().name().data());
+ if (mParameterNames.count(varName) > 0)
+ {
+ // We found a redefined var so queue replacement
+ mReplacements.emplace_back(DeferredReplacementBlock{
+ &symNode->variable(),
+ CreateTempVariable(mSymbolTable, &symNode->variable().getType()),
+ mFunctionBody});
+ }
+ }
+ }
+ return true;
+ }
+ // Perform replacement of vars for any deferred replacements that were identified
+ [[nodiscard]] bool executeReplacements(TCompiler *compiler)
+ {
+ for (DeferredReplacementBlock &replace : mReplacements)
+ {
+ if (!ReplaceVariable(compiler, replace.functionBody, replace.originalVariable,
+ replace.replacementVariable))
+ {
+ return false;
+ }
+ }
+ mReplacements.clear();
+ return true;
+ }
+
+ private:
+ std::unordered_set<std::string> mParameterNames;
+ TIntermBlock *mFunctionBody;
+ std::vector<DeferredReplacementBlock> mReplacements;
+};
+
+} // anonymous namespace
+
+// Replaces every occurrence of a variable with another variable.
+[[nodiscard]] bool ReplaceShadowingVariables(TCompiler *compiler,
+ TIntermBlock *root,
+ TSymbolTable *symbolTable)
+{
+ ReplaceShadowingVariablesTraverser traverser(symbolTable);
+ root->traverse(&traverser);
+ if (!traverser.executeReplacements(compiler))
+ {
+ return false;
+ }
+ return traverser.updateTree(compiler, root);
+}
+
+} // namespace sh