summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
blob: 01d627937cd86cfcfa899655a1a99d417b9b539f (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
//
// Copyright (c) 2002-2015 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.
//
// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names
// from more complex expressions, assigning them to a temporary variable a#.
// Examples where a, b and c are all arrays:
// (a = b) == (a = c) is split into a = b; type[n] a1 = a; a = c; type[n] a2 = a; a1 == a2;
// type d = type[n](...)[i]; is split into type[n] a1 = type[n](...); type d = a1[i];

#include "compiler/translator/SeparateExpressionsReturningArrays.h"

#include "compiler/translator/IntermNodePatternMatcher.h"
#include "compiler/translator/IntermTraverse.h"

namespace sh
{

namespace
{

// Traverser that separates one array expression into a statement at a time.
class SeparateExpressionsTraverser : public TIntermTraverser
{
  public:
    SeparateExpressionsTraverser(TSymbolTable *symbolTable);

    bool visitBinary(Visit visit, TIntermBinary *node) override;
    bool visitAggregate(Visit visit, TIntermAggregate *node) override;

    void nextIteration();
    bool foundArrayExpression() const { return mFoundArrayExpression; }

  protected:
    // Marked to true once an operation that needs to be hoisted out of the expression has been
    // found. After that, no more AST updates are performed on that traversal.
    bool mFoundArrayExpression;

    IntermNodePatternMatcher mPatternToSeparateMatcher;
};

SeparateExpressionsTraverser::SeparateExpressionsTraverser(TSymbolTable *symbolTable)
    : TIntermTraverser(true, false, false, symbolTable),
      mFoundArrayExpression(false),
      mPatternToSeparateMatcher(IntermNodePatternMatcher::kExpressionReturningArray)
{
}

// Performs a shallow copy of an assignment node.
// These shallow copies are useful when a node gets inserted into an aggregate node
// and also needs to be replaced in its original location by a different node.
TIntermBinary *CopyAssignmentNode(TIntermBinary *node)
{
    return new TIntermBinary(node->getOp(), node->getLeft(), node->getRight());
}

bool SeparateExpressionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
{
    if (mFoundArrayExpression)
        return false;

    // Return if the expression is not an array or if we're not inside a complex expression.
    if (!mPatternToSeparateMatcher.match(node, getParentNode()))
        return true;

    ASSERT(node->getOp() == EOpAssign);

    mFoundArrayExpression = true;

    TIntermSequence insertions;
    insertions.push_back(CopyAssignmentNode(node));
    // TODO(oetuaho): In some cases it would be more optimal to not add the temporary node, but just
    // use the original target of the assignment. Care must be taken so that this doesn't happen
    // when the same array symbol is a target of assignment more than once in one expression.
    insertions.push_back(createTempInitDeclaration(node->getLeft()));
    insertStatementsInParentBlock(insertions);

    queueReplacement(createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);

    return false;
}

bool SeparateExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
{
    if (mFoundArrayExpression)
        return false;  // No need to traverse further

    if (!mPatternToSeparateMatcher.match(node, getParentNode()))
        return true;

    ASSERT(node->isConstructor() || node->getOp() == EOpCallFunctionInAST);

    mFoundArrayExpression = true;

    TIntermSequence insertions;
    insertions.push_back(createTempInitDeclaration(node->shallowCopy()));
    insertStatementsInParentBlock(insertions);

    queueReplacement(createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);

    return false;
}

void SeparateExpressionsTraverser::nextIteration()
{
    mFoundArrayExpression = false;
    nextTemporaryId();
}

}  // namespace

void SeparateExpressionsReturningArrays(TIntermNode *root, TSymbolTable *symbolTable)
{
    SeparateExpressionsTraverser traverser(symbolTable);
    // Separate one expression at a time, and reset the traverser between iterations.
    do
    {
        traverser.nextIteration();
        root->traverse(&traverser);
        if (traverser.foundArrayExpression())
            traverser.updateTree();
    } while (traverser.foundArrayExpression());
}

}  // namespace sh