1 //
2 // Copyright 2002 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/tree_ops/d3d/SeparateExpressionsReturningArrays.h"
13 
14 #include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
15 #include "compiler/translator/tree_util/IntermNode_util.h"
16 #include "compiler/translator/tree_util/IntermTraverse.h"
17 
18 namespace sh
19 {
20 
21 namespace
22 {
23 
24 // Traverser that separates one array expression into a statement at a time.
25 class SeparateExpressionsTraverser : public TIntermTraverser
26 {
27   public:
28     SeparateExpressionsTraverser(TSymbolTable *symbolTable);
29 
30     bool visitBinary(Visit visit, TIntermBinary *node) override;
31     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
32 
33     void nextIteration();
foundArrayExpression() const34     bool foundArrayExpression() const { return mFoundArrayExpression; }
35 
36   protected:
37     // Marked to true once an operation that needs to be hoisted out of the expression has been
38     // found. After that, no more AST updates are performed on that traversal.
39     bool mFoundArrayExpression;
40 
41     IntermNodePatternMatcher mPatternToSeparateMatcher;
42 };
43 
SeparateExpressionsTraverser(TSymbolTable * symbolTable)44 SeparateExpressionsTraverser::SeparateExpressionsTraverser(TSymbolTable *symbolTable)
45     : TIntermTraverser(true, false, false, symbolTable),
46       mFoundArrayExpression(false),
47       mPatternToSeparateMatcher(IntermNodePatternMatcher::kExpressionReturningArray)
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     TIntermDeclaration *arrayVariableDeclaration;
77     TVariable *arrayVariable =
78         DeclareTempVariable(mSymbolTable, node->getLeft(), EvqTemporary, &arrayVariableDeclaration);
79     insertions.push_back(arrayVariableDeclaration);
80     insertStatementsInParentBlock(insertions);
81 
82     queueReplacement(CreateTempSymbolNode(arrayVariable), OriginalNode::IS_DROPPED);
83 
84     return false;
85 }
86 
visitAggregate(Visit visit,TIntermAggregate * node)87 bool SeparateExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
88 {
89     if (mFoundArrayExpression)
90         return false;  // No need to traverse further
91 
92     if (!mPatternToSeparateMatcher.match(node, getParentNode()))
93         return true;
94 
95     ASSERT(node->isConstructor() || node->getOp() == EOpCallFunctionInAST);
96 
97     mFoundArrayExpression = true;
98 
99     TIntermDeclaration *arrayVariableDeclaration;
100     TVariable *arrayVariable = DeclareTempVariable(mSymbolTable, node->shallowCopy(), EvqTemporary,
101                                                    &arrayVariableDeclaration);
102     insertStatementInParentBlock(arrayVariableDeclaration);
103 
104     queueReplacement(CreateTempSymbolNode(arrayVariable), OriginalNode::IS_DROPPED);
105 
106     return false;
107 }
108 
nextIteration()109 void SeparateExpressionsTraverser::nextIteration()
110 {
111     mFoundArrayExpression = false;
112 }
113 
114 }  // namespace
115 
SeparateExpressionsReturningArrays(TCompiler * compiler,TIntermNode * root,TSymbolTable * symbolTable)116 bool SeparateExpressionsReturningArrays(TCompiler *compiler,
117                                         TIntermNode *root,
118                                         TSymbolTable *symbolTable)
119 {
120     SeparateExpressionsTraverser traverser(symbolTable);
121     // Separate one expression at a time, and reset the traverser between iterations.
122     do
123     {
124         traverser.nextIteration();
125         root->traverse(&traverser);
126         if (traverser.foundArrayExpression())
127         {
128             if (!traverser.updateTree(compiler, root))
129             {
130                 return false;
131             }
132         }
133     } while (traverser.foundArrayExpression());
134 
135     return true;
136 }
137 
138 }  // namespace sh
139