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