Put each array declarator into a separate declaration in HLSL output
Since HLSL doesn't support arrays as l-values, HLSL output needs to split
declarators that initialize arrays to variable declaration and assignment
implemented via a function call. To prepare for this, it is necessary that each
declarator has its own declaration.
BUG=angleproject:941
TEST=angle_end2end_tests, WebGL conformance tests
Change-Id: I43dee487578561c01dbde90c2f55a93dda2f057a
Reviewed-on: https://chromium-review.googlesource.com/266001
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index f335bfd..a8018a4 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -195,6 +195,20 @@
return false;
}
+bool TIntermAggregate::replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements)
+{
+ for (auto it = mSequence.begin(); it < mSequence.end(); ++it)
+ {
+ if (*it == original)
+ {
+ it = mSequence.erase(it);
+ it = mSequence.insert(it, replacements.begin(), replacements.end());
+ return true;
+ }
+ }
+ return false;
+}
+
void TIntermAggregate::setPrecisionFromChildren()
{
if (getBasicType() == EbtBool)
@@ -1497,4 +1511,12 @@
}
}
}
+ for (size_t ii = 0; ii < mMultiReplacements.size(); ++ii)
+ {
+ const NodeReplaceWithMultipleEntry &replacement = mMultiReplacements[ii];
+ ASSERT(replacement.parent);
+ bool replaced = replacement.parent->replaceChildNodeWithMultiple(
+ replacement.original, replacement.replacements);
+ ASSERT(replaced);
+ }
}
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 57d772c..0bacd3d 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -422,7 +422,7 @@
virtual void traverse(TIntermTraverser *);
virtual bool replaceChildNode(
TIntermNode *original, TIntermNode *replacement);
-
+ bool replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements);
// Conservatively assume function calls and other aggregate operators have side-effects
virtual bool hasSideEffects() const { return true; }
@@ -625,7 +625,7 @@
const bool rightToLeft;
// If traversers need to replace nodes, they can add the replacements in
- // mReplacements during traversal and the user of the traverser should call
+ // mReplacements/mMultiReplacements during traversal and the user of the traverser should call
// this function after traversal to perform them.
void updateTree();
@@ -636,6 +636,7 @@
// All the nodes from root to the current node's parent during traversing.
TVector<TIntermNode *> mPath;
+ // To replace a single node with another on the parent node
struct NodeUpdateEntry
{
NodeUpdateEntry(TIntermNode *_parent,
@@ -653,9 +654,26 @@
bool originalBecomesChildOfReplacement;
};
+ // To replace a single node with multiple nodes on the parent aggregate node
+ struct NodeReplaceWithMultipleEntry
+ {
+ NodeReplaceWithMultipleEntry(TIntermAggregate *_parent, TIntermNode *_original, TIntermSequence _replacements)
+ : parent(_parent),
+ original(_original),
+ replacements(_replacements)
+ {
+ }
+
+ TIntermAggregate *parent;
+ TIntermNode *original;
+ TIntermSequence replacements;
+ };
+
// During traversing, save all the changes that need to happen into
- // mReplacements, then do them by calling updateTree().
+ // mReplacements/mMultiReplacements, then do them by calling updateTree().
+ // Multi replacements are processed after single replacements.
std::vector<NodeUpdateEntry> mReplacements;
+ std::vector<NodeReplaceWithMultipleEntry> mMultiReplacements;
};
//
diff --git a/src/compiler/translator/SeparateDeclarations.cpp b/src/compiler/translator/SeparateDeclarations.cpp
new file mode 100644
index 0000000..7ea1ce0
--- /dev/null
+++ b/src/compiler/translator/SeparateDeclarations.cpp
@@ -0,0 +1,86 @@
+//
+// 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.
+//
+// The SeparateArrayDeclarations function processes declarations that contain array declarators. Each declarator in
+// such declarations gets its own declaration.
+// This is useful as an intermediate step when initialization needs to be separated from declaration.
+// Example:
+// int a[1] = int[1](1), b[1] = int[1](2);
+// gets transformed when run through this class into the AST equivalent of:
+// int a[1] = int[1](1);
+// int b[1] = int[1](2);
+
+#include "compiler/translator/SeparateDeclarations.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace
+{
+
+class SeparateDeclarations : private TIntermTraverser
+{
+ public:
+ static void apply(TIntermNode *root);
+ private:
+ SeparateDeclarations();
+ bool visitAggregate(Visit, TIntermAggregate *node) override;
+};
+
+void SeparateDeclarations::apply(TIntermNode *root)
+{
+ SeparateDeclarations separateDecl;
+ root->traverse(&separateDecl);
+ separateDecl.updateTree();
+}
+
+SeparateDeclarations::SeparateDeclarations()
+ : TIntermTraverser(true, false, false)
+{
+}
+
+bool SeparateDeclarations::visitAggregate(Visit, TIntermAggregate *node)
+{
+ if (node->getOp() == EOpDeclaration)
+ {
+ TIntermSequence *sequence = node->getSequence();
+ bool sequenceContainsArrays = false;
+ for (size_t ii = 0; ii < sequence->size(); ++ii)
+ {
+ TIntermTyped *typed = sequence->at(ii)->getAsTyped();
+ if (typed != nullptr && typed->isArray())
+ {
+ sequenceContainsArrays = true;
+ break;
+ }
+ }
+ if (sequence->size() > 1 && sequenceContainsArrays)
+ {
+ TIntermAggregate *parentAgg = getParentNode()->getAsAggregate();
+ ASSERT(parentAgg != nullptr);
+
+ TIntermSequence replacementDeclarations;
+ for (size_t ii = 0; ii < sequence->size(); ++ii)
+ {
+ TIntermAggregate *replacementDeclaration = new TIntermAggregate;
+
+ replacementDeclaration->setOp(EOpDeclaration);
+ replacementDeclaration->getSequence()->push_back(sequence->at(ii));
+ replacementDeclaration->setLine(sequence->at(ii)->getLine());
+ replacementDeclarations.push_back(replacementDeclaration);
+ }
+
+ mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, replacementDeclarations));
+ }
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+void SeparateArrayDeclarations(TIntermNode *root)
+{
+ SeparateDeclarations::apply(root);
+}
diff --git a/src/compiler/translator/SeparateDeclarations.h b/src/compiler/translator/SeparateDeclarations.h
new file mode 100644
index 0000000..2aa5294
--- /dev/null
+++ b/src/compiler/translator/SeparateDeclarations.h
@@ -0,0 +1,22 @@
+//
+// 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.
+//
+// The SeparateArrayDeclarations function processes declarations that contain array declarators. Each declarator in
+// such declarations gets its own declaration.
+// This is useful as an intermediate step when initialization needs to be separated from declaration.
+// Example:
+// int a[1] = int[1](1), b[1] = int[1](2);
+// gets transformed when run through this class into the AST equivalent of:
+// int a[1] = int[1](1);
+// int b[1] = int[1](2);
+
+#ifndef COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
+#define COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
+
+class TIntermNode;
+
+void SeparateArrayDeclarations(TIntermNode *root);
+
+#endif // COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
diff --git a/src/compiler/translator/TranslatorHLSL.cpp b/src/compiler/translator/TranslatorHLSL.cpp
index f6275de..d861be1 100644
--- a/src/compiler/translator/TranslatorHLSL.cpp
+++ b/src/compiler/translator/TranslatorHLSL.cpp
@@ -7,6 +7,7 @@
#include "compiler/translator/TranslatorHLSL.h"
#include "compiler/translator/OutputHLSL.h"
+#include "compiler/translator/SeparateDeclarations.h"
#include "compiler/translator/SimplifyArrayAssignment.h"
TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
@@ -19,6 +20,8 @@
const ShBuiltInResources &resources = getResources();
int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
+ SeparateArrayDeclarations(root);
+
SimplifyArrayAssignment simplify;
root->traverse(&simplify);