summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/compiler/translator/tree_util/ReplaceShadowingVariables.cpp
blob: 47842eaadeb9b1127242e4d74e081a6060e22000 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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