Remove empty switch statements from translated shaders
The native HLSL compiler does not accept switch statements with an
empty statement list. The simplest way to accommodate this is to
simply remove them from the AST after parsing and some initial
pruning.
This is done by the new RemoveEmptySwitchStatements traverser. It
preserves init statements of switch statements in case they have side
effects. So for example
switch(++i) {}
gets translated to
++i;
BUG=angleproject:2206
TEST=angle_end2end_tests
Change-Id: I550a3c9b010a3566016bdfd93344ac30fd860604
Reviewed-on: https://chromium-review.googlesource.com/742922
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler.gypi b/src/compiler.gypi
index e84f1a3..b57aca9 100644
--- a/src/compiler.gypi
+++ b/src/compiler.gypi
@@ -107,6 +107,8 @@
'compiler/translator/RegenerateStructNames.h',
'compiler/translator/RemoveArrayLengthMethod.cpp',
'compiler/translator/RemoveArrayLengthMethod.h',
+ 'compiler/translator/RemoveEmptySwitchStatements.cpp',
+ 'compiler/translator/RemoveEmptySwitchStatements.h',
'compiler/translator/RemoveInvariantDeclaration.cpp',
'compiler/translator/RemoveInvariantDeclaration.h',
'compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.cpp',
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 82fc287..07ea1d6 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -28,6 +28,7 @@
#include "compiler/translator/PruneNoOps.h"
#include "compiler/translator/RegenerateStructNames.h"
#include "compiler/translator/RemoveArrayLengthMethod.h"
+#include "compiler/translator/RemoveEmptySwitchStatements.h"
#include "compiler/translator/RemoveInvariantDeclaration.h"
#include "compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h"
#include "compiler/translator/RemovePow.h"
@@ -393,6 +394,9 @@
if (success)
RemoveNoOpCasesFromEndOfSwitchStatements(root, &symbolTable);
+ // Remove empty switch statements - this makes output simpler.
+ RemoveEmptySwitchStatements(root);
+
// Create the function DAG and check there is no recursion
if (success)
success = initCallDag(root);
diff --git a/src/compiler/translator/RemoveEmptySwitchStatements.cpp b/src/compiler/translator/RemoveEmptySwitchStatements.cpp
new file mode 100644
index 0000000..b39c912
--- /dev/null
+++ b/src/compiler/translator/RemoveEmptySwitchStatements.cpp
@@ -0,0 +1,56 @@
+//
+// Copyright (c) 2017 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.
+//
+// RemoveEmptySwitchStatements.cpp: Remove switch statements that have an empty statement list.
+
+#include "compiler/translator/RemoveEmptySwitchStatements.h"
+
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class RemoveEmptySwitchStatementsTraverser : public TIntermTraverser
+{
+ public:
+ RemoveEmptySwitchStatementsTraverser() : TIntermTraverser(true, false, false) {}
+
+ bool visitSwitch(Visit visit, TIntermSwitch *node);
+};
+
+bool RemoveEmptySwitchStatementsTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
+{
+ if (node->getStatementList()->getSequence()->empty())
+ {
+ // Just output the init statement.
+ if (node->getInit()->hasSideEffects())
+ {
+ queueReplacement(node->getInit(), OriginalNode::IS_DROPPED);
+ }
+ else
+ {
+ TIntermSequence emptyReplacement;
+ ASSERT(getParentNode()->getAsBlock());
+ mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(),
+ node, emptyReplacement));
+ }
+ return false; // Nothing inside the child nodes to traverse.
+ }
+ return true;
+}
+
+} // anonymous namespace
+
+void RemoveEmptySwitchStatements(TIntermBlock *root)
+{
+ RemoveEmptySwitchStatementsTraverser traverser;
+ root->traverse(&traverser);
+ traverser.updateTree();
+}
+
+} // namespace sh
diff --git a/src/compiler/translator/RemoveEmptySwitchStatements.h b/src/compiler/translator/RemoveEmptySwitchStatements.h
new file mode 100644
index 0000000..1018a50
--- /dev/null
+++ b/src/compiler/translator/RemoveEmptySwitchStatements.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) 2017 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.
+//
+// RemoveEmptySwitchStatements.h: Remove switch statements that have an empty statement list.
+
+#ifndef COMPILER_TRANSLATOR_REMOVEEMPTYSWITCHSTATEMENTS_H_
+#define COMPILER_TRANSLATOR_REMOVEEMPTYSWITCHSTATEMENTS_H_
+
+namespace sh
+{
+class TIntermBlock;
+
+void RemoveEmptySwitchStatements(TIntermBlock *root);
+}
+
+#endif // COMPILER_TRANSLATOR_REMOVEEMPTYSWITCHSTATEMENTS_H_
\ No newline at end of file
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index 4e7bb7f..67247ce 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -3691,6 +3691,30 @@
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
+// Test that an empty switch/case statement is translated in a way that compiles and executes the
+// init-statement.
+TEST_P(GLSLTest_ES3, EmptySwitch)
+{
+ const std::string &fragmentShader =
+ R"(#version 300 es
+
+ precision highp float;
+
+ uniform int u_zero;
+ out vec4 my_FragColor;
+
+ void main()
+ {
+ int i = u_zero;
+ switch(++i) {}
+ my_FragColor = (i == 1) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
+ })";
+
+ ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShader);
+ drawQuad(program.get(), "inputAttribute", 0.5f);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+}
+
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(GLSLTest,
ES2_D3D9(),