Initialize all output variables.

BUG=angleproject:1441
TEST=bots

Change-Id: Ia4cf415d8346c3234bf0f548a178ee3ea8cd35c4
Reviewed-on: https://chromium-review.googlesource.com/362110
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Commit-Queue: Zhenyao Mo <zmo@chromium.org>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index e937587..c790ac1 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -362,9 +362,10 @@
                     infoSink.info << "too many uniforms";
                 }
             }
-            if (success && shaderType == GL_VERTEX_SHADER &&
-                (compileOptions & SH_INIT_VARYINGS_WITHOUT_STATIC_USE))
-                initializeVaryingsWithoutStaticUse(root);
+            if (success && (compileOptions & SH_INIT_OUTPUT_VARIABLES))
+            {
+                initializeOutputVariables(root, shaderType);
+            }
         }
 
         if (success && (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS))
@@ -810,37 +811,32 @@
 
 void TCompiler::initializeGLPosition(TIntermNode* root)
 {
-    InitializeVariables::InitVariableInfoList variables;
-    InitializeVariables::InitVariableInfo var(
-        "gl_Position", TType(EbtFloat, EbpUndefined, EvqPosition, 4));
-    variables.push_back(var);
-    InitializeVariables initializer(variables);
-    root->traverse(&initializer);
+    InitVariableList list;
+    sh::ShaderVariable var(GL_FLOAT_VEC4, 0);
+    var.name = "gl_Position";
+    list.push_back(var);
+    InitializeVariables(root, list);
 }
 
-void TCompiler::initializeVaryingsWithoutStaticUse(TIntermNode* root)
+void TCompiler::initializeOutputVariables(TIntermNode *root, sh::GLenum shaderType)
 {
-    InitializeVariables::InitVariableInfoList variables;
-    for (size_t ii = 0; ii < varyings.size(); ++ii)
+    InitVariableList list;
+    if (shaderType == GL_VERTEX_SHADER)
     {
-        const sh::Varying& varying = varyings[ii];
-        if (varying.staticUse)
-            continue;
-        unsigned char primarySize = static_cast<unsigned char>(gl::VariableColumnCount(varying.type));
-        unsigned char secondarySize = static_cast<unsigned char>(gl::VariableRowCount(varying.type));
-        TType type(EbtFloat, EbpUndefined, EvqVaryingOut, primarySize, secondarySize, varying.isArray());
-        TString name = varying.name.c_str();
-        if (varying.isArray())
+        for (auto var : varyings)
         {
-            type.setArraySize(varying.arraySize);
-            name = name.substr(0, name.find_first_of('['));
+            sh::ExpandVariable(var, var.name, var.mappedName, false, &list);
         }
-
-        InitializeVariables::InitVariableInfo var(name, type);
-        variables.push_back(var);
     }
-    InitializeVariables initializer(variables);
-    root->traverse(&initializer);
+    else
+    {
+        ASSERT(shaderType == GL_FRAGMENT_SHADER);
+        for (auto var : outputVariables)
+        {
+            list.push_back(var);
+        }
+    }
+    InitializeVariables(root, list);
 }
 
 const TExtensionBehavior& TCompiler::getExtensionBehavior() const
diff --git a/src/compiler/translator/Compiler.h b/src/compiler/translator/Compiler.h
index 99c155c..337bfc9 100644
--- a/src/compiler/translator/Compiler.h
+++ b/src/compiler/translator/Compiler.h
@@ -130,11 +130,9 @@
     // Returns true if, after applying the packing rules in the GLSL 1.017 spec
     // Appendix A, section 7, the shader does not use too many uniforms.
     bool enforcePackingRestrictions();
-    // Insert statements to initialize varyings without static use in the beginning
-    // of main(). It is to work around a Mac driver where such varyings in a vertex
-    // shader may be optimized out incorrectly at compile time, causing a link failure.
-    // This function should only be applied to vertex shaders.
-    void initializeVaryingsWithoutStaticUse(TIntermNode* root);
+    // Insert statements to initialize output variables in the beginning of main().
+    // This is to avoid undefined behaviors.
+    void initializeOutputVariables(TIntermNode *root, sh::GLenum shaderType);
     // Insert gl_Position = vec4(0,0,0,0) to the beginning of main().
     // It is to work around a Linux driver bug where missing this causes compile failure
     // while spec says it is allowed.
diff --git a/src/compiler/translator/InitializeVariables.cpp b/src/compiler/translator/InitializeVariables.cpp
index 86d3e6b..482260a 100644
--- a/src/compiler/translator/InitializeVariables.cpp
+++ b/src/compiler/translator/InitializeVariables.cpp
@@ -6,23 +6,40 @@
 
 #include "compiler/translator/InitializeVariables.h"
 
+#include "angle_gl.h"
 #include "common/debug.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/util.h"
 
 namespace
 {
 
-TIntermConstantUnion *constructFloatConstUnionNode(const TType &type)
+TIntermConstantUnion *constructConstUnionNode(const TType &type)
 {
     TType myType = type;
-    unsigned char size = static_cast<unsigned char>(myType.getNominalSize());
-    if (myType.isMatrix())
-        size *= size;
-    TConstantUnion *u = new TConstantUnion[size];
-    for (int ii = 0; ii < size; ++ii)
-        u[ii].setFConst(0.0f);
-
     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;
 }
@@ -37,9 +54,33 @@
     return node;
 }
 
-}  // namespace anonymous
+class VariableInitializer : public TIntermTraverser
+{
+  public:
+    VariableInitializer(const InitVariableList &vars)
+        : TIntermTraverser(true, false, false), mVariables(vars), mCodeInserted(false)
+    {
+    }
 
-bool InitializeVariables::visitAggregate(Visit visit, TIntermAggregate *node)
+  protected:
+    bool visitBinary(Visit, TIntermBinary *node) override { return false; }
+    bool visitUnary(Visit, TIntermUnary *node) override { return false; }
+    bool visitSelection(Visit, TIntermSelection *node) override { return false; }
+    bool visitLoop(Visit, TIntermLoop *node) override { return false; }
+    bool visitBranch(Visit, TIntermBranch *node) override { return false; }
+
+    bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+
+  private:
+    void insertInitCode(TIntermSequence *sequence);
+
+    const InitVariableList &mVariables;
+    bool mCodeInserted;
+};
+
+// VariableInitializer implementation.
+
+bool VariableInitializer::visitAggregate(Visit visit, TIntermAggregate *node)
 {
     bool visitChildren = !mCodeInserted;
     switch (node->getOp())
@@ -77,28 +118,33 @@
     return visitChildren;
 }
 
-void InitializeVariables::insertInitCode(TIntermSequence *sequence)
+void VariableInitializer::insertInitCode(TIntermSequence *sequence)
 {
     for (size_t ii = 0; ii < mVariables.size(); ++ii)
     {
-        const InitVariableInfo &varInfo = mVariables[ii];
-
-        if (varInfo.type.isArray())
+        const sh::ShaderVariable &var = mVariables[ii];
+        ASSERT(!var.isStruct());
+        TType type   = sh::ConvertShaderVariableTypeToTType(var.type);
+        TString name = TString(var.name.c_str());
+        if (var.isArray())
         {
-            for (int index = varInfo.type.getArraySize() - 1; index >= 0; --index)
+            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, varInfo.name, varInfo.type);
+                TIntermSymbol *symbol      = new TIntermSymbol(0, name, type);
                 indexDirect->setLeft(symbol);
                 TIntermConstantUnion *indexNode = constructIndexNode(index);
                 indexDirect->setRight(indexNode);
 
                 assign->setLeft(indexDirect);
 
-                TIntermConstantUnion *zeroConst = constructFloatConstUnionNode(varInfo.type);
+                TIntermConstantUnion *zeroConst = constructConstUnionNode(type);
                 assign->setRight(zeroConst);
             }
         }
@@ -106,12 +152,19 @@
         {
             TIntermBinary *assign = new TIntermBinary(EOpAssign);
             sequence->insert(sequence->begin(), assign);
-            TIntermSymbol *symbol = new TIntermSymbol(0, varInfo.name, varInfo.type);
+            TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
             assign->setLeft(symbol);
-            TIntermConstantUnion *zeroConst = constructFloatConstUnionNode(varInfo.type);
+            TIntermConstantUnion *zeroConst = constructConstUnionNode(type);
             assign->setRight(zeroConst);
         }
 
     }
 }
 
+}  // namespace anonymous
+
+void InitializeVariables(TIntermNode *root, const InitVariableList &vars)
+{
+    VariableInitializer initializer(vars);
+    root->traverse(&initializer);
+}
diff --git a/src/compiler/translator/InitializeVariables.h b/src/compiler/translator/InitializeVariables.h
index 2a141ec..f826032 100644
--- a/src/compiler/translator/InitializeVariables.h
+++ b/src/compiler/translator/InitializeVariables.h
@@ -7,45 +7,12 @@
 #ifndef COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
 #define COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
 
-#include "compiler/translator/IntermNode.h"
+#include <GLSLANG/ShaderLang.h>
 
-class InitializeVariables : public TIntermTraverser
-{
-  public:
-    struct InitVariableInfo
-    {
-        TString name;
-        TType type;
+class TIntermNode;
 
-        InitVariableInfo(const TString &_name, const TType &_type)
-            : name(_name),
-              type(_type)
-        {
-        }
-    };
-    typedef TVector<InitVariableInfo> InitVariableInfoList;
+typedef std::vector<sh::ShaderVariable> InitVariableList;
 
-    InitializeVariables(const InitVariableInfoList &vars)
-        : TIntermTraverser(true, false, false),
-          mVariables(vars),
-          mCodeInserted(false)
-    {
-    }
-
-  protected:
-    bool visitBinary(Visit, TIntermBinary *node) override { return false; }
-    bool visitUnary(Visit, TIntermUnary *node) override { return false; }
-    bool visitSelection(Visit, TIntermSelection *node) override { return false; }
-    bool visitLoop(Visit, TIntermLoop *node) override { return false; }
-    bool visitBranch(Visit, TIntermBranch *node) override { return false; }
-
-    bool visitAggregate(Visit visit, TIntermAggregate *node) override;
-
-  private:
-    void insertInitCode(TIntermSequence *sequence);
-
-    InitVariableInfoList mVariables;
-    bool mCodeInserted;
-};
+void InitializeVariables(TIntermNode *root, const InitVariableList &vars);
 
 #endif  // COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
diff --git a/src/compiler/translator/util.cpp b/src/compiler/translator/util.cpp
index 0131137..84988ae 100644
--- a/src/compiler/translator/util.cpp
+++ b/src/compiler/translator/util.cpp
@@ -278,6 +278,58 @@
     }
 }
 
+TType ConvertShaderVariableTypeToTType(sh::GLenum type)
+{
+    switch (type)
+    {
+        case GL_FLOAT:
+            return TType(EbtFloat);
+        case GL_FLOAT_VEC2:
+            return TType(EbtFloat, 2);
+        case GL_FLOAT_VEC3:
+            return TType(EbtFloat, 3);
+        case GL_FLOAT_VEC4:
+            return TType(EbtFloat, 4);
+        case GL_FLOAT_MAT2:
+            return TType(EbtFloat, 2, 2);
+        case GL_FLOAT_MAT3:
+            return TType(EbtFloat, 3, 3);
+        case GL_FLOAT_MAT4:
+            return TType(EbtFloat, 4, 4);
+        case GL_FLOAT_MAT2x3:
+            return TType(EbtFloat, 2, 3);
+        case GL_FLOAT_MAT2x4:
+            return TType(EbtFloat, 2, 4);
+        case GL_FLOAT_MAT3x2:
+            return TType(EbtFloat, 3, 2);
+        case GL_FLOAT_MAT3x4:
+            return TType(EbtFloat, 3, 4);
+        case GL_FLOAT_MAT4x2:
+            return TType(EbtFloat, 4, 2);
+        case GL_FLOAT_MAT4x3:
+            return TType(EbtFloat, 4, 3);
+        case GL_INT:
+            return TType(EbtInt);
+        case GL_INT_VEC2:
+            return TType(EbtInt, 2);
+        case GL_INT_VEC3:
+            return TType(EbtInt, 3);
+        case GL_INT_VEC4:
+            return TType(EbtInt, 4);
+        case GL_UNSIGNED_INT:
+            return TType(EbtUInt);
+        case GL_UNSIGNED_INT_VEC2:
+            return TType(EbtUInt, 2);
+        case GL_UNSIGNED_INT_VEC3:
+            return TType(EbtUInt, 3);
+        case GL_UNSIGNED_INT_VEC4:
+            return TType(EbtUInt, 4);
+        default:
+            UNREACHABLE();
+            return TType();
+    }
+}
+
 GetVariableTraverser::GetVariableTraverser(const TSymbolTable &symbolTable)
     : mSymbolTable(symbolTable)
 {
diff --git a/src/compiler/translator/util.h b/src/compiler/translator/util.h
index ea7a35a..35c6a55 100644
--- a/src/compiler/translator/util.h
+++ b/src/compiler/translator/util.h
@@ -36,6 +36,8 @@
 bool IsVarying(TQualifier qualifier);
 InterpolationType GetInterpolationType(TQualifier qualifier);
 TString ArrayString(const TType &type);
+// Handles only basic output variable types.
+TType ConvertShaderVariableTypeToTType(sh::GLenum type);
 
 class GetVariableTraverser : angle::NonCopyable
 {