Implement ESSL3 modf

This is the first built-in function that has an out parameter, so l-value
checks are added for built-ins.

BUG=angle:918

Change-Id: Ifd5befe955224f706f864e25107879c9cdce9e9f
Reviewed-on: https://chromium-review.googlesource.com/250780
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index e88f7c4..d4f81c7 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -290,6 +290,20 @@
     symbolTable.insertBuiltIn(COMMON_BUILTINS, float3, "smoothstep", float1, float1, float3);
     symbolTable.insertBuiltIn(COMMON_BUILTINS, float4, "smoothstep", float1, float1, float4);
 
+    TType *outFloat1 = new TType(EbtFloat);
+    TType *outFloat2 = new TType(EbtFloat, 2);
+    TType *outFloat3 = new TType(EbtFloat, 3);
+    TType *outFloat4 = new TType(EbtFloat, 4);
+    outFloat1->setQualifier(EvqOut);
+    outFloat2->setQualifier(EvqOut);
+    outFloat3->setQualifier(EvqOut);
+    outFloat4->setQualifier(EvqOut);
+
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "modf", float1, outFloat1);
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, float2, "modf", float2, outFloat2);
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, float3, "modf", float3, outFloat3);
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "modf", float4, outFloat4);
+
     TType *bool1 = new TType(EbtBool);
     TType *bool2 = new TType(EbtBool, 2);
     TType *bool3 = new TType(EbtBool, 3);
@@ -905,6 +919,7 @@
 
     symbolTable.relateToOperator(ESSL3_BUILTINS, "abs",           EOpAbs);
     symbolTable.relateToOperator(ESSL3_BUILTINS, "sign",          EOpSign);
+    symbolTable.relateToOperator(ESSL3_BUILTINS, "modf",          EOpModf);
     symbolTable.relateToOperator(ESSL3_BUILTINS, "min",           EOpMin);
     symbolTable.relateToOperator(ESSL3_BUILTINS, "max",           EOpMax);
     symbolTable.relateToOperator(ESSL3_BUILTINS, "clamp",         EOpClamp);
diff --git a/src/compiler/translator/Operator.cpp b/src/compiler/translator/Operator.cpp
index a534ab6..ef78607 100644
--- a/src/compiler/translator/Operator.cpp
+++ b/src/compiler/translator/Operator.cpp
@@ -94,6 +94,7 @@
       case EOpCeil: return "ceil";
       case EOpFract: return "fract";
       case EOpMod: return "mod";
+      case EOpModf: return "modf";
       case EOpMin: return "min";
       case EOpMax: return "max";
       case EOpClamp: return "clamp";
diff --git a/src/compiler/translator/Operator.h b/src/compiler/translator/Operator.h
index 282e80f..4884f1c 100644
--- a/src/compiler/translator/Operator.h
+++ b/src/compiler/translator/Operator.h
@@ -114,6 +114,7 @@
     EOpCeil,
     EOpFract,
     EOpMod,
+    EOpModf,
     EOpMin,
     EOpMax,
     EOpClamp,
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index 20fa74e..175edbd 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -864,6 +864,9 @@
       case EOpMod:
         writeBuiltInFunctionTriplet(visit, "mod(", useEmulatedFunction);
         break;
+      case EOpModf:
+        writeBuiltInFunctionTriplet(visit, "modf(", useEmulatedFunction);
+        break;
       case EOpPow:
         writeBuiltInFunctionTriplet(visit, "pow(", useEmulatedFunction);
         break;
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 1615762..2a2c97c 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -2139,6 +2139,7 @@
         ASSERT(node->getUseEmulatedFunction());
         writeEmulatedFunctionTriplet(visit, "mod(");
         break;
+      case EOpModf:             outputTriplet(visit, "modf(", ", ", ")");              break;
       case EOpPow:              outputTriplet(visit, "pow(", ", ", ")");               break;
       case EOpAtan:
         ASSERT(node->getSequence()->size() == 2);   // atan(x) is a unary operator
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index ff57a8f..beac6b7 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -991,6 +991,26 @@
     return false;
 }
 
+bool TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *aggregate)
+{
+    for (size_t i = 0; i < fnCandidate->getParamCount(); ++i)
+    {
+        TQualifier qual = fnCandidate->getParam(i).type->getQualifier();
+        if (qual == EvqOut || qual == EvqInOut)
+        {
+            TIntermTyped *node = (*(aggregate->getSequence()))[i]->getAsTyped();
+            if (lValueErrorCheck(node->getLine(), "assign", node))
+            {
+                error(node->getLine(),
+                    "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
+                recover();
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 bool TParseContext::supportsExtension(const char* extension)
 {
     const TExtensionBehavior& extbehavior = extensionBehavior();
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 35de1b5..2890bd0 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -108,6 +108,7 @@
     bool extensionErrorCheck(const TSourceLoc& line, const TString&);
     bool singleDeclarationErrorCheck(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier);
     bool layoutLocationErrorCheck(const TSourceLoc& location, const TLayoutQualifier &layoutQualifier);
+    bool functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *);
 
     const TPragma& pragma() const { return directiveHandler.pragma(); }
     const TExtensionBehavior& extensionBehavior() const { return directiveHandler.extensionBehavior(); }
diff --git a/src/compiler/translator/glslang.y b/src/compiler/translator/glslang.y
index d21d28b..3d857a1 100644
--- a/src/compiler/translator/glslang.y
+++ b/src/compiler/translator/glslang.y
@@ -360,6 +360,9 @@
                         aggregate->setType(fnCandidate->getReturnType());
                         aggregate->setPrecisionFromChildren();
                         $$ = aggregate;
+
+                        // Some built-in functions have out parameters too.
+                        context->functionCallLValueErrorCheck(fnCandidate, aggregate);
                     }
                 } else {
                     // This is a real function call
@@ -380,16 +383,7 @@
 
                     $$ = aggregate;
 
-                    TQualifier qual;
-                    for (size_t i = 0; i < fnCandidate->getParamCount(); ++i) {
-                        qual = fnCandidate->getParam(i).type->getQualifier();
-                        if (qual == EvqOut || qual == EvqInOut) {
-                            if (context->lValueErrorCheck($$->getLine(), "assign", (*($$->getAsAggregate()->getSequence()))[i]->getAsTyped())) {
-                                context->error($1.intermNode->getLine(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
-                                context->recover();
-                            }
-                        }
-                    }
+                    context->functionCallLValueErrorCheck(fnCandidate, aggregate);
                 }
             } else {
                 // error message was put out by PaFindFunction()
diff --git a/src/compiler/translator/glslang_tab.cpp b/src/compiler/translator/glslang_tab.cpp
index d1c8d28..711bc52 100644
--- a/src/compiler/translator/glslang_tab.cpp
+++ b/src/compiler/translator/glslang_tab.cpp
@@ -688,32 +688,32 @@
 static const yytype_uint16 yyrline[] =
 {
        0,   205,   205,   206,   209,   233,   236,   241,   246,   251,
-     256,   262,   265,   268,   271,   274,   277,   283,   291,   408,
-     411,   419,   422,   428,   432,   439,   445,   454,   462,   465,
-     475,   478,   481,   484,   494,   495,   496,   497,   505,   506,
-     509,   512,   519,   520,   523,   529,   530,   534,   541,   542,
-     545,   548,   551,   557,   558,   561,   567,   568,   575,   576,
-     583,   584,   591,   592,   598,   599,   605,   606,   612,   613,
-     630,   631,   644,   645,   646,   647,   651,   652,   653,   657,
-     661,   665,   669,   676,   679,   690,   698,   706,   733,   739,
-     750,   754,   758,   762,   769,   825,   828,   835,   843,   864,
-     885,   895,   923,   928,   938,   943,   953,   956,   959,   962,
-     968,   975,   978,   982,   986,   990,   997,  1001,  1005,  1012,
-    1016,  1020,  1027,  1036,  1042,  1045,  1051,  1057,  1064,  1073,
-    1082,  1090,  1093,  1100,  1104,  1111,  1114,  1118,  1122,  1131,
-    1140,  1148,  1158,  1170,  1173,  1176,  1182,  1189,  1192,  1198,
-    1201,  1204,  1210,  1213,  1228,  1232,  1236,  1240,  1244,  1248,
-    1253,  1258,  1263,  1268,  1273,  1278,  1283,  1288,  1293,  1298,
-    1303,  1308,  1313,  1318,  1323,  1328,  1333,  1338,  1343,  1348,
-    1353,  1357,  1361,  1365,  1369,  1373,  1377,  1381,  1385,  1389,
-    1393,  1397,  1401,  1405,  1409,  1413,  1421,  1429,  1433,  1446,
-    1446,  1449,  1449,  1455,  1458,  1474,  1477,  1486,  1490,  1496,
-    1503,  1518,  1522,  1526,  1527,  1533,  1534,  1535,  1536,  1537,
-    1541,  1542,  1542,  1542,  1552,  1553,  1557,  1557,  1558,  1558,
-    1563,  1566,  1576,  1579,  1585,  1586,  1590,  1598,  1602,  1612,
-    1617,  1634,  1634,  1639,  1639,  1646,  1646,  1654,  1657,  1663,
-    1666,  1672,  1676,  1683,  1690,  1697,  1704,  1715,  1724,  1728,
-    1735,  1738,  1744,  1744
+     256,   262,   265,   268,   271,   274,   277,   283,   291,   402,
+     405,   413,   416,   422,   426,   433,   439,   448,   456,   459,
+     469,   472,   475,   478,   488,   489,   490,   491,   499,   500,
+     503,   506,   513,   514,   517,   523,   524,   528,   535,   536,
+     539,   542,   545,   551,   552,   555,   561,   562,   569,   570,
+     577,   578,   585,   586,   592,   593,   599,   600,   606,   607,
+     624,   625,   638,   639,   640,   641,   645,   646,   647,   651,
+     655,   659,   663,   670,   673,   684,   692,   700,   727,   733,
+     744,   748,   752,   756,   763,   819,   822,   829,   837,   858,
+     879,   889,   917,   922,   932,   937,   947,   950,   953,   956,
+     962,   969,   972,   976,   980,   984,   991,   995,   999,  1006,
+    1010,  1014,  1021,  1030,  1036,  1039,  1045,  1051,  1058,  1067,
+    1076,  1084,  1087,  1094,  1098,  1105,  1108,  1112,  1116,  1125,
+    1134,  1142,  1152,  1164,  1167,  1170,  1176,  1183,  1186,  1192,
+    1195,  1198,  1204,  1207,  1222,  1226,  1230,  1234,  1238,  1242,
+    1247,  1252,  1257,  1262,  1267,  1272,  1277,  1282,  1287,  1292,
+    1297,  1302,  1307,  1312,  1317,  1322,  1327,  1332,  1337,  1342,
+    1347,  1351,  1355,  1359,  1363,  1367,  1371,  1375,  1379,  1383,
+    1387,  1391,  1395,  1399,  1403,  1407,  1415,  1423,  1427,  1440,
+    1440,  1443,  1443,  1449,  1452,  1468,  1471,  1480,  1484,  1490,
+    1497,  1512,  1516,  1520,  1521,  1527,  1528,  1529,  1530,  1531,
+    1535,  1536,  1536,  1536,  1546,  1547,  1551,  1551,  1552,  1552,
+    1557,  1560,  1570,  1573,  1579,  1580,  1584,  1592,  1596,  1606,
+    1611,  1628,  1628,  1633,  1633,  1640,  1640,  1648,  1651,  1657,
+    1660,  1666,  1670,  1677,  1684,  1691,  1698,  1709,  1718,  1722,
+    1729,  1732,  1738,  1738
 };
 #endif
 
@@ -2525,6 +2525,9 @@
                         aggregate->setType(fnCandidate->getReturnType());
                         aggregate->setPrecisionFromChildren();
                         (yyval.interm.intermTypedNode) = aggregate;
+
+                        // Some built-in functions have out parameters too.
+                        context->functionCallLValueErrorCheck(fnCandidate, aggregate);
                     }
                 } else {
                     // This is a real function call
@@ -2545,16 +2548,7 @@
 
                     (yyval.interm.intermTypedNode) = aggregate;
 
-                    TQualifier qual;
-                    for (size_t i = 0; i < fnCandidate->getParamCount(); ++i) {
-                        qual = fnCandidate->getParam(i).type->getQualifier();
-                        if (qual == EvqOut || qual == EvqInOut) {
-                            if (context->lValueErrorCheck((yyval.interm.intermTypedNode)->getLine(), "assign", (*((yyval.interm.intermTypedNode)->getAsAggregate()->getSequence()))[i]->getAsTyped())) {
-                                context->error((yyvsp[0].interm).intermNode->getLine(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
-                                context->recover();
-                            }
-                        }
-                    }
+                    context->functionCallLValueErrorCheck(fnCandidate, aggregate);
                 }
             } else {
                 // error message was put out by PaFindFunction()
diff --git a/src/compiler/translator/intermOut.cpp b/src/compiler/translator/intermOut.cpp
index 3fef88d..614cdc4 100644
--- a/src/compiler/translator/intermOut.cpp
+++ b/src/compiler/translator/intermOut.cpp
@@ -421,6 +421,7 @@
       case EOpVectorNotEqual:   out << "NotEqual";                      break;
 
       case EOpMod:           out << "mod";         break;
+      case EOpModf:          out << "modf";        break;
       case EOpPow:           out << "pow";         break;
 
       case EOpAtan:          out << "arc tangent"; break;