Implement equality ops for nested structs.

This fixes the WebGL test glsl_misc_struct_equals as well as
several dEQP tests in functional.shaders.struct.

BUG=391957
BUG=angle:910

Change-Id: I09f3cd3f51bbc3541b64dbcfddfe01884ddba6f5
Reviewed-on: https://chromium-review.googlesource.com/247083
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 44d348a..3ee58ce 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -326,6 +326,15 @@
     out << mUniformHLSL->uniformsHeader(mOutputType, mReferencedUniforms);
     out << mUniformHLSL->interfaceBlocksHeader(mReferencedInterfaceBlocks);
 
+    if (!mStructEqualityFunctions.empty())
+    {
+        out << "\n// Structure equality functions\n\n";
+        for (const auto &eqFunction : mStructEqualityFunctions)
+        {
+            out << eqFunction.functionDefinition << "\n";
+        }
+    }
+
     if (mUsesDiscardRewriting)
     {
         out << "#define ANGLE_USES_DISCARD_REWRITING\n";
@@ -1533,50 +1542,23 @@
                 outputTriplet(visit, "(", " != ", ")");
             }
         }
-        else if (node->getLeft()->getBasicType() == EbtStruct)
-        {
-            if (node->getOp() == EOpEqual)
-            {
-                out << "(";
-            }
-            else
-            {
-                out << "!(";
-            }
-
-            const TStructure &structure = *node->getLeft()->getType().getStruct();
-            const TFieldList &fields = structure.fields();
-
-            for (size_t i = 0; i < fields.size(); i++)
-            {
-                const TField *field = fields[i];
-
-                node->getLeft()->traverse(this);
-                out << "." + DecorateField(field->name(), structure) + " == ";
-                node->getRight()->traverse(this);
-                out << "." + DecorateField(field->name(), structure);
-
-                if (i < fields.size() - 1)
-                {
-                    out << " && ";
-                }
-            }
-
-            out << ")";
-
-            return false;
-        }
         else
         {
-            ASSERT(node->getLeft()->isMatrix() || node->getLeft()->isVector());
-
-            if (node->getOp() == EOpEqual)
+            if (visit == PreVisit && node->getOp() == EOpNotEqual)
             {
-                outputTriplet(visit, "all(", " == ", ")");
+                out << "!";
+            }
+
+            if (node->getLeft()->getBasicType() == EbtStruct)
+            {
+                const TStructure &structure = *node->getLeft()->getType().getStruct();
+                const TString &functionName = addStructEqualityFunction(structure);
+                outputTriplet(visit, functionName + "(", ", ", ")");
             }
             else
             {
-                outputTriplet(visit, "!all(", " == ", ")");
+                ASSERT(node->getLeft()->isMatrix() || node->getLeft()->isVector());
+                outputTriplet(visit, "all(", " == ", ")");
             }
         }
         break;
@@ -2861,4 +2843,65 @@
         << "\n";
 }
 
+TString OutputHLSL::addStructEqualityFunction(const TStructure &structure)
+{
+    const TFieldList &fields = structure.fields();
+
+    for (const auto &eqFunction : mStructEqualityFunctions)
+    {
+        if (eqFunction.structure == &structure)
+        {
+            return eqFunction.functionName;
+        }
+    }
+
+    const TString &structNameString = StructNameString(structure);
+
+    StructEqualityFunction function;
+    function.structure = &structure;
+    function.functionName = "angle_eq_" + structNameString;
+
+    TString &func = function.functionDefinition;
+
+    func = "bool " + function.functionName + "(" + structNameString + " a, " + structNameString + " b)\n" +
+           "{\n"
+           "    return ";
+
+    for (size_t i = 0; i < fields.size(); i++)
+    {
+        const TField *field = fields[i];
+        const TType *fieldType = field->type();
+
+        const TString &fieldNameA = "a." + Decorate(field->name());
+        const TString &fieldNameB = "b." + Decorate(field->name());
+
+        if (i > 0)
+        {
+            func += " && ";
+        }
+
+        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 + ")";
+        }
+    }
+
+    func = func + ";\n" + "}\n";
+
+    mStructEqualityFunctions.push_back(function);
+
+    return function.functionName;
+}
+
 }
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index dec421c..db13d57 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -73,6 +73,9 @@
     bool writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression);
     void writeDeferredGlobalInitializers(TInfoSinkBase &out);
 
+    // Returns the function name
+    TString addStructEqualityFunction(const TStructure &structure);
+
     TParseContext &mContext;
     const ShShaderOutput mOutputType;
     UnfoldShortCircuit *mUnfoldShortCircuit;
@@ -160,6 +163,17 @@
     // shader input structure, which we set in the D3D main function. Instead, we can initialize
     // 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
+    {
+        const TStructure *structure;
+        TString functionName;
+        TString functionDefinition;
+    };
+
+    std::vector<StructEqualityFunction> mStructEqualityFunctions;
 };
 
 }