compiler: Work around a HLSL compiler aliasing opt bug.

BUG=angleproject:1448

Change-Id: I7d5bcbd100069152cea0cb03bc4fa6af1044460b
Reviewed-on: https://chromium-review.googlesource.com/376020
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/AddDefaultReturnStatements.cpp b/src/compiler/translator/AddDefaultReturnStatements.cpp
index 97111d1..06aa738 100644
--- a/src/compiler/translator/AddDefaultReturnStatements.cpp
+++ b/src/compiler/translator/AddDefaultReturnStatements.cpp
@@ -54,48 +54,13 @@
         return true;
     }
 
-    static TIntermTyped *GenerateTypeConstructor(const TType &returnType)
-    {
-        // Base case, constructing a single element
-        if (!returnType.isArray())
-        {
-            size_t objectSize             = returnType.getObjectSize();
-            TConstantUnion *constantUnion = new TConstantUnion[objectSize];
-            for (size_t constantIdx = 0; constantIdx < objectSize; constantIdx++)
-            {
-                constantUnion[constantIdx].setFConst(0.0f);
-            }
-
-            TIntermConstantUnion *intermConstantUnion =
-                new TIntermConstantUnion(constantUnion, returnType);
-            return intermConstantUnion;
-        }
-
-        // Recursive case, construct an array of single elements
-        TIntermAggregate *constructorAggrigate =
-            new TIntermAggregate(TypeToConstructorOperator(returnType));
-        constructorAggrigate->setType(returnType);
-
-        size_t arraySize = returnType.getArraySize();
-        for (size_t arrayIdx = 0; arrayIdx < arraySize; arrayIdx++)
-        {
-            TType arrayElementType(returnType);
-            arrayElementType.clearArrayness();
-
-            constructorAggrigate->getSequence()->push_back(
-                GenerateTypeConstructor(arrayElementType));
-        }
-
-        return constructorAggrigate;
-    }
-
     bool visitAggregate(Visit, TIntermAggregate *node) override
     {
         TType returnType;
         if (IsFunctionWithoutReturnStatement(node, &returnType))
         {
             TIntermBranch *branch =
-                new TIntermBranch(EOpReturn, GenerateTypeConstructor(returnType));
+                new TIntermBranch(EOpReturn, TIntermTyped::CreateZero(returnType));
 
             TIntermAggregate *lastNode = node->getSequence()->back()->getAsAggregate();
             lastNode->getSequence()->push_back(branch);
diff --git a/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp b/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
new file mode 100644
index 0000000..bca9338
--- /dev/null
+++ b/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
@@ -0,0 +1,106 @@
+//
+// Copyright (c) 2016 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.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+//      may record a variable as aliasing another. Sometimes the alias information gets garbled
+//      so we work around this issue by breaking the aliasing chain in inner loops.
+
+#include "BreakVariableAliasingInInnerLoops.h"
+
+#include "compiler/translator/IntermNode.h"
+
+// A HLSL compiler developer gave us more details on the root cause and the workaround needed:
+//     The root problem is that if the HLSL compiler is applying aliasing information even on
+//     incomplete simulations (in this case, a single pass). The bug is triggered by an assignment
+//     that comes from a series of assignments, possibly with swizzled or ternary operators with
+//     known conditionals, where the source is before the loop.
+//     So, a workaround is to add a +0 term to variables the first time they are assigned to in
+//     an inner loop (if they are declared in an outside scope, otherwise there is no need).
+//     This will break the aliasing chain.
+
+// For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because
+// the bug only shows up with swizzles, and ternary assignment, whole array or whole structure
+// assignment don't need a workaround.
+
+namespace
+{
+
+class AliasingBreaker : public TIntermTraverser
+{
+  public:
+    AliasingBreaker() : TIntermTraverser(true, false, true) {}
+
+  protected:
+    bool visitBinary(Visit visit, TIntermBinary *binary)
+    {
+        if (visit != PreVisit)
+        {
+            return false;
+        }
+
+        if (mLoopLevel < 2 || !binary->isAssignment())
+        {
+            return true;
+        }
+
+        TIntermTyped *B = binary->getRight();
+        TType type      = B->getType();
+
+        if (!type.isScalar() && !type.isVector() && !type.isMatrix())
+        {
+            return true;
+        }
+
+        if (type.isArray() || IsSampler(type.getBasicType()))
+        {
+            return true;
+        }
+
+        // We have a scalar / vector / matrix assignment with loop depth 2.
+        // Transform it from
+        //    A = B
+        // to
+        //    A = (B + typeof<B>(0));
+
+        TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, TIntermTyped::CreateZero(type));
+        bPlusZero->setLine(B->getLine());
+
+        binary->setRight(bPlusZero);
+
+        return true;
+    }
+
+    bool visitLoop(Visit visit, TIntermLoop *loop)
+    {
+        if (visit == PreVisit)
+        {
+            mLoopLevel++;
+        }
+        else
+        {
+            ASSERT(mLoopLevel > 0);
+            mLoopLevel--;
+        }
+
+        return true;
+    }
+
+  private:
+    int mLoopLevel = 0;
+};
+
+}  // anonymous namespace
+
+namespace sh
+{
+
+void BreakVariableAliasingInInnerLoops(TIntermNode *root)
+{
+    AliasingBreaker breaker;
+    root->traverse(&breaker);
+}
+
+}  // namespace sh
diff --git a/src/compiler/translator/BreakVariableAliasingInInnerLoops.h b/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
new file mode 100644
index 0000000..b1d906f
--- /dev/null
+++ b/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2016 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.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+//      may record a variable as aliasing another. Sometimes the alias information gets garbled
+//      so we work around this issue by breaking the aliasing chain in inner loops.
+
+#ifndef COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
+#define COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
+
+class TIntermNode;
+
+namespace sh
+{
+
+void BreakVariableAliasingInInnerLoops(TIntermNode *root);
+
+}  // namespace sh
+
+#endif  // COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
diff --git a/src/compiler/translator/EmulateGLFragColorBroadcast.cpp b/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
index a664d9e..c3aad7b 100644
--- a/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
+++ b/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
@@ -17,44 +17,18 @@
 namespace
 {
 
-TIntermConstantUnion *constructIndexNode(int index)
-{
-    TConstantUnion *u = new TConstantUnion[1];
-    u[0].setIConst(index);
-
-    TType type(EbtInt, EbpUndefined, EvqConst, 1);
-    TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
-    return node;
-}
-
-TIntermBinary *constructGLFragDataNode(int index)
-{
-    TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect);
-    TIntermSymbol *symbol      = new TIntermSymbol(0, "gl_FragData", TType(EbtFloat, 4));
-    indexDirect->setLeft(symbol);
-    TIntermConstantUnion *indexNode = constructIndexNode(index);
-    indexDirect->setRight(indexNode);
-    return indexDirect;
-}
-
-TIntermBinary *constructGLFragDataAssignNode(int index)
-{
-    TIntermBinary *assign = new TIntermBinary(EOpAssign);
-    assign->setLeft(constructGLFragDataNode(index));
-    assign->setRight(constructGLFragDataNode(0));
-    assign->setType(TType(EbtFloat, 4));
-    return assign;
-}
-
 class GLFragColorBroadcastTraverser : public TIntermTraverser
 {
   public:
-    GLFragColorBroadcastTraverser()
-        : TIntermTraverser(true, false, false), mMainSequence(nullptr), mGLFragColorUsed(false)
+    GLFragColorBroadcastTraverser(int maxDrawBuffers)
+        : TIntermTraverser(true, false, false),
+          mMainSequence(nullptr),
+          mGLFragColorUsed(false),
+          mMaxDrawBuffers(maxDrawBuffers)
     {
     }
 
-    void broadcastGLFragColor(int maxDrawBuffers);
+    void broadcastGLFragColor();
 
     bool isGLFragColorUsed() const { return mGLFragColorUsed; }
 
@@ -62,11 +36,39 @@
     void visitSymbol(TIntermSymbol *node) override;
     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
 
+    TIntermBinary *constructGLFragDataNode(int index) const;
+    TIntermBinary *constructGLFragDataAssignNode(int index) const;
+
   private:
     TIntermSequence *mMainSequence;
     bool mGLFragColorUsed;
+    int mMaxDrawBuffers;
 };
 
+TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index) const
+{
+    TType gl_FragDataElementType = TType(EbtFloat, 4);
+    TType gl_FragDataType        = gl_FragDataElementType;
+    gl_FragDataType.setArraySize(mMaxDrawBuffers);
+
+    TIntermSymbol *symbol   = new TIntermSymbol(0, "gl_FragData", gl_FragDataType);
+    TIntermTyped *indexNode = TIntermTyped::CreateIndexNode(index);
+
+    TIntermBinary *binary = new TIntermBinary(EOpIndexDirect);
+    binary->setLeft(symbol);
+    binary->setRight(indexNode);
+    binary->setType(gl_FragDataElementType);
+    return binary;
+}
+
+TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataAssignNode(int index) const
+{
+    TIntermTyped *fragDataIndex = constructGLFragDataNode(index);
+    TIntermTyped *fragDataZero  = constructGLFragDataNode(0);
+
+    return new TIntermBinary(EOpAssign, fragDataIndex, fragDataZero);
+}
+
 void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node)
 {
     if (node->getSymbol() == "gl_FragColor")
@@ -101,9 +103,9 @@
     return true;
 }
 
-void GLFragColorBroadcastTraverser::broadcastGLFragColor(int maxDrawBuffers)
+void GLFragColorBroadcastTraverser::broadcastGLFragColor()
 {
-    ASSERT(maxDrawBuffers > 1);
+    ASSERT(mMaxDrawBuffers > 1);
     if (!mGLFragColorUsed)
     {
         return;
@@ -113,7 +115,7 @@
     //   gl_FragData[1] = gl_FragData[0];
     //   ...
     //   gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
-    for (int colorIndex = 1; colorIndex < maxDrawBuffers; ++colorIndex)
+    for (int colorIndex = 1; colorIndex < mMaxDrawBuffers; ++colorIndex)
     {
         mMainSequence->insert(mMainSequence->end(), constructGLFragDataAssignNode(colorIndex));
     }
@@ -126,12 +128,12 @@
                                  std::vector<sh::OutputVariable> *outputVariables)
 {
     ASSERT(maxDrawBuffers > 1);
-    GLFragColorBroadcastTraverser traverser;
+    GLFragColorBroadcastTraverser traverser(maxDrawBuffers);
     root->traverse(&traverser);
     if (traverser.isGLFragColorUsed())
     {
         traverser.updateTree();
-        traverser.broadcastGLFragColor(maxDrawBuffers);
+        traverser.broadcastGLFragColor();
         for (auto &var : *outputVariables)
         {
             if (var.name == "gl_FragColor")
diff --git a/src/compiler/translator/InitializeVariables.cpp b/src/compiler/translator/InitializeVariables.cpp
index 21f0093..6b01d62 100644
--- a/src/compiler/translator/InitializeVariables.cpp
+++ b/src/compiler/translator/InitializeVariables.cpp
@@ -14,46 +14,6 @@
 namespace
 {
 
-TIntermConstantUnion *constructConstUnionNode(const TType &type)
-{
-    TType myType = type;
-    myType.clearArrayness();
-    myType.setQualifier(EvqConst);
-    size_t size          = myType.getObjectSize();
-    TConstantUnion *u = new TConstantUnion[size];
-    for (size_t ii = 0; ii < size; ++ii)
-    {
-        switch (type.getBasicType())
-        {
-            case EbtFloat:
-                u[ii].setFConst(0.0f);
-                break;
-            case EbtInt:
-                u[ii].setIConst(0);
-                break;
-            case EbtUInt:
-                u[ii].setUConst(0u);
-                break;
-            default:
-                UNREACHABLE();
-                return nullptr;
-        }
-    }
-
-    TIntermConstantUnion *node = new TIntermConstantUnion(u, myType);
-    return node;
-}
-
-TIntermConstantUnion *constructIndexNode(int index)
-{
-    TConstantUnion *u = new TConstantUnion[1];
-    u[0].setIConst(index);
-
-    TType type(EbtInt, EbpUndefined, EvqConst, 1);
-    TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
-    return node;
-}
-
 class VariableInitializer : public TIntermTraverser
 {
   public:
@@ -120,73 +80,44 @@
 
 void VariableInitializer::insertInitCode(TIntermSequence *sequence)
 {
-    for (size_t ii = 0; ii < mVariables.size(); ++ii)
+    for (const auto &var : mVariables)
     {
-        const sh::ShaderVariable &var = mVariables[ii];
         TString name = TString(var.name.c_str());
+        TType type   = sh::GetShaderVariableType(var);
+
+        // Assign the array elements one by one to keep the AST compatible with ESSL 1.00 which
+        // doesn't have array assignment.
         if (var.isArray())
         {
-            TType type = sh::ConvertShaderVariableTypeToTType(var.type);
             size_t pos = name.find_last_of('[');
             if (pos != TString::npos)
+            {
                 name = name.substr(0, pos);
-            for (int index = static_cast<int>(var.arraySize) - 1; index >= 0; --index)
-            {
-                TIntermBinary *assign = new TIntermBinary(EOpAssign);
-                sequence->insert(sequence->begin(), assign);
-
-                TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect);
-                TIntermSymbol *symbol      = new TIntermSymbol(0, name, type);
-                indexDirect->setLeft(symbol);
-                TIntermConstantUnion *indexNode = constructIndexNode(index);
-                indexDirect->setRight(indexNode);
-
-                assign->setLeft(indexDirect);
-
-                TIntermConstantUnion *zeroConst = constructConstUnionNode(type);
-                assign->setRight(zeroConst);
             }
-        }
-        else if (var.isStruct())
-        {
-            TFieldList *fields = new TFieldList;
-            TSourceLoc loc;
-            for (auto field : var.fields)
-            {
-                fields->push_back(new TField(nullptr, new TString(field.name.c_str()), loc));
-            }
-            TStructure *structure = new TStructure(new TString(var.structName.c_str()), fields);
-            TType type;
-            type.setStruct(structure);
-            for (int fieldIndex = 0; fieldIndex < static_cast<int>(var.fields.size()); ++fieldIndex)
-            {
-                TIntermBinary *assign = new TIntermBinary(EOpAssign);
-                sequence->insert(sequence->begin(), assign);
+            TType elementType = type;
+            elementType.clearArrayness();
 
-                TIntermBinary *indexDirectStruct = new TIntermBinary(EOpIndexDirectStruct);
-                TIntermSymbol *symbol            = new TIntermSymbol(0, name, type);
-                indexDirectStruct->setLeft(symbol);
-                TIntermConstantUnion *indexNode = constructIndexNode(fieldIndex);
-                indexDirectStruct->setRight(indexNode);
-                assign->setLeft(indexDirectStruct);
+            for (unsigned int i = 0; i < var.arraySize; ++i)
+            {
+                TIntermBinary *element = new TIntermBinary(EOpIndexDirect);
+                element->setLeft(new TIntermSymbol(0, name, type));
+                element->setRight(TIntermTyped::CreateIndexNode(i));
+                element->setType(elementType);
 
-                const sh::ShaderVariable &field = var.fields[fieldIndex];
-                TType fieldType                 = sh::ConvertShaderVariableTypeToTType(field.type);
-                TIntermConstantUnion *zeroConst = constructConstUnionNode(fieldType);
-                assign->setRight(zeroConst);
+                TIntermTyped *zero        = TIntermTyped::CreateZero(elementType);
+                TIntermBinary *assignment = new TIntermBinary(EOpAssign, element, zero);
+
+                sequence->insert(sequence->begin(), assignment);
             }
         }
         else
         {
-            TType type            = sh::ConvertShaderVariableTypeToTType(var.type);
-            TIntermBinary *assign = new TIntermBinary(EOpAssign);
-            sequence->insert(sequence->begin(), assign);
             TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
-            assign->setLeft(symbol);
-            TIntermConstantUnion *zeroConst = constructConstUnionNode(type);
-            assign->setRight(zeroConst);
-        }
+            TIntermTyped *zero    = TIntermTyped::CreateZero(type);
 
+            TIntermBinary *assign = new TIntermBinary(EOpAssign, symbol, zero);
+            sequence->insert(sequence->begin(), assign);
+        }
     }
 }
 
diff --git a/src/compiler/translator/InitializeVariables.h b/src/compiler/translator/InitializeVariables.h
index f826032..dce1083 100644
--- a/src/compiler/translator/InitializeVariables.h
+++ b/src/compiler/translator/InitializeVariables.h
@@ -13,6 +13,7 @@
 
 typedef std::vector<sh::ShaderVariable> InitVariableList;
 
+// This function cannot currently initialize structures containing arrays for an ESSL 1.00 backend.
 void InitializeVariables(TIntermNode *root, const InitVariableList &vars);
 
 #endif  // COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index dcf4787..b00022a 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -21,6 +21,7 @@
 #include "compiler/translator/HashNames.h"
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
 
 namespace
 {
@@ -336,6 +337,83 @@
     return true;
 }
 
+// static
+TIntermTyped *TIntermTyped::CreateIndexNode(int index)
+{
+    TConstantUnion *u = new TConstantUnion[1];
+    u[0].setIConst(index);
+
+    TType type(EbtInt, EbpUndefined, EvqConst, 1);
+    TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
+    return node;
+}
+
+// static
+TIntermTyped *TIntermTyped::CreateZero(const TType &type)
+{
+    TType constType(type);
+    constType.setQualifier(EvqConst);
+
+    if (!type.isArray() && type.getBasicType() != EbtStruct)
+    {
+        ASSERT(type.isScalar() || type.isVector() || type.isMatrix());
+
+        size_t size       = constType.getObjectSize();
+        TConstantUnion *u = new TConstantUnion[size];
+        for (size_t i = 0; i < size; ++i)
+        {
+            switch (type.getBasicType())
+            {
+                case EbtFloat:
+                    u[i].setFConst(0.0f);
+                    break;
+                case EbtInt:
+                    u[i].setIConst(0);
+                    break;
+                case EbtUInt:
+                    u[i].setUConst(0u);
+                    break;
+                case EbtBool:
+                    u[i].setBConst(false);
+                    break;
+                default:
+                    UNREACHABLE();
+                    return nullptr;
+            }
+        }
+
+        TIntermConstantUnion *node = new TIntermConstantUnion(u, constType);
+        return node;
+    }
+
+    TIntermAggregate *constructor = new TIntermAggregate(sh::TypeToConstructorOperator(type));
+    constructor->setType(constType);
+
+    if (type.isArray())
+    {
+        TType elementType(type);
+        elementType.clearArrayness();
+
+        size_t arraySize = type.getArraySize();
+        for (size_t i = 0; i < arraySize; ++i)
+        {
+            constructor->getSequence()->push_back(CreateZero(elementType));
+        }
+    }
+    else
+    {
+        ASSERT(type.getBasicType() == EbtStruct);
+
+        TStructure *structure = type.getStruct();
+        for (const auto &field : structure->fields())
+        {
+            constructor->getSequence()->push_back(CreateZero(*field->type()));
+        }
+    }
+
+    return constructor;
+}
+
 TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node)
 {
     mUnionArrayPointer = node.mUnionArrayPointer;
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 457e9ba..5b4c5d9 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -159,6 +159,9 @@
 
     bool isConstructorWithOnlyConstantUnionParameters();
 
+    static TIntermTyped *CreateIndexNode(int index);
+    static TIntermTyped *CreateZero(const TType &type);
+
   protected:
     TType mType;
 
diff --git a/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp b/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
index 775c5d8..6a70aa5 100644
--- a/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
+++ b/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
@@ -37,21 +37,11 @@
     return false;
 }
 
-TIntermConstantUnion *ConstructIndexNode(int index)
-{
-    TConstantUnion *u = new TConstantUnion[1];
-    u[0].setIConst(index);
-
-    TType type(EbtInt, EbpUndefined, EvqConst, 1);
-    TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
-    return node;
-}
-
 TIntermBinary *ConstructVectorIndexBinaryNode(TIntermSymbol *symbolNode, int index)
 {
     TIntermBinary *binary = new TIntermBinary(EOpIndexDirect);
     binary->setLeft(symbolNode);
-    TIntermConstantUnion *indexNode = ConstructIndexNode(index);
+    TIntermTyped *indexNode = TIntermTyped::CreateIndexNode(index);
     binary->setRight(indexNode);
     return binary;
 }
@@ -64,7 +54,7 @@
 
     TIntermBinary *binary = new TIntermBinary(EOpIndexDirect);
     binary->setLeft(colVectorNode);
-    TIntermConstantUnion *rowIndexNode = ConstructIndexNode(rowIndex);
+    TIntermTyped *rowIndexNode = TIntermTyped::CreateIndexNode(rowIndex);
     binary->setRight(rowIndexNode);
     return binary;
 }
diff --git a/src/compiler/translator/TranslatorHLSL.cpp b/src/compiler/translator/TranslatorHLSL.cpp
index 6721d89..fb08bec 100644
--- a/src/compiler/translator/TranslatorHLSL.cpp
+++ b/src/compiler/translator/TranslatorHLSL.cpp
@@ -8,6 +8,7 @@
 
 #include "compiler/translator/AddDefaultReturnStatements.h"
 #include "compiler/translator/ArrayReturnValueToOutParameter.h"
+#include "compiler/translator/BreakVariableAliasingInInnerLoops.h"
 #include "compiler/translator/EmulatePrecision.h"
 #include "compiler/translator/ExpandIntegerPowExpressions.h"
 #include "compiler/translator/IntermNodePatternMatcher.h"
@@ -75,6 +76,12 @@
         sh::RewriteElseBlocks(root, getTemporaryIndex());
     }
 
+    // Work around an HLSL compiler frontend aliasing optimization bug.
+    // TODO(cwallez) The date is 2016-08-25, Microsoft said the bug would be fixed
+    // in the next release of d3dcompiler.dll, it would be nice to detect the DLL
+    // version and only apply the workaround if it is too old.
+    sh::BreakVariableAliasingInInnerLoops(root);
+
     bool precisionEmulation =
         getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
 
diff --git a/src/compiler/translator/util.cpp b/src/compiler/translator/util.cpp
index 9b59ccc..d6339a8 100644
--- a/src/compiler/translator/util.cpp
+++ b/src/compiler/translator/util.cpp
@@ -278,9 +278,9 @@
     }
 }
 
-TType ConvertShaderVariableTypeToTType(sh::GLenum type)
+TType GetShaderVariableBasicType(const sh::ShaderVariable &var)
 {
-    switch (type)
+    switch (var.type)
     {
         case GL_FLOAT:
             return TType(EbtFloat);
@@ -330,6 +330,35 @@
     }
 }
 
+TType GetShaderVariableType(const sh::ShaderVariable &var)
+{
+    TType type;
+    if (var.isStruct())
+    {
+        TFieldList *fields = new TFieldList;
+        TSourceLoc loc;
+        for (const auto &field : var.fields)
+        {
+            TType *fieldType = new TType(GetShaderVariableType(field));
+            fields->push_back(new TField(fieldType, new TString(field.name.c_str()), loc));
+        }
+        TStructure *structure = new TStructure(new TString(var.structName.c_str()), fields);
+
+        type.setBasicType(EbtStruct);
+        type.setStruct(structure);
+    }
+    else
+    {
+        type = GetShaderVariableBasicType(var);
+    }
+
+    if (var.isArray())
+    {
+        type.setArraySize(var.elementCount());
+    }
+    return type;
+}
+
 TOperator TypeToConstructorOperator(const TType &type)
 {
     switch (type.getBasicType())
diff --git a/src/compiler/translator/util.h b/src/compiler/translator/util.h
index 3f24f66..4c1713a 100644
--- a/src/compiler/translator/util.h
+++ b/src/compiler/translator/util.h
@@ -37,8 +37,9 @@
 bool IsVarying(TQualifier qualifier);
 InterpolationType GetInterpolationType(TQualifier qualifier);
 TString ArrayString(const TType &type);
-// Handles only basic output variable types.
-TType ConvertShaderVariableTypeToTType(sh::GLenum type);
+
+TType GetShaderVariableBasicType(const sh::ShaderVariable &var);
+TType GetShaderVariableType(const sh::ShaderVariable &var);
 
 TOperator TypeToConstructorOperator(const TType &type);