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