1 //
2 // Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // SeparateExpressionsReturningArrays splits array-returning expressions that are not array names
7 // from more complex expressions, assigning them to a temporary variable a#.
8 // Examples where a, b and c are all arrays:
9 // (a = b) == (a = c) is split into a = b; type[n] a1 = a; a = c; type[n] a2 = a; a1 == a2;
10 // type d = type[n](...)[i]; is split into type[n] a1 = type[n](...); type d = a1[i];
11 
12 #include "compiler/translator/SeparateExpressionsReturningArrays.h"
13 
14 #include "compiler/translator/IntermNodePatternMatcher.h"
15 #include "compiler/translator/IntermTraverse.h"
16 
17 namespace sh
18 {
19 
20 namespace
21 {
22 
23 // Traverser that separates one array expression into a statement at a time.
24 class SeparateExpressionsTraverser : public TIntermTraverser
25 {
26   public:
27     SeparateExpressionsTraverser(TSymbolTable *symbolTable);
28 
29     bool visitBinary(Visit visit, TIntermBinary *node) override;
30     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
31 
32     void nextIteration();
foundArrayExpression() const33     bool foundArrayExpression() const { return mFoundArrayExpression; }
34 
35   protected:
36     // Marked to true once an operation that needs to be hoisted out of the expression has been
37     // found. After that, no more AST updates are performed on that traversal.
38     bool mFoundArrayExpression;
39 
40     IntermNodePatternMatcher mPatternToSeparateMatcher;
41 };
42 
SeparateExpressionsTraverser(TSymbolTable * symbolTable)43 SeparateExpressionsTraverser::SeparateExpressionsTraverser(TSymbolTable *symbolTable)
44     : TIntermTraverser(true, false, false, symbolTable),
45       mFoundArrayExpression(false),
46       mPatternToSeparateMatcher(IntermNodePatternMatcher::kExpressionReturningArray)
47 {
48 }
49 
50 // Performs a shallow copy of an assignment node.
51 // These shallow copies are useful when a node gets inserted into an aggregate node
52 // and also needs to be replaced in its original location by a different node.
CopyAssignmentNode(TIntermBinary * node)53 TIntermBinary *CopyAssignmentNode(TIntermBinary *node)
54 {
55     return new TIntermBinary(node->getOp(), node->getLeft(), node->getRight());
56 }
57 
visitBinary(Visit visit,TIntermBinary * node)58 bool SeparateExpressionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
59 {
60     if (mFoundArrayExpression)
61         return false;
62 
63     // Return if the expression is not an array or if we're not inside a complex expression.
64     if (!mPatternToSeparateMatcher.match(node, getParentNode()))
65         return true;
66 
67     ASSERT(node->getOp() == EOpAssign);
68 
69     mFoundArrayExpression = true;
70 
71     TIntermSequence insertions;
72     insertions.push_back(CopyAssignmentNode(node));
73     // TODO(oetuaho): In some cases it would be more optimal to not add the temporary node, but just
74     // use the original target of the assignment. Care must be taken so that this doesn't happen
75     // when the same array symbol is a target of assignment more than once in one expression.
76     insertions.push_back(createTempInitDeclaration(node->getLeft()));
77     insertStatementsInParentBlock(insertions);
78 
79     queueReplacement(createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);
80 
81     return false;
82 }
83 
visitAggregate(Visit visit,TIntermAggregate * node)84 bool SeparateExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
85 {
86     if (mFoundArrayExpression)
87         return false;  // No need to traverse further
88 
89     if (!mPatternToSeparateMatcher.match(node, getParentNode()))
90         return true;
91 
92     ASSERT(node->isConstructor() || node->getOp() == EOpCallFunctionInAST);
93 
94     mFoundArrayExpression = true;
95 
96     TIntermSequence insertions;
97     insertions.push_back(createTempInitDeclaration(node->shallowCopy()));
98     insertStatementsInParentBlock(insertions);
99 
100     queueReplacement(createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);
101 
102     return false;
103 }
104 
nextIteration()105 void SeparateExpressionsTraverser::nextIteration()
106 {
107     mFoundArrayExpression = false;
108     nextTemporaryId();
109 }
110 
111 }  // namespace
112 
SeparateExpressionsReturningArrays(TIntermNode * root,TSymbolTable * symbolTable)113 void SeparateExpressionsReturningArrays(TIntermNode *root, TSymbolTable *symbolTable)
114 {
115     SeparateExpressionsTraverser traverser(symbolTable);
116     // Separate one expression at a time, and reset the traverser between iterations.
117     do
118     {
119         traverser.nextIteration();
120         root->traverse(&traverser);
121         if (traverser.foundArrayExpression())
122             traverser.updateTree();
123     } while (traverser.foundArrayExpression());
124 }
125 
126 }  // namespace sh
127