Initialize uninitialized locals in GLSL output

Guarantee that local variables are initialized before they are used
in GLSL output. In HLSL output all variables were already being
initialized.

Locals are initialized using an AST transform. The local variable init
can only be run after some simplification of the AST, so that it is
able to handle complex cases like:

for (int i[2], j = i[0]; i[0] < 3; ++i[0]) {
}

If we're dealing with ESSL 1.00 which lacks array constructors, in
this kind of case the uninitialized array initialization code needs to
be hoisted out of the loop init statement, and the code also needs to
make sure that j's initializer is run after i is initialized.

Another complex case involves nameless structs. This can be an issue
also in ESSL 3.00 and above:

for (struct { float f; } s; s.f < 1.0; ++s.f) {
}

Since the struct doesn't have a name, its constructor can not be used.
We solve this by initializing the struct members individually,
similarly to how arrays are initialized in ESSL 1.00.

Initializing local variables is disabled on Mac and Android for now.
On Mac, invalid behavior was exposed in the WebGL 2.0 tests when
enabling it. On Android, the dEQP test runs failed for an unknown
reason. Bugs have been opened to resolve these issues later.

BUG=angleproject:1966
TEST=angle_end2end_tests, WebGL conformance tests

Change-Id: Ic06927f5b6cc9619bc82c647ee966605cd80bab2
Reviewed-on: https://chromium-review.googlesource.com/504728
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 4ea6e75..e3b7ae3 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -18,6 +18,7 @@
 #include "compiler/translator/EmulatePrecision.h"
 #include "compiler/translator/Initialize.h"
 #include "compiler/translator/InitializeVariables.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
 #include "compiler/translator/ParseContext.h"
 #include "compiler/translator/PruneEmptyDeclarations.h"
 #include "compiler/translator/RegenerateStructNames.h"
@@ -25,6 +26,8 @@
 #include "compiler/translator/RemovePow.h"
 #include "compiler/translator/RewriteDoWhile.h"
 #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
+#include "compiler/translator/SeparateDeclarations.h"
+#include "compiler/translator/SimplifyLoopConditions.h"
 #include "compiler/translator/UnfoldShortCircuitAST.h"
 #include "compiler/translator/UseInterfaceBlockFields.h"
 #include "compiler/translator/ValidateLimitations.h"
@@ -474,6 +477,29 @@
         {
             DeferGlobalInitializers(root);
         }
+
+        if (success && (compileOptions & SH_INITIALIZE_UNINITIALIZED_LOCALS) && getOutputType())
+        {
+            // Initialize uninitialized local variables.
+            // In some cases initializing can generate extra statements in the parent block, such as
+            // when initializing nameless structs or initializing arrays in ESSL 1.00. In that case
+            // we need to first simplify loop conditions and separate declarations. If we don't
+            // follow the Appendix A limitations, loop init statements can declare arrays or
+            // nameless structs and have multiple declarations.
+
+            if (!shouldRunLoopAndIndexingValidation(compileOptions))
+            {
+                SimplifyLoopConditions(root,
+                                       IntermNodePatternMatcher::kMultiDeclaration |
+                                           IntermNodePatternMatcher::kArrayDeclaration |
+                                           IntermNodePatternMatcher::kNamelessStructDeclaration,
+                                       getTemporaryIndex(), getSymbolTable(), getShaderVersion());
+            }
+            // We only really need to separate array declarations and nameless struct declarations,
+            // but it's simpler to just use the regular SeparateDeclarations.
+            SeparateDeclarations(root);
+            InitializeUninitializedLocals(root, getShaderVersion());
+        }
     }
 
     if (success)