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