Add support for function prototypes in Pipeline stage.

Previously we did not have a Pipeline callback function for prototyping
a function, so prototypes would be discarded during translation. This
failure mode can be seen in http://review.skia.org/454741, where
FunctionPrototype.sksl is made more complex (thwarting the inliner).
This causes us to emit invalid GLSL, and dm asserts/fails in the SkSL
tests: http://screen/4PkEEWn4m4tF5e7

This CL makes the same changes to FunctionPrototype, but does not crash.

Change-Id: Ia342c7811a454f62f52677440d247e628a1bdc4f
Bug: skia:12488
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/454740
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/src/gpu/effects/GrSkSLFP.cpp b/src/gpu/effects/GrSkSLFP.cpp
index 3239ec4..a31e07e 100644
--- a/src/gpu/effects/GrSkSLFP.cpp
+++ b/src/gpu/effects/GrSkSLFP.cpp
@@ -108,6 +108,10 @@
                 }
             }
 
+            void declareFunction(const char* decl) override {
+                fArgs.fFragBuilder->emitFunctionPrototype(decl);
+            }
+
             void defineStruct(const char* definition) override {
                 fArgs.fFragBuilder->definitionAppend(definition);
             }
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.cpp b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
index 7884c5f..a84d7b9 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
@@ -85,6 +85,10 @@
     this->functions().append(";\n");
 }
 
+void GrGLSLShaderBuilder::emitFunctionPrototype(const char* declaration) {
+    this->functions().appendf("%s;\n", declaration);
+}
+
 void GrGLSLShaderBuilder::codeAppend(std::unique_ptr<SkSL::Statement> stmt) {
     SkASSERT(SkSL::dsl::DSLWriter::CurrentProcessor());
     SkASSERT(stmt);
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.h b/src/gpu/glsl/GrGLSLShaderBuilder.h
index b1f6fbc..0ebf11c 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.h
@@ -143,6 +143,8 @@
                                const char* mangledName,
                                SkSpan<const GrShaderVar> args);
 
+    void emitFunctionPrototype(const char* declaration);
+
     /** Emits a helper function outside of main() in the fragment shader. */
     void emitFunction(GrSLType returnType,
                       const char* mangledName,
diff --git a/src/sksl/SkSLMain.cpp b/src/sksl/SkSLMain.cpp
index 4cfde6c..26c9f77 100644
--- a/src/sksl/SkSLMain.cpp
+++ b/src/sksl/SkSLMain.cpp
@@ -408,6 +408,10 @@
                             fOutput += String(decl) + "{" + body + "}";
                         }
 
+                        void declareFunction(const char* decl) override {
+                            fOutput += String(decl) + ";";
+                        }
+
                         void defineStruct(const char* definition) override {
                             fOutput += definition;
                         }
diff --git a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
index 9e7bcdd..066cad8 100644
--- a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
@@ -72,6 +72,7 @@
     void writeFunctionPrototype(const FunctionPrototype& f);
 
     String modifierString(const Modifiers& modifiers);
+    String functionDeclaration(const FunctionDeclaration& decl);
 
     // Handles arrays correctly, eg: `float x[2]`
     String typedVariable(const Type& type, skstd::string_view name);
@@ -338,6 +339,12 @@
         fCastReturnsToHalf = false;
     }
 
+    fCallbacks->defineFunction(this->functionDeclaration(decl).c_str(),
+                               body.fBuffer.str().c_str(),
+                               decl.isMain());
+}
+
+String PipelineStageCodeGenerator::functionDeclaration(const FunctionDeclaration& decl) {
     // This is similar to decl.description(), but substitutes a mangled name, and handles modifiers
     // on the function (e.g. `inline`) and its parameters (e.g. `inout`).
     String declString =
@@ -348,22 +355,18 @@
                            this->functionName(decl).c_str());
     const char* separator = "";
     for (const Variable* p : decl.parameters()) {
-        // TODO: Handle arrays
-        declString.appendf("%s%s%s %s",
-                           separator,
-                           this->modifierString(p->modifiers()).c_str(),
-                           this->typeName(p->type()).c_str(),
-                           String(p->name()).c_str());
+        declString.append(separator);
+        declString.append(this->modifierString(p->modifiers()));
+        declString.append(this->typedVariable(p->type(), p->name()).c_str());
         separator = ", ";
     }
-    declString.append(")");
 
-    fCallbacks->defineFunction(declString.c_str(), body.fBuffer.str().c_str(), decl.isMain());
+    return declString + ")";
 }
 
 void PipelineStageCodeGenerator::writeFunctionPrototype(const FunctionPrototype& f) {
     const FunctionDeclaration& decl = f.declaration();
-    (void)this->functionName(decl);
+    fCallbacks->declareFunction(this->functionDeclaration(decl).c_str());
 }
 
 void PipelineStageCodeGenerator::writeGlobalVarDeclaration(const GlobalVarDeclaration& g) {
diff --git a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.h b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.h
index 8394650..cd5cd31 100644
--- a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.h
+++ b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.h
@@ -24,6 +24,7 @@
 
         virtual String getMangledName(const char* name) { return name; }
         virtual void   defineFunction(const char* declaration, const char* body, bool isMain) = 0;
+        virtual void   declareFunction(const char* declaration) = 0;
         virtual void   defineStruct(const char* definition) = 0;
         virtual void   declareGlobal(const char* declaration) = 0;