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