Support equality between structs containing arrays in HLSL output

This requires sorting all equality functions together so that struct
equality functions can have dependencies on array equality functions, but
support for array equality functions that have dependencies on struct
equality functions is also maintained.

There's no automated test coverage for this specifically. The change was
tested by manually inspecting shader output and ensuring that there were
no test regressions in tests listed below.

TEST=dEQP-GLES3.functional.shaders.*
BUG=angleproject:954

Change-Id: If7199ab2446804afae50f103bb625101172882b9
Reviewed-on: https://chromium-review.googlesource.com/261550
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 4410bb0..a7004a6 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -158,6 +158,14 @@
     SafeDelete(mUnfoldShortCircuit);
     SafeDelete(mStructureHLSL);
     SafeDelete(mUniformHLSL);
+    for (auto &eqFunction : mStructEqualityFunctions)
+    {
+        SafeDelete(eqFunction);
+    }
+    for (auto &eqFunction : mArrayEqualityFunctions)
+    {
+        SafeDelete(eqFunction);
+    }
 }
 
 void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink)
@@ -329,21 +337,12 @@
     out << mUniformHLSL->uniformsHeader(mOutputType, mReferencedUniforms);
     out << mUniformHLSL->interfaceBlocksHeader(mReferencedInterfaceBlocks);
 
-    if (!mStructEqualityFunctions.empty())
+    if (!mEqualityFunctions.empty())
     {
-        out << "\n// Structure equality functions\n\n";
-        for (const auto &eqFunction : mStructEqualityFunctions)
+        out << "\n// Equality functions\n\n";
+        for (const auto &eqFunction : mEqualityFunctions)
         {
-            out << eqFunction.functionDefinition << "\n";
-        }
-    }
-
-    if (!mArrayEqualityFunctions.empty())
-    {
-        out << "\n// Array equality functions\n\n";
-        for (const auto &eqFunction : mArrayEqualityFunctions)
-        {
-            out << eqFunction.functionDefinition << "\n";
+            out << eqFunction->functionDefinition << "\n";
         }
     }
 
@@ -2961,23 +2960,23 @@
 
     for (const auto &eqFunction : mStructEqualityFunctions)
     {
-        if (eqFunction.structure == &structure)
+        if (eqFunction->structure == &structure)
         {
-            return eqFunction.functionName;
+            return eqFunction->functionName;
         }
     }
 
     const TString &structNameString = StructNameString(structure);
 
-    StructEqualityFunction function;
-    function.structure = &structure;
-    function.functionName = "angle_eq_" + structNameString;
+    StructEqualityFunction *function = new StructEqualityFunction();
+    function->structure = &structure;
+    function->functionName = "angle_eq_" + structNameString;
 
-    TString &func = function.functionDefinition;
+    TInfoSinkBase fnOut;
 
-    func = "bool " + function.functionName + "(" + structNameString + " a, " + structNameString + " b)\n" +
-           "{\n"
-           "    return ";
+    fnOut << "bool " << function->functionName << "(" << structNameString << " a, " << structNameString + " b)\n"
+          << "{\n"
+             "    return ";
 
     for (size_t i = 0; i < fields.size(); i++)
     {
@@ -2987,67 +2986,55 @@
         const TString &fieldNameA = "a." + Decorate(field->name());
         const TString &fieldNameB = "b." + Decorate(field->name());
 
-        // TODO (oetuaho): Use outputEqual() here instead
-
         if (i > 0)
         {
-            func += " && ";
+            fnOut << " && ";
         }
 
-        if (fieldType->isArray())
-        {
-            // TODO (oetuaho): This requires sorting array and struct equality functions together.
-            UNIMPLEMENTED();
-        }
-        else if (fieldType->getBasicType() == EbtStruct)
-        {
-            const TStructure &fieldStruct = *fieldType->getStruct();
-            const TString &functionName = addStructEqualityFunction(fieldStruct);
-            func += functionName + "(" + fieldNameA + ", " + fieldNameB + ")";
-        }
-        else if (fieldType->isScalar())
-        {
-            func += "(" + fieldNameA + " == " + fieldNameB + ")";
-        }
-        else
-        {
-            ASSERT(fieldType->isMatrix() || fieldType->isVector());
-            func += "all(" + fieldNameA + " == " + fieldNameB + ")";
-        }
+        fnOut << "(";
+        outputEqual(PreVisit, *fieldType, EOpEqual, fnOut);
+        fnOut << fieldNameA;
+        outputEqual(InVisit, *fieldType, EOpEqual, fnOut);
+        fnOut << fieldNameB;
+        outputEqual(PostVisit, *fieldType, EOpEqual, fnOut);
+        fnOut << ")";
     }
 
-    func = func + ";\n" + "}\n";
+    fnOut << ";\n" << "}\n";
+
+    function->functionDefinition = fnOut.c_str();
 
     mStructEqualityFunctions.push_back(function);
+    mEqualityFunctions.push_back(function);
 
-    return function.functionName;
+    return function->functionName;
 }
 
 TString OutputHLSL::addArrayEqualityFunction(const TType& type)
 {
     for (const auto &eqFunction : mArrayEqualityFunctions)
     {
-        if (eqFunction.type == type)
+        if (eqFunction->type == type)
         {
-            return eqFunction.functionName;
+            return eqFunction->functionName;
         }
     }
 
     const TString &typeName = TypeString(type);
 
-    ArrayEqualityFunction function;
-    function.type = type;
+    ArrayEqualityFunction *function = new ArrayEqualityFunction();
+    function->type = type;
 
     TInfoSinkBase fnNameOut;
     fnNameOut << "angle_eq_" << type.getArraySize() << "_" << typeName;
-    function.functionName = fnNameOut.c_str();
+    function->functionName = fnNameOut.c_str();
 
     TType nonArrayType = type;
     nonArrayType.clearArrayness();
 
     TInfoSinkBase fnOut;
 
-    fnOut << "bool " << function.functionName << "("
+    fnOut << "bool " << function->functionName << "("
           << typeName << "[" << type.getArraySize() << "] a, "
           << typeName << "[" << type.getArraySize() << "] b)\n"
           << "{\n"
@@ -3066,11 +3053,12 @@
              "    return true;\n"
              "}\n";
 
-    function.functionDefinition = fnOut.c_str();
+    function->functionDefinition = fnOut.c_str();
 
     mArrayEqualityFunctions.push_back(function);
+    mEqualityFunctions.push_back(function);
 
-    return function.functionName;
+    return function->functionName;
 }
 
 }
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index 4b85580..4083376 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -186,25 +186,31 @@
     // these static globals after we initialize our other globals.
     std::vector<std::pair<TIntermSymbol*, TIntermTyped*>> mDeferredGlobalInitializers;
 
-    // A list of structure equality comparison functions. It's important to preserve the order at
-    // which we add the functions, since nested structures call each other recursively.
-    struct StructEqualityFunction
+    struct EqualityFunction
+    {
+        TString functionName;
+        TString functionDefinition;
+
+        virtual ~EqualityFunction() {}
+    };
+
+    // A list of all equality comparison functions. It's important to preserve the order at
+    // which we add the functions, since nested structures call each other recursively, and
+    // structure equality functions may need to call array equality functions and vice versa.
+    // The ownership of the pointers is maintained by the type-specific arrays.
+    std::vector<EqualityFunction*> mEqualityFunctions;
+
+    struct StructEqualityFunction : public EqualityFunction
     {
         const TStructure *structure;
-        TString functionName;
-        TString functionDefinition;
     };
+    std::vector<StructEqualityFunction*> mStructEqualityFunctions;
 
-    std::vector<StructEqualityFunction> mStructEqualityFunctions;
-
-    struct ArrayEqualityFunction
+    struct ArrayEqualityFunction : public EqualityFunction
     {
         TType type;
-        TString functionName;
-        TString functionDefinition;
     };
-
-    std::vector<ArrayEqualityFunction> mArrayEqualityFunctions;
+    std::vector<ArrayEqualityFunction*> mArrayEqualityFunctions;
 };
 
 }