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(¶meterType);
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