HLSL: Insert return statements into functions that are missing them.

It's allowed to not have all code paths return a value in ESSL but the HLSL
compiler detects this and generates an error.  Work around this by adding
dummy return statements at the end of each function that doesn't have one.

TEST=deqp/data/gles2/shaders/functions.html

BUG=angleproject:1015
BUG=478572

Change-Id: I2913f90f0994d4caf25cc43b16b9fc4e9efb19a5
Reviewed-on: https://chromium-review.googlesource.com/362085
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/AddDefaultReturnStatements.cpp b/src/compiler/translator/AddDefaultReturnStatements.cpp
new file mode 100644
index 0000000..97111d1
--- /dev/null
+++ b/src/compiler/translator/AddDefaultReturnStatements.cpp
@@ -0,0 +1,116 @@
+//
+// 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.
+//
+// AddDefaultReturnStatements.cpp: Add default return statements to functions that do not end in a
+//                                 return.
+//
+
+#include "compiler/translator/AddDefaultReturnStatements.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class AddDefaultReturnStatementsTraverser : private TIntermTraverser
+{
+  public:
+    static void Apply(TIntermNode *root)
+    {
+        AddDefaultReturnStatementsTraverser separateInit;
+        root->traverse(&separateInit);
+        separateInit.updateTree();
+    }
+
+  private:
+    AddDefaultReturnStatementsTraverser() : TIntermTraverser(true, false, false) {}
+
+    static bool IsFunctionWithoutReturnStatement(TIntermAggregate *node, TType *returnType)
+    {
+        *returnType = node->getType();
+        if (node->getOp() != EOpFunction || node->getType().getBasicType() == EbtVoid)
+        {
+            return false;
+        }
+
+        TIntermAggregate *lastNode = node->getSequence()->back()->getAsAggregate();
+        if (lastNode == nullptr)
+        {
+            return true;
+        }
+
+        TIntermBranch *returnNode = lastNode->getSequence()->front()->getAsBranchNode();
+        if (returnNode != nullptr && returnNode->getFlowOp() == EOpReturn)
+        {
+            return false;
+        }
+
+        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));
+
+            TIntermAggregate *lastNode = node->getSequence()->back()->getAsAggregate();
+            lastNode->getSequence()->push_back(branch);
+
+            return false;
+        }
+
+        return true;
+    }
+};
+}  // anonymous namespace
+
+void AddDefaultReturnStatements(TIntermNode *node)
+{
+    AddDefaultReturnStatementsTraverser::Apply(node);
+}
+
+}  // namespace sh
diff --git a/src/compiler/translator/AddDefaultReturnStatements.h b/src/compiler/translator/AddDefaultReturnStatements.h
new file mode 100644
index 0000000..d765a7a
--- /dev/null
+++ b/src/compiler/translator/AddDefaultReturnStatements.h
@@ -0,0 +1,22 @@
+//
+// 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.
+//
+// AddDefaultReturnStatements.h: Add default return statements to functions that do not end in a
+//                               return.
+//
+
+#ifndef COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
+#define COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
+
+class TIntermNode;
+
+namespace sh
+{
+
+void AddDefaultReturnStatements(TIntermNode *node);
+
+}  // namespace sh
+
+#endif  // COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index ad5aa13..a96c4b6 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -2241,135 +2241,7 @@
     }
     else
     {
-        switch (publicType.type)
-        {
-            case EbtFloat:
-                if (publicType.isMatrix())
-                {
-                    switch (publicType.getCols())
-                    {
-                        case 2:
-                            switch (publicType.getRows())
-                            {
-                                case 2:
-                                    op = EOpConstructMat2;
-                                    break;
-                                case 3:
-                                    op = EOpConstructMat2x3;
-                                    break;
-                                case 4:
-                                    op = EOpConstructMat2x4;
-                                    break;
-                            }
-                            break;
-                        case 3:
-                            switch (publicType.getRows())
-                            {
-                                case 2:
-                                    op = EOpConstructMat3x2;
-                                    break;
-                                case 3:
-                                    op = EOpConstructMat3;
-                                    break;
-                                case 4:
-                                    op = EOpConstructMat3x4;
-                                    break;
-                            }
-                            break;
-                        case 4:
-                            switch (publicType.getRows())
-                            {
-                                case 2:
-                                    op = EOpConstructMat4x2;
-                                    break;
-                                case 3:
-                                    op = EOpConstructMat4x3;
-                                    break;
-                                case 4:
-                                    op = EOpConstructMat4;
-                                    break;
-                            }
-                            break;
-                    }
-                }
-                else
-                {
-                    switch (publicType.getNominalSize())
-                    {
-                        case 1:
-                            op = EOpConstructFloat;
-                            break;
-                        case 2:
-                            op = EOpConstructVec2;
-                            break;
-                        case 3:
-                            op = EOpConstructVec3;
-                            break;
-                        case 4:
-                            op = EOpConstructVec4;
-                            break;
-                    }
-                }
-                break;
-
-            case EbtInt:
-                switch (publicType.getNominalSize())
-                {
-                    case 1:
-                        op = EOpConstructInt;
-                        break;
-                    case 2:
-                        op = EOpConstructIVec2;
-                        break;
-                    case 3:
-                        op = EOpConstructIVec3;
-                        break;
-                    case 4:
-                        op = EOpConstructIVec4;
-                        break;
-                }
-                break;
-
-            case EbtUInt:
-                switch (publicType.getNominalSize())
-                {
-                    case 1:
-                        op = EOpConstructUInt;
-                        break;
-                    case 2:
-                        op = EOpConstructUVec2;
-                        break;
-                    case 3:
-                        op = EOpConstructUVec3;
-                        break;
-                    case 4:
-                        op = EOpConstructUVec4;
-                        break;
-                }
-                break;
-
-            case EbtBool:
-                switch (publicType.getNominalSize())
-                {
-                    case 1:
-                        op = EOpConstructBool;
-                        break;
-                    case 2:
-                        op = EOpConstructBVec2;
-                        break;
-                    case 3:
-                        op = EOpConstructBVec3;
-                        break;
-                    case 4:
-                        op = EOpConstructBVec4;
-                        break;
-                }
-                break;
-
-            default:
-                break;
-        }
-
+        op = sh::TypeToConstructorOperator(TType(publicType));
         if (op == EOpNull)
         {
             error(publicType.line, "cannot construct this type", getBasicString(publicType.type));
diff --git a/src/compiler/translator/TranslatorHLSL.cpp b/src/compiler/translator/TranslatorHLSL.cpp
index d120ac5..0a45de7 100644
--- a/src/compiler/translator/TranslatorHLSL.cpp
+++ b/src/compiler/translator/TranslatorHLSL.cpp
@@ -6,6 +6,7 @@
 
 #include "compiler/translator/TranslatorHLSL.h"
 
+#include "compiler/translator/AddDefaultReturnStatements.h"
 #include "compiler/translator/ArrayReturnValueToOutParameter.h"
 #include "compiler/translator/EmulatePrecision.h"
 #include "compiler/translator/IntermNodePatternMatcher.h"
@@ -28,6 +29,8 @@
     const ShBuiltInResources &resources = getResources();
     int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
 
+    sh::AddDefaultReturnStatements(root);
+
     SeparateDeclarations(root);
 
     // TODO (oetuaho): Sequence operators should also be split in case there is dynamic indexing of
diff --git a/src/compiler/translator/util.cpp b/src/compiler/translator/util.cpp
index 84988ae..41276c6 100644
--- a/src/compiler/translator/util.cpp
+++ b/src/compiler/translator/util.cpp
@@ -330,6 +330,134 @@
     }
 }
 
+TOperator TypeToConstructorOperator(const TType &type)
+{
+    switch (type.getBasicType())
+    {
+        case EbtFloat:
+            if (type.isMatrix())
+            {
+                switch (type.getCols())
+                {
+                    case 2:
+                        switch (type.getRows())
+                        {
+                            case 2:
+                                return EOpConstructMat2;
+                            case 3:
+                                return EOpConstructMat2x3;
+                            case 4:
+                                return EOpConstructMat2x4;
+                            default:
+                                break;
+                        }
+                        break;
+
+                    case 3:
+                        switch (type.getRows())
+                        {
+                            case 2:
+                                return EOpConstructMat3x2;
+                            case 3:
+                                return EOpConstructMat3;
+                            case 4:
+                                return EOpConstructMat3x4;
+                            default:
+                                break;
+                        }
+                        break;
+
+                    case 4:
+                        switch (type.getRows())
+                        {
+                            case 2:
+                                return EOpConstructMat4x2;
+                            case 3:
+                                return EOpConstructMat4x3;
+                            case 4:
+                                return EOpConstructMat4;
+                            default:
+                                break;
+                        }
+                        break;
+                }
+            }
+            else
+            {
+                switch (type.getNominalSize())
+                {
+                    case 1:
+                        return EOpConstructFloat;
+                    case 2:
+                        return EOpConstructVec2;
+                    case 3:
+                        return EOpConstructVec3;
+                    case 4:
+                        return EOpConstructVec4;
+                    default:
+                        break;
+                }
+            }
+            break;
+
+        case EbtInt:
+            switch (type.getNominalSize())
+            {
+                case 1:
+                    return EOpConstructInt;
+                case 2:
+                    return EOpConstructIVec2;
+                case 3:
+                    return EOpConstructIVec3;
+                case 4:
+                    return EOpConstructIVec4;
+                default:
+                    break;
+            }
+            break;
+
+        case EbtUInt:
+            switch (type.getNominalSize())
+            {
+                case 1:
+                    return EOpConstructUInt;
+                case 2:
+                    return EOpConstructUVec2;
+                case 3:
+                    return EOpConstructUVec3;
+                case 4:
+                    return EOpConstructUVec4;
+                default:
+                    break;
+            }
+            break;
+
+        case EbtBool:
+            switch (type.getNominalSize())
+            {
+                case 1:
+                    return EOpConstructBool;
+                case 2:
+                    return EOpConstructBVec2;
+                case 3:
+                    return EOpConstructBVec3;
+                case 4:
+                    return EOpConstructBVec4;
+                default:
+                    break;
+            }
+            break;
+
+        case EbtStruct:
+            return EOpConstructStruct;
+
+        default:
+            break;
+    }
+
+    return EOpNull;
+}
+
 GetVariableTraverser::GetVariableTraverser(const TSymbolTable &symbolTable)
     : mSymbolTable(symbolTable)
 {
diff --git a/src/compiler/translator/util.h b/src/compiler/translator/util.h
index 35c6a55..4e577b1 100644
--- a/src/compiler/translator/util.h
+++ b/src/compiler/translator/util.h
@@ -12,6 +12,7 @@
 #include "angle_gl.h"
 #include <GLSLANG/ShaderLang.h>
 
+#include "compiler/translator/Operator.h"
 #include "compiler/translator/Types.h"
 
 // strtof_clamp is like strtof but
@@ -39,6 +40,8 @@
 // Handles only basic output variable types.
 TType ConvertShaderVariableTypeToTType(sh::GLenum type);
 
+TOperator TypeToConstructorOperator(const TType &type);
+
 class GetVariableTraverser : angle::NonCopyable
 {
   public: