Add symbol table function to get TFunction for a built-in op

Built-in function parameter qualifiers are stored in the symbol table.
Some AST traversers need the qualifier information for ops to
determine whether a node is being written to. Add an utility function
that maps a TIntermAggregate node to a symbol table entry, so that the
traversers can get to this information in a convenient way.

This will be necessary for adding more built-ins that have out
parameters from ESSL 3.10.

BUG=angleproject:1730
TEST=angle_unittests

Change-Id: I4bc622d70b2326a04cc858ff1258c22320c590dc
Reviewed-on: https://chromium-review.googlesource.com/431109
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 11f63a9..6c3518d 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -373,7 +373,8 @@
         bool multiview2 = IsExtensionEnabled(extensionBehavior, "GL_OVR_multiview2");
         if (success && compileResources.OVR_multiview && IsWebGLBasedSpec(shaderSpec) &&
             (IsExtensionEnabled(extensionBehavior, "GL_OVR_multiview") || multiview2))
-            success = ValidateMultiviewWebGL(root, shaderType, multiview2, &mDiagnostics);
+            success = ValidateMultiviewWebGL(root, shaderType, symbolTable, shaderVersion,
+                                             multiview2, &mDiagnostics);
 
         // Fail compilation if precision emulation not supported.
         if (success && getResources().WEBGL_debug_shader_precision &&
diff --git a/src/compiler/translator/IntermTraverse.cpp b/src/compiler/translator/IntermTraverse.cpp
index b5d42cc..b58b27c 100644
--- a/src/compiler/translator/IntermTraverse.cpp
+++ b/src/compiler/translator/IntermTraverse.cpp
@@ -680,26 +680,9 @@
             // 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())
+            if (!node->isConstructor() && node->getOp() != EOpFunctionCall)
             {
-                // 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());
-                }
+                builtInFunc = mSymbolTable.findBuiltInOp(node, mShaderVersion);
             }
 
             size_t paramIndex = 0;
diff --git a/src/compiler/translator/SymbolTable.cpp b/src/compiler/translator/SymbolTable.cpp
index 44c4ca4..beda47a 100644
--- a/src/compiler/translator/SymbolTable.cpp
+++ b/src/compiler/translator/SymbolTable.cpp
@@ -14,7 +14,9 @@
 #endif
 
 #include "compiler/translator/SymbolTable.h"
+
 #include "compiler/translator/Cache.h"
+#include "compiler/translator/IntermNode.h"
 
 #include <stdio.h>
 #include <algorithm>
@@ -153,6 +155,30 @@
     return 0;
 }
 
+TFunction *TSymbolTable::findBuiltInOp(TIntermAggregate *callNode, int shaderVersion) const
+{
+    ASSERT(!callNode->isConstructor());
+    ASSERT(callNode->getOp() != EOpFunctionCall);
+    TString opString = GetOperatorString(callNode->getOp());
+    // The return type doesn't affect the mangled name of the function, which is used to look it up.
+    TType dummyReturnType;
+    TFunction call(&opString, &dummyReturnType, callNode->getOp());
+    TIntermSequence *sequence = callNode->getSequence();
+    for (auto *child : *sequence)
+    {
+        TType *paramType = child->getAsTyped()->getTypePointer();
+        TConstParameter p(paramType);
+        call.addParameter(p);
+    }
+
+    TSymbol *sym = findBuiltIn(call.getMangledName(), shaderVersion);
+    ASSERT(sym != nullptr && sym->isFunction());
+
+    TFunction *builtInFunc = static_cast<TFunction *>(sym);
+    ASSERT(builtInFunc->getParamCount() == sequence->size());
+    return builtInFunc;
+}
+
 TSymbolTable::~TSymbolTable()
 {
     while (table.size() > 0)
diff --git a/src/compiler/translator/SymbolTable.h b/src/compiler/translator/SymbolTable.h
index 88933d1..2a4cfff 100644
--- a/src/compiler/translator/SymbolTable.h
+++ b/src/compiler/translator/SymbolTable.h
@@ -41,6 +41,8 @@
 namespace sh
 {
 
+class TIntermAggregate;
+
 // Symbol base class. (Can build functions or variables out of these...)
 class TSymbol : angle::NonCopyable
 {
@@ -424,6 +426,10 @@
 
     TSymbol *findBuiltIn(const TString &name, int shaderVersion) const;
 
+    // Helper front-end for regular findBuiltIn that constructs the mangled function name from
+    // callNode.
+    TFunction *findBuiltInOp(TIntermAggregate *callNode, int shaderVersion) const;
+
     TSymbolTableLevel *getOuterLevel()
     {
         assert(currentLevel() >= 1);
diff --git a/src/compiler/translator/ValidateMultiviewWebGL.cpp b/src/compiler/translator/ValidateMultiviewWebGL.cpp
index 11740c3..08ae816 100644
--- a/src/compiler/translator/ValidateMultiviewWebGL.cpp
+++ b/src/compiler/translator/ValidateMultiviewWebGL.cpp
@@ -31,6 +31,8 @@
     // errors.
     static bool validate(TIntermBlock *root,
                          GLenum shaderType,
+                         const TSymbolTable &symbolTable,
+                         int shaderVersion,
                          bool multiview2,
                          TDiagnostics *diagnostics);
 
@@ -44,7 +46,11 @@
     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
 
   private:
-    ValidateMultiviewTraverser(GLenum shaderType, bool multiview2, TDiagnostics *diagnostics);
+    ValidateMultiviewTraverser(GLenum shaderType,
+                               const TSymbolTable &symbolTable,
+                               int shaderVersion,
+                               bool multiview2,
+                               TDiagnostics *diagnostics);
 
     static bool IsGLPosition(TIntermNode *node);
     static bool IsGLViewIDOVR(TIntermNode *node);
@@ -56,6 +62,8 @@
     bool mValid;
     bool mMultiview2;
     GLenum mShaderType;
+    const TSymbolTable &mSymbolTable;
+    const int mShaderVersion;
 
     bool mInsideViewIDConditional;  // Only set if mMultiview2 is false.
     bool mInsideRestrictedAssignment;
@@ -67,22 +75,29 @@
 
 bool ValidateMultiviewTraverser::validate(TIntermBlock *root,
                                           GLenum shaderType,
+                                          const TSymbolTable &symbolTable,
+                                          int shaderVersion,
                                           bool multiview2,
                                           TDiagnostics *diagnostics)
 {
-    ValidateMultiviewTraverser validate(shaderType, multiview2, diagnostics);
+    ValidateMultiviewTraverser validate(shaderType, symbolTable, shaderVersion, multiview2,
+                                        diagnostics);
     ASSERT(root);
     root->traverse(&validate);
     return validate.isValid();
 }
 
 ValidateMultiviewTraverser::ValidateMultiviewTraverser(GLenum shaderType,
+                                                       const TSymbolTable &symbolTable,
+                                                       int shaderVersion,
                                                        bool multiview2,
                                                        TDiagnostics *diagnostics)
     : TIntermTraverser(true, true, true),
       mValid(true),
       mMultiview2(multiview2),
       mShaderType(shaderType),
+      mSymbolTable(symbolTable),
+      mShaderVersion(shaderVersion),
       mInsideViewIDConditional(false),
       mInsideRestrictedAssignment(false),
       mGLPositionAllowed(multiview2),
@@ -361,17 +376,21 @@
                 mValid = false;
             }
         }
-        else if (node->getOp() == EOpModf)
+        else if (!node->isConstructor())
         {
-            // TODO(oetuaho@nvidia.com): It's quite hacky to hard-code modf - should maybe refactor
-            // out parameter detecting functionality in LValueTrackingTraverser so that it could be
-            // used here as well?
-            // LValueTrackingTraverser itself seems like a bad fit with the needs of this traverser.
-            mDiagnostics->error(node->getLine(),
-                                "Disallowed use of a function with an out parameter inside "
-                                "assignment to gl_Position.x when using OVR_multiview",
-                                GetOperatorString(node->getOp()));
-            mValid = false;
+            TFunction *builtInFunc = mSymbolTable.findBuiltInOp(node, mShaderVersion);
+            for (size_t paramIndex = 0u; paramIndex < builtInFunc->getParamCount(); ++paramIndex)
+            {
+                TQualifier qualifier = builtInFunc->getParam(paramIndex).type->getQualifier();
+                if (qualifier == EvqOut || qualifier == EvqInOut)
+                {
+                    mDiagnostics->error(node->getLine(),
+                                        "Disallowed use of a function with an out parameter inside "
+                                        "assignment to gl_Position.x when using OVR_multiview",
+                                        GetOperatorString(node->getOp()));
+                    mValid = false;
+                }
+            }
         }
     }
     return true;
@@ -381,6 +400,8 @@
 
 bool ValidateMultiviewWebGL(TIntermBlock *root,
                             GLenum shaderType,
+                            const TSymbolTable &symbolTable,
+                            int shaderVersion,
                             bool multiview2,
                             TDiagnostics *diagnostics)
 {
@@ -388,7 +409,8 @@
     {
         return true;
     }
-    return ValidateMultiviewTraverser::validate(root, shaderType, multiview2, diagnostics);
+    return ValidateMultiviewTraverser::validate(root, shaderType, symbolTable, shaderVersion,
+                                                multiview2, diagnostics);
 }
 
 }  // namespace sh
diff --git a/src/compiler/translator/ValidateMultiviewWebGL.h b/src/compiler/translator/ValidateMultiviewWebGL.h
index 0d8d11b..371a03d 100644
--- a/src/compiler/translator/ValidateMultiviewWebGL.h
+++ b/src/compiler/translator/ValidateMultiviewWebGL.h
@@ -17,11 +17,14 @@
 {
 class TDiagnostics;
 class TIntermBlock;
+class TSymbolTable;
 
 // Check for errors and output error messages with diagnostics.
 // Returns true if there are no errors.
 bool ValidateMultiviewWebGL(TIntermBlock *root,
                             sh::GLenum shaderType,
+                            const TSymbolTable &symbolTable,
+                            int shaderVersion,
                             bool multiview2,
                             TDiagnostics *diagnostics);