2 * Copyright 2021 Google LLC.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
10 #include "include/private/SkStringView.h"
11 #include "src/sksl/SkSLCompiler.h"
12 #include "src/sksl/ir/SkSLUnresolvedFunction.h"
16 static IntrinsicKind identify_intrinsic(std::string_view functionName) {
17 #define SKSL_INTRINSIC(name) {#name, k_##name##_IntrinsicKind},
18 static const auto* kAllIntrinsics = new SkTHashMap<std::string_view, IntrinsicKind>{
23 if (skstd::starts_with(functionName, '$')) {
24 functionName.remove_prefix(1);
27 IntrinsicKind* kind = kAllIntrinsics->find(functionName);
28 return kind ? *kind : kNotIntrinsic;
31 static bool check_modifiers(const Context& context,
33 const Modifiers& modifiers) {
34 const int permitted = Modifiers::kHasSideEffects_Flag |
35 Modifiers::kInline_Flag |
36 Modifiers::kNoInline_Flag |
37 (context.fConfig->fIsBuiltinCode ? Modifiers::kES3_Flag : 0);
38 modifiers.checkPermitted(context, pos, permitted, /*permittedLayoutFlags=*/0);
39 if ((modifiers.fFlags & Modifiers::kInline_Flag) &&
40 (modifiers.fFlags & Modifiers::kNoInline_Flag)) {
41 context.fErrors->error(pos, "functions cannot be both 'inline' and 'noinline'");
47 static bool check_return_type(const Context& context, Position pos, const Type& returnType) {
48 ErrorReporter& errors = *context.fErrors;
49 if (returnType.isArray()) {
50 errors.error(pos, "functions may not return type '" + returnType.displayName() + "'");
53 if (context.fConfig->strictES2Mode() && returnType.isOrContainsArray()) {
54 errors.error(pos, "functions may not return structs containing arrays");
57 if (!context.fConfig->fIsBuiltinCode && returnType.componentType().isOpaque()) {
58 errors.error(pos, "functions may not return opaque type '" + returnType.displayName() +
65 static bool check_parameters(const Context& context,
66 std::vector<std::unique_ptr<Variable>>& parameters,
68 auto typeIsValidForColor = [&](const Type& type) {
69 return type.matches(*context.fTypes.fHalf4) || type.matches(*context.fTypes.fFloat4);
72 // The first color parameter passed to main() is the input color; the second is the dest color.
73 static constexpr int kBuiltinColorIDs[] = {SK_INPUT_COLOR_BUILTIN, SK_DEST_COLOR_BUILTIN};
74 unsigned int builtinColorIndex = 0;
76 // Check modifiers on each function parameter.
77 for (auto& param : parameters) {
78 param->modifiers().checkPermitted(context, param->modifiersPosition(),
79 Modifiers::kConst_Flag | Modifiers::kIn_Flag | Modifiers::kOut_Flag,
80 /*permittedLayoutFlags=*/0);
81 const Type& type = param->type();
82 // Only the (builtin) declarations of 'sample' are allowed to have shader/colorFilter or FP
83 // parameters. You can pass other opaque types to functions safely; this restriction is
84 // specific to "child" objects.
85 if (type.isEffectChild() && !context.fConfig->fIsBuiltinCode) {
86 context.fErrors->error(param->fPosition, "parameters of type '" + type.displayName() +
91 Modifiers m = param->modifiers();
92 bool modifiersChanged = false;
94 // The `in` modifier on function parameters is implicit, so we can replace `in float x` with
95 // `float x`. This prevents any ambiguity when matching a function by its param types.
96 if (Modifiers::kIn_Flag == (m.fFlags & (Modifiers::kOut_Flag | Modifiers::kIn_Flag))) {
97 m.fFlags &= ~(Modifiers::kOut_Flag | Modifiers::kIn_Flag);
98 modifiersChanged = true;
102 if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind) &&
103 context.fConfig->fKind != ProgramKind::kMeshFragment &&
104 context.fConfig->fKind != ProgramKind::kMeshVertex) {
105 // We verify that the signature is fully correct later. For now, if this is a
106 // runtime effect of any flavor, a float2 param is supposed to be the coords, and a
107 // half4/float parameter is supposed to be the input or destination color:
108 if (type.matches(*context.fTypes.fFloat2)) {
109 m.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN;
110 modifiersChanged = true;
111 } else if (typeIsValidForColor(type) &&
112 builtinColorIndex < SK_ARRAY_COUNT(kBuiltinColorIDs)) {
113 m.fLayout.fBuiltin = kBuiltinColorIDs[builtinColorIndex++];
114 modifiersChanged = true;
116 } else if (ProgramConfig::IsFragment(context.fConfig->fKind)) {
117 // For testing purposes, we have .sksl inputs that are treated as both runtime
118 // effects and fragment shaders. To make that work, fragment shaders are allowed to
119 // have a coords parameter.
120 if (type.matches(*context.fTypes.fFloat2)) {
121 m.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN;
122 modifiersChanged = true;
127 if (modifiersChanged) {
128 param->setModifiers(context.fModifiersPool->add(m));
134 static bool check_main_signature(const Context& context, Position pos, const Type& returnType,
135 std::vector<std::unique_ptr<Variable>>& parameters) {
136 ErrorReporter& errors = *context.fErrors;
137 ProgramKind kind = context.fConfig->fKind;
139 auto typeIsValidForColor = [&](const Type& type) {
140 return type.matches(*context.fTypes.fHalf4) || type.matches(*context.fTypes.fFloat4);
143 auto typeIsValidForAttributes = [&](const Type& type) {
144 return type.isStruct() && type.name() == "Attributes";
147 auto typeIsValidForVaryings = [&](const Type& type) {
148 return type.isStruct() && type.name() == "Varyings";
151 auto paramIsCoords = [&](int idx) {
152 const Variable& p = *parameters[idx];
153 return p.type().matches(*context.fTypes.fFloat2) &&
154 p.modifiers().fFlags == 0 &&
155 p.modifiers().fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN;
158 auto paramIsBuiltinColor = [&](int idx, int builtinID) {
159 const Variable& p = *parameters[idx];
160 return typeIsValidForColor(p.type()) &&
161 p.modifiers().fFlags == 0 &&
162 p.modifiers().fLayout.fBuiltin == builtinID;
165 auto paramIsInAttributes = [&](int idx) {
166 const Variable& p = *parameters[idx];
167 return typeIsValidForAttributes(p.type()) && p.modifiers().fFlags == 0;
170 auto paramIsOutVaryings = [&](int idx) {
171 const Variable& p = *parameters[idx];
172 return typeIsValidForVaryings(p.type()) && p.modifiers().fFlags == Modifiers::kOut_Flag;
175 auto paramIsInVaryings = [&](int idx) {
176 const Variable& p = *parameters[idx];
177 return typeIsValidForVaryings(p.type()) && p.modifiers().fFlags == 0;
180 auto paramIsOutColor = [&](int idx) {
181 const Variable& p = *parameters[idx];
182 return typeIsValidForColor(p.type()) && p.modifiers().fFlags == Modifiers::kOut_Flag;
185 auto paramIsInputColor = [&](int n) { return paramIsBuiltinColor(n, SK_INPUT_COLOR_BUILTIN); };
186 auto paramIsDestColor = [&](int n) { return paramIsBuiltinColor(n, SK_DEST_COLOR_BUILTIN); };
189 case ProgramKind::kRuntimeColorFilter: {
190 // (half4|float4) main(half4|float4)
191 if (!typeIsValidForColor(returnType)) {
192 errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
195 bool validParams = (parameters.size() == 1 && paramIsInputColor(0));
197 errors.error(pos, "'main' parameter must be 'vec4', 'float4', or 'half4'");
202 case ProgramKind::kRuntimeShader:
203 case ProgramKind::kPrivateRuntimeShader: {
204 // (half4|float4) main(float2) -or- (half4|float4) main(float2, half4|float4)
205 if (!typeIsValidForColor(returnType)) {
206 errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
210 (parameters.size() == 1 && paramIsCoords(0)) ||
211 (parameters.size() == 2 && paramIsCoords(0) && paramIsInputColor(1));
213 errors.error(pos, "'main' parameters must be (float2, (vec4|float4|half4)?)");
218 case ProgramKind::kRuntimeBlender: {
219 // (half4|float4) main(half4|float4, half4|float4)
220 if (!typeIsValidForColor(returnType)) {
221 errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
224 if (!(parameters.size() == 2 &&
225 paramIsInputColor(0) &&
226 paramIsDestColor(1))) {
227 errors.error(pos, "'main' parameters must be (vec4|float4|half4, "
228 "vec4|float4|half4)");
233 case ProgramKind::kMeshVertex: {
234 // float2 main(Attributes, out Varyings)
235 if (!returnType.matches(*context.fTypes.fFloat2)) {
236 errors.error(pos, "'main' must return: 'vec2' or 'float2'");
239 if (!(parameters.size() == 2 && paramIsInAttributes(0) && paramIsOutVaryings(1))) {
240 errors.error(pos, "'main' parameters must be (Attributes, out Varyings");
245 case ProgramKind::kMeshFragment: {
246 // float2 main(Varyings) -or- float2 main(Varyings, out half4|float4]) -or-
247 // void main(Varyings) -or- void main(Varyings, out half4|float4])
248 if (!returnType.matches(*context.fTypes.fFloat2) &&
249 !returnType.matches(*context.fTypes.fVoid)) {
250 errors.error(pos, "'main' must return: 'vec2', 'float2', 'or' 'void'");
253 if (!((parameters.size() == 1 && paramIsInVaryings(0)) ||
254 (parameters.size() == 2 && paramIsInVaryings(0) && paramIsOutColor(1)))) {
255 errors.error(pos, "'main' parameters must be (Varyings, (out (half4|float4))?)");
260 case ProgramKind::kGeneric:
261 // No rules apply here
263 case ProgramKind::kFragment:
264 case ProgramKind::kGraphiteFragment: {
265 bool validParams = (parameters.size() == 0) ||
266 (parameters.size() == 1 && paramIsCoords(0));
268 errors.error(pos, "shader 'main' must be main() or main(float2)");
273 case ProgramKind::kVertex:
274 case ProgramKind::kGraphiteVertex:
275 if (parameters.size()) {
276 errors.error(pos, "shader 'main' must have zero parameters");
285 * Checks for a previously existing declaration of this function, reporting errors if there is an
286 * incompatible symbol. Returns true and sets outExistingDecl to point to the existing declaration
287 * (or null if none) on success, returns false on error.
289 static bool find_existing_declaration(const Context& context,
290 SymbolTable& symbols,
292 std::string_view name,
293 std::vector<std::unique_ptr<Variable>>& parameters,
294 Position returnTypePos,
295 const Type* returnType,
296 const FunctionDeclaration** outExistingDecl) {
297 ErrorReporter& errors = *context.fErrors;
298 const Symbol* entry = symbols[name];
299 *outExistingDecl = nullptr;
301 std::vector<const FunctionDeclaration*> functions;
302 switch (entry->kind()) {
303 case Symbol::Kind::kUnresolvedFunction:
304 functions = entry->as<UnresolvedFunction>().functions();
306 case Symbol::Kind::kFunctionDeclaration:
307 functions.push_back(&entry->as<FunctionDeclaration>());
310 errors.error(pos, "symbol '" + std::string(name) + "' was already defined");
313 for (const FunctionDeclaration* other : functions) {
314 SkASSERT(name == other->name());
315 if (parameters.size() != other->parameters().size()) {
319 for (size_t i = 0; i < parameters.size(); i++) {
320 if (!parameters[i]->type().matches(other->parameters()[i]->type())) {
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());
334 FunctionDeclaration invalidDecl(pos,
337 std::move(paramPtrs),
339 context.fConfig->fIsBuiltinCode);
340 errors.error(returnTypePos,
341 "functions '" + invalidDecl.description() + "' and '" +
342 other->description() + "' differ only in return type");
345 for (size_t i = 0; i < parameters.size(); i++) {
346 if (parameters[i]->modifiers() != other->parameters()[i]->modifiers()) {
347 errors.error(parameters[i]->fPosition, "modifiers on parameter " +
348 std::to_string(i + 1) + " differ between declaration and definition");
352 if (other->definition() && !other->isBuiltin()) {
353 errors.error(pos, "duplicate definition of " + other->description());
356 *outExistingDecl = other;
363 FunctionDeclaration::FunctionDeclaration(Position pos,
364 const Modifiers* modifiers,
365 std::string_view name,
366 std::vector<const Variable*> parameters,
367 const Type* returnType,
369 : INHERITED(pos, kSymbolKind, name, /*type=*/nullptr)
370 , fDefinition(nullptr)
371 , fModifiers(modifiers)
372 , fParameters(std::move(parameters))
373 , fReturnType(returnType)
375 , fIsMain(name == "main")
376 , fIntrinsicKind(builtin ? identify_intrinsic(name) : kNotIntrinsic) {}
378 const FunctionDeclaration* FunctionDeclaration::Convert(
379 const Context& context,
380 SymbolTable& symbols,
382 Position modifiersPosition,
383 const Modifiers* modifiers,
384 std::string_view name,
385 std::vector<std::unique_ptr<Variable>> parameters,
386 Position returnTypePos,
387 const Type* returnType) {
388 bool isMain = (name == "main");
390 const FunctionDeclaration* decl = nullptr;
391 if (!check_modifiers(context, modifiersPosition, *modifiers) ||
392 !check_return_type(context, returnTypePos, *returnType) ||
393 !check_parameters(context, parameters, isMain) ||
394 (isMain && !check_main_signature(context, pos, *returnType, parameters)) ||
395 !find_existing_declaration(context, symbols, pos, name, parameters, returnTypePos,
396 returnType, &decl)) {
399 std::vector<const Variable*> finalParameters;
400 finalParameters.reserve(parameters.size());
401 for (std::unique_ptr<Variable>& param : parameters) {
402 finalParameters.push_back(symbols.takeOwnershipOfSymbol(std::move(param)));
407 auto result = std::make_unique<FunctionDeclaration>(pos,
410 std::move(finalParameters),
412 context.fConfig->fIsBuiltinCode);
413 return symbols.add(std::move(result));
416 std::string FunctionDeclaration::mangledName() const {
417 if ((this->isBuiltin() && !this->definition()) || this->isMain()) {
418 // Builtins without a definition (like `sin` or `sqrt`) must use their real names.
419 return std::string(this->name());
421 // Built-in functions can have a $ prefix, which will fail to compile in GLSL/Metal. Remove the
422 // $ and add a unique mangling specifier, so user code can't conflict with the name.
423 std::string_view name = this->name();
424 const char* builtinMarker = "";
425 if (skstd::starts_with(name, '$')) {
426 name.remove_prefix(1);
427 builtinMarker = "Q"; // a unique, otherwise-unused mangle character
429 // GLSL forbids two underscores in a row; add an extra character if necessary to avoid this.
430 const char* splitter = skstd::ends_with(name, '_') ? "x_" : "_";
431 // Rename function to `funcname_returntypeparamtypes`.
432 std::string result = std::string(name) + splitter + builtinMarker +
433 this->returnType().abbreviatedName();
434 for (const Variable* p : this->parameters()) {
435 result += p->type().abbreviatedName();
440 std::string FunctionDeclaration::description() const {
441 std::string result = this->returnType().displayName() + " " + std::string(this->name()) + "(";
442 std::string separator;
443 for (const Variable* p : this->parameters()) {
446 result += p->type().displayName();
454 bool FunctionDeclaration::matches(const FunctionDeclaration& f) const {
455 if (this->name() != f.name()) {
458 const std::vector<const Variable*>& parameters = this->parameters();
459 const std::vector<const Variable*>& otherParameters = f.parameters();
460 if (parameters.size() != otherParameters.size()) {
463 for (size_t i = 0; i < parameters.size(); i++) {
464 if (!parameters[i]->type().matches(otherParameters[i]->type())) {
471 bool FunctionDeclaration::determineFinalTypes(const ExpressionArray& arguments,
472 ParamTypes* outParameterTypes,
473 const Type** outReturnType) const {
474 const std::vector<const Variable*>& parameters = this->parameters();
475 SkASSERT(arguments.size() == parameters.size());
477 outParameterTypes->reserve_back(arguments.size());
478 int genericIndex = -1;
479 for (size_t i = 0; i < arguments.size(); i++) {
480 // Non-generic parameters are final as-is.
481 const Type& parameterType = parameters[i]->type();
482 if (parameterType.typeKind() != Type::TypeKind::kGeneric) {
483 outParameterTypes->push_back(¶meterType);
486 // We use the first generic parameter we find to lock in the generic index;
487 // e.g. if we find `float3` here, all `$genType`s will be assumed to be `float3`.
488 const std::vector<const Type*>& types = parameterType.coercibleTypes();
489 if (genericIndex == -1) {
490 for (size_t j = 0; j < types.size(); j++) {
491 if (arguments[i]->type().canCoerceTo(*types[j], /*allowNarrowing=*/true)) {
496 if (genericIndex == -1) {
497 // The passed-in type wasn't a match for ANY of the generic possibilities.
498 // This function isn't a match at all.
502 outParameterTypes->push_back(types[genericIndex]);
504 // Apply the generic index to our return type.
505 const Type& returnType = this->returnType();
506 if (returnType.typeKind() == Type::TypeKind::kGeneric) {
507 if (genericIndex == -1) {
508 // We don't support functions with a generic return type and no other generics.
511 *outReturnType = returnType.coercibleTypes()[genericIndex];
513 *outReturnType = &returnType;