Detect when built-in modf requires an l-value in AST traversal

This fixes an omission that out parameter tracking had inherited from
EmulatePrecision. Accurate tracking of when values are written is
required for converting dynamic indexing of vectors and matrices to
function calls.

A new test covering this is added to angle_unittests.

TEST=angle_unittests
BUG=angleproject:1116

Change-Id: I05c5fd60355117d0053b84110748ae221375a790
Reviewed-on: https://chromium-review.googlesource.com/290562
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/EmulatePrecision.cpp b/src/compiler/translator/EmulatePrecision.cpp
index a4cb028..b79e336 100644
--- a/src/compiler/translator/EmulatePrecision.cpp
+++ b/src/compiler/translator/EmulatePrecision.cpp
@@ -291,8 +291,9 @@
 
 }  // namespace anonymous
 
-EmulatePrecision::EmulatePrecision()
-    : TLValueTrackingTraverser(true, true, true), mDeclaringVariables(false)
+EmulatePrecision::EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion)
+    : TLValueTrackingTraverser(true, true, true, symbolTable, shaderVersion),
+      mDeclaringVariables(false)
 {}
 
 void EmulatePrecision::visitSymbol(TIntermSymbol *node)
diff --git a/src/compiler/translator/EmulatePrecision.h b/src/compiler/translator/EmulatePrecision.h
index d2a28f7..ec6bba5 100644
--- a/src/compiler/translator/EmulatePrecision.h
+++ b/src/compiler/translator/EmulatePrecision.h
@@ -21,7 +21,7 @@
 class EmulatePrecision : public TLValueTrackingTraverser
 {
   public:
-    EmulatePrecision();
+    EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion);
 
     virtual void visitSymbol(TIntermSymbol *node);
     virtual bool visitBinary(Visit visit, TIntermBinary *node);
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 62db306..9622f07 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -42,6 +42,8 @@
 class TInfoSinkBase;
 class TIntermRaw;
 
+class TSymbolTable;
+
 //
 // Base class for the tree nodes
 //
@@ -797,10 +799,16 @@
 class TLValueTrackingTraverser : public TIntermTraverser
 {
   public:
-    TLValueTrackingTraverser(bool preVisit, bool inVisit, bool postVisit)
+    TLValueTrackingTraverser(bool preVisit,
+                             bool inVisit,
+                             bool postVisit,
+                             const TSymbolTable &symbolTable,
+                             int shaderVersion)
         : TIntermTraverser(preVisit, inVisit, postVisit),
           mOperatorRequiresLValue(false),
-          mInFunctionCallOutParameter(false)
+          mInFunctionCallOutParameter(false),
+          mSymbolTable(symbolTable),
+          mShaderVersion(shaderVersion)
     {
     }
     virtual ~TLValueTrackingTraverser() {}
@@ -849,6 +857,9 @@
 
     // Map from mangled function names to their parameter sequences
     TMap<TString, TIntermSequence *, TStringComparator> mFunctionMap;
+
+    const TSymbolTable &mSymbolTable;
+    const int mShaderVersion;
 };
 
 //
diff --git a/src/compiler/translator/IntermTraverse.cpp b/src/compiler/translator/IntermTraverse.cpp
index d39e779..7f213fc 100644
--- a/src/compiler/translator/IntermTraverse.cpp
+++ b/src/compiler/translator/IntermTraverse.cpp
@@ -6,6 +6,7 @@
 
 #include "compiler/translator/IntermNode.h"
 #include "compiler/translator/InfoSink.h"
+#include "compiler/translator/SymbolTable.h"
 
 void TIntermSymbol::traverse(TIntermTraverser *it)
 {
@@ -473,9 +474,41 @@
             if (node->getOp() == EOpSequence)
                 pushParentBlock(node);
 
+            // Find the built-in function corresponding to this op so that we can determine the
+            // in/out qualifiers of its parameters.
+            TFunction *builtInFunc = nullptr;
+            TString opString = GetOperatorString(node->getOp());
+            if (!node->isConstructor() && !opString.empty())
+            {
+                // The return type doesn't affect the mangled name of the function, which is used
+                // to look it up from the symbol table.
+                TType dummyReturnType;
+                TFunction call(&opString, &dummyReturnType, node->getOp());
+                for (auto *child : *sequence)
+                {
+                    TType *paramType = child->getAsTyped()->getTypePointer();
+                    TConstParameter p(paramType);
+                    call.addParameter(p);
+                }
+
+                TSymbol *sym = mSymbolTable.findBuiltIn(call.getMangledName(), mShaderVersion);
+                if (sym != nullptr && sym->isFunction())
+                {
+                    builtInFunc = static_cast<TFunction *>(sym);
+                    ASSERT(builtInFunc->getParamCount() == sequence->size());
+                }
+            }
+
+            size_t paramIndex = 0;
+
             for (auto *child : *sequence)
             {
+                TQualifier qualifier = EvqIn;
+                if (builtInFunc != nullptr)
+                    qualifier = builtInFunc->getParam(paramIndex).type->getQualifier();
+                setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut);
                 child->traverse(this);
+
                 if (visit && inVisit)
                 {
                     if (child != sequence->back())
@@ -484,8 +517,12 @@
 
                 if (node->getOp() == EOpSequence)
                     incrementParentBlockPos();
+
+                ++paramIndex;
             }
 
+            setInFunctionCallOutParameter(false);
+
             if (node->getOp() == EOpSequence)
                 popParentBlock();
         }
diff --git a/src/compiler/translator/TranslatorESSL.cpp b/src/compiler/translator/TranslatorESSL.cpp
index 0dbc093..362915a 100644
--- a/src/compiler/translator/TranslatorESSL.cpp
+++ b/src/compiler/translator/TranslatorESSL.cpp
@@ -43,7 +43,7 @@
 
     if (precisionEmulation)
     {
-        EmulatePrecision emulatePrecision;
+        EmulatePrecision emulatePrecision(getSymbolTable(), shaderVer);
         root->traverse(&emulatePrecision);
         emulatePrecision.updateTree();
         emulatePrecision.writeEmulationHelpers(sink, SH_ESSL_OUTPUT);
diff --git a/src/compiler/translator/TranslatorGLSL.cpp b/src/compiler/translator/TranslatorGLSL.cpp
index 305e6bb..c9a2bcd 100644
--- a/src/compiler/translator/TranslatorGLSL.cpp
+++ b/src/compiler/translator/TranslatorGLSL.cpp
@@ -44,7 +44,7 @@
 
     if (precisionEmulation)
     {
-        EmulatePrecision emulatePrecision;
+        EmulatePrecision emulatePrecision(getSymbolTable(), getShaderVersion());
         root->traverse(&emulatePrecision);
         emulatePrecision.updateTree();
         emulatePrecision.writeEmulationHelpers(sink, getOutputType());