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