• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/private/SkStringView.h"
11 #include "src/sksl/SkSLCompiler.h"
12 #include "src/sksl/ir/SkSLUnresolvedFunction.h"
13 
14 namespace SkSL {
15 
identify_intrinsic(std::string_view functionName)16 static IntrinsicKind identify_intrinsic(std::string_view functionName) {
17     #define SKSL_INTRINSIC(name) {#name, k_##name##_IntrinsicKind},
18     static const auto* kAllIntrinsics = new std::unordered_map<std::string_view, IntrinsicKind>{
19         SKSL_INTRINSIC_LIST
20     };
21     #undef SKSL_INTRINSIC
22 
23     if (skstd::starts_with(functionName, '$')) {
24         functionName.remove_prefix(1);
25     }
26 
27     auto iter = kAllIntrinsics->find(functionName);
28     if (iter != kAllIntrinsics->end()) {
29         return iter->second;
30     }
31 
32     return kNotIntrinsic;
33 }
34 
check_modifiers(const Context & context,int line,const Modifiers & modifiers)35 static bool check_modifiers(const Context& context,
36                             int line,
37                             const Modifiers& modifiers) {
38     const int permitted = Modifiers::kHasSideEffects_Flag |
39                           Modifiers::kInline_Flag |
40                           Modifiers::kNoInline_Flag |
41                           (context.fConfig->fIsBuiltinCode ? Modifiers::kES3_Flag : 0);
42     modifiers.checkPermitted(context, line, permitted, /*permittedLayoutFlags=*/0);
43     if ((modifiers.fFlags & Modifiers::kInline_Flag) &&
44         (modifiers.fFlags & Modifiers::kNoInline_Flag)) {
45         context.fErrors->error(line, "functions cannot be both 'inline' and 'noinline'");
46         return false;
47     }
48     return true;
49 }
50 
check_return_type(const Context & context,int line,const Type & returnType)51 static bool check_return_type(const Context& context, int line, const Type& returnType) {
52     ErrorReporter& errors = *context.fErrors;
53     if (returnType.isArray()) {
54         errors.error(line, "functions may not return type '" + returnType.displayName() + "'");
55         return false;
56     }
57     if (context.fConfig->strictES2Mode() && returnType.isOrContainsArray()) {
58         errors.error(line, "functions may not return structs containing arrays");
59         return false;
60     }
61     if (!context.fConfig->fIsBuiltinCode && returnType.componentType().isOpaque()) {
62         errors.error(line, "functions may not return opaque type '" + returnType.displayName() +
63                            "'");
64         return false;
65     }
66     return true;
67 }
68 
check_parameters(const Context & context,std::vector<std::unique_ptr<Variable>> & parameters,bool isMain)69 static bool check_parameters(const Context& context,
70                              std::vector<std::unique_ptr<Variable>>& parameters,
71                              bool isMain) {
72     auto typeIsValidForColor = [&](const Type& type) {
73         return type.matches(*context.fTypes.fHalf4) || type.matches(*context.fTypes.fFloat4);
74     };
75 
76     // The first color parameter passed to main() is the input color; the second is the dest color.
77     static constexpr int kBuiltinColorIDs[] = {SK_INPUT_COLOR_BUILTIN, SK_DEST_COLOR_BUILTIN};
78     unsigned int builtinColorIndex = 0;
79 
80     // Check modifiers on each function parameter.
81     for (auto& param : parameters) {
82         param->modifiers().checkPermitted(context, param->fLine,
83                 Modifiers::kConst_Flag | Modifiers::kIn_Flag | Modifiers::kOut_Flag,
84                 /*permittedLayoutFlags=*/0);
85         const Type& type = param->type();
86         // Only the (builtin) declarations of 'sample' are allowed to have shader/colorFilter or FP
87         // parameters. You can pass other opaque types to functions safely; this restriction is
88         // specific to "child" objects.
89         if (type.isEffectChild() && !context.fConfig->fIsBuiltinCode) {
90             context.fErrors->error(param->fLine, "parameters of type '" + type.displayName() +
91                                                  "' not allowed");
92             return false;
93         }
94 
95         Modifiers m = param->modifiers();
96         bool modifiersChanged = false;
97 
98         // The `in` modifier on function parameters is implicit, so we can replace `in float x` with
99         // `float x`. This prevents any ambiguity when matching a function by its param types.
100         if (Modifiers::kIn_Flag == (m.fFlags & (Modifiers::kOut_Flag | Modifiers::kIn_Flag))) {
101             m.fFlags &= ~(Modifiers::kOut_Flag | Modifiers::kIn_Flag);
102             modifiersChanged = true;
103         }
104 
105         if (isMain) {
106             if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind) &&
107                 context.fConfig->fKind != ProgramKind::kCustomMeshFragment &&
108                 context.fConfig->fKind != ProgramKind::kCustomMeshVertex) {
109                 // We verify that the signature is fully correct later. For now, if this is a
110                 // runtime effect of any flavor, a float2 param is supposed to be the coords, and a
111                 // half4/float parameter is supposed to be the input or destination color:
112                 if (type.matches(*context.fTypes.fFloat2)) {
113                     m.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN;
114                     modifiersChanged = true;
115                 } else if (typeIsValidForColor(type) &&
116                            builtinColorIndex < SK_ARRAY_COUNT(kBuiltinColorIDs)) {
117                     m.fLayout.fBuiltin = kBuiltinColorIDs[builtinColorIndex++];
118                     modifiersChanged = true;
119                 }
120             } else if (context.fConfig->fKind == ProgramKind::kFragment) {
121                 // For testing purposes, we have .sksl inputs that are treated as both runtime
122                 // effects and fragment shaders. To make that work, fragment shaders are allowed to
123                 // have a coords parameter.
124                 if (type.matches(*context.fTypes.fFloat2)) {
125                     m.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN;
126                     modifiersChanged = true;
127                 }
128             }
129         }
130 
131         if (modifiersChanged) {
132             param->setModifiers(context.fModifiersPool->add(m));
133         }
134     }
135     return true;
136 }
137 
check_main_signature(const Context & context,int line,const Type & returnType,std::vector<std::unique_ptr<Variable>> & parameters)138 static bool check_main_signature(const Context& context, int line, const Type& returnType,
139                                  std::vector<std::unique_ptr<Variable>>& parameters) {
140     ErrorReporter& errors = *context.fErrors;
141     ProgramKind kind = context.fConfig->fKind;
142 
143     auto typeIsValidForColor = [&](const Type& type) {
144         return type.matches(*context.fTypes.fHalf4) || type.matches(*context.fTypes.fFloat4);
145     };
146 
147     auto typeIsValidForAttributes = [&](const Type& type) {
148         return type.isStruct() && type.name() == "Attributes";
149     };
150 
151     auto typeIsValidForVaryings = [&](const Type& type) {
152         return type.isStruct() && type.name() == "Varyings";
153     };
154 
155     auto paramIsCoords = [&](int idx) {
156         const Variable& p = *parameters[idx];
157         return p.type().matches(*context.fTypes.fFloat2) &&
158                p.modifiers().fFlags == 0 &&
159                p.modifiers().fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN;
160     };
161 
162     auto paramIsBuiltinColor = [&](int idx, int builtinID) {
163         const Variable& p = *parameters[idx];
164         return typeIsValidForColor(p.type()) &&
165                p.modifiers().fFlags == 0 &&
166                p.modifiers().fLayout.fBuiltin == builtinID;
167     };
168 
169     auto paramIsInAttributes = [&](int idx) {
170         const Variable& p = *parameters[idx];
171         return typeIsValidForAttributes(p.type()) && p.modifiers().fFlags == 0;
172     };
173 
174     auto paramIsOutVaryings = [&](int idx) {
175         const Variable& p = *parameters[idx];
176         return typeIsValidForVaryings(p.type()) && p.modifiers().fFlags == Modifiers::kOut_Flag;
177     };
178 
179     auto paramIsInVaryings = [&](int idx) {
180         const Variable& p = *parameters[idx];
181         return typeIsValidForVaryings(p.type()) && p.modifiers().fFlags == 0;
182     };
183 
184     auto paramIsOutColor = [&](int idx) {
185         const Variable& p = *parameters[idx];
186         return typeIsValidForColor(p.type()) && p.modifiers().fFlags == Modifiers::kOut_Flag;
187     };
188 
189     auto paramIsInputColor = [&](int n) { return paramIsBuiltinColor(n, SK_INPUT_COLOR_BUILTIN); };
190     auto paramIsDestColor  = [&](int n) { return paramIsBuiltinColor(n, SK_DEST_COLOR_BUILTIN); };
191 
192     switch (kind) {
193         case ProgramKind::kRuntimeColorFilter: {
194             // (half4|float4) main(half4|float4)
195             if (!typeIsValidForColor(returnType)) {
196                 errors.error(line, "'main' must return: 'vec4', 'float4', or 'half4'");
197                 return false;
198             }
199             bool validParams = (parameters.size() == 1 && paramIsInputColor(0));
200             if (!validParams) {
201                 errors.error(line, "'main' parameter must be 'vec4', 'float4', or 'half4'");
202                 return false;
203             }
204             break;
205         }
206         case ProgramKind::kRuntimeShader: {
207             // (half4|float4) main(float2)  -or-  (half4|float4) main(float2, half4|float4)
208             if (!typeIsValidForColor(returnType)) {
209                 errors.error(line, "'main' must return: 'vec4', 'float4', or 'half4'");
210                 return false;
211             }
212             bool validParams =
213                     (parameters.size() == 1 && paramIsCoords(0)) ||
214                     (parameters.size() == 2 && paramIsCoords(0) && paramIsInputColor(1));
215             if (!validParams) {
216                 errors.error(line, "'main' parameters must be (float2, (vec4|float4|half4)?)");
217                 return false;
218             }
219             break;
220         }
221         case ProgramKind::kRuntimeBlender: {
222             // (half4|float4) main(half4|float4, half4|float4)
223             if (!typeIsValidForColor(returnType)) {
224                 errors.error(line, "'main' must return: 'vec4', 'float4', or 'half4'");
225                 return false;
226             }
227             if (!(parameters.size() == 2 &&
228                   paramIsInputColor(0) &&
229                   paramIsDestColor(1))) {
230                 errors.error(line, "'main' parameters must be (vec4|float4|half4, "
231                                                                 "vec4|float4|half4)");
232                 return false;
233             }
234             break;
235         }
236         case ProgramKind::kCustomMeshVertex: {
237             // float2 main(Attributes, out Varyings)
238             if (!returnType.matches(*context.fTypes.fFloat2)) {
239                 errors.error(line, "'main' must return: 'vec2' or 'float2'");
240                 return false;
241             }
242             if (!(parameters.size() == 2 && paramIsInAttributes(0) && paramIsOutVaryings(1))) {
243                 errors.error(line, "'main' parameters must be (Attributes, out Varyings");
244                 return false;
245             }
246             break;
247         }
248         case ProgramKind::kCustomMeshFragment: {
249             // float2 main(Varyings) -or- float2 main(Varyings, out half4|float4]) -or-
250             // void main(Varyings) -or- void main(Varyings, out half4|float4])
251             if (!returnType.matches(*context.fTypes.fFloat2) &&
252                 !returnType.matches(*context.fTypes.fVoid)) {
253                 errors.error(line, "'main' must return: 'vec2', 'float2', 'or' 'void'");
254                 return false;
255             }
256             if (!((parameters.size() == 1 && paramIsInVaryings(0)) ||
257                   (parameters.size() == 2 && paramIsInVaryings(0) && paramIsOutColor(1)))) {
258                 errors.error(line, "'main' parameters must be (Varyings, (out (half4|float4))?)");
259                 return false;
260             }
261             break;
262         }
263         case ProgramKind::kGeneric:
264             // No rules apply here
265             break;
266         case ProgramKind::kFragment: {
267             bool validParams = (parameters.size() == 0) ||
268                                (parameters.size() == 1 && paramIsCoords(0));
269             if (!validParams) {
270                 errors.error(line, "shader 'main' must be main() or main(float2)");
271                 return false;
272             }
273             break;
274         }
275         case ProgramKind::kVertex:
276             if (parameters.size()) {
277                 errors.error(line, "shader 'main' must have zero parameters");
278                 return false;
279             }
280             break;
281     }
282     return true;
283 }
284 
285 /**
286  * Checks for a previously existing declaration of this function, reporting errors if there is an
287  * incompatible symbol. Returns true and sets outExistingDecl to point to the existing declaration
288  * (or null if none) on success, returns false on error.
289  */
find_existing_declaration(const Context & context,SymbolTable & symbols,int line,std::string_view name,std::vector<std::unique_ptr<Variable>> & parameters,const Type * returnType,const FunctionDeclaration ** outExistingDecl)290 static bool find_existing_declaration(const Context& context,
291                                       SymbolTable& symbols,
292                                       int line,
293                                       std::string_view name,
294                                       std::vector<std::unique_ptr<Variable>>& parameters,
295                                       const Type* returnType,
296                                       const FunctionDeclaration** outExistingDecl) {
297     ErrorReporter& errors = *context.fErrors;
298     const Symbol* entry = symbols[name];
299     *outExistingDecl = nullptr;
300     if (entry) {
301         std::vector<const FunctionDeclaration*> functions;
302         switch (entry->kind()) {
303             case Symbol::Kind::kUnresolvedFunction:
304                 functions = entry->as<UnresolvedFunction>().functions();
305                 break;
306             case Symbol::Kind::kFunctionDeclaration:
307                 functions.push_back(&entry->as<FunctionDeclaration>());
308                 break;
309             default:
310                 errors.error(line, "symbol '" + std::string(name) + "' was already defined");
311                 return false;
312         }
313         for (const FunctionDeclaration* other : functions) {
314             SkASSERT(name == other->name());
315             if (parameters.size() != other->parameters().size()) {
316                 continue;
317             }
318             bool match = true;
319             for (size_t i = 0; i < parameters.size(); i++) {
320                 if (!parameters[i]->type().matches(other->parameters()[i]->type())) {
321                     match = false;
322                     break;
323                 }
324             }
325             if (!match) {
326                 continue;
327             }
328             if (!returnType->matches(other->returnType())) {
329                 std::vector<const Variable*> paramPtrs;
330                 paramPtrs.reserve(parameters.size());
331                 for (std::unique_ptr<Variable>& param : parameters) {
332                     paramPtrs.push_back(param.get());
333                 }
334                 FunctionDeclaration invalidDecl(line,
335                                                 &other->modifiers(),
336                                                 name,
337                                                 std::move(paramPtrs),
338                                                 returnType,
339                                                 context.fConfig->fIsBuiltinCode);
340                 errors.error(line,
341                              "functions '" + invalidDecl.description() + "' and '" +
342                              other->description() + "' differ only in return type");
343                 return false;
344             }
345             for (size_t i = 0; i < parameters.size(); i++) {
346                 if (parameters[i]->modifiers() != other->parameters()[i]->modifiers()) {
347                     errors.error(line, "modifiers on parameter " + std::to_string(i + 1) +
348                                        " differ between declaration and definition");
349                     return false;
350                 }
351             }
352             if (other->definition() && !other->isBuiltin()) {
353                 errors.error(line, "duplicate definition of " + other->description());
354                 return false;
355             }
356             *outExistingDecl = other;
357             break;
358         }
359     }
360     return true;
361 }
362 
FunctionDeclaration(int line,const Modifiers * modifiers,std::string_view name,std::vector<const Variable * > parameters,const Type * returnType,bool builtin)363 FunctionDeclaration::FunctionDeclaration(int line,
364                                          const Modifiers* modifiers,
365                                          std::string_view name,
366                                          std::vector<const Variable*> parameters,
367                                          const Type* returnType,
368                                          bool builtin)
369         : INHERITED(line, kSymbolKind, name, /*type=*/nullptr)
370         , fDefinition(nullptr)
371         , fModifiers(modifiers)
372         , fParameters(std::move(parameters))
373         , fReturnType(returnType)
374         , fBuiltin(builtin)
375         , fIsMain(name == "main")
376         , fIntrinsicKind(builtin ? identify_intrinsic(name) : kNotIntrinsic) {}
377 
Convert(const Context & context,SymbolTable & symbols,int line,const Modifiers * modifiers,std::string_view name,std::vector<std::unique_ptr<Variable>> parameters,const Type * returnType)378 const FunctionDeclaration* FunctionDeclaration::Convert(
379         const Context& context,
380         SymbolTable& symbols,
381         int line,
382         const Modifiers* modifiers,
383         std::string_view name,
384         std::vector<std::unique_ptr<Variable>> parameters,
385         const Type* returnType) {
386     bool isMain = (name == "main");
387 
388     const FunctionDeclaration* decl = nullptr;
389     if (!check_modifiers(context, line, *modifiers) ||
390         !check_return_type(context, line, *returnType) ||
391         !check_parameters(context, parameters, isMain) ||
392         (isMain && !check_main_signature(context, line, *returnType, parameters)) ||
393         !find_existing_declaration(context, symbols, line, name, parameters, returnType, &decl)) {
394         return nullptr;
395     }
396     std::vector<const Variable*> finalParameters;
397     finalParameters.reserve(parameters.size());
398     for (std::unique_ptr<Variable>& param : parameters) {
399         finalParameters.push_back(symbols.takeOwnershipOfSymbol(std::move(param)));
400     }
401     if (decl) {
402         return decl;
403     }
404     auto result = std::make_unique<FunctionDeclaration>(line, modifiers, name,
405                                                         std::move(finalParameters), returnType,
406                                                         context.fConfig->fIsBuiltinCode);
407     return symbols.add(std::move(result));
408 }
409 
mangledName() const410 std::string FunctionDeclaration::mangledName() const {
411     if ((this->isBuiltin() && !this->definition()) || this->isMain()) {
412         // Builtins without a definition (like `sin` or `sqrt`) must use their real names.
413         return std::string(this->name());
414     }
415     // Built-in functions can have a $ prefix, which will fail to compile in GLSL/Metal. Remove the
416     // $ and add a unique mangling specifier, so user code can't conflict with the name.
417     std::string_view name = this->name();
418     const char* builtinMarker = "";
419     if (skstd::starts_with(name, '$')) {
420         name.remove_prefix(1);
421         builtinMarker = "Q";  // a unique, otherwise-unused mangle character
422     }
423     // GLSL forbids two underscores in a row; add an extra character if necessary to avoid this.
424     const char* splitter = skstd::ends_with(name, '_') ? "x_" : "_";
425     // Rename function to `funcname_returntypeparamtypes`.
426     std::string result = std::string(name) + splitter + builtinMarker +
427                          this->returnType().abbreviatedName();
428     for (const Variable* p : this->parameters()) {
429         result += p->type().abbreviatedName();
430     }
431     return result;
432 }
433 
description() const434 std::string FunctionDeclaration::description() const {
435     std::string result = this->returnType().displayName() + " " + std::string(this->name()) + "(";
436     std::string separator;
437     for (const Variable* p : this->parameters()) {
438         result += separator;
439         separator = ", ";
440         result += p->type().displayName();
441         result += " ";
442         result += p->name();
443     }
444     result += ")";
445     return result;
446 }
447 
matches(const FunctionDeclaration & f) const448 bool FunctionDeclaration::matches(const FunctionDeclaration& f) const {
449     if (this->name() != f.name()) {
450         return false;
451     }
452     const std::vector<const Variable*>& parameters = this->parameters();
453     const std::vector<const Variable*>& otherParameters = f.parameters();
454     if (parameters.size() != otherParameters.size()) {
455         return false;
456     }
457     for (size_t i = 0; i < parameters.size(); i++) {
458         if (!parameters[i]->type().matches(otherParameters[i]->type())) {
459             return false;
460         }
461     }
462     return true;
463 }
464 
determineFinalTypes(const ExpressionArray & arguments,ParamTypes * outParameterTypes,const Type ** outReturnType) const465 bool FunctionDeclaration::determineFinalTypes(const ExpressionArray& arguments,
466                                               ParamTypes* outParameterTypes,
467                                               const Type** outReturnType) const {
468     const std::vector<const Variable*>& parameters = this->parameters();
469     SkASSERT(arguments.size() == parameters.size());
470 
471     outParameterTypes->reserve_back(arguments.size());
472     int genericIndex = -1;
473     for (size_t i = 0; i < arguments.size(); i++) {
474         // Non-generic parameters are final as-is.
475         const Type& parameterType = parameters[i]->type();
476         if (parameterType.typeKind() != Type::TypeKind::kGeneric) {
477             outParameterTypes->push_back(&parameterType);
478             continue;
479         }
480         // We use the first generic parameter we find to lock in the generic index;
481         // e.g. if we find `float3` here, all `$genType`s will be assumed to be `float3`.
482         const std::vector<const Type*>& types = parameterType.coercibleTypes();
483         if (genericIndex == -1) {
484             for (size_t j = 0; j < types.size(); j++) {
485                 if (arguments[i]->type().canCoerceTo(*types[j], /*allowNarrowing=*/true)) {
486                     genericIndex = j;
487                     break;
488                 }
489             }
490             if (genericIndex == -1) {
491                 // The passed-in type wasn't a match for ANY of the generic possibilities.
492                 // This function isn't a match at all.
493                 return false;
494             }
495         }
496         outParameterTypes->push_back(types[genericIndex]);
497     }
498     // Apply the generic index to our return type.
499     const Type& returnType = this->returnType();
500     if (returnType.typeKind() == Type::TypeKind::kGeneric) {
501         if (genericIndex == -1) {
502             // We don't support functions with a generic return type and no other generics.
503             return false;
504         }
505         *outReturnType = returnType.coercibleTypes()[genericIndex];
506     } else {
507         *outReturnType = &returnType;
508     }
509     return true;
510 }
511 
512 }  // namespace SkSL
513