Support writing initializers using HLSL literal syntax

Instead of using constructor functions to initialize variables, it is
better to use literal initializer syntax provided by HLSL when it is
possible. This way shader complexity is reduced and constant array
initialization doesn't have to go through as many AST transformations.

Before this patch, vec4 initialization would result in the following
kind of HLSL:

float4 f = float4(1.0, 2.0, 3.0, 4.0);

After this patch, it will be:

float4 f = {1.0, 2.0, 3.0, 4.0};

Before this patch, vec2 array initialization would result in the
following kind of HLSL:

float2 f[2] = {0, 0, 0, 0};
angle_construct_into_2_float2(f, float2(1.0, 2.0), float2(3.0, 4.0));

After this patch, it will be:

float2 f[2] = {1.0, 2.0, 3.0, 4.0};

BUG=angleproject:1094
BUG=541551
TEST=WebGL conformance tests

Change-Id: I9816a8d95a2cba3964922f6b561862d478da6145
Reviewed-on: https://chromium-review.googlesource.com/311160
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 1c7b0c4..dea6fc6 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -35,6 +35,45 @@
     return node->getAsAggregate() != nullptr && node->getAsAggregate()->getOp() == EOpSequence;
 }
 
+void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion)
+{
+    ASSERT(constUnion != nullptr);
+    switch (constUnion->getType())
+    {
+        case EbtFloat:
+            out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst()));
+            break;
+        case EbtInt:
+            out << constUnion->getIConst();
+            break;
+        case EbtUInt:
+            out << constUnion->getUConst();
+            break;
+        case EbtBool:
+            out << constUnion->getBConst();
+            break;
+        default:
+            UNREACHABLE();
+    }
+}
+
+const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out,
+                                              const TConstantUnion *const constUnion,
+                                              const size_t size)
+{
+    const TConstantUnion *constUnionIterated = constUnion;
+    for (size_t i = 0; i < size; i++, constUnionIterated++)
+    {
+        WriteSingleConstant(out, constUnionIterated);
+
+        if (i != size - 1)
+        {
+            out << ", ";
+        }
+    }
+    return constUnionIterated;
+}
+
 } // namespace
 
 namespace sh
@@ -1514,6 +1553,10 @@
                 // Skip initializing the rest of the expression
                 return false;
             }
+            else if (writeConstantInitialization(out, symbolNode, expression))
+            {
+                return false;
+            }
         }
         else if (visit == InVisit)
         {
@@ -2913,10 +2956,13 @@
     }
 }
 
-const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const TConstantUnion *constUnion)
+const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type,
+                                                     const TConstantUnion *const constUnion)
 {
     TInfoSinkBase &out = getInfoSink();
 
+    const TConstantUnion *constUnionIterated = constUnion;
+
     const TStructure* structure = type.getStruct();
     if (structure)
     {
@@ -2927,7 +2973,7 @@
         for (size_t i = 0; i < fields.size(); i++)
         {
             const TType *fieldType = fields[i]->type();
-            constUnion = writeConstantUnion(*fieldType, constUnion);
+            constUnionIterated     = writeConstantUnion(*fieldType, constUnionIterated);
 
             if (i != fields.size() - 1)
             {
@@ -2946,31 +2992,14 @@
         {
             out << TypeString(type) << "(";
         }
-
-        for (size_t i = 0; i < size; i++, constUnion++)
-        {
-            switch (constUnion->getType())
-            {
-              case EbtFloat: out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); break;
-              case EbtInt:   out << constUnion->getIConst(); break;
-              case EbtUInt:  out << constUnion->getUConst(); break;
-              case EbtBool:  out << constUnion->getBConst(); break;
-              default: UNREACHABLE();
-            }
-
-            if (i != size - 1)
-            {
-                out << ", ";
-            }
-        }
-
+        constUnionIterated = WriteConstantUnionArray(out, constUnionIterated, size);
         if (writeType)
         {
             out << ")";
         }
     }
 
-    return constUnion;
+    return constUnionIterated;
 }
 
 void OutputHLSL::writeEmulatedFunctionTriplet(Visit visit, const char *preStr)
@@ -3000,6 +3029,68 @@
     return false;
 }
 
+bool OutputHLSL::canWriteAsHLSLLiteral(TIntermTyped *expression)
+{
+    // We support writing constant unions and constructors that only take constant unions as
+    // parameters as HLSL literals.
+    if (expression->getAsConstantUnion())
+    {
+        return true;
+    }
+    if (expression->getQualifier() != EvqConst || !expression->getAsAggregate() ||
+        !expression->getAsAggregate()->isConstructor())
+    {
+        return false;
+    }
+    TIntermAggregate *constructor = expression->getAsAggregate();
+    for (TIntermNode *&node : *constructor->getSequence())
+    {
+        if (!node->getAsConstantUnion())
+            return false;
+    }
+    return true;
+}
+
+bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out,
+                                             TIntermSymbol *symbolNode,
+                                             TIntermTyped *expression)
+{
+    if (canWriteAsHLSLLiteral(expression))
+    {
+        symbolNode->traverse(this);
+        if (expression->getType().isArray())
+        {
+            out << "[" << expression->getType().getArraySize() << "]";
+        }
+        out << " = {";
+        if (expression->getAsConstantUnion())
+        {
+            TIntermConstantUnion *nodeConst  = expression->getAsConstantUnion();
+            const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
+            WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
+        }
+        else
+        {
+            TIntermAggregate *constructor = expression->getAsAggregate();
+            ASSERT(constructor != nullptr);
+            for (TIntermNode *&node : *constructor->getSequence())
+            {
+                TIntermConstantUnion *nodeConst = node->getAsConstantUnion();
+                ASSERT(nodeConst);
+                const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
+                WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
+                if (node != constructor->getSequence()->back())
+                {
+                    out << ", ";
+                }
+            }
+        }
+        out << "}";
+        return true;
+    }
+    return false;
+}
+
 void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out)
 {
     out << "#define ANGLE_USES_DEFERRED_INIT\n"