Fix frexp support in Metal.

Our Metal codegen assumes that out params are pointers, but Metal's
built-in frexp actually takes a reference for the exponent, not a
pointer. We now add in a helper function to translate.

Change-Id: I24686347d07151dd99a1ff1c43aff2b35c3181e5
Bug: skia:10762
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/328387
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni
index 3b2a95a..07d4292 100644
--- a/gn/sksl_tests.gni
+++ b/gn/sksl_tests.gni
@@ -305,9 +305,9 @@
   "$_tests/sksl/glsl/TypePrecision.sksl",
   "$_tests/sksl/inliner/InlinerCanBeDisabled.sksl",
   "$_tests/sksl/inliner/InlinerWrapsEarlyReturnsWithDoWhileBlock.sksl",
+  "$_tests/sksl/shared/CrossIntrinsic.sksl",
   "$_tests/sksl/shared/Derivatives.sksl",
   "$_tests/sksl/shared/DerivativesFlipY.sksl",
-  "$_tests/sksl/shared/CrossIntrinsic.sksl",
   "$_tests/sksl/workarounds/AbsInt.sksl",
   "$_tests/sksl/workarounds/BlendGuardedDivide.sksl",
   "$_tests/sksl/workarounds/BlendModesAllZeroVec.sksl",
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index 7fc1dea..22f111c 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -228,6 +228,17 @@
     } else if (builtin && name == "inverse") {
         SkASSERT(arguments.size() == 1);
         this->writeInverseHack(*arguments[0]);
+    } else if (builtin && name == "frexp") {
+        // Our Metal codegen assumes that out params are pointers, but Metal's built-in frexp
+        // actually takes a reference for the exponent, not a pointer. We add in a helper function
+        // here to translate.
+        SkASSERT(arguments.size() == 2);
+        auto [iter, newlyCreated] = fHelpers.insert("frexp");
+        if (newlyCreated) {
+            fExtraFunctions.printf(
+                    "float frexp(float arg, thread int* exp) { return frexp(arg, *exp); }\n");
+        }
+        this->write("frexp");
     } else if (builtin && name == "dFdx") {
         this->write("dfdx");
     } else if (builtin && name == "dFdy") {
diff --git a/tests/sksl/shared/golden/FrExp.metal b/tests/sksl/shared/golden/FrExp.metal
index 3d788ec..6431593 100644
--- a/tests/sksl/shared/golden/FrExp.metal
+++ b/tests/sksl/shared/golden/FrExp.metal
@@ -6,6 +6,7 @@
 struct Outputs {
     float4 sk_FragColor [[color(0)]];
 };
+float frexp(float arg, thread int* exp) { return frexp(arg, *exp); }
 fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
     Outputs _outputStruct;
     thread Outputs* _out = &_outputStruct;