summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp')
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp b/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
new file mode 100644
index 0000000000..e8e1a21d9c
--- /dev/null
+++ b/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
@@ -0,0 +1,169 @@
+//
+// 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/IntermNode.h"
+
+namespace
+{
+
+// Traverser that separates one array expression into a statement at a time.
+class SeparateExpressionsTraverser : public TIntermTraverser
+{
+ public:
+ SeparateExpressionsTraverser();
+
+ 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;
+};
+
+SeparateExpressionsTraverser::SeparateExpressionsTraverser()
+ : TIntermTraverser(true, false, false),
+ mFoundArrayExpression(false)
+{
+}
+
+// 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)
+{
+ TIntermBinary *copyNode = new TIntermBinary(node->getOp());
+ copyNode->setLeft(node->getLeft());
+ copyNode->setRight(node->getRight());
+ copyNode->setType(node->getType());
+ return copyNode;
+}
+
+// Performs a shallow copy of a constructor/function call node.
+TIntermAggregate *CopyAggregateNode(TIntermAggregate *node)
+{
+ TIntermAggregate *copyNode = new TIntermAggregate(node->getOp());
+ TIntermSequence *copySeq = copyNode->getSequence();
+ copySeq->insert(copySeq->begin(), node->getSequence()->begin(), node->getSequence()->end());
+ copyNode->setType(node->getType());
+ copyNode->setFunctionId(node->getFunctionId());
+ if (node->isUserDefined())
+ {
+ copyNode->setUserDefined();
+ }
+ copyNode->setNameObj(node->getNameObj());
+ return copyNode;
+}
+
+bool SeparateExpressionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (mFoundArrayExpression)
+ return false;
+
+ // Early return if the expression is not an array or if we're not inside a complex expression.
+ if (!node->getType().isArray() || parentNodeIsBlock())
+ return true;
+
+ switch (node->getOp())
+ {
+ case 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);
+
+ NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(node->getType()), false);
+ mReplacements.push_back(replaceVariable);
+ }
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool SeparateExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFoundArrayExpression)
+ return false; // No need to traverse further
+
+ if (getParentNode() != nullptr)
+ {
+ TIntermBinary *parentBinary = getParentNode()->getAsBinaryNode();
+ bool parentIsAssignment = (parentBinary != nullptr &&
+ (parentBinary->getOp() == EOpAssign || parentBinary->getOp() == EOpInitialize));
+
+ if (!node->getType().isArray() || parentNodeIsBlock() || parentIsAssignment)
+ return true;
+
+ if (node->isConstructor())
+ {
+ mFoundArrayExpression = true;
+
+ TIntermSequence insertions;
+ insertions.push_back(createTempInitDeclaration(CopyAggregateNode(node)));
+ insertStatementsInParentBlock(insertions);
+
+ NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(node->getType()), false);
+ mReplacements.push_back(replaceVariable);
+
+ return false;
+ }
+ else if (node->getOp() == EOpFunctionCall)
+ {
+ mFoundArrayExpression = true;
+
+ TIntermSequence insertions;
+ insertions.push_back(createTempInitDeclaration(CopyAggregateNode(node)));
+ insertStatementsInParentBlock(insertions);
+
+ NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(node->getType()), false);
+ mReplacements.push_back(replaceVariable);
+
+ return false;
+ }
+ }
+ return true;
+}
+
+void SeparateExpressionsTraverser::nextIteration()
+{
+ mFoundArrayExpression = false;
+ nextTemporaryIndex();
+}
+
+} // namespace
+
+void SeparateExpressionsReturningArrays(TIntermNode *root, unsigned int *temporaryIndex)
+{
+ SeparateExpressionsTraverser traverser;
+ ASSERT(temporaryIndex != nullptr);
+ traverser.useTemporaryIndex(temporaryIndex);
+ // 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());
+}