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 | |
| 16 | static bool check_modifiers(const Context& context, int offset, const Modifiers& modifiers) { |
| 17 | IRGenerator::CheckModifiers( |
| 18 | context, |
| 19 | offset, |
| 20 | modifiers, |
| 21 | Modifiers::kHasSideEffects_Flag | Modifiers::kInline_Flag | Modifiers::kNoInline_Flag, |
| 22 | /*permittedLayoutFlags=*/0); |
| 23 | if ((modifiers.fFlags & Modifiers::kInline_Flag) && |
| 24 | (modifiers.fFlags & Modifiers::kNoInline_Flag)) { |
| 25 | context.fErrors.error(offset, "functions cannot be both 'inline' and 'noinline'"); |
| 26 | return false; |
| 27 | } |
| 28 | return true; |
| 29 | } |
| 30 | |
| 31 | static bool check_return_type(const Context& context, int offset, const Type& returnType, |
| 32 | bool isBuiltin) { |
| 33 | ErrorReporter& errors = context.fErrors; |
| 34 | if (returnType.isArray()) { |
| 35 | errors.error(offset, "functions may not return type '" + returnType.displayName() + "'"); |
| 36 | return false; |
| 37 | } |
| 38 | if (context.fConfig->strictES2Mode() && returnType.isOrContainsArray()) { |
| 39 | errors.error(offset, "functions may not return structs containing arrays"); |
| 40 | return false; |
| 41 | } |
| 42 | if (!isBuiltin && !returnType.isVoid() && returnType.componentType().isOpaque()) { |
| 43 | errors.error(offset, "functions may not return opaque type '" + returnType.displayName() + |
| 44 | "'"); |
| 45 | return false; |
| 46 | } |
| 47 | return true; |
| 48 | } |
| 49 | |
| 50 | static bool check_parameters(const Context& context, ModifiersPool& modifiersPool, |
| 51 | std::vector<std::unique_ptr<Variable>>& parameters, bool isMain, |
| 52 | bool isBuiltin) { |
| 53 | auto typeIsValidForColor = [&](const Type& type) { |
| 54 | return type == *context.fTypes.fHalf4 || type == *context.fTypes.fFloat4; |
| 55 | }; |
| 56 | |
| 57 | // Check modifiers on each function parameter. |
| 58 | for (auto& param : parameters) { |
| 59 | IRGenerator::CheckModifiers(context, param->fOffset, param->modifiers(), |
| 60 | Modifiers::kConst_Flag | Modifiers::kIn_Flag | |
| 61 | Modifiers::kOut_Flag, /*permittedLayoutFlags=*/0); |
| 62 | const Type& type = param->type(); |
| 63 | // Only the (builtin) declarations of 'sample' are allowed to have shader/colorFilter or FP |
| 64 | // parameters. You can pass other opaque types to functions safely; this restriction is |
| 65 | // specific to "child" objects. |
| 66 | if ((type.isEffectChild() || type.isFragmentProcessor()) && !isBuiltin) { |
| 67 | context.fErrors.error(param->fOffset, "parameters of type '" + type.displayName() + |
| 68 | "' not allowed"); |
| 69 | return false; |
| 70 | } |
| 71 | |
| 72 | Modifiers m = param->modifiers(); |
| 73 | ProgramKind kind = context.fConfig->fKind; |
| 74 | if (isMain && (kind == ProgramKind::kRuntimeColorFilter || |
| 75 | kind == ProgramKind::kRuntimeShader || |
| 76 | kind == ProgramKind::kFragmentProcessor)) { |
| 77 | // We verify that the signature is fully correct later. For now, if this is an .fp or |
| 78 | // runtime effect of any flavor, a float2 param is supposed to be the coords, and |
| 79 | // a half4/float parameter is supposed to be the input color: |
| 80 | if (type == *context.fTypes.fFloat2) { |
| 81 | m.fLayout.fBuiltin = SK_MAIN_COORDS_BUILTIN; |
| 82 | } else if(typeIsValidForColor(type)) { |
| 83 | m.fLayout.fBuiltin = SK_INPUT_COLOR_BUILTIN; |
| 84 | } |
| 85 | if (m.fLayout.fBuiltin) { |
| 86 | param->setModifiers(modifiersPool.add(m)); |
| 87 | } |
| 88 | } |
| 89 | if (isMain && (kind == ProgramKind::kFragment)) { |
| 90 | // For testing purposes, we have .sksl inputs that are treated as both runtime effects |
| 91 | // and fragment shaders. To make that work, fragment shaders are allowed to have a |
| 92 | // coords parameter. We turn it into sk_FragCoord. |
| 93 | if (type == *context.fTypes.fFloat2) { |
| 94 | m.fLayout.fBuiltin = SK_FRAGCOORD_BUILTIN; |
| 95 | param->setModifiers(modifiersPool.add(m)); |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | return true; |
| 100 | } |
| 101 | |
| 102 | static bool check_main_signature(const Context& context, int offset, const Type& returnType, |
| 103 | std::vector<std::unique_ptr<Variable>>& parameters, |
| 104 | bool isBuiltin) { |
| 105 | ErrorReporter& errors = context.fErrors; |
| 106 | ProgramKind kind = context.fConfig->fKind; |
| 107 | |
| 108 | auto typeIsValidForColor = [&](const Type& type) { |
| 109 | return type == *context.fTypes.fHalf4 || type == *context.fTypes.fFloat4; |
| 110 | }; |
| 111 | |
| 112 | auto paramIsCoords = [&](int idx) { |
| 113 | const Variable& p = *parameters[idx]; |
| 114 | return p.type() == *context.fTypes.fFloat2 && |
| 115 | p.modifiers().fFlags == 0 && |
| 116 | p.modifiers().fLayout.fBuiltin == (kind == ProgramKind::kFragment |
| 117 | ? SK_FRAGCOORD_BUILTIN |
| 118 | : SK_MAIN_COORDS_BUILTIN); |
| 119 | }; |
| 120 | |
| 121 | auto paramIsInputColor = [&](int idx) { |
| 122 | return typeIsValidForColor(parameters[idx]->type()) && |
| 123 | parameters[idx]->modifiers().fFlags == 0 && |
| 124 | parameters[idx]->modifiers().fLayout.fBuiltin == SK_INPUT_COLOR_BUILTIN; |
| 125 | }; |
| 126 | |
| 127 | switch (kind) { |
| 128 | case ProgramKind::kRuntimeColorFilter: { |
| 129 | // (half4|float4) main(half4|float4) |
| 130 | if (!typeIsValidForColor(returnType)) { |
| 131 | errors.error(offset, "'main' must return: 'vec4', 'float4', or 'half4'"); |
| 132 | return false; |
| 133 | } |
| 134 | bool validParams = (parameters.size() == 1 && paramIsInputColor(0)); |
| 135 | if (!validParams) { |
| 136 | errors.error(offset, "'main' parameter must be 'vec4', 'float4', or 'half4'"); |
| 137 | return false; |
| 138 | } |
| 139 | break; |
| 140 | } |
| 141 | case ProgramKind::kRuntimeShader: { |
| 142 | // (half4|float4) main(float2) -or- (half4|float4) main(float2, half4|float4) |
| 143 | if (!typeIsValidForColor(returnType)) { |
| 144 | errors.error(offset, "'main' must return: 'vec4', 'float4', or 'half4'"); |
| 145 | return false; |
| 146 | } |
| 147 | bool validParams = |
| 148 | (parameters.size() == 1 && paramIsCoords(0)) || |
| 149 | (parameters.size() == 2 && paramIsCoords(0) && paramIsInputColor(1)); |
| 150 | if (!validParams) { |
| 151 | errors.error(offset, "'main' parameters must be (float2, (vec4|float4|half4)?)"); |
| 152 | return false; |
| 153 | } |
| 154 | break; |
| 155 | } |
| 156 | case ProgramKind::kFragmentProcessor: { |
| 157 | if (returnType != *context.fTypes.fHalf4) { |
| 158 | errors.error(offset, ".fp 'main' must return 'half4'"); |
| 159 | return false; |
| 160 | } |
| 161 | bool validParams = (parameters.size() == 0) || |
| 162 | (parameters.size() == 1 && paramIsCoords(0)); |
| 163 | if (!validParams) { |
| 164 | errors.error(offset, ".fp 'main' must be declared main() or main(float2)"); |
| 165 | return false; |
| 166 | } |
| 167 | break; |
| 168 | } |
| 169 | case ProgramKind::kGeneric: |
| 170 | // No rules apply here |
| 171 | break; |
| 172 | case ProgramKind::kFragment: { |
| 173 | bool validParams = (parameters.size() == 0) || |
| 174 | (parameters.size() == 1 && paramIsCoords(0)); |
| 175 | if (!validParams) { |
| 176 | errors.error(offset, "shader 'main' must be main() or main(float2)"); |
| 177 | return false; |
| 178 | } |
| 179 | break; |
| 180 | } |
| 181 | case ProgramKind::kVertex: |
| 182 | case ProgramKind::kGeometry: |
| 183 | if (parameters.size()) { |
| 184 | errors.error(offset, "shader 'main' must have zero parameters"); |
| 185 | return false; |
| 186 | } |
| 187 | break; |
| 188 | } |
| 189 | return true; |
| 190 | } |
| 191 | |
| 192 | /** |
| 193 | * Checks for a previously existing declaration of this function, reporting errors if there is an |
| 194 | * incompatible symbol. Returns true and sets outExistingDecl to point to the existing declaration |
| 195 | * (or null if none) on success, returns false on error. |
| 196 | */ |
| 197 | static bool find_existing_declaration(const Context& context, SymbolTable& symbols, int offset, |
| 198 | StringFragment name, |
| 199 | std::vector<std::unique_ptr<Variable>>& parameters, |
| 200 | const Type* returnType, bool isBuiltin, |
| 201 | const FunctionDeclaration** outExistingDecl) { |
| 202 | ErrorReporter& errors = context.fErrors; |
| 203 | const Symbol* entry = symbols[name]; |
| 204 | *outExistingDecl = nullptr; |
| 205 | if (entry) { |
| 206 | std::vector<const FunctionDeclaration*> functions; |
| 207 | switch (entry->kind()) { |
| 208 | case Symbol::Kind::kUnresolvedFunction: |
| 209 | functions = entry->as<UnresolvedFunction>().functions(); |
| 210 | break; |
| 211 | case Symbol::Kind::kFunctionDeclaration: |
| 212 | functions.push_back(&entry->as<FunctionDeclaration>()); |
| 213 | break; |
| 214 | default: |
| 215 | errors.error(offset, "symbol '" + name + "' was already defined"); |
| 216 | return false; |
| 217 | } |
| 218 | for (const FunctionDeclaration* other : functions) { |
| 219 | SkASSERT(name == other->name()); |
| 220 | if (parameters.size() != other->parameters().size()) { |
| 221 | continue; |
| 222 | } |
| 223 | bool match = true; |
| 224 | for (size_t i = 0; i < parameters.size(); i++) { |
| 225 | if (parameters[i]->type() != other->parameters()[i]->type()) { |
| 226 | match = false; |
| 227 | break; |
| 228 | } |
| 229 | } |
| 230 | if (!match) { |
| 231 | continue; |
| 232 | } |
| 233 | if (*returnType != other->returnType()) { |
| 234 | std::vector<const Variable*> paramPtrs; |
| 235 | paramPtrs.reserve(parameters.size()); |
| 236 | for (std::unique_ptr<Variable>& param : parameters) { |
| 237 | paramPtrs.push_back(param.get()); |
| 238 | } |
| 239 | FunctionDeclaration invalidDecl(offset, |
| 240 | &other->modifiers(), |
| 241 | name, |
| 242 | std::move(paramPtrs), |
| 243 | returnType, |
| 244 | isBuiltin); |
| 245 | errors.error(offset, |
| 246 | "functions '" + invalidDecl.description() + "' and '" + |
| 247 | other->description() + "' differ only in return type"); |
| 248 | return false; |
| 249 | } |
| 250 | for (size_t i = 0; i < parameters.size(); i++) { |
| 251 | if (parameters[i]->modifiers() != other->parameters()[i]->modifiers()) { |
| 252 | errors.error(offset, |
| 253 | "modifiers on parameter " + to_string((uint64_t)i + 1) + |
| 254 | " differ between declaration and definition"); |
| 255 | return false; |
| 256 | } |
| 257 | } |
| 258 | if (other->definition() && !other->isBuiltin()) { |
| 259 | errors.error(offset, "duplicate definition of " + other->description()); |
| 260 | return false; |
| 261 | } |
| 262 | *outExistingDecl = other; |
| 263 | break; |
| 264 | } |
| 265 | } |
| 266 | return true; |
| 267 | } |
| 268 | |
| 269 | const FunctionDeclaration* FunctionDeclaration::Convert(const Context& context, |
| 270 | SymbolTable& symbols, ModifiersPool& modifiersPool, int offset, const Modifiers* modifiers, |
| 271 | StringFragment name, std::vector<std::unique_ptr<Variable>> parameters, |
| 272 | const Type* returnType, bool isBuiltin) { |
| 273 | bool isMain = (name == "main"); |
| 274 | |
| 275 | const FunctionDeclaration* decl = nullptr; |
| 276 | if (!check_modifiers(context, offset, *modifiers) || |
| 277 | !check_return_type(context, offset, *returnType, isBuiltin) || |
| 278 | !check_parameters(context, modifiersPool, parameters, isMain, isBuiltin) || |
| 279 | (isMain && !check_main_signature(context, offset, *returnType, parameters, isBuiltin)) || |
| 280 | !find_existing_declaration(context, symbols, offset, name, parameters, returnType, |
| 281 | isBuiltin, &decl)) { |
| 282 | return nullptr; |
| 283 | } |
| 284 | std::vector<const Variable*> finalParameters; |
| 285 | finalParameters.reserve(parameters.size()); |
| 286 | for (auto& param : parameters) { |
| 287 | finalParameters.push_back(symbols.takeOwnershipOfSymbol(std::move(param))); |
| 288 | } |
| 289 | if (decl) { |
| 290 | return decl; |
| 291 | } |
| 292 | auto result = std::make_unique<FunctionDeclaration>(offset, modifiers, name, |
| 293 | std::move(finalParameters), returnType, |
| 294 | isBuiltin); |
| 295 | return symbols.add(std::move(result)); |
| 296 | } |
| 297 | |
| 298 | } // namespace SkSL |