1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2021 Google LLC.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDeclaration.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEnumBitMask.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkStringView.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLBuiltinTypes.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLDefines.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPosition.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramKind.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLString.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpression.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLayout.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifierFlags.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifiers.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSymbolTable.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLType.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariable.h"
30*c8dee2aaSAndroid Build Coastguard Worker
31*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
32*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
33*c8dee2aaSAndroid Build Coastguard Worker
34*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
37*c8dee2aaSAndroid Build Coastguard Worker
check_modifiers(const Context & context,Position pos,ModifierFlags modifierFlags)38*c8dee2aaSAndroid Build Coastguard Worker static bool check_modifiers(const Context& context, Position pos, ModifierFlags modifierFlags) {
39*c8dee2aaSAndroid Build Coastguard Worker const ModifierFlags permitted = ModifierFlag::kInline |
40*c8dee2aaSAndroid Build Coastguard Worker ModifierFlag::kNoInline |
41*c8dee2aaSAndroid Build Coastguard Worker (context.fConfig->isBuiltinCode() ? ModifierFlag::kES3 |
42*c8dee2aaSAndroid Build Coastguard Worker ModifierFlag::kPure |
43*c8dee2aaSAndroid Build Coastguard Worker ModifierFlag::kExport
44*c8dee2aaSAndroid Build Coastguard Worker : ModifierFlag::kNone);
45*c8dee2aaSAndroid Build Coastguard Worker modifierFlags.checkPermittedFlags(context, pos, permitted);
46*c8dee2aaSAndroid Build Coastguard Worker if (modifierFlags.isInline() && modifierFlags.isNoInline()) {
47*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(pos, "functions cannot be both 'inline' and 'noinline'");
48*c8dee2aaSAndroid Build Coastguard Worker return false;
49*c8dee2aaSAndroid Build Coastguard Worker }
50*c8dee2aaSAndroid Build Coastguard Worker return true;
51*c8dee2aaSAndroid Build Coastguard Worker }
52*c8dee2aaSAndroid Build Coastguard Worker
check_return_type(const Context & context,Position pos,const Type & returnType)53*c8dee2aaSAndroid Build Coastguard Worker static bool check_return_type(const Context& context, Position pos, const Type& returnType) {
54*c8dee2aaSAndroid Build Coastguard Worker ErrorReporter& errors = *context.fErrors;
55*c8dee2aaSAndroid Build Coastguard Worker if (returnType.isArray()) {
56*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "functions may not return type '" + returnType.displayName() + "'");
57*c8dee2aaSAndroid Build Coastguard Worker return false;
58*c8dee2aaSAndroid Build Coastguard Worker }
59*c8dee2aaSAndroid Build Coastguard Worker if (context.fConfig->strictES2Mode() && returnType.isOrContainsArray()) {
60*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "functions may not return structs containing arrays");
61*c8dee2aaSAndroid Build Coastguard Worker return false;
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker if (!context.fConfig->isBuiltinCode() && returnType.componentType().isOpaque()) {
64*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "functions may not return opaque type '" + returnType.displayName() +
65*c8dee2aaSAndroid Build Coastguard Worker "'");
66*c8dee2aaSAndroid Build Coastguard Worker return false;
67*c8dee2aaSAndroid Build Coastguard Worker }
68*c8dee2aaSAndroid Build Coastguard Worker return true;
69*c8dee2aaSAndroid Build Coastguard Worker }
70*c8dee2aaSAndroid Build Coastguard Worker
check_parameters(const Context & context,TArray<std::unique_ptr<Variable>> & parameters,ModifierFlags modifierFlags,IntrinsicKind intrinsicKind)71*c8dee2aaSAndroid Build Coastguard Worker static bool check_parameters(const Context& context,
72*c8dee2aaSAndroid Build Coastguard Worker TArray<std::unique_ptr<Variable>>& parameters,
73*c8dee2aaSAndroid Build Coastguard Worker ModifierFlags modifierFlags,
74*c8dee2aaSAndroid Build Coastguard Worker IntrinsicKind intrinsicKind) {
75*c8dee2aaSAndroid Build Coastguard Worker // Check modifiers on each function parameter.
76*c8dee2aaSAndroid Build Coastguard Worker for (auto& param : parameters) {
77*c8dee2aaSAndroid Build Coastguard Worker const Type& type = param->type();
78*c8dee2aaSAndroid Build Coastguard Worker ModifierFlags permittedFlags = ModifierFlag::kConst | ModifierFlag::kIn;
79*c8dee2aaSAndroid Build Coastguard Worker LayoutFlags permittedLayoutFlags = LayoutFlag::kNone;
80*c8dee2aaSAndroid Build Coastguard Worker if (!type.isOpaque()) {
81*c8dee2aaSAndroid Build Coastguard Worker permittedFlags |= ModifierFlag::kOut;
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker if (type.isStorageTexture()) {
84*c8dee2aaSAndroid Build Coastguard Worker // We allow `readonly`, `writeonly` and `layout(pixel-format)` on storage textures.
85*c8dee2aaSAndroid Build Coastguard Worker permittedFlags |= ModifierFlag::kReadOnly | ModifierFlag::kWriteOnly;
86*c8dee2aaSAndroid Build Coastguard Worker permittedLayoutFlags |= LayoutFlag::kAllPixelFormats;
87*c8dee2aaSAndroid Build Coastguard Worker
88*c8dee2aaSAndroid Build Coastguard Worker // Intrinsics are allowed to accept any pixel format, but user code must explicitly
89*c8dee2aaSAndroid Build Coastguard Worker // specify a pixel format like `layout(rgba32f)`.
90*c8dee2aaSAndroid Build Coastguard Worker if (intrinsicKind == kNotIntrinsic &&
91*c8dee2aaSAndroid Build Coastguard Worker !(param->layout().fFlags & LayoutFlag::kAllPixelFormats)) {
92*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(param->fPosition, "storage texture parameters must specify "
93*c8dee2aaSAndroid Build Coastguard Worker "a pixel format layout-qualifier");
94*c8dee2aaSAndroid Build Coastguard Worker return false;
95*c8dee2aaSAndroid Build Coastguard Worker }
96*c8dee2aaSAndroid Build Coastguard Worker }
97*c8dee2aaSAndroid Build Coastguard Worker param->modifierFlags().checkPermittedFlags(context, param->modifiersPosition(),
98*c8dee2aaSAndroid Build Coastguard Worker permittedFlags);
99*c8dee2aaSAndroid Build Coastguard Worker param->layout().checkPermittedLayout(context, param->modifiersPosition(),
100*c8dee2aaSAndroid Build Coastguard Worker permittedLayoutFlags);
101*c8dee2aaSAndroid Build Coastguard Worker
102*c8dee2aaSAndroid Build Coastguard Worker // Public Runtime Effects aren't allowed to pass shader/colorFilter/blender types to
103*c8dee2aaSAndroid Build Coastguard Worker // function calls. You can pass other opaque types to functions safely; this restriction is
104*c8dee2aaSAndroid Build Coastguard Worker // specific to "child" objects.
105*c8dee2aaSAndroid Build Coastguard Worker if (!ProgramConfig::AllowsPrivateIdentifiers(context.fConfig->fKind) &&
106*c8dee2aaSAndroid Build Coastguard Worker type.isEffectChild()) {
107*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(param->fPosition, "parameters of type '" + type.displayName() +
108*c8dee2aaSAndroid Build Coastguard Worker "' not allowed");
109*c8dee2aaSAndroid Build Coastguard Worker return false;
110*c8dee2aaSAndroid Build Coastguard Worker }
111*c8dee2aaSAndroid Build Coastguard Worker
112*c8dee2aaSAndroid Build Coastguard Worker // Pure functions should not change any state, and should be safe to eliminate if their
113*c8dee2aaSAndroid Build Coastguard Worker // result is not used; this is incompatible with out-parameters, so we forbid it here.
114*c8dee2aaSAndroid Build Coastguard Worker // (We don't exhaustively guard against pure functions changing global state in other ways,
115*c8dee2aaSAndroid Build Coastguard Worker // though, since they aren't allowed in user code.)
116*c8dee2aaSAndroid Build Coastguard Worker if (modifierFlags.isPure() && (param->modifierFlags() & ModifierFlag::kOut)) {
117*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(param->modifiersPosition(),
118*c8dee2aaSAndroid Build Coastguard Worker "pure functions cannot have out parameters");
119*c8dee2aaSAndroid Build Coastguard Worker return false;
120*c8dee2aaSAndroid Build Coastguard Worker }
121*c8dee2aaSAndroid Build Coastguard Worker }
122*c8dee2aaSAndroid Build Coastguard Worker return true;
123*c8dee2aaSAndroid Build Coastguard Worker }
124*c8dee2aaSAndroid Build Coastguard Worker
type_is_valid_for_color(const Type & type)125*c8dee2aaSAndroid Build Coastguard Worker static bool type_is_valid_for_color(const Type& type) {
126*c8dee2aaSAndroid Build Coastguard Worker return type.isVector() && type.columns() == 4 && type.componentType().isFloat();
127*c8dee2aaSAndroid Build Coastguard Worker }
128*c8dee2aaSAndroid Build Coastguard Worker
type_is_valid_for_coords(const Type & type)129*c8dee2aaSAndroid Build Coastguard Worker static bool type_is_valid_for_coords(const Type& type) {
130*c8dee2aaSAndroid Build Coastguard Worker return type.isVector() && type.highPrecision() && type.columns() == 2 &&
131*c8dee2aaSAndroid Build Coastguard Worker type.componentType().isFloat();
132*c8dee2aaSAndroid Build Coastguard Worker }
133*c8dee2aaSAndroid Build Coastguard Worker
check_main_signature(const Context & context,Position pos,const Type & returnType,TArray<std::unique_ptr<Variable>> & parameters)134*c8dee2aaSAndroid Build Coastguard Worker static bool check_main_signature(const Context& context, Position pos, const Type& returnType,
135*c8dee2aaSAndroid Build Coastguard Worker TArray<std::unique_ptr<Variable>>& parameters) {
136*c8dee2aaSAndroid Build Coastguard Worker ErrorReporter& errors = *context.fErrors;
137*c8dee2aaSAndroid Build Coastguard Worker ProgramKind kind = context.fConfig->fKind;
138*c8dee2aaSAndroid Build Coastguard Worker
139*c8dee2aaSAndroid Build Coastguard Worker auto typeIsValidForAttributes = [](const Type& type) {
140*c8dee2aaSAndroid Build Coastguard Worker return type.isStruct() && type.name() == "Attributes";
141*c8dee2aaSAndroid Build Coastguard Worker };
142*c8dee2aaSAndroid Build Coastguard Worker
143*c8dee2aaSAndroid Build Coastguard Worker auto typeIsValidForVaryings = [](const Type& type) {
144*c8dee2aaSAndroid Build Coastguard Worker return type.isStruct() && type.name() == "Varyings";
145*c8dee2aaSAndroid Build Coastguard Worker };
146*c8dee2aaSAndroid Build Coastguard Worker
147*c8dee2aaSAndroid Build Coastguard Worker auto paramIsCoords = [&](int idx) {
148*c8dee2aaSAndroid Build Coastguard Worker const Variable& p = *parameters[idx];
149*c8dee2aaSAndroid Build Coastguard Worker return type_is_valid_for_coords(p.type()) && p.modifierFlags() == ModifierFlag::kNone;
150*c8dee2aaSAndroid Build Coastguard Worker };
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker auto paramIsColor = [&](int idx) {
153*c8dee2aaSAndroid Build Coastguard Worker const Variable& p = *parameters[idx];
154*c8dee2aaSAndroid Build Coastguard Worker return type_is_valid_for_color(p.type()) && p.modifierFlags() == ModifierFlag::kNone;
155*c8dee2aaSAndroid Build Coastguard Worker };
156*c8dee2aaSAndroid Build Coastguard Worker
157*c8dee2aaSAndroid Build Coastguard Worker auto paramIsConstInAttributes = [&](int idx) {
158*c8dee2aaSAndroid Build Coastguard Worker const Variable& p = *parameters[idx];
159*c8dee2aaSAndroid Build Coastguard Worker return typeIsValidForAttributes(p.type()) && p.modifierFlags() == ModifierFlag::kConst;
160*c8dee2aaSAndroid Build Coastguard Worker };
161*c8dee2aaSAndroid Build Coastguard Worker
162*c8dee2aaSAndroid Build Coastguard Worker auto paramIsConstInVaryings = [&](int idx) {
163*c8dee2aaSAndroid Build Coastguard Worker const Variable& p = *parameters[idx];
164*c8dee2aaSAndroid Build Coastguard Worker return typeIsValidForVaryings(p.type()) && p.modifierFlags() == ModifierFlag::kConst;
165*c8dee2aaSAndroid Build Coastguard Worker };
166*c8dee2aaSAndroid Build Coastguard Worker
167*c8dee2aaSAndroid Build Coastguard Worker auto paramIsOutColor = [&](int idx) {
168*c8dee2aaSAndroid Build Coastguard Worker const Variable& p = *parameters[idx];
169*c8dee2aaSAndroid Build Coastguard Worker return type_is_valid_for_color(p.type()) && p.modifierFlags() == ModifierFlag::kOut;
170*c8dee2aaSAndroid Build Coastguard Worker };
171*c8dee2aaSAndroid Build Coastguard Worker
172*c8dee2aaSAndroid Build Coastguard Worker switch (kind) {
173*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kRuntimeColorFilter:
174*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kPrivateRuntimeColorFilter: {
175*c8dee2aaSAndroid Build Coastguard Worker // (half4|float4) main(half4|float4)
176*c8dee2aaSAndroid Build Coastguard Worker if (!type_is_valid_for_color(returnType)) {
177*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
178*c8dee2aaSAndroid Build Coastguard Worker return false;
179*c8dee2aaSAndroid Build Coastguard Worker }
180*c8dee2aaSAndroid Build Coastguard Worker bool validParams = (parameters.size() == 1 && paramIsColor(0));
181*c8dee2aaSAndroid Build Coastguard Worker if (!validParams) {
182*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "'main' parameter must be 'vec4', 'float4', or 'half4'");
183*c8dee2aaSAndroid Build Coastguard Worker return false;
184*c8dee2aaSAndroid Build Coastguard Worker }
185*c8dee2aaSAndroid Build Coastguard Worker break;
186*c8dee2aaSAndroid Build Coastguard Worker }
187*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kRuntimeShader:
188*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kPrivateRuntimeShader: {
189*c8dee2aaSAndroid Build Coastguard Worker // (half4|float4) main(float2)
190*c8dee2aaSAndroid Build Coastguard Worker if (!type_is_valid_for_color(returnType)) {
191*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
192*c8dee2aaSAndroid Build Coastguard Worker return false;
193*c8dee2aaSAndroid Build Coastguard Worker }
194*c8dee2aaSAndroid Build Coastguard Worker if (!(parameters.size() == 1 && paramIsCoords(0))) {
195*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "'main' parameter must be 'float2' or 'vec2'");
196*c8dee2aaSAndroid Build Coastguard Worker return false;
197*c8dee2aaSAndroid Build Coastguard Worker }
198*c8dee2aaSAndroid Build Coastguard Worker break;
199*c8dee2aaSAndroid Build Coastguard Worker }
200*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kRuntimeBlender:
201*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kPrivateRuntimeBlender: {
202*c8dee2aaSAndroid Build Coastguard Worker // (half4|float4) main(half4|float4, half4|float4)
203*c8dee2aaSAndroid Build Coastguard Worker if (!type_is_valid_for_color(returnType)) {
204*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "'main' must return: 'vec4', 'float4', or 'half4'");
205*c8dee2aaSAndroid Build Coastguard Worker return false;
206*c8dee2aaSAndroid Build Coastguard Worker }
207*c8dee2aaSAndroid Build Coastguard Worker if (!(parameters.size() == 2 && paramIsColor(0) && paramIsColor(1))) {
208*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "'main' parameters must be (vec4|float4|half4, "
209*c8dee2aaSAndroid Build Coastguard Worker "vec4|float4|half4)");
210*c8dee2aaSAndroid Build Coastguard Worker return false;
211*c8dee2aaSAndroid Build Coastguard Worker }
212*c8dee2aaSAndroid Build Coastguard Worker break;
213*c8dee2aaSAndroid Build Coastguard Worker }
214*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kMeshVertex: {
215*c8dee2aaSAndroid Build Coastguard Worker // Varyings main(const Attributes)
216*c8dee2aaSAndroid Build Coastguard Worker if (!typeIsValidForVaryings(returnType)) {
217*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "'main' must return 'Varyings'.");
218*c8dee2aaSAndroid Build Coastguard Worker return false;
219*c8dee2aaSAndroid Build Coastguard Worker }
220*c8dee2aaSAndroid Build Coastguard Worker if (!(parameters.size() == 1 && paramIsConstInAttributes(0))) {
221*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "'main' parameter must be 'const Attributes'.");
222*c8dee2aaSAndroid Build Coastguard Worker return false;
223*c8dee2aaSAndroid Build Coastguard Worker }
224*c8dee2aaSAndroid Build Coastguard Worker break;
225*c8dee2aaSAndroid Build Coastguard Worker }
226*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kMeshFragment: {
227*c8dee2aaSAndroid Build Coastguard Worker // float2 main(const Varyings) -or- float2 main(const Varyings, out half4|float4)
228*c8dee2aaSAndroid Build Coastguard Worker if (!type_is_valid_for_coords(returnType)) {
229*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "'main' must return: 'vec2' or 'float2'");
230*c8dee2aaSAndroid Build Coastguard Worker return false;
231*c8dee2aaSAndroid Build Coastguard Worker }
232*c8dee2aaSAndroid Build Coastguard Worker if (!((parameters.size() == 1 && paramIsConstInVaryings(0)) ||
233*c8dee2aaSAndroid Build Coastguard Worker (parameters.size() == 2 && paramIsConstInVaryings(0) && paramIsOutColor(1)))) {
234*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos,
235*c8dee2aaSAndroid Build Coastguard Worker "'main' parameters must be (const Varyings, (out (half4|float4))?)");
236*c8dee2aaSAndroid Build Coastguard Worker return false;
237*c8dee2aaSAndroid Build Coastguard Worker }
238*c8dee2aaSAndroid Build Coastguard Worker break;
239*c8dee2aaSAndroid Build Coastguard Worker }
240*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kFragment:
241*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kGraphiteFragment:
242*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kGraphiteFragmentES2: {
243*c8dee2aaSAndroid Build Coastguard Worker bool validParams = (parameters.size() == 0) ||
244*c8dee2aaSAndroid Build Coastguard Worker (parameters.size() == 1 && paramIsCoords(0));
245*c8dee2aaSAndroid Build Coastguard Worker if (!validParams) {
246*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "shader 'main' must be main() or main(float2)");
247*c8dee2aaSAndroid Build Coastguard Worker return false;
248*c8dee2aaSAndroid Build Coastguard Worker }
249*c8dee2aaSAndroid Build Coastguard Worker break;
250*c8dee2aaSAndroid Build Coastguard Worker }
251*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kVertex:
252*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kGraphiteVertex:
253*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kGraphiteVertexES2:
254*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kCompute:
255*c8dee2aaSAndroid Build Coastguard Worker if (!returnType.matches(*context.fTypes.fVoid)) {
256*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "'main' must return 'void'");
257*c8dee2aaSAndroid Build Coastguard Worker return false;
258*c8dee2aaSAndroid Build Coastguard Worker }
259*c8dee2aaSAndroid Build Coastguard Worker if (parameters.size()) {
260*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "shader 'main' must have zero parameters");
261*c8dee2aaSAndroid Build Coastguard Worker return false;
262*c8dee2aaSAndroid Build Coastguard Worker }
263*c8dee2aaSAndroid Build Coastguard Worker break;
264*c8dee2aaSAndroid Build Coastguard Worker }
265*c8dee2aaSAndroid Build Coastguard Worker return true;
266*c8dee2aaSAndroid Build Coastguard Worker }
267*c8dee2aaSAndroid Build Coastguard Worker
268*c8dee2aaSAndroid Build Coastguard Worker /**
269*c8dee2aaSAndroid Build Coastguard Worker * Given a concrete type (`float3`) and a generic type (`$genType`), returns the index of the
270*c8dee2aaSAndroid Build Coastguard Worker * concrete type within the generic type's typelist. Returns -1 if there is no match.
271*c8dee2aaSAndroid Build Coastguard Worker */
find_generic_index(const Type & concreteType,const Type & genericType,bool allowNarrowing)272*c8dee2aaSAndroid Build Coastguard Worker static int find_generic_index(const Type& concreteType,
273*c8dee2aaSAndroid Build Coastguard Worker const Type& genericType,
274*c8dee2aaSAndroid Build Coastguard Worker bool allowNarrowing) {
275*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const Type* const> genericTypes = genericType.coercibleTypes();
276*c8dee2aaSAndroid Build Coastguard Worker for (size_t index = 0; index < genericTypes.size(); ++index) {
277*c8dee2aaSAndroid Build Coastguard Worker if (concreteType.canCoerceTo(*genericTypes[index], allowNarrowing)) {
278*c8dee2aaSAndroid Build Coastguard Worker return index;
279*c8dee2aaSAndroid Build Coastguard Worker }
280*c8dee2aaSAndroid Build Coastguard Worker }
281*c8dee2aaSAndroid Build Coastguard Worker return -1;
282*c8dee2aaSAndroid Build Coastguard Worker }
283*c8dee2aaSAndroid Build Coastguard Worker
284*c8dee2aaSAndroid Build Coastguard Worker /** Returns true if the types match, or if `concreteType` can be found in `maybeGenericType`. */
type_generically_matches(const Type & concreteType,const Type & maybeGenericType)285*c8dee2aaSAndroid Build Coastguard Worker static bool type_generically_matches(const Type& concreteType, const Type& maybeGenericType) {
286*c8dee2aaSAndroid Build Coastguard Worker return maybeGenericType.isGeneric()
287*c8dee2aaSAndroid Build Coastguard Worker ? find_generic_index(concreteType, maybeGenericType, /*allowNarrowing=*/false) != -1
288*c8dee2aaSAndroid Build Coastguard Worker : concreteType.matches(maybeGenericType);
289*c8dee2aaSAndroid Build Coastguard Worker }
290*c8dee2aaSAndroid Build Coastguard Worker
291*c8dee2aaSAndroid Build Coastguard Worker /**
292*c8dee2aaSAndroid Build Coastguard Worker * Checks a parameter list (params) against the parameters of a function that was declared earlier
293*c8dee2aaSAndroid Build Coastguard Worker * (otherParams). Returns true if they match, even if the parameters in `otherParams` contain
294*c8dee2aaSAndroid Build Coastguard Worker * generic types.
295*c8dee2aaSAndroid Build Coastguard Worker */
parameters_match(SkSpan<const std::unique_ptr<Variable>> params,SkSpan<Variable * const> otherParams)296*c8dee2aaSAndroid Build Coastguard Worker static bool parameters_match(SkSpan<const std::unique_ptr<Variable>> params,
297*c8dee2aaSAndroid Build Coastguard Worker SkSpan<Variable* const> otherParams) {
298*c8dee2aaSAndroid Build Coastguard Worker // If the param lists are different lengths, they're definitely not a match.
299*c8dee2aaSAndroid Build Coastguard Worker if (params.size() != otherParams.size()) {
300*c8dee2aaSAndroid Build Coastguard Worker return false;
301*c8dee2aaSAndroid Build Coastguard Worker }
302*c8dee2aaSAndroid Build Coastguard Worker
303*c8dee2aaSAndroid Build Coastguard Worker // Figure out a consistent generic index (or bail if we find a contradiction).
304*c8dee2aaSAndroid Build Coastguard Worker int genericIndex = -1;
305*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < params.size(); ++i) {
306*c8dee2aaSAndroid Build Coastguard Worker const Type* paramType = ¶ms[i]->type();
307*c8dee2aaSAndroid Build Coastguard Worker const Type* otherParamType = &otherParams[i]->type();
308*c8dee2aaSAndroid Build Coastguard Worker
309*c8dee2aaSAndroid Build Coastguard Worker if (otherParamType->isGeneric()) {
310*c8dee2aaSAndroid Build Coastguard Worker int genericIndexForThisParam = find_generic_index(*paramType, *otherParamType,
311*c8dee2aaSAndroid Build Coastguard Worker /*allowNarrowing=*/false);
312*c8dee2aaSAndroid Build Coastguard Worker if (genericIndexForThisParam == -1) {
313*c8dee2aaSAndroid Build Coastguard Worker // The type wasn't a match for this generic at all; these params can't be a match.
314*c8dee2aaSAndroid Build Coastguard Worker return false;
315*c8dee2aaSAndroid Build Coastguard Worker }
316*c8dee2aaSAndroid Build Coastguard Worker if (genericIndex != -1 && genericIndex != genericIndexForThisParam) {
317*c8dee2aaSAndroid Build Coastguard Worker // The generic index mismatches from what we determined on a previous parameter.
318*c8dee2aaSAndroid Build Coastguard Worker return false;
319*c8dee2aaSAndroid Build Coastguard Worker }
320*c8dee2aaSAndroid Build Coastguard Worker genericIndex = genericIndexForThisParam;
321*c8dee2aaSAndroid Build Coastguard Worker }
322*c8dee2aaSAndroid Build Coastguard Worker }
323*c8dee2aaSAndroid Build Coastguard Worker
324*c8dee2aaSAndroid Build Coastguard Worker // Now that we've determined a generic index (if we needed one), do a parameter check.
325*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < params.size(); i++) {
326*c8dee2aaSAndroid Build Coastguard Worker const Type* paramType = ¶ms[i]->type();
327*c8dee2aaSAndroid Build Coastguard Worker const Type* otherParamType = &otherParams[i]->type();
328*c8dee2aaSAndroid Build Coastguard Worker
329*c8dee2aaSAndroid Build Coastguard Worker // Make generic types concrete.
330*c8dee2aaSAndroid Build Coastguard Worker if (otherParamType->isGeneric()) {
331*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(genericIndex != -1);
332*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(genericIndex < (int)otherParamType->coercibleTypes().size());
333*c8dee2aaSAndroid Build Coastguard Worker otherParamType = otherParamType->coercibleTypes()[genericIndex];
334*c8dee2aaSAndroid Build Coastguard Worker }
335*c8dee2aaSAndroid Build Coastguard Worker // Detect type mismatches.
336*c8dee2aaSAndroid Build Coastguard Worker if (!paramType->matches(*otherParamType)) {
337*c8dee2aaSAndroid Build Coastguard Worker return false;
338*c8dee2aaSAndroid Build Coastguard Worker }
339*c8dee2aaSAndroid Build Coastguard Worker }
340*c8dee2aaSAndroid Build Coastguard Worker return true;
341*c8dee2aaSAndroid Build Coastguard Worker }
342*c8dee2aaSAndroid Build Coastguard Worker
343*c8dee2aaSAndroid Build Coastguard Worker /**
344*c8dee2aaSAndroid Build Coastguard Worker * Checks for a previously existing declaration of this function, reporting errors if there is an
345*c8dee2aaSAndroid Build Coastguard Worker * incompatible symbol. Returns true and sets outExistingDecl to point to the existing declaration
346*c8dee2aaSAndroid Build Coastguard Worker * (or null if none) on success, returns false on error.
347*c8dee2aaSAndroid Build Coastguard Worker */
find_existing_declaration(const Context & context,Position pos,ModifierFlags modifierFlags,IntrinsicKind intrinsicKind,std::string_view name,TArray<std::unique_ptr<Variable>> & parameters,Position returnTypePos,const Type * returnType,FunctionDeclaration ** outExistingDecl)348*c8dee2aaSAndroid Build Coastguard Worker static bool find_existing_declaration(const Context& context,
349*c8dee2aaSAndroid Build Coastguard Worker Position pos,
350*c8dee2aaSAndroid Build Coastguard Worker ModifierFlags modifierFlags,
351*c8dee2aaSAndroid Build Coastguard Worker IntrinsicKind intrinsicKind,
352*c8dee2aaSAndroid Build Coastguard Worker std::string_view name,
353*c8dee2aaSAndroid Build Coastguard Worker TArray<std::unique_ptr<Variable>>& parameters,
354*c8dee2aaSAndroid Build Coastguard Worker Position returnTypePos,
355*c8dee2aaSAndroid Build Coastguard Worker const Type* returnType,
356*c8dee2aaSAndroid Build Coastguard Worker FunctionDeclaration** outExistingDecl) {
357*c8dee2aaSAndroid Build Coastguard Worker auto invalidDeclDescription = [&]() -> std::string {
358*c8dee2aaSAndroid Build Coastguard Worker TArray<Variable*> paramPtrs;
359*c8dee2aaSAndroid Build Coastguard Worker paramPtrs.reserve_exact(parameters.size());
360*c8dee2aaSAndroid Build Coastguard Worker for (std::unique_ptr<Variable>& param : parameters) {
361*c8dee2aaSAndroid Build Coastguard Worker paramPtrs.push_back(param.get());
362*c8dee2aaSAndroid Build Coastguard Worker }
363*c8dee2aaSAndroid Build Coastguard Worker return FunctionDeclaration(context,
364*c8dee2aaSAndroid Build Coastguard Worker pos,
365*c8dee2aaSAndroid Build Coastguard Worker modifierFlags,
366*c8dee2aaSAndroid Build Coastguard Worker name,
367*c8dee2aaSAndroid Build Coastguard Worker std::move(paramPtrs),
368*c8dee2aaSAndroid Build Coastguard Worker returnType,
369*c8dee2aaSAndroid Build Coastguard Worker intrinsicKind)
370*c8dee2aaSAndroid Build Coastguard Worker .description();
371*c8dee2aaSAndroid Build Coastguard Worker };
372*c8dee2aaSAndroid Build Coastguard Worker
373*c8dee2aaSAndroid Build Coastguard Worker ErrorReporter& errors = *context.fErrors;
374*c8dee2aaSAndroid Build Coastguard Worker Symbol* entry = context.fSymbolTable->findMutable(name);
375*c8dee2aaSAndroid Build Coastguard Worker *outExistingDecl = nullptr;
376*c8dee2aaSAndroid Build Coastguard Worker if (entry) {
377*c8dee2aaSAndroid Build Coastguard Worker if (!entry->is<FunctionDeclaration>()) {
378*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "symbol '" + std::string(name) + "' was already defined");
379*c8dee2aaSAndroid Build Coastguard Worker return false;
380*c8dee2aaSAndroid Build Coastguard Worker }
381*c8dee2aaSAndroid Build Coastguard Worker for (FunctionDeclaration* other = &entry->as<FunctionDeclaration>(); other;
382*c8dee2aaSAndroid Build Coastguard Worker other = other->mutableNextOverload()) {
383*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(name == other->name());
384*c8dee2aaSAndroid Build Coastguard Worker if (!parameters_match(parameters, other->parameters())) {
385*c8dee2aaSAndroid Build Coastguard Worker continue;
386*c8dee2aaSAndroid Build Coastguard Worker }
387*c8dee2aaSAndroid Build Coastguard Worker if (!type_generically_matches(*returnType, other->returnType())) {
388*c8dee2aaSAndroid Build Coastguard Worker errors.error(returnTypePos, "functions '" + invalidDeclDescription() + "' and '" +
389*c8dee2aaSAndroid Build Coastguard Worker other->description() + "' differ only in return type");
390*c8dee2aaSAndroid Build Coastguard Worker return false;
391*c8dee2aaSAndroid Build Coastguard Worker }
392*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < parameters.size(); i++) {
393*c8dee2aaSAndroid Build Coastguard Worker if (parameters[i]->modifierFlags() != other->parameters()[i]->modifierFlags() ||
394*c8dee2aaSAndroid Build Coastguard Worker parameters[i]->layout() != other->parameters()[i]->layout()) {
395*c8dee2aaSAndroid Build Coastguard Worker errors.error(parameters[i]->fPosition,
396*c8dee2aaSAndroid Build Coastguard Worker "modifiers on parameter " + std::to_string(i + 1) +
397*c8dee2aaSAndroid Build Coastguard Worker " differ between declaration and definition");
398*c8dee2aaSAndroid Build Coastguard Worker return false;
399*c8dee2aaSAndroid Build Coastguard Worker }
400*c8dee2aaSAndroid Build Coastguard Worker }
401*c8dee2aaSAndroid Build Coastguard Worker if (other->isIntrinsic()) {
402*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "duplicate definition of intrinsic function '" +
403*c8dee2aaSAndroid Build Coastguard Worker std::string(name) + "'");
404*c8dee2aaSAndroid Build Coastguard Worker return false;
405*c8dee2aaSAndroid Build Coastguard Worker }
406*c8dee2aaSAndroid Build Coastguard Worker if (modifierFlags != other->modifierFlags()) {
407*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "functions '" + invalidDeclDescription() + "' and '" +
408*c8dee2aaSAndroid Build Coastguard Worker other->description() + "' differ only in modifiers");
409*c8dee2aaSAndroid Build Coastguard Worker return false;
410*c8dee2aaSAndroid Build Coastguard Worker }
411*c8dee2aaSAndroid Build Coastguard Worker *outExistingDecl = other;
412*c8dee2aaSAndroid Build Coastguard Worker break;
413*c8dee2aaSAndroid Build Coastguard Worker }
414*c8dee2aaSAndroid Build Coastguard Worker if (!*outExistingDecl && entry->as<FunctionDeclaration>().isMain()) {
415*c8dee2aaSAndroid Build Coastguard Worker errors.error(pos, "duplicate definition of 'main'");
416*c8dee2aaSAndroid Build Coastguard Worker return false;
417*c8dee2aaSAndroid Build Coastguard Worker }
418*c8dee2aaSAndroid Build Coastguard Worker }
419*c8dee2aaSAndroid Build Coastguard Worker return true;
420*c8dee2aaSAndroid Build Coastguard Worker }
421*c8dee2aaSAndroid Build Coastguard Worker
FunctionDeclaration(const Context & context,Position pos,ModifierFlags modifierFlags,std::string_view name,TArray<Variable * > parameters,const Type * returnType,IntrinsicKind intrinsicKind)422*c8dee2aaSAndroid Build Coastguard Worker FunctionDeclaration::FunctionDeclaration(const Context& context,
423*c8dee2aaSAndroid Build Coastguard Worker Position pos,
424*c8dee2aaSAndroid Build Coastguard Worker ModifierFlags modifierFlags,
425*c8dee2aaSAndroid Build Coastguard Worker std::string_view name,
426*c8dee2aaSAndroid Build Coastguard Worker TArray<Variable*> parameters,
427*c8dee2aaSAndroid Build Coastguard Worker const Type* returnType,
428*c8dee2aaSAndroid Build Coastguard Worker IntrinsicKind intrinsicKind)
429*c8dee2aaSAndroid Build Coastguard Worker : INHERITED(pos, kIRNodeKind, name, /*type=*/nullptr)
430*c8dee2aaSAndroid Build Coastguard Worker , fDefinition(nullptr)
431*c8dee2aaSAndroid Build Coastguard Worker , fParameters(std::move(parameters))
432*c8dee2aaSAndroid Build Coastguard Worker , fReturnType(returnType)
433*c8dee2aaSAndroid Build Coastguard Worker , fModifierFlags(modifierFlags)
434*c8dee2aaSAndroid Build Coastguard Worker , fIntrinsicKind(intrinsicKind)
435*c8dee2aaSAndroid Build Coastguard Worker , fModuleType(context.fConfig->fModuleType)
436*c8dee2aaSAndroid Build Coastguard Worker , fIsMain(name == "main") {
437*c8dee2aaSAndroid Build Coastguard Worker int builtinColorIndex = 0;
438*c8dee2aaSAndroid Build Coastguard Worker for (const Variable* param : fParameters) {
439*c8dee2aaSAndroid Build Coastguard Worker // None of the parameters are allowed to be be null.
440*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(param);
441*c8dee2aaSAndroid Build Coastguard Worker
442*c8dee2aaSAndroid Build Coastguard Worker // Keep track of arguments to main for runtime effects.
443*c8dee2aaSAndroid Build Coastguard Worker if (fIsMain) {
444*c8dee2aaSAndroid Build Coastguard Worker if (ProgramConfig::IsRuntimeShader(context.fConfig->fKind) ||
445*c8dee2aaSAndroid Build Coastguard Worker ProgramConfig::IsFragment(context.fConfig->fKind)) {
446*c8dee2aaSAndroid Build Coastguard Worker // If this is a runtime shader, a float2 param is supposed to be the coords.
447*c8dee2aaSAndroid Build Coastguard Worker // For testing purposes, we have .sksl inputs that are treated as both runtime
448*c8dee2aaSAndroid Build Coastguard Worker // effects and fragment shaders. To make that work, fragment shaders are allowed to
449*c8dee2aaSAndroid Build Coastguard Worker // have a coords parameter as well.
450*c8dee2aaSAndroid Build Coastguard Worker if (type_is_valid_for_coords(param->type())) {
451*c8dee2aaSAndroid Build Coastguard Worker fHasMainCoordsParameter = true;
452*c8dee2aaSAndroid Build Coastguard Worker }
453*c8dee2aaSAndroid Build Coastguard Worker } else if (ProgramConfig::IsRuntimeColorFilter(context.fConfig->fKind) ||
454*c8dee2aaSAndroid Build Coastguard Worker ProgramConfig::IsRuntimeBlender(context.fConfig->fKind)) {
455*c8dee2aaSAndroid Build Coastguard Worker // If this is a runtime color filter or blender, the params are an input color,
456*c8dee2aaSAndroid Build Coastguard Worker // followed by a destination color for blenders.
457*c8dee2aaSAndroid Build Coastguard Worker if (type_is_valid_for_color(param->type())) {
458*c8dee2aaSAndroid Build Coastguard Worker switch (builtinColorIndex++) {
459*c8dee2aaSAndroid Build Coastguard Worker case 0: fHasMainInputColorParameter = true; break;
460*c8dee2aaSAndroid Build Coastguard Worker case 1: fHasMainDestColorParameter = true; break;
461*c8dee2aaSAndroid Build Coastguard Worker default: /* unknown color parameter */ break;
462*c8dee2aaSAndroid Build Coastguard Worker }
463*c8dee2aaSAndroid Build Coastguard Worker }
464*c8dee2aaSAndroid Build Coastguard Worker }
465*c8dee2aaSAndroid Build Coastguard Worker }
466*c8dee2aaSAndroid Build Coastguard Worker }
467*c8dee2aaSAndroid Build Coastguard Worker }
468*c8dee2aaSAndroid Build Coastguard Worker
Convert(const Context & context,Position pos,const Modifiers & modifiers,std::string_view name,TArray<std::unique_ptr<Variable>> parameters,Position returnTypePos,const Type * returnType)469*c8dee2aaSAndroid Build Coastguard Worker FunctionDeclaration* FunctionDeclaration::Convert(const Context& context,
470*c8dee2aaSAndroid Build Coastguard Worker Position pos,
471*c8dee2aaSAndroid Build Coastguard Worker const Modifiers& modifiers,
472*c8dee2aaSAndroid Build Coastguard Worker std::string_view name,
473*c8dee2aaSAndroid Build Coastguard Worker TArray<std::unique_ptr<Variable>> parameters,
474*c8dee2aaSAndroid Build Coastguard Worker Position returnTypePos,
475*c8dee2aaSAndroid Build Coastguard Worker const Type* returnType) {
476*c8dee2aaSAndroid Build Coastguard Worker // No layout flag is permissible on a function.
477*c8dee2aaSAndroid Build Coastguard Worker modifiers.fLayout.checkPermittedLayout(context, pos,
478*c8dee2aaSAndroid Build Coastguard Worker /*permittedLayoutFlags=*/LayoutFlag::kNone);
479*c8dee2aaSAndroid Build Coastguard Worker
480*c8dee2aaSAndroid Build Coastguard Worker // If requested, apply the `noinline` modifier to every function. This allows us to test Runtime
481*c8dee2aaSAndroid Build Coastguard Worker // Effects without any inlining, even when the code is later added to a paint.
482*c8dee2aaSAndroid Build Coastguard Worker ModifierFlags modifierFlags = modifiers.fFlags;
483*c8dee2aaSAndroid Build Coastguard Worker if (context.fConfig->fSettings.fForceNoInline) {
484*c8dee2aaSAndroid Build Coastguard Worker modifierFlags &= ~ModifierFlag::kInline;
485*c8dee2aaSAndroid Build Coastguard Worker modifierFlags |= ModifierFlag::kNoInline;
486*c8dee2aaSAndroid Build Coastguard Worker }
487*c8dee2aaSAndroid Build Coastguard Worker
488*c8dee2aaSAndroid Build Coastguard Worker bool isMain = (name == "main");
489*c8dee2aaSAndroid Build Coastguard Worker IntrinsicKind intrinsicKind = context.fConfig->isBuiltinCode() ? FindIntrinsicKind(name)
490*c8dee2aaSAndroid Build Coastguard Worker : kNotIntrinsic;
491*c8dee2aaSAndroid Build Coastguard Worker FunctionDeclaration* decl = nullptr;
492*c8dee2aaSAndroid Build Coastguard Worker if (!check_modifiers(context, modifiers.fPosition, modifierFlags) ||
493*c8dee2aaSAndroid Build Coastguard Worker !check_return_type(context, returnTypePos, *returnType) ||
494*c8dee2aaSAndroid Build Coastguard Worker !check_parameters(context, parameters, modifierFlags, intrinsicKind) ||
495*c8dee2aaSAndroid Build Coastguard Worker (isMain && !check_main_signature(context, pos, *returnType, parameters)) ||
496*c8dee2aaSAndroid Build Coastguard Worker !find_existing_declaration(context, pos, modifierFlags, intrinsicKind, name, parameters,
497*c8dee2aaSAndroid Build Coastguard Worker returnTypePos, returnType, &decl)) {
498*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
499*c8dee2aaSAndroid Build Coastguard Worker }
500*c8dee2aaSAndroid Build Coastguard Worker TArray<Variable*> finalParameters;
501*c8dee2aaSAndroid Build Coastguard Worker finalParameters.reserve_exact(parameters.size());
502*c8dee2aaSAndroid Build Coastguard Worker for (std::unique_ptr<Variable>& param : parameters) {
503*c8dee2aaSAndroid Build Coastguard Worker finalParameters.push_back(context.fSymbolTable->takeOwnershipOfSymbol(std::move(param)));
504*c8dee2aaSAndroid Build Coastguard Worker }
505*c8dee2aaSAndroid Build Coastguard Worker if (decl) {
506*c8dee2aaSAndroid Build Coastguard Worker return decl;
507*c8dee2aaSAndroid Build Coastguard Worker }
508*c8dee2aaSAndroid Build Coastguard Worker return context.fSymbolTable->add(
509*c8dee2aaSAndroid Build Coastguard Worker context,
510*c8dee2aaSAndroid Build Coastguard Worker std::make_unique<FunctionDeclaration>(context,
511*c8dee2aaSAndroid Build Coastguard Worker pos,
512*c8dee2aaSAndroid Build Coastguard Worker modifierFlags,
513*c8dee2aaSAndroid Build Coastguard Worker name,
514*c8dee2aaSAndroid Build Coastguard Worker std::move(finalParameters),
515*c8dee2aaSAndroid Build Coastguard Worker returnType,
516*c8dee2aaSAndroid Build Coastguard Worker intrinsicKind));
517*c8dee2aaSAndroid Build Coastguard Worker }
518*c8dee2aaSAndroid Build Coastguard Worker
mangledName() const519*c8dee2aaSAndroid Build Coastguard Worker std::string FunctionDeclaration::mangledName() const {
520*c8dee2aaSAndroid Build Coastguard Worker if ((this->isBuiltin() && !this->definition()) || this->isMain()) {
521*c8dee2aaSAndroid Build Coastguard Worker // Builtins without a definition (like `sin` or `sqrt`) must use their real names.
522*c8dee2aaSAndroid Build Coastguard Worker return std::string(this->name());
523*c8dee2aaSAndroid Build Coastguard Worker }
524*c8dee2aaSAndroid Build Coastguard Worker // Built-in functions can have a $ prefix, which will fail to compile in GLSL. Remove the
525*c8dee2aaSAndroid Build Coastguard Worker // $ and add a unique mangling specifier, so user code can't conflict with the name.
526*c8dee2aaSAndroid Build Coastguard Worker std::string_view name = this->name();
527*c8dee2aaSAndroid Build Coastguard Worker const char* builtinMarker = "";
528*c8dee2aaSAndroid Build Coastguard Worker if (skstd::starts_with(name, '$')) {
529*c8dee2aaSAndroid Build Coastguard Worker name.remove_prefix(1);
530*c8dee2aaSAndroid Build Coastguard Worker builtinMarker = "Q"; // a unique, otherwise-unused mangle character
531*c8dee2aaSAndroid Build Coastguard Worker }
532*c8dee2aaSAndroid Build Coastguard Worker // Rename function to `funcname_returntypeparamtypes`.
533*c8dee2aaSAndroid Build Coastguard Worker std::string result = std::string(name) + "_" + builtinMarker +
534*c8dee2aaSAndroid Build Coastguard Worker this->returnType().abbreviatedName();
535*c8dee2aaSAndroid Build Coastguard Worker for (const Variable* p : this->parameters()) {
536*c8dee2aaSAndroid Build Coastguard Worker result += p->type().abbreviatedName();
537*c8dee2aaSAndroid Build Coastguard Worker }
538*c8dee2aaSAndroid Build Coastguard Worker return result;
539*c8dee2aaSAndroid Build Coastguard Worker }
540*c8dee2aaSAndroid Build Coastguard Worker
description() const541*c8dee2aaSAndroid Build Coastguard Worker std::string FunctionDeclaration::description() const {
542*c8dee2aaSAndroid Build Coastguard Worker std::string result = (fModifierFlags ? fModifierFlags.description() + " " : std::string()) +
543*c8dee2aaSAndroid Build Coastguard Worker this->returnType().displayName() + " " + std::string(this->name()) + "(";
544*c8dee2aaSAndroid Build Coastguard Worker auto separator = SkSL::String::Separator();
545*c8dee2aaSAndroid Build Coastguard Worker for (const Variable* p : this->parameters()) {
546*c8dee2aaSAndroid Build Coastguard Worker result += separator();
547*c8dee2aaSAndroid Build Coastguard Worker result += p->description();
548*c8dee2aaSAndroid Build Coastguard Worker }
549*c8dee2aaSAndroid Build Coastguard Worker result += ")";
550*c8dee2aaSAndroid Build Coastguard Worker return result;
551*c8dee2aaSAndroid Build Coastguard Worker }
552*c8dee2aaSAndroid Build Coastguard Worker
matches(const FunctionDeclaration & f) const553*c8dee2aaSAndroid Build Coastguard Worker bool FunctionDeclaration::matches(const FunctionDeclaration& f) const {
554*c8dee2aaSAndroid Build Coastguard Worker if (this->name() != f.name()) {
555*c8dee2aaSAndroid Build Coastguard Worker return false;
556*c8dee2aaSAndroid Build Coastguard Worker }
557*c8dee2aaSAndroid Build Coastguard Worker SkSpan<Variable* const> parameters = this->parameters();
558*c8dee2aaSAndroid Build Coastguard Worker SkSpan<Variable* const> otherParameters = f.parameters();
559*c8dee2aaSAndroid Build Coastguard Worker if (parameters.size() != otherParameters.size()) {
560*c8dee2aaSAndroid Build Coastguard Worker return false;
561*c8dee2aaSAndroid Build Coastguard Worker }
562*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < parameters.size(); i++) {
563*c8dee2aaSAndroid Build Coastguard Worker if (!parameters[i]->type().matches(otherParameters[i]->type())) {
564*c8dee2aaSAndroid Build Coastguard Worker return false;
565*c8dee2aaSAndroid Build Coastguard Worker }
566*c8dee2aaSAndroid Build Coastguard Worker }
567*c8dee2aaSAndroid Build Coastguard Worker return true;
568*c8dee2aaSAndroid Build Coastguard Worker }
569*c8dee2aaSAndroid Build Coastguard Worker
determineFinalTypes(const ExpressionArray & arguments,ParamTypes * outParameterTypes,const Type ** outReturnType) const570*c8dee2aaSAndroid Build Coastguard Worker bool FunctionDeclaration::determineFinalTypes(const ExpressionArray& arguments,
571*c8dee2aaSAndroid Build Coastguard Worker ParamTypes* outParameterTypes,
572*c8dee2aaSAndroid Build Coastguard Worker const Type** outReturnType) const {
573*c8dee2aaSAndroid Build Coastguard Worker SkSpan<Variable* const> parameters = this->parameters();
574*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(SkToSizeT(arguments.size()) == parameters.size());
575*c8dee2aaSAndroid Build Coastguard Worker
576*c8dee2aaSAndroid Build Coastguard Worker outParameterTypes->reserve_exact(arguments.size());
577*c8dee2aaSAndroid Build Coastguard Worker int genericIndex = -1;
578*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < arguments.size(); i++) {
579*c8dee2aaSAndroid Build Coastguard Worker // Non-generic parameters are final as-is.
580*c8dee2aaSAndroid Build Coastguard Worker const Type& parameterType = parameters[i]->type();
581*c8dee2aaSAndroid Build Coastguard Worker if (!parameterType.isGeneric()) {
582*c8dee2aaSAndroid Build Coastguard Worker outParameterTypes->push_back(¶meterType);
583*c8dee2aaSAndroid Build Coastguard Worker continue;
584*c8dee2aaSAndroid Build Coastguard Worker }
585*c8dee2aaSAndroid Build Coastguard Worker // We use the first generic parameter we find to lock in the generic index;
586*c8dee2aaSAndroid Build Coastguard Worker // e.g. if we find `float3` here, all `$genType`s will be assumed to be `float3`.
587*c8dee2aaSAndroid Build Coastguard Worker if (genericIndex == -1) {
588*c8dee2aaSAndroid Build Coastguard Worker genericIndex = find_generic_index(arguments[i]->type(), parameterType,
589*c8dee2aaSAndroid Build Coastguard Worker /*allowNarrowing=*/true);
590*c8dee2aaSAndroid Build Coastguard Worker if (genericIndex == -1) {
591*c8dee2aaSAndroid Build Coastguard Worker // The passed-in type wasn't a match for ANY of the generic possibilities.
592*c8dee2aaSAndroid Build Coastguard Worker // This function isn't a match at all.
593*c8dee2aaSAndroid Build Coastguard Worker return false;
594*c8dee2aaSAndroid Build Coastguard Worker }
595*c8dee2aaSAndroid Build Coastguard Worker }
596*c8dee2aaSAndroid Build Coastguard Worker outParameterTypes->push_back(parameterType.coercibleTypes()[genericIndex]);
597*c8dee2aaSAndroid Build Coastguard Worker }
598*c8dee2aaSAndroid Build Coastguard Worker // Apply the generic index to our return type.
599*c8dee2aaSAndroid Build Coastguard Worker const Type& returnType = this->returnType();
600*c8dee2aaSAndroid Build Coastguard Worker if (returnType.isGeneric()) {
601*c8dee2aaSAndroid Build Coastguard Worker if (genericIndex == -1) {
602*c8dee2aaSAndroid Build Coastguard Worker // We don't support functions with a generic return type and no other generics.
603*c8dee2aaSAndroid Build Coastguard Worker return false;
604*c8dee2aaSAndroid Build Coastguard Worker }
605*c8dee2aaSAndroid Build Coastguard Worker *outReturnType = returnType.coercibleTypes()[genericIndex];
606*c8dee2aaSAndroid Build Coastguard Worker } else {
607*c8dee2aaSAndroid Build Coastguard Worker *outReturnType = &returnType;
608*c8dee2aaSAndroid Build Coastguard Worker }
609*c8dee2aaSAndroid Build Coastguard Worker return true;
610*c8dee2aaSAndroid Build Coastguard Worker }
611*c8dee2aaSAndroid Build Coastguard Worker
612*c8dee2aaSAndroid Build Coastguard Worker } // namespace SkSL
613