1 /*
2  * Copyright 2021 Google LLC.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
9 
10 #include "src/sksl/SkSLCompiler.h"
11 #include "src/sksl/SkSLIRGenerator.h"
12 #include "src/sksl/ir/SkSLUnresolvedFunction.h"
13 
14 namespace SkSL {
15 
identify_intrinsic(const String & functionName)16 static IntrinsicKind identify_intrinsic(const String& functionName) {
17     #define SKSL_INTRINSIC(name) {#name, k_##name##_IntrinsicKind},
18     static const auto* kAllIntrinsics = new std::unordered_map<String, IntrinsicKind>{
19         SKSL_INTRINSIC_LIST
20     };
21     #undef SKSL_INTRINSIC
22 
23     auto iter = kAllIntrinsics->find(functionName);
24     if (iter != kAllIntrinsics->end()) {
25         return iter->second;
26     }
27 
28     return kNotIntrinsic;
29 }
30 
check_modifiers(const Context & context,int offset,const Modifiers & modifiers)31 static bool check_modifiers(const Context& context, int offset, const Modifiers& modifiers) {
32     IRGenerator::CheckModifiers(
33             context,
34             offset,
35             modifiers,
36             Modifiers::kHasSideEffects_Flag | Modifiers::kInline_Flag | Modifiers::kNoInline_Flag,
37             /*permittedLayoutFlags=*/0);
38     if ((modifiers.fFlags & Modifiers::kInline_Flag) &&
39         (modifiers.fFlags & Modifiers::kNoInline_Flag)) {
40         context.fErrors.error(offset, "functions cannot be both 'inline' and 'noinline'");
41         return false;
42     }
43     return true;
44 }
45 
check_return_type(const Context & context,int offset,const Type & returnType,bool isBuiltin)46 static bool check_return_type(const Context& context, int offset, const Type& returnType,
47                               bool isBuiltin) {
48     ErrorReporter& errors = context.fErrors;
49     if (returnType.isArray()) {
50         errors.error(offset, "functions may not return type '" + returnType.displayName() + "'");
51         return false;
52     }
53     if (context.fConfig->strictES2Mode() && returnType.isOrContainsArray()) {
54         errors.error(offset, "functions may not return structs containing arrays");
55         return false;
56     }
57     if (!isBuiltin && !returnType.isVoid() && returnType.componentType().isOpaque()) {
58         errors.error(offset, "functions may not return opaque type '" + returnType.displayName() +
59                              "'");
60         return false;
61     }
62     return true;
63 }
64 
check_parameters(const Context & context,std::vector<std::unique_ptr<Variable>> & parameters,bool isMain,bool isBuiltin)65 static bool check_parameters(const Context& context,
66                              std::vector<std::unique_ptr<Variable>>& parameters, bool isMain,
67                              bool isBuiltin) {
68     auto typeIsValidForColor = [&](const Type& type) {
69         return type == *context.fTypes.fHalf4 || type == *context.fTypes.fFloat4;
70     };
71 
72     // Check modifiers on each function parameter.
73     for (auto& param : parameters) {
74         IRGenerator::CheckModifiers(context, param->fOffset, param->modifiers(),
75                                     Modifiers::kConst_Flag | Modifiers::kIn_Flag |
76                                     Modifiers::kOut_Flag, /*permittedLayoutFlags=*/0);
77         const Type& type = param->type();
78         // Only the (builtin) declarations of 'sample' are allowed to have shader/colorFilter or FP
79         // parameters. You can pass other opaque types to functions safely; this restriction is
80         // specific to "child" objects.
81         if ((type.isEffectChild() || type.isFragmentProcessor()) && !isBuiltin) {
82             context.fErrors.error(param->fOffset, "parameters of type '" + type.displayName() +
83                                                   "' not allowed");
84             return false;
85         }
86 
87         Modifiers m = param->modifiers();
88         ProgramKind kind = context.fConfig->fKind;
89         if (isMain && (kind == ProgramKind::kRuntimeColorFilter ||
90                        kind == ProgramKind::kRuntimeShader ||
91                        kind == ProgramKind::kFragmentProcessor)) {
92             // We verify that the signature is fully correct later. For now, if this is an .fp or
93             // runtime effect of any flavor, a float2 param is supposed to be the coords, and
94             // a half4/float parameter is supposed to be the input color:
95             if (type == *context.fTypes.fFloat2) {
96                 m.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN;
97             } else if(typeIsValidForColor(type)) {
98                 m.fLayout.fBuiltin = SK_INPUT_COLOR_BUILTIN;
99             }
100             if (m.fLayout.fBuiltin) {
101                 param->setModifiers(context.fModifiersPool->add(m));
102             }
103         }
104         if (isMain && (kind == ProgramKind::kFragment)) {
105             // For testing purposes, we have .sksl inputs that are treated as both runtime effects
106             // and fragment shaders. To make that work, fragment shaders are allowed to have a
107             // coords parameter. We turn it into sk_FragCoord.
108             if (type == *context.fTypes.fFloat2) {
109                 m.fLayout.fBuiltin = SK_FRAGCOORD_BUILTIN;
110                 param->setModifiers(context.fModifiersPool->add(m));
111             }
112         }
113     }
114     return true;
115 }
116 
check_main_signature(const Context & context,int offset,const Type & returnType,std::vector<std::unique_ptr<Variable>> & parameters,bool isBuiltin)117 static bool check_main_signature(const Context& context, int offset, const Type& returnType,
118                                  std::vector<std::unique_ptr<Variable>>& parameters,
119                                  bool isBuiltin) {
120     ErrorReporter& errors = context.fErrors;
121     ProgramKind kind = context.fConfig->fKind;
122 
123     auto typeIsValidForColor = [&](const Type& type) {
124         return type == *context.fTypes.fHalf4 || type == *context.fTypes.fFloat4;
125     };
126 
127     auto paramIsCoords = [&](int idx) {
128         const Variable& p = *parameters[idx];
129         return p.type() == *context.fTypes.fFloat2 &&
130                p.modifiers().fFlags == 0 &&
131                p.modifiers().fLayout.fBuiltin == (kind == ProgramKind::kFragment
132                                                            ? SK_FRAGCOORD_BUILTIN
133                                                            : SK_MAIN_COORDS_BUILTIN);
134     };
135 
136     auto paramIsInputColor = [&](int idx) {
137         return typeIsValidForColor(parameters[idx]->type()) &&
138                parameters[idx]->modifiers().fFlags == 0 &&
139                parameters[idx]->modifiers().fLayout.fBuiltin == SK_INPUT_COLOR_BUILTIN;
140     };
141 
142     switch (kind) {
143         case ProgramKind::kRuntimeColorFilter: {
144             // (half4|float4) main(half4|float4)
145             if (!typeIsValidForColor(returnType)) {
146                 errors.error(offset, "'main' must return: 'vec4', 'float4', or 'half4'");
147                 return false;
148             }
149             bool validParams = (parameters.size() == 1 && paramIsInputColor(0));
150             if (!validParams) {
151                 errors.error(offset, "'main' parameter must be 'vec4', 'float4', or 'half4'");
152                 return false;
153             }
154             break;
155         }
156         case ProgramKind::kRuntimeShader: {
157             // (half4|float4) main(float2)  -or-  (half4|float4) main(float2, half4|float4)
158             if (!typeIsValidForColor(returnType)) {
159                 errors.error(offset, "'main' must return: 'vec4', 'float4', or 'half4'");
160                 return false;
161             }
162             bool validParams =
163                     (parameters.size() == 1 && paramIsCoords(0)) ||
164                     (parameters.size() == 2 && paramIsCoords(0) && paramIsInputColor(1));
165             if (!validParams) {
166                 errors.error(offset, "'main' parameters must be (float2, (vec4|float4|half4)?)");
167                 return false;
168             }
169             break;
170         }
171         case ProgramKind::kFragmentProcessor: {
172             if (returnType != *context.fTypes.fHalf4) {
173                 errors.error(offset, ".fp 'main' must return 'half4'");
174                 return false;
175             }
176             bool validParams = (parameters.size() == 0) ||
177                                (parameters.size() == 1 && paramIsCoords(0));
178             if (!validParams) {
179                 errors.error(offset, ".fp 'main' must be declared main() or main(float2)");
180                 return false;
181             }
182             break;
183         }
184         case ProgramKind::kGeneric:
185             // No rules apply here
186             break;
187         case ProgramKind::kFragment: {
188             bool validParams = (parameters.size() == 0) ||
189                                (parameters.size() == 1 && paramIsCoords(0));
190             if (!validParams) {
191                 errors.error(offset, "shader 'main' must be main() or main(float2)");
192                 return false;
193             }
194             break;
195         }
196         case ProgramKind::kVertex:
197         case ProgramKind::kGeometry:
198             if (parameters.size()) {
199                 errors.error(offset, "shader 'main' must have zero parameters");
200                 return false;
201             }
202             break;
203     }
204     return true;
205 }
206 
207 /**
208  * Checks for a previously existing declaration of this function, reporting errors if there is an
209  * incompatible symbol. Returns true and sets outExistingDecl to point to the existing declaration
210  * (or null if none) on success, returns false on error.
211  */
find_existing_declaration(const Context & context,SymbolTable & symbols,int offset,StringFragment name,std::vector<std::unique_ptr<Variable>> & parameters,const Type * returnType,bool isBuiltin,const FunctionDeclaration ** outExistingDecl)212 static bool find_existing_declaration(const Context& context, SymbolTable& symbols, int offset,
213                                       StringFragment name,
214                                       std::vector<std::unique_ptr<Variable>>& parameters,
215                                       const Type* returnType, bool isBuiltin,
216                                       const FunctionDeclaration** outExistingDecl) {
217     ErrorReporter& errors = context.fErrors;
218     const Symbol* entry = symbols[name];
219     *outExistingDecl = nullptr;
220     if (entry) {
221         std::vector<const FunctionDeclaration*> functions;
222         switch (entry->kind()) {
223             case Symbol::Kind::kUnresolvedFunction:
224                 functions = entry->as<UnresolvedFunction>().functions();
225                 break;
226             case Symbol::Kind::kFunctionDeclaration:
227                 functions.push_back(&entry->as<FunctionDeclaration>());
228                 break;
229             default:
230                 errors.error(offset, "symbol '" + name + "' was already defined");
231                 return false;
232         }
233         for (const FunctionDeclaration* other : functions) {
234             SkASSERT(name == other->name());
235             if (parameters.size() != other->parameters().size()) {
236                 continue;
237             }
238             bool match = true;
239             for (size_t i = 0; i < parameters.size(); i++) {
240                 if (parameters[i]->type() != other->parameters()[i]->type()) {
241                     match = false;
242                     break;
243                 }
244             }
245             if (!match) {
246                 continue;
247             }
248             if (*returnType != other->returnType()) {
249                 std::vector<const Variable*> paramPtrs;
250                 paramPtrs.reserve(parameters.size());
251                 for (std::unique_ptr<Variable>& param : parameters) {
252                     paramPtrs.push_back(param.get());
253                 }
254                 FunctionDeclaration invalidDecl(offset,
255                                                 &other->modifiers(),
256                                                 name,
257                                                 std::move(paramPtrs),
258                                                 returnType,
259                                                 isBuiltin);
260                 errors.error(offset,
261                              "functions '" + invalidDecl.description() + "' and '" +
262                              other->description() + "' differ only in return type");
263                 return false;
264             }
265             for (size_t i = 0; i < parameters.size(); i++) {
266                 if (parameters[i]->modifiers() != other->parameters()[i]->modifiers()) {
267                     errors.error(offset,
268                                  "modifiers on parameter " + to_string((uint64_t)i + 1) +
269                                  " differ between declaration and definition");
270                     return false;
271                 }
272             }
273             if (other->definition() && !other->isBuiltin()) {
274                 errors.error(offset, "duplicate definition of " + other->description());
275                 return false;
276             }
277             *outExistingDecl = other;
278             break;
279         }
280     }
281     return true;
282 }
283 
FunctionDeclaration(int offset,const Modifiers * modifiers,StringFragment name,std::vector<const Variable * > parameters,const Type * returnType,bool builtin)284 FunctionDeclaration::FunctionDeclaration(int offset,
285                                          const Modifiers* modifiers,
286                                          StringFragment name,
287                                          std::vector<const Variable*> parameters,
288                                          const Type* returnType,
289                                          bool builtin)
290         : INHERITED(offset, kSymbolKind, name, /*type=*/nullptr)
291         , fDefinition(nullptr)
292         , fModifiers(modifiers)
293         , fParameters(std::move(parameters))
294         , fReturnType(returnType)
295         , fBuiltin(builtin)
296         , fIsMain(name == "main")
297         , fIntrinsicKind(builtin ? identify_intrinsic(name) : kNotIntrinsic) {}
298 
Convert(const Context & context,SymbolTable & symbols,int offset,const Modifiers * modifiers,StringFragment name,std::vector<std::unique_ptr<Variable>> parameters,const Type * returnType,bool isBuiltin)299 const FunctionDeclaration* FunctionDeclaration::Convert(const Context& context,
300         SymbolTable& symbols, int offset, const Modifiers* modifiers,
301         StringFragment name, std::vector<std::unique_ptr<Variable>> parameters,
302         const Type* returnType, bool isBuiltin) {
303     bool isMain = (name == "main");
304 
305     const FunctionDeclaration* decl = nullptr;
306     if (!check_modifiers(context, offset, *modifiers) ||
307         !check_return_type(context, offset, *returnType, isBuiltin) ||
308         !check_parameters(context, parameters, isMain, isBuiltin) ||
309         (isMain && !check_main_signature(context, offset, *returnType, parameters, isBuiltin)) ||
310         !find_existing_declaration(context, symbols, offset, name, parameters, returnType,
311                                    isBuiltin, &decl)) {
312         return nullptr;
313     }
314     std::vector<const Variable*> finalParameters;
315     finalParameters.reserve(parameters.size());
316     for (std::unique_ptr<Variable>& param : parameters) {
317         finalParameters.push_back(symbols.takeOwnershipOfSymbol(std::move(param)));
318     }
319     if (decl) {
320         return decl;
321     }
322     auto result = std::make_unique<FunctionDeclaration>(offset, modifiers, name,
323                                                         std::move(finalParameters), returnType,
324                                                         isBuiltin);
325     return symbols.add(std::move(result));
326 }
327 
mangledName() const328 String FunctionDeclaration::mangledName() const {
329     if ((this->isBuiltin() && !this->definition()) || this->isMain()) {
330         // Builtins without a definition (like `sin` or `sqrt`) must use their real names.
331         return this->name();
332     }
333     // GLSL forbids two underscores in a row; add an extra character if necessary to avoid this.
334     const char* splitter = this->name().endsWith("_") ? "x_" : "_";
335     // Rename function to `funcname_returntypeparamtypes`.
336     String result = this->name() + splitter + this->returnType().abbreviatedName();
337     for (const Variable* p : this->parameters()) {
338         result += p->type().abbreviatedName();
339     }
340     return result;
341 }
342 
description() const343 String FunctionDeclaration::description() const {
344     String result = this->returnType().displayName() + " " + this->name() + "(";
345     String separator;
346     for (const Variable* p : this->parameters()) {
347         result += separator;
348         separator = ", ";
349         result += p->type().displayName();
350         result += " ";
351         result += p->name();
352     }
353     result += ")";
354     return result;
355 }
356 
matches(const FunctionDeclaration & f) const357 bool FunctionDeclaration::matches(const FunctionDeclaration& f) const {
358     if (this->name() != f.name()) {
359         return false;
360     }
361     const std::vector<const Variable*>& parameters = this->parameters();
362     const std::vector<const Variable*>& otherParameters = f.parameters();
363     if (parameters.size() != otherParameters.size()) {
364         return false;
365     }
366     for (size_t i = 0; i < parameters.size(); i++) {
367         if (parameters[i]->type() != otherParameters[i]->type()) {
368             return false;
369         }
370     }
371     return true;
372 }
373 
determineFinalTypes(const ExpressionArray & arguments,ParamTypes * outParameterTypes,const Type ** outReturnType) const374 bool FunctionDeclaration::determineFinalTypes(const ExpressionArray& arguments,
375                                               ParamTypes* outParameterTypes,
376                                               const Type** outReturnType) const {
377     const std::vector<const Variable*>& parameters = this->parameters();
378     SkASSERT(arguments.size() == parameters.size());
379 
380     outParameterTypes->reserve_back(arguments.size());
381     int genericIndex = -1;
382     for (size_t i = 0; i < arguments.size(); i++) {
383         // Non-generic parameters are final as-is.
384         const Type& parameterType = parameters[i]->type();
385         if (parameterType.typeKind() != Type::TypeKind::kGeneric) {
386             outParameterTypes->push_back(&parameterType);
387             continue;
388         }
389         // We use the first generic parameter we find to lock in the generic index;
390         // e.g. if we find `float3` here, all `$genType`s will be assumed to be `float3`.
391         const std::vector<const Type*>& types = parameterType.coercibleTypes();
392         if (genericIndex == -1) {
393             for (size_t j = 0; j < types.size(); j++) {
394                 if (arguments[i]->type().canCoerceTo(*types[j], /*allowNarrowing=*/true)) {
395                     genericIndex = j;
396                     break;
397                 }
398             }
399             if (genericIndex == -1) {
400                 // The passed-in type wasn't a match for ANY of the generic possibilities.
401                 // This function isn't a match at all.
402                 return false;
403             }
404         }
405         outParameterTypes->push_back(types[genericIndex]);
406     }
407     // Apply the generic index to our return type.
408     const Type& returnType = this->returnType();
409     if (returnType.typeKind() == Type::TypeKind::kGeneric) {
410         if (genericIndex == -1) {
411             // We don't support functions with a generic return type and no other generics.
412             return false;
413         }
414         *outReturnType = returnType.coercibleTypes()[genericIndex];
415     } else {
416         *outReturnType = &returnType;
417     }
418     return true;
419 }
420 
421 }  // namespace SkSL
422