Bug 1347866 - Part2: Revert "translator: remove code related to for-loop unrolling" draft
authorChih-Yi Leu <cleu@mozilla.com>
Mon, 10 Apr 2017 16:38:03 +0800
changeset 559628 5a5f0f04246bd5766d54c7a9526fd0c44b927192
parent 559627 42441a657f93a31bfa8132bf39795d9eec0d4aea
child 559629 7b2bb0148f3c408d1ed3bc3cbabb7637518d460f
push id53134
push userbmo:cleu@mozilla.com
push dateMon, 10 Apr 2017 08:50:37 +0000
bugs1347866
milestone55.0a1
Bug 1347866 - Part2: Revert "translator: remove code related to for-loop unrolling" This reverts commit 1b896c62934be40cf8a37dc28fabc15590c89a5d; r?jgilbert MozReview-Commit-ID: BFypWbjiCVF
gfx/angle/include/GLSLANG/ShaderLang.h
gfx/angle/moz.build
gfx/angle/src/commit.h
gfx/angle/src/compiler.gypi
gfx/angle/src/compiler/translator/Compiler.cpp
gfx/angle/src/compiler/translator/ForLoopUnroll.cpp
gfx/angle/src/compiler/translator/ForLoopUnroll.h
gfx/angle/src/compiler/translator/IntermNode.h
gfx/angle/src/compiler/translator/LoopInfo.cpp
gfx/angle/src/compiler/translator/LoopInfo.h
gfx/angle/src/compiler/translator/MMap.h
gfx/angle/src/compiler/translator/OutputGLSLBase.cpp
gfx/angle/src/compiler/translator/OutputGLSLBase.h
gfx/angle/src/compiler/translator/ValidateLimitations.cpp
gfx/angle/src/compiler/translator/ValidateLimitations.h
gfx/angle/src/libANGLE/moz.build
gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp
--- a/gfx/angle/include/GLSLANG/ShaderLang.h
+++ b/gfx/angle/include/GLSLANG/ShaderLang.h
@@ -70,39 +70,22 @@ enum ShShaderOutput
 
 const ShCompileOptions SH_VALIDATE                           = 0;
 const ShCompileOptions SH_VALIDATE_LOOP_INDEXING             = UINT64_C(1) << 0;
 const ShCompileOptions SH_INTERMEDIATE_TREE                  = UINT64_C(1) << 1;
 const ShCompileOptions SH_OBJECT_CODE                        = UINT64_C(1) << 2;
 const ShCompileOptions SH_VARIABLES                          = UINT64_C(1) << 3;
 const ShCompileOptions SH_LINE_DIRECTIVES                    = UINT64_C(1) << 4;
 const ShCompileOptions SH_SOURCE_PATH                        = UINT64_C(1) << 5;
-
-// This flag will keep invariant declaration for input in fragment shader for GLSL >=4.20 on AMD.
-// From GLSL >= 4.20, it's optional to add invariant for fragment input, but GPU vendors have
-// different implementations about this. Some drivers forbid invariant in fragment for GLSL>= 4.20,
-// e.g. Linux Mesa, some drivers treat that as optional, e.g. NVIDIA, some drivers require invariant
-// must match between vertex and fragment shader, e.g. AMD. The behavior on AMD is obviously wrong.
-// Remove invariant for input in fragment shader to workaround the restriction on Intel Mesa.
-// But don't remove on AMD Linux to avoid triggering the bug on AMD.
-const ShCompileOptions SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT = UINT64_C(1) << 6;
-
-// Due to spec difference between GLSL 4.1 or lower and ESSL3, some platforms (for example, Mac OSX
-// core profile) require a variable's "invariant"/"centroid" qualifiers to match between vertex and
-// fragment shader. A simple solution to allow such shaders to link is to omit the two qualifiers.
-// AMD driver in Linux requires invariant qualifier to match between vertex and fragment shaders,
-// while ESSL3 disallows invariant qualifier in fragment shader and GLSL >= 4.2 doesn't require
-// invariant qualifier to match between shaders. Remove invariant qualifier from vertex shader to
-// workaround AMD driver bug.
-// Note that the two flags take effect on ESSL3 input shaders translated to GLSL 4.1 or lower and to
-// GLSL 4.2 or newer on Linux AMD.
-// TODO(zmo): This is not a good long-term solution. Simply dropping these qualifiers may break some
-// developers' content. A more complex workaround of dynamically generating, compiling, and
-// re-linking shaders that use these qualifiers should be implemented.
-const ShCompileOptions SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3 = UINT64_C(1) << 7;
+const ShCompileOptions SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = UINT64_C(1) << 6;
+// If a sampler array index happens to be a loop index,
+//   1) if its type is integer, unroll the loop.
+//   2) if its type is float, fail the shader compile.
+// This is to work around a mac driver bug.
+const ShCompileOptions SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX = UINT64_C(1) << 7;
 
 // This flag works around bug in Intel Mac drivers related to abs(i) where
 // i is an integer.
 const ShCompileOptions SH_EMULATE_ABS_INT_FUNCTION = UINT64_C(1) << 8;
 
 // Enforce the GLSL 1.017 Appendix A section 7 packing restrictions.
 // This flag only enforces (and can only enforce) the packing
 // restrictions for uniform variables in both vertex and fragment
@@ -198,16 +181,39 @@ const ShCompileOptions SH_EMULATE_ISNAN_
 // This flag will use all uniforms of unused std140 and shared uniform blocks at the
 // beginning of the vertex/fragment shader's main(). It is intended as a workaround for Mac
 // drivers with shader version 4.10. In those drivers, they will treat unused
 // std140 and shared uniform blocks' members as inactive. However, WebGL2.0 based on
 // OpenGL ES3.0.4 requires all members of a named uniform block declared with a shared or std140
 // layout qualifier to be considered active. The uniform block itself is also considered active.
 const ShCompileOptions SH_USE_UNUSED_STANDARD_SHARED_BLOCKS = UINT64_C(1) << 28;
 
+// This flag will keep invariant declaration for input in fragment shader for GLSL >=4.20 on AMD.
+// From GLSL >= 4.20, it's optional to add invariant for fragment input, but GPU vendors have
+// different implementations about this. Some drivers forbid invariant in fragment for GLSL>= 4.20,
+// e.g. Linux Mesa, some drivers treat that as optional, e.g. NVIDIA, some drivers require invariant
+// must match between vertex and fragment shader, e.g. AMD. The behavior on AMD is obviously wrong.
+// Remove invariant for input in fragment shader to workaround the restriction on Intel Mesa.
+// But don't remove on AMD Linux to avoid triggering the bug on AMD.
+const ShCompileOptions SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT = UINT64_C(1) << 29;
+
+// Due to spec difference between GLSL 4.1 or lower and ESSL3, some platforms (for example, Mac OSX
+// core profile) require a variable's "invariant"/"centroid" qualifiers to match between vertex and
+// fragment shader. A simple solution to allow such shaders to link is to omit the two qualifiers.
+// AMD driver in Linux requires invariant qualifier to match between vertex and fragment shaders,
+// while ESSL3 disallows invariant qualifier in fragment shader and GLSL >= 4.2 doesn't require
+// invariant qualifier to match between shaders. Remove invariant qualifier from vertex shader to
+// workaround AMD driver bug.
+// Note that the two flags take effect on ESSL3 input shaders translated to GLSL 4.1 or lower and to
+// GLSL 4.2 or newer on Linux AMD.
+// TODO(zmo): This is not a good long-term solution. Simply dropping these qualifiers may break some
+// developers' content. A more complex workaround of dynamically generating, compiling, and
+// re-linking shaders that use these qualifiers should be implemented.
+const ShCompileOptions SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3 = UINT64_C(1) << 30;
+
 // Defines alternate strategies for implementing array index clamping.
 enum ShArrayIndexClampingStrategy
 {
     // Use the clamp intrinsic for array index clamping.
     SH_CLAMP_WITH_CLAMP_INTRINSIC = 1,
 
     // Use a user-defined function for array index clamping.
     SH_CLAMP_WITH_USER_DEFINED_INT_CLAMP_FUNCTION
--- a/gfx/angle/moz.build
+++ b/gfx/angle/moz.build
@@ -45,26 +45,28 @@ UNIFIED_SOURCES += [
     'src/compiler/translator/ConstantUnion.cpp',
     'src/compiler/translator/DeferGlobalInitializers.cpp',
     'src/compiler/translator/Diagnostics.cpp',
     'src/compiler/translator/DirectiveHandler.cpp',
     'src/compiler/translator/EmulatePrecision.cpp',
     'src/compiler/translator/ExpandIntegerPowExpressions.cpp',
     'src/compiler/translator/ExtensionGLSL.cpp',
     'src/compiler/translator/FlagStd140Structs.cpp',
+    'src/compiler/translator/ForLoopUnroll.cpp',
     'src/compiler/translator/InfoSink.cpp',
     'src/compiler/translator/Initialize.cpp',
     'src/compiler/translator/InitializeDll.cpp',
     'src/compiler/translator/InitializeParseContext.cpp',
     'src/compiler/translator/InitializeVariables.cpp',
     'src/compiler/translator/Intermediate.cpp',
     'src/compiler/translator/IntermNode.cpp',
     'src/compiler/translator/IntermNodePatternMatcher.cpp',
     'src/compiler/translator/intermOut.cpp',
     'src/compiler/translator/IntermTraverse.cpp',
+    'src/compiler/translator/LoopInfo.cpp',
     'src/compiler/translator/Operator.cpp',
     'src/compiler/translator/OutputESSL.cpp',
     'src/compiler/translator/OutputGLSL.cpp',
     'src/compiler/translator/OutputGLSLBase.cpp',
     'src/compiler/translator/OutputHLSL.cpp',
     'src/compiler/translator/ParseContext.cpp',
     'src/compiler/translator/PoolAlloc.cpp',
     'src/compiler/translator/PruneEmptyDeclarations.cpp',
--- a/gfx/angle/src/commit.h
+++ b/gfx/angle/src/commit.h
@@ -1,3 +1,14 @@
-#define ANGLE_COMMIT_HASH "dec065540d5f"
+//
+// Copyright (c) 2014 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.
+//
+// commit.h:
+//   This is a default commit hash header, when git is not available.
+//
+
+#define ANGLE_COMMIT_HASH "unknown hash"
 #define ANGLE_COMMIT_HASH_SIZE 12
-#define ANGLE_COMMIT_DATE "2017-04-10 16:10:11 +0800"
+#define ANGLE_COMMIT_DATE "unknown date"
+
+#define ANGLE_DISABLE_PROGRAM_BINARY_LOAD
--- a/gfx/angle/src/compiler.gypi
+++ b/gfx/angle/src/compiler.gypi
@@ -49,16 +49,18 @@
             'compiler/translator/EmulateGLFragColorBroadcast.h',
             'compiler/translator/EmulatePrecision.cpp',
             'compiler/translator/EmulatePrecision.h',
             'compiler/translator/ExpandIntegerPowExpressions.cpp',
             'compiler/translator/ExpandIntegerPowExpressions.h',
             'compiler/translator/ExtensionBehavior.h',
             'compiler/translator/FlagStd140Structs.cpp',
             'compiler/translator/FlagStd140Structs.h',
+            'compiler/translator/ForLoopUnroll.cpp',
+            'compiler/translator/ForLoopUnroll.h',
             'compiler/translator/HashNames.h',
             'compiler/translator/InfoSink.cpp',
             'compiler/translator/InfoSink.h',
             'compiler/translator/Initialize.cpp',
             'compiler/translator/Initialize.h',
             'compiler/translator/InitializeDll.cpp',
             'compiler/translator/InitializeDll.h',
             'compiler/translator/InitializeGlobals.h',
@@ -66,16 +68,19 @@
             'compiler/translator/InitializeParseContext.h',
             'compiler/translator/InitializeVariables.cpp',
             'compiler/translator/InitializeVariables.h',
             'compiler/translator/IntermNode.h',
             'compiler/translator/IntermNode.cpp',
             'compiler/translator/IntermTraverse.cpp',
             'compiler/translator/Intermediate.h',
             'compiler/translator/Intermediate.cpp',
+            'compiler/translator/LoopInfo.cpp',
+            'compiler/translator/LoopInfo.h',
+            'compiler/translator/MMap.h',
             'compiler/translator/NodeSearch.h',
             'compiler/translator/Operator.cpp',
             'compiler/translator/Operator.h',
             'compiler/translator/ParseContext.cpp',
             'compiler/translator/ParseContext.h',
             'compiler/translator/PoolAlloc.cpp',
             'compiler/translator/PoolAlloc.h',
             'compiler/translator/Pragma.h',
--- a/gfx/angle/src/compiler/translator/Compiler.cpp
+++ b/gfx/angle/src/compiler/translator/Compiler.cpp
@@ -11,16 +11,17 @@
 #include "angle_gl.h"
 #include "common/utilities.h"
 #include "compiler/translator/AddAndTrueToLoopCondition.h"
 #include "compiler/translator/Cache.h"
 #include "compiler/translator/CallDAG.h"
 #include "compiler/translator/DeferGlobalInitializers.h"
 #include "compiler/translator/EmulateGLFragColorBroadcast.h"
 #include "compiler/translator/EmulatePrecision.h"
+#include "compiler/translator/ForLoopUnroll.h"
 #include "compiler/translator/Initialize.h"
 #include "compiler/translator/InitializeParseContext.h"
 #include "compiler/translator/InitializeVariables.h"
 #include "compiler/translator/ParseContext.h"
 #include "compiler/translator/PruneEmptyDeclarations.h"
 #include "compiler/translator/RegenerateStructNames.h"
 #include "compiler/translator/RemoveInvariantDeclaration.h"
 #include "compiler/translator/RemovePow.h"
@@ -364,16 +365,36 @@ TIntermBlock *TCompiler::compileTreeImpl
             if (!EmulatePrecision::SupportedInLanguage(outputType))
             {
                 infoSink.info.prefix(EPrefixError);
                 infoSink.info << "Precision emulation not supported for this output type.";
                 success = false;
             }
         }
 
+        // Unroll for-loop markup needs to happen after validateLimitations pass.
+        if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
+        {
+            ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex,
+                                       shouldRunLoopAndIndexingValidation(compileOptions));
+            root->traverse(&marker);
+        }
+        if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX))
+        {
+            ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex,
+                                       shouldRunLoopAndIndexingValidation(compileOptions));
+            root->traverse(&marker);
+            if (marker.samplerArrayIndexIsFloatLoopIndex())
+            {
+                infoSink.info.prefix(EPrefixError);
+                infoSink.info << "sampler array index is float loop index";
+                success = false;
+            }
+        }
+
         // Built-in function emulation needs to happen after validateLimitations pass.
         if (success)
         {
             // TODO(jmadill): Remove global pool allocator.
             GetGlobalPoolAllocator()->lock();
             initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions);
             GetGlobalPoolAllocator()->unlock();
             builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
@@ -480,16 +501,27 @@ bool TCompiler::compile(const char *cons
 
     // Apply key workarounds.
     if (shouldFlattenPragmaStdglInvariantAll())
     {
         // This should be harmless to do in all cases, but for the moment, do it only conditionally.
         compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL;
     }
 
+    ShCompileOptions unrollFlags =
+        SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX | SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
+    if ((compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION) != 0 &&
+        (compileOptions & unrollFlags) != 0)
+    {
+        infoSink.info.prefix(EPrefixError);
+        infoSink.info
+            << "Unsupported compile flag combination: unroll & ADD_TRUE_TO_LOOP_CONDITION";
+        return false;
+    }
+
     TScopedPoolAllocator scopedAlloc(&allocator);
     TIntermBlock *root = compileTreeImpl(shaderStrings, numStrings, compileOptions);
 
     if (root)
     {
         if (compileOptions & SH_INTERMEDIATE_TREE)
             TIntermediate::outputTree(root, infoSink.info);
 
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ForLoopUnroll.cpp
@@ -0,0 +1,102 @@
+//
+// Copyright (c) 2002-2013 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.
+//
+
+#include "compiler/translator/ForLoopUnroll.h"
+
+#include "compiler/translator/ValidateLimitations.h"
+#include "angle_gl.h"
+
+namespace sh
+{
+
+bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node)
+{
+    if (mUnrollCondition != kSamplerArrayIndex)
+        return true;
+
+    // If a sampler array index is also the loop index,
+    //   1) if the index type is integer, mark the loop for unrolling;
+    //   2) if the index type if float, set a flag to later fail compile.
+    switch (node->getOp())
+    {
+      case EOpIndexIndirect:
+        if (node->getLeft() != NULL && node->getRight() != NULL && node->getLeft()->getAsSymbolNode())
+        {
+            TIntermSymbol *symbol = node->getLeft()->getAsSymbolNode();
+            if (IsSampler(symbol->getBasicType()) && symbol->isArray() && !mLoopStack.empty())
+            {
+                mVisitSamplerArrayIndexNodeInsideLoop = true;
+                node->getRight()->traverse(this);
+                mVisitSamplerArrayIndexNodeInsideLoop = false;
+                // We have already visited all the children.
+                return false;
+            }
+        }
+        break;
+      default:
+        break;
+    }
+    return true;
+}
+
+bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node)
+{
+    bool canBeUnrolled = mHasRunLoopValidation;
+    if (!mHasRunLoopValidation)
+    {
+        canBeUnrolled = ValidateLimitations::IsLimitedForLoop(node);
+    }
+    if (mUnrollCondition == kIntegerIndex && canBeUnrolled)
+    {
+        // Check if loop index type is integer.
+        // This is called after ValidateLimitations pass, so the loop has the limited form specified
+        // in ESSL 1.00 appendix A.
+        TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
+        TIntermSymbol *symbol = (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
+        if (symbol->getBasicType() == EbtInt)
+            node->setUnrollFlag(true);
+    }
+
+    TIntermNode *body = node->getBody();
+    if (body != nullptr)
+    {
+        if (canBeUnrolled)
+        {
+            mLoopStack.push(node);
+            body->traverse(this);
+            mLoopStack.pop();
+        }
+        else
+        {
+            body->traverse(this);
+        }
+    }
+    // The loop is fully processed - no need to visit children.
+    return false;
+}
+
+void ForLoopUnrollMarker::visitSymbol(TIntermSymbol* symbol)
+{
+    if (!mVisitSamplerArrayIndexNodeInsideLoop)
+        return;
+    TIntermLoop *loop = mLoopStack.findLoop(symbol);
+    if (loop)
+    {
+        switch (symbol->getBasicType())
+        {
+          case EbtFloat:
+            mSamplerArrayIndexIsFloatLoopIndex = true;
+            break;
+          case EbtInt:
+            loop->setUnrollFlag(true);
+            break;
+          default:
+            UNREACHABLE();
+        }
+    }
+}
+
+}  // namespace sh
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ForLoopUnroll.h
@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2011 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.
+//
+
+#ifndef COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
+#define COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
+
+#include "compiler/translator/LoopInfo.h"
+
+namespace sh
+{
+
+// This class detects for-loops that needs to be unrolled.
+// Currently we support two unroll conditions:
+//   1) kForLoopWithIntegerIndex: unroll if the index type is integer.
+//   2) kForLoopWithSamplerArrayIndex: unroll where a sampler array index
+//      is also the loop integer index, and reject and fail a compile
+//      where a sampler array index is also the loop float index.
+class ForLoopUnrollMarker : public TIntermTraverser
+{
+  public:
+    enum UnrollCondition
+    {
+        kIntegerIndex,
+        kSamplerArrayIndex
+    };
+
+    ForLoopUnrollMarker(UnrollCondition condition, bool hasRunLoopValidation)
+        : TIntermTraverser(true, false, false),
+          mUnrollCondition(condition),
+          mSamplerArrayIndexIsFloatLoopIndex(false),
+          mVisitSamplerArrayIndexNodeInsideLoop(false),
+          mHasRunLoopValidation(hasRunLoopValidation)
+    {
+    }
+
+    bool visitBinary(Visit, TIntermBinary *node) override;
+    bool visitLoop(Visit, TIntermLoop *node) override;
+    void visitSymbol(TIntermSymbol *node) override;
+
+    bool samplerArrayIndexIsFloatLoopIndex() const
+    {
+        return mSamplerArrayIndexIsFloatLoopIndex;
+    }
+
+  private:
+    UnrollCondition mUnrollCondition;
+    TLoopStack mLoopStack;
+    bool mSamplerArrayIndexIsFloatLoopIndex;
+    bool mVisitSamplerArrayIndexNodeInsideLoop;
+    bool mHasRunLoopValidation;
+};
+
+}  // namespace sh
+
+#endif // COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
--- a/gfx/angle/src/compiler/translator/IntermNode.h
+++ b/gfx/angle/src/compiler/translator/IntermNode.h
@@ -196,17 +196,17 @@ enum TLoopType
 class TIntermLoop : public TIntermNode
 {
   public:
     TIntermLoop(TLoopType type,
                 TIntermNode *init,
                 TIntermTyped *cond,
                 TIntermTyped *expr,
                 TIntermBlock *body)
-        : mType(type), mInit(init), mCond(cond), mExpr(expr), mBody(body)
+        : mType(type), mInit(init), mCond(cond), mExpr(expr), mBody(body), mUnrollFlag(false)
     {
     }
 
     TIntermLoop *getAsLoopNode() override { return this; }
     void traverse(TIntermTraverser *it) override;
     bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
 
     TLoopType getType() const { return mType; }
@@ -214,22 +214,27 @@ class TIntermLoop : public TIntermNode
     TIntermTyped *getCondition() { return mCond; }
     TIntermTyped *getExpression() { return mExpr; }
     TIntermBlock *getBody() { return mBody; }
 
     void setCondition(TIntermTyped *condition) { mCond = condition; }
     void setExpression(TIntermTyped *expression) { mExpr = expression; }
     void setBody(TIntermBlock *body) { mBody = body; }
 
+    void setUnrollFlag(bool flag) { mUnrollFlag = flag; }
+    bool getUnrollFlag() const { return mUnrollFlag; }
+
   protected:
     TLoopType mType;
     TIntermNode *mInit;  // for-loop initialization
     TIntermTyped *mCond; // loop exit condition
     TIntermTyped *mExpr; // for-loop expression
     TIntermBlock *mBody;  // loop body
+
+    bool mUnrollFlag; // Whether the loop should be unrolled or not.
 };
 
 //
 // Handle break, continue, return, and kill.
 //
 class TIntermBranch : public TIntermNode
 {
   public:
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/LoopInfo.cpp
@@ -0,0 +1,214 @@
+//
+// Copyright (c) 2002-2014 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.
+//
+
+#include "compiler/translator/LoopInfo.h"
+
+namespace sh
+{
+
+namespace
+{
+
+int EvaluateIntConstant(TIntermConstantUnion *node)
+{
+    ASSERT(node && node->getUnionArrayPointer());
+    return node->getIConst(0);
+}
+
+int GetLoopIntIncrement(TIntermLoop *node)
+{
+    TIntermNode *expr = node->getExpression();
+    // for expression has one of the following forms:
+    //     loop_index++
+    //     loop_index--
+    //     loop_index += constant_expression
+    //     loop_index -= constant_expression
+    //     ++loop_index
+    //     --loop_index
+    // The last two forms are not specified in the spec, but I am assuming
+    // its an oversight.
+    TIntermUnary *unOp = expr->getAsUnaryNode();
+    TIntermBinary *binOp = unOp ? NULL : expr->getAsBinaryNode();
+
+    TOperator op = EOpNull;
+    TIntermConstantUnion *incrementNode = NULL;
+    if (unOp)
+    {
+        op = unOp->getOp();
+    }
+    else if (binOp)
+    {
+        op = binOp->getOp();
+        ASSERT(binOp->getRight());
+        incrementNode = binOp->getRight()->getAsConstantUnion();
+        ASSERT(incrementNode);
+    }
+
+    int increment = 0;
+    // The operator is one of: ++ -- += -=.
+    switch (op)
+    {
+      case EOpPostIncrement:
+      case EOpPreIncrement:
+        ASSERT(unOp && !binOp);
+        increment = 1;
+        break;
+      case EOpPostDecrement:
+      case EOpPreDecrement:
+        ASSERT(unOp && !binOp);
+        increment = -1;
+        break;
+      case EOpAddAssign:
+        ASSERT(!unOp && binOp);
+        increment = EvaluateIntConstant(incrementNode);
+        break;
+      case EOpSubAssign:
+        ASSERT(!unOp && binOp);
+        increment = - EvaluateIntConstant(incrementNode);
+        break;
+      default:
+        UNREACHABLE();
+    }
+
+    return increment;
+}
+
+}  // namespace anonymous
+
+TLoopIndexInfo::TLoopIndexInfo()
+    : mId(-1),
+      mType(EbtVoid),
+      mInitValue(0),
+      mStopValue(0),
+      mIncrementValue(0),
+      mOp(EOpNull),
+      mCurrentValue(0)
+{
+}
+
+void TLoopIndexInfo::fillInfo(TIntermLoop *node)
+{
+    if (node == NULL)
+        return;
+
+    // Here we assume all the operations are valid, because the loop node is
+    // already validated in ValidateLimitations.
+    TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
+    TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
+    TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
+
+    mId = symbol->getId();
+    mType = symbol->getBasicType();
+
+    if (mType == EbtInt)
+    {
+        TIntermConstantUnion* initNode = declInit->getRight()->getAsConstantUnion();
+        mInitValue = EvaluateIntConstant(initNode);
+        mCurrentValue = mInitValue;
+        mIncrementValue = GetLoopIntIncrement(node);
+
+        TIntermBinary* binOp = node->getCondition()->getAsBinaryNode();
+        mStopValue = EvaluateIntConstant(
+            binOp->getRight()->getAsConstantUnion());
+        mOp = binOp->getOp();
+    }
+}
+
+bool TLoopIndexInfo::satisfiesLoopCondition() const
+{
+    // Relational operator is one of: > >= < <= == or !=.
+    switch (mOp)
+    {
+      case EOpEqual:
+        return (mCurrentValue == mStopValue);
+      case EOpNotEqual:
+        return (mCurrentValue != mStopValue);
+      case EOpLessThan:
+        return (mCurrentValue < mStopValue);
+      case EOpGreaterThan:
+        return (mCurrentValue > mStopValue);
+      case EOpLessThanEqual:
+        return (mCurrentValue <= mStopValue);
+      case EOpGreaterThanEqual:
+        return (mCurrentValue >= mStopValue);
+      default:
+        UNREACHABLE();
+        return false;
+    }
+}
+
+TLoopInfo::TLoopInfo()
+    : loop(NULL)
+{
+}
+
+TLoopInfo::TLoopInfo(TIntermLoop *node)
+    : loop(node)
+{
+    index.fillInfo(node);
+}
+
+TIntermLoop *TLoopStack::findLoop(TIntermSymbol *symbol)
+{
+    if (!symbol)
+        return NULL;
+    for (iterator iter = begin(); iter != end(); ++iter)
+    {
+        if (iter->index.getId() == symbol->getId())
+            return iter->loop;
+    }
+    return NULL;
+}
+
+TLoopIndexInfo *TLoopStack::getIndexInfo(TIntermSymbol *symbol)
+{
+    if (!symbol)
+        return NULL;
+    for (iterator iter = begin(); iter != end(); ++iter)
+    {
+        if (iter->index.getId() == symbol->getId())
+            return &(iter->index);
+    }
+    return NULL;
+}
+
+void TLoopStack::step()
+{
+    ASSERT(!empty());
+    rbegin()->index.step();
+}
+
+bool TLoopStack::satisfiesLoopCondition()
+{
+    ASSERT(!empty());
+    return rbegin()->index.satisfiesLoopCondition();
+}
+
+bool TLoopStack::needsToReplaceSymbolWithValue(TIntermSymbol *symbol)
+{
+    TIntermLoop *loop = findLoop(symbol);
+    return loop && loop->getUnrollFlag();
+}
+
+int TLoopStack::getLoopIndexValue(TIntermSymbol *symbol)
+{
+    TLoopIndexInfo *info = getIndexInfo(symbol);
+    ASSERT(info);
+    return info->getCurrentValue();
+}
+
+void TLoopStack::push(TIntermLoop *loop)
+{
+    TLoopInfo info(loop);
+    push_back(info);
+}
+
+void TLoopStack::pop()
+{
+    pop_back();
+}
+
+}  // namespace sh
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/LoopInfo.h
@@ -0,0 +1,85 @@
+//
+// Copyright (c) 2002-2014 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.
+//
+
+#ifndef COMPILER_TRANSLATOR_LOOPINFO_H_
+#define COMPILER_TRANSLATOR_LOOPINFO_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+class TLoopIndexInfo
+{
+  public:
+    TLoopIndexInfo();
+
+    // If type is EbtInt, fill all fields of the structure with info
+    // extracted from a loop node.
+    // If type is not EbtInt, only fill id and type.
+    void fillInfo(TIntermLoop *node);
+
+    int getId() const { return mId; }
+    void setId(int id) { mId = id; }
+    TBasicType getType() const { return mType; }
+    void setType(TBasicType type) { mType = type; }
+    int getCurrentValue() const { return mCurrentValue; }
+
+    void step() { mCurrentValue += mIncrementValue; }
+
+    // Check if the current value satisfies the loop condition.
+    bool satisfiesLoopCondition() const;
+
+  private:
+    int mId;
+    TBasicType mType;  // Either EbtInt or EbtFloat
+
+    // Below fields are only valid if the index's type is int.
+    int mInitValue;
+    int mStopValue;
+    int mIncrementValue;
+    TOperator mOp;
+    int mCurrentValue;
+};
+
+struct TLoopInfo
+{
+    TLoopIndexInfo index;
+    TIntermLoop *loop;
+
+    TLoopInfo();
+    TLoopInfo(TIntermLoop *node);
+};
+
+class TLoopStack : public TVector<TLoopInfo>
+{
+  public:
+    // Search loop stack for a loop whose index matches the input symbol.
+    TIntermLoop *findLoop(TIntermSymbol *symbol);
+
+    // Find the loop index info in the loop stack by the input symbol.
+    TLoopIndexInfo *getIndexInfo(TIntermSymbol *symbol);
+
+    // Update the currentValue for the next loop iteration.
+    void step();
+
+    // Return false if loop condition is no longer satisfied.
+    bool satisfiesLoopCondition();
+
+    // Check if the symbol is the index of a loop that's unrolled.
+    bool needsToReplaceSymbolWithValue(TIntermSymbol *symbol);
+
+    // Return the current value of a given loop index symbol.
+    int getLoopIndexValue(TIntermSymbol *symbol);
+
+    void push(TIntermLoop *info);
+    void pop();
+};
+
+}  // namespace sh
+
+#endif // COMPILER_TRANSLATOR_LOOPINFO_H_
+
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/MMap.h
@@ -0,0 +1,56 @@
+//
+// Copyright (c) 2002-2010 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.
+//
+
+#ifndef COMPILER_TRANSLATOR_MMAP_H_
+#define COMPILER_TRANSLATOR_MMAP_H_
+
+//
+// Encapsulate memory mapped files
+//
+
+class TMMap {
+public:
+    TMMap(const char* fileName) : 
+        fSize(-1), // -1 is the error value returned by GetFileSize()
+        fp(NULL),
+        fBuff(0)   // 0 is the error value returned by MapViewOfFile()
+    {
+        if ((fp = fopen(fileName, "r")) == NULL)
+            return;
+        char c = getc(fp);
+        fSize = 0;
+        while (c != EOF) {
+            fSize++;
+            c = getc(fp);
+        }
+        if (c == EOF)
+            fSize++;
+        rewind(fp);
+        fBuff = (char*)malloc(sizeof(char) * fSize);
+        int count = 0;
+        c = getc(fp);
+        while (c != EOF) {
+            fBuff[count++] = c;
+            c = getc(fp);
+        }
+        fBuff[count++] = c;
+    }
+
+    char* getData() { return fBuff; }
+    int   getSize() { return fSize; }
+
+    ~TMMap() {
+        if (fp != NULL)
+            fclose(fp);
+    }
+    
+private:
+    int             fSize;      // size of file to map in
+    FILE *fp;
+    char*           fBuff;      // the actual data;
+};
+
+#endif // COMPILER_TRANSLATOR_MMAP_H_
--- a/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp
+++ b/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp
@@ -384,17 +384,20 @@ void TOutputGLSLBase::writeConstructorTr
     {
         writeTriplet(visit, nullptr, ", ", ")");
     }
 }
 
 void TOutputGLSLBase::visitSymbol(TIntermSymbol *node)
 {
     TInfoSinkBase &out = objSink();
-    out << hashVariableName(node->getName());
+    if (mLoopUnrollStack.needsToReplaceSymbolWithValue(node))
+        out << mLoopUnrollStack.getLoopIndexValue(node);
+    else
+        out << hashVariableName(node->getName());
 
     if (mDeclaringVariables && node->getType().isArray())
         out << arrayBrackets(node->getType());
 }
 
 void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion *node)
 {
     writeConstantUnion(node->getType(), node->getUnionArrayPointer());
@@ -1129,32 +1132,59 @@ bool TOutputGLSLBase::visitDeclaration(V
 bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node)
 {
     TInfoSinkBase &out = objSink();
 
     incrementDepth(node);
 
     TLoopType loopType = node->getType();
 
+    // Only for loops can be unrolled
+    ASSERT(!node->getUnrollFlag() || loopType == ELoopFor);
+
     if (loopType == ELoopFor)  // for loop
     {
-        out << "for (";
-        if (node->getInit())
-            node->getInit()->traverse(this);
-        out << "; ";
+        if (!node->getUnrollFlag())
+        {
+            out << "for (";
+            if (node->getInit())
+                node->getInit()->traverse(this);
+            out << "; ";
+
+            if (node->getCondition())
+                node->getCondition()->traverse(this);
+            out << "; ";
+
+            if (node->getExpression())
+                node->getExpression()->traverse(this);
+            out << ")\n";
 
-        if (node->getCondition())
-            node->getCondition()->traverse(this);
-        out << "; ";
+            visitCodeBlock(node->getBody());
+        }
+        else
+        {
+            // Need to put a one-iteration loop here to handle break.
+            TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
+            TIntermSymbol *indexSymbol =
+                (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
+            TString name = hashVariableName(indexSymbol->getName());
+            out << "for (int " << name << " = 0; "
+                << name << " < 1; "
+                << "++" << name << ")\n";
 
-        if (node->getExpression())
-            node->getExpression()->traverse(this);
-        out << ")\n";
-
-        visitCodeBlock(node->getBody());
+            out << "{\n";
+            mLoopUnrollStack.push(node);
+            while (mLoopUnrollStack.satisfiesLoopCondition())
+            {
+                visitCodeBlock(node->getBody());
+                mLoopUnrollStack.step();
+            }
+            mLoopUnrollStack.pop();
+            out << "}\n";
+        }
     }
     else if (loopType == ELoopWhile)  // while loop
     {
         out << "while (";
         ASSERT(node->getCondition() != NULL);
         node->getCondition()->traverse(this);
         out << ")\n";
 
--- a/gfx/angle/src/compiler/translator/OutputGLSLBase.h
+++ b/gfx/angle/src/compiler/translator/OutputGLSLBase.h
@@ -5,16 +5,17 @@
 //
 
 #ifndef COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_
 #define COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_
 
 #include <set>
 
 #include "compiler/translator/IntermNode.h"
+#include "compiler/translator/LoopInfo.h"
 #include "compiler/translator/ParseContext.h"
 
 namespace sh
 {
 
 class TOutputGLSLBase : public TIntermTraverser
 {
   public:
@@ -86,16 +87,19 @@ class TOutputGLSLBase : public TIntermTr
     const char *mapQualifierToString(TQualifier qialifier);
 
     TInfoSinkBase &mObjSink;
     bool mDeclaringVariables;
 
     // This set contains all the ids of the structs from every scope.
     std::set<int> mDeclaredStructs;
 
+    // Stack of loops that need to be unrolled.
+    TLoopStack mLoopUnrollStack;
+
     ShArrayIndexClampingStrategy mClampingStrategy;
 
     // name hashing.
     ShHashFunction64 mHashFunction;
 
     NameMap &mNameMap;
 
     TSymbolTable &mSymbolTable;
--- a/gfx/angle/src/compiler/translator/ValidateLimitations.cpp
+++ b/gfx/angle/src/compiler/translator/ValidateLimitations.cpp
@@ -11,62 +11,52 @@
 #include "angle_gl.h"
 
 namespace sh
 {
 
 namespace
 {
 
-int GetLoopSymbolId(TIntermLoop *loop)
-{
-    // Here we assume all the operations are valid, because the loop node is
-    // already validated before this call.
-    TIntermSequence *declSeq = loop->getInit()->getAsDeclarationNode()->getSequence();
-    TIntermBinary *declInit  = (*declSeq)[0]->getAsBinaryNode();
-    TIntermSymbol *symbol    = declInit->getLeft()->getAsSymbolNode();
-
-    return symbol->getId();
-}
-
 // Traverses a node to check if it represents a constant index expression.
 // Definition:
 // constant-index-expressions are a superset of constant-expressions.
 // Constant-index-expressions can include loop indices as defined in
 // GLSL ES 1.0 spec, Appendix A, section 4.
 // The following are constant-index-expressions:
 // - Constant expressions
 // - Loop indices as defined in section 4
 // - Expressions composed of both of the above
 class ValidateConstIndexExpr : public TIntermTraverser
 {
   public:
-    ValidateConstIndexExpr(const std::vector<int> &loopSymbols)
-        : TIntermTraverser(true, false, false), mValid(true), mLoopSymbolIds(loopSymbols)
+    ValidateConstIndexExpr(TLoopStack& stack)
+        : TIntermTraverser(true, false, false),
+          mValid(true),
+          mLoopStack(stack)
     {
     }
 
     // Returns true if the parsed node represents a constant index expression.
     bool isValid() const { return mValid; }
 
     void visitSymbol(TIntermSymbol *symbol) override
     {
         // Only constants and loop indices are allowed in a
         // constant index expression.
         if (mValid)
         {
-            bool isLoopSymbol = std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(),
-                                          symbol->getId()) != mLoopSymbolIds.end();
-            mValid = (symbol->getQualifier() == EvqConst) || isLoopSymbol;
+            mValid = (symbol->getQualifier() == EvqConst) ||
+                     (mLoopStack.findLoop(symbol));
         }
     }
 
   private:
     bool mValid;
-    const std::vector<int> mLoopSymbolIds;
+    TLoopStack& mLoopStack;
 };
 
 }  // namespace anonymous
 
 ValidateLimitations::ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink)
     : TIntermTraverser(true, false, false),
       mShaderType(shaderType),
       mSink(sink),
@@ -85,19 +75,19 @@ bool ValidateLimitations::IsLimitedForLo
     validate.mValidateInnerLoops = false;
     if (!validate.validateLoopType(loop))
         return false;
     if (!validate.validateForLoopHeader(loop))
         return false;
     TIntermNode *body = loop->getBody();
     if (body != nullptr)
     {
-        validate.mLoopSymbolIds.push_back(GetLoopSymbolId(loop));
+        validate.mLoopStack.push(loop);
         body->traverse(&validate);
-        validate.mLoopSymbolIds.pop_back();
+        validate.mLoopStack.pop();
     }
     return (validate.mNumErrors == 0);
 }
 
 bool ValidateLimitations::visitBinary(Visit, TIntermBinary *node)
 {
     // Check if loop index is modified in the loop body.
     validateOperation(node, node->getLeft());
@@ -145,19 +135,19 @@ bool ValidateLimitations::visitLoop(Visi
         return false;
 
     if (!validateForLoopHeader(node))
         return false;
 
     TIntermNode *body = node->getBody();
     if (body != NULL)
     {
-        mLoopSymbolIds.push_back(GetLoopSymbolId(node));
+        mLoopStack.push(node);
         body->traverse(this);
-        mLoopSymbolIds.pop_back();
+        mLoopStack.pop();
     }
 
     // The loop is fully processed - no need to visit children.
     return false;
 }
 
 void ValidateLimitations::error(TSourceLoc loc,
                                 const char *reason, const char *token)
@@ -168,23 +158,22 @@ void ValidateLimitations::error(TSourceL
         mSink->location(loc);
         (*mSink) << "'" << token << "' : " << reason << "\n";
     }
     ++mNumErrors;
 }
 
 bool ValidateLimitations::withinLoopBody() const
 {
-    return !mLoopSymbolIds.empty();
+    return !mLoopStack.empty();
 }
 
 bool ValidateLimitations::isLoopIndex(TIntermSymbol *symbol)
 {
-    return std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(), symbol->getId()) !=
-           mLoopSymbolIds.end();
+    return mLoopStack.findLoop(symbol) != NULL;
 }
 
 bool ValidateLimitations::validateLoopType(TIntermLoop *node)
 {
     TLoopType type = node->getType();
     if (type == ELoopFor)
         return true;
 
@@ -480,17 +469,17 @@ bool ValidateLimitations::isConstExpr(TI
     ASSERT(node != nullptr);
     return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
 }
 
 bool ValidateLimitations::isConstIndexExpr(TIntermNode *node)
 {
     ASSERT(node != NULL);
 
-    ValidateConstIndexExpr validate(mLoopSymbolIds);
+    ValidateConstIndexExpr validate(mLoopStack);
     node->traverse(&validate);
     return validate.isValid();
 }
 
 bool ValidateLimitations::validateIndexing(TIntermBinary *node)
 {
     ASSERT((node->getOp() == EOpIndexDirect) ||
            (node->getOp() == EOpIndexIndirect));
--- a/gfx/angle/src/compiler/translator/ValidateLimitations.h
+++ b/gfx/angle/src/compiler/translator/ValidateLimitations.h
@@ -3,16 +3,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
 
 #ifndef COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
 #define COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
 
 #include "compiler/translator/IntermNode.h"
+#include "compiler/translator/LoopInfo.h"
 
 namespace sh
 {
 
 class TInfoSinkBase;
 
 // Traverses intermediate tree to ensure that the shader does not exceed the
 // minimum functionality mandated in GLSL 1.0 spec, Appendix A.
@@ -52,16 +53,16 @@ class ValidateLimitations : public TInte
     // mandated in GLSL 1.0 spec, Appendix A, Section 5.
     bool isConstExpr(TIntermNode *node);
     bool isConstIndexExpr(TIntermNode *node);
     bool validateIndexing(TIntermBinary *node);
 
     sh::GLenum mShaderType;
     TInfoSinkBase *mSink;
     int mNumErrors;
-    std::vector<int> mLoopSymbolIds;
+    TLoopStack mLoopStack;
     bool mValidateIndexing;
     bool mValidateInnerLoops;
 };
 
 }  // namespace sh
 
 #endif // COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
--- a/gfx/angle/src/libANGLE/moz.build
+++ b/gfx/angle/src/libANGLE/moz.build
@@ -46,26 +46,28 @@ UNIFIED_SOURCES += [
     '../compiler/translator/ConstantUnion.cpp',
     '../compiler/translator/DeferGlobalInitializers.cpp',
     '../compiler/translator/Diagnostics.cpp',
     '../compiler/translator/DirectiveHandler.cpp',
     '../compiler/translator/EmulatePrecision.cpp',
     '../compiler/translator/ExpandIntegerPowExpressions.cpp',
     '../compiler/translator/ExtensionGLSL.cpp',
     '../compiler/translator/FlagStd140Structs.cpp',
+    '../compiler/translator/ForLoopUnroll.cpp',
     '../compiler/translator/InfoSink.cpp',
     '../compiler/translator/Initialize.cpp',
     '../compiler/translator/InitializeDll.cpp',
     '../compiler/translator/InitializeParseContext.cpp',
     '../compiler/translator/InitializeVariables.cpp',
     '../compiler/translator/Intermediate.cpp',
     '../compiler/translator/IntermNode.cpp',
     '../compiler/translator/IntermNodePatternMatcher.cpp',
     '../compiler/translator/intermOut.cpp',
     '../compiler/translator/IntermTraverse.cpp',
+    '../compiler/translator/LoopInfo.cpp',
     '../compiler/translator/Operator.cpp',
     '../compiler/translator/OutputESSL.cpp',
     '../compiler/translator/OutputGLSL.cpp',
     '../compiler/translator/OutputGLSLBase.cpp',
     '../compiler/translator/OutputHLSL.cpp',
     '../compiler/translator/ParseContext.cpp',
     '../compiler/translator/PoolAlloc.cpp',
     '../compiler/translator/PruneEmptyDeclarations.cpp',
--- a/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp
+++ b/gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp
@@ -144,16 +144,22 @@ class MalformedComputeShaderTest : publi
     {
         ShBuiltInResources resources;
         sh::InitBuiltInResources(&resources);
         mTranslator = new TranslatorESSL(GL_COMPUTE_SHADER, SH_GLES3_1_SPEC);
         ASSERT_TRUE(mTranslator->Init(resources));
     }
 };
 
+class UnrollForLoopsTest : public MalformedShaderTest
+{
+  public:
+    UnrollForLoopsTest() { mExtraCompileOptions = SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX; }
+};
+
 // This is a test for a bug that used to exist in ANGLE:
 // Calling a function with all parameters missing should not succeed.
 TEST_F(MalformedShaderTest, FunctionParameterMismatch)
 {
     const std::string &shaderString =
         "precision mediump float;\n"
         "float fun(float a) {\n"
         "   return a * 2.0;\n"
@@ -1434,16 +1440,55 @@ TEST_F(MalformedWebGL1ShaderTest, NonCon
         "    }\n"
         "}\n";
     if (compile(shaderString))
     {
         FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
     }
 }
 
+// Regression test for an old crash bug in ANGLE.
+// ForLoopUnroll used to crash when it encountered a while loop.
+TEST_F(UnrollForLoopsTest, WhileLoop)
+{
+    const std::string &shaderString =
+        "precision mediump float;\n"
+        "void main()\n"
+        "{\n"
+        "    while (true) {\n"
+        "        gl_FragColor = vec4(0.0);\n"
+        "        break;\n"
+        "    }\n"
+        "}\n";
+    if (!compile(shaderString))
+    {
+        FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
+    }
+}
+
+// Regression test for an old crash bug in ANGLE.
+// ForLoopUnroll used to crash when it encountered a loop that didn't fit the ESSL 1.00
+// Appendix A limitations.
+TEST_F(UnrollForLoopsTest, UnlimitedForLoop)
+{
+    const std::string &shaderString =
+        "precision mediump float;\n"
+        "void main()\n"
+        "{\n"
+        "    for (;true;) {\n"
+        "        gl_FragColor = vec4(0.0);\n"
+        "        break;\n"
+        "    }\n"
+        "}\n";
+    if (!compile(shaderString))
+    {
+        FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
+    }
+}
+
 // Check that indices that are not integers are rejected.
 // The check should be done even if ESSL 1.00 Appendix A limitations are not applied.
 TEST_F(MalformedShaderTest, NonIntegerIndex)
 {
     const std::string &shaderString =
         "precision mediump float;\n"
         "void main()\n"
         "{\n"