Ethan Nicholas | 1ff7609 | 2021-01-28 10:02:43 -0500 | [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 | |
Ethan Nicholas | daed259 | 2021-03-04 14:30:25 -0500 | [diff] [blame] | 8 | #include "include/sksl/DSLFunction.h" |
Ethan Nicholas | 1ff7609 | 2021-01-28 10:02:43 -0500 | [diff] [blame] | 9 | |
Ethan Nicholas | daed259 | 2021-03-04 14:30:25 -0500 | [diff] [blame] | 10 | #include "include/sksl/DSLVar.h" |
Ethan Nicholas | 1ff7609 | 2021-01-28 10:02:43 -0500 | [diff] [blame] | 11 | #include "src/sksl/SkSLAnalysis.h" |
| 12 | #include "src/sksl/SkSLCompiler.h" |
| 13 | #include "src/sksl/SkSLIRGenerator.h" |
Ethan Nicholas | daed259 | 2021-03-04 14:30:25 -0500 | [diff] [blame] | 14 | #include "src/sksl/dsl/priv/DSLWriter.h" |
Ethan Nicholas | 57709e1 | 2021-08-10 15:02:51 -0400 | [diff] [blame] | 15 | #include "src/sksl/ir/SkSLFunctionPrototype.h" |
Ethan Nicholas | 1ff7609 | 2021-01-28 10:02:43 -0500 | [diff] [blame] | 16 | #include "src/sksl/ir/SkSLReturnStatement.h" |
| 17 | |
| 18 | namespace SkSL { |
| 19 | |
| 20 | namespace dsl { |
| 21 | |
Ethan Nicholas | 292a09d | 2021-07-14 09:52:16 -0400 | [diff] [blame] | 22 | void DSLFunction::init(DSLModifiers modifiers, const DSLType& returnType, skstd::string_view name, |
Ethan Nicholas | 6f20b8d | 2021-08-31 07:40:24 -0400 | [diff] [blame] | 23 | SkTArray<DSLParameter*> params, PositionInfo pos) { |
Ethan Nicholas | 600481f | 2021-09-01 09:39:34 -0400 | [diff] [blame] | 24 | fPosition = pos; |
Ethan Nicholas | 292a09d | 2021-07-14 09:52:16 -0400 | [diff] [blame] | 25 | // Conservatively assume all user-defined functions have side effects. |
| 26 | if (!DSLWriter::IsModule()) { |
| 27 | modifiers.fModifiers.fFlags |= Modifiers::kHasSideEffects_Flag; |
| 28 | } |
| 29 | |
Ethan Nicholas | 459777a | 2021-07-16 11:16:27 -0400 | [diff] [blame] | 30 | if (DSLWriter::Settings().fForceNoInline) { |
Ethan Nicholas | 292a09d | 2021-07-14 09:52:16 -0400 | [diff] [blame] | 31 | // Apply the `noinline` modifier to every function. This allows us to test Runtime |
| 32 | // Effects without any inlining, even when the code is later added to a paint. |
| 33 | modifiers.fModifiers.fFlags &= ~Modifiers::kInline_Flag; |
| 34 | modifiers.fModifiers.fFlags |= Modifiers::kNoInline_Flag; |
| 35 | } |
| 36 | |
Ethan Nicholas | 371f6e1 | 2021-05-04 14:30:02 -0400 | [diff] [blame] | 37 | std::vector<std::unique_ptr<Variable>> paramVars; |
Ethan Nicholas | daed259 | 2021-03-04 14:30:25 -0500 | [diff] [blame] | 38 | paramVars.reserve(params.size()); |
Ethan Nicholas | a2d22b2 | 2021-07-15 10:35:54 -0400 | [diff] [blame] | 39 | for (DSLParameter* param : params) { |
Ethan Nicholas | 961d944 | 2021-03-16 16:37:29 -0400 | [diff] [blame] | 40 | if (param->fDeclared) { |
Ethan Nicholas | 4a5e22a | 2021-08-13 17:29:51 -0400 | [diff] [blame] | 41 | DSLWriter::ReportError("parameter has already been used in another function"); |
Ethan Nicholas | 961d944 | 2021-03-16 16:37:29 -0400 | [diff] [blame] | 42 | } |
Ethan Nicholas | 51b4b86 | 2021-08-31 16:12:40 -0400 | [diff] [blame] | 43 | SkASSERT(!param->fInitialValue.hasValue()); |
Ethan Nicholas | a2d22b2 | 2021-07-15 10:35:54 -0400 | [diff] [blame] | 44 | SkASSERT(!param->fDeclaration); |
Ethan Nicholas | 961d944 | 2021-03-16 16:37:29 -0400 | [diff] [blame] | 45 | param->fDeclared = true; |
Ethan Nicholas | a2d22b2 | 2021-07-15 10:35:54 -0400 | [diff] [blame] | 46 | std::unique_ptr<SkSL::Variable> paramVar = DSLWriter::CreateParameterVar(*param); |
Ethan Nicholas | 6b3c6fc | 2021-05-04 15:36:06 -0400 | [diff] [blame] | 47 | if (!paramVar) { |
| 48 | return; |
| 49 | } |
Ethan Nicholas | 371f6e1 | 2021-05-04 14:30:02 -0400 | [diff] [blame] | 50 | paramVars.push_back(std::move(paramVar)); |
Ethan Nicholas | daed259 | 2021-03-04 14:30:25 -0500 | [diff] [blame] | 51 | } |
Ethan Nicholas | 6b3c6fc | 2021-05-04 15:36:06 -0400 | [diff] [blame] | 52 | SkASSERT(paramVars.size() == params.size()); |
Ethan Nicholas | 371f6e1 | 2021-05-04 14:30:02 -0400 | [diff] [blame] | 53 | fDecl = SkSL::FunctionDeclaration::Convert(DSLWriter::Context(), |
| 54 | *DSLWriter::SymbolTable(), |
Ethan Nicholas | 89cfde1 | 2021-09-27 11:20:34 -0400 | [diff] [blame^] | 55 | pos.line(), |
Ethan Nicholas | 292a09d | 2021-07-14 09:52:16 -0400 | [diff] [blame] | 56 | DSLWriter::Modifiers(modifiers.fModifiers), |
Ethan Nicholas | e0531f5 | 2021-07-26 16:07:23 -0400 | [diff] [blame] | 57 | name == "main" ? name : DSLWriter::Name(name), |
Ethan Nicholas | 722cb67 | 2021-05-06 10:47:06 -0400 | [diff] [blame] | 58 | std::move(paramVars), &returnType.skslType(), |
Ethan Nicholas | 292a09d | 2021-07-14 09:52:16 -0400 | [diff] [blame] | 59 | DSLWriter::IsModule()); |
Ethan Nicholas | 600481f | 2021-09-01 09:39:34 -0400 | [diff] [blame] | 60 | DSLWriter::ReportErrors(pos); |
Ethan Nicholas | 292a09d | 2021-07-14 09:52:16 -0400 | [diff] [blame] | 61 | if (fDecl) { |
| 62 | for (size_t i = 0; i < params.size(); ++i) { |
| 63 | params[i]->fVar = fDecl->parameters()[i]; |
Ethan Nicholas | 5c4463e | 2021-08-29 14:31:19 -0400 | [diff] [blame] | 64 | params[i]->fInitialized = true; |
Ethan Nicholas | 292a09d | 2021-07-14 09:52:16 -0400 | [diff] [blame] | 65 | } |
Ethan Nicholas | 57709e1 | 2021-08-10 15:02:51 -0400 | [diff] [blame] | 66 | // We don't know when this function is going to be defined; go ahead and add a prototype in |
| 67 | // case the definition is delayed. If we end up defining the function immediately, we'll |
| 68 | // remove the prototype in define(). |
| 69 | DSLWriter::ProgramElements().push_back(std::make_unique<SkSL::FunctionPrototype>( |
Ethan Nicholas | 89cfde1 | 2021-09-27 11:20:34 -0400 | [diff] [blame^] | 70 | pos.line(), fDecl, DSLWriter::IsModule())); |
Ethan Nicholas | 292a09d | 2021-07-14 09:52:16 -0400 | [diff] [blame] | 71 | } |
Ethan Nicholas | daed259 | 2021-03-04 14:30:25 -0500 | [diff] [blame] | 72 | } |
| 73 | |
Ethan Nicholas | c9d65f0 | 2021-09-10 11:57:46 -0400 | [diff] [blame] | 74 | void DSLFunction::define(DSLBlock block, PositionInfo pos) { |
John Stiles | 3b20489 | 2021-08-27 17:35:35 -0400 | [diff] [blame] | 75 | std::unique_ptr<SkSL::Block> body = block.release(); |
Ethan Nicholas | 371f6e1 | 2021-05-04 14:30:02 -0400 | [diff] [blame] | 76 | if (!fDecl) { |
Ethan Nicholas | 292a09d | 2021-07-14 09:52:16 -0400 | [diff] [blame] | 77 | // Evidently we failed to create the declaration; error should already have been reported. |
| 78 | // Release the block so we don't fail its destructor assert. |
Ethan Nicholas | 371f6e1 | 2021-05-04 14:30:02 -0400 | [diff] [blame] | 79 | return; |
| 80 | } |
Ethan Nicholas | 57709e1 | 2021-08-10 15:02:51 -0400 | [diff] [blame] | 81 | if (!DSLWriter::ProgramElements().empty()) { |
| 82 | // If the last ProgramElement was the prototype for this function, it was unnecessary and we |
| 83 | // can remove it. |
| 84 | const SkSL::ProgramElement& last = *DSLWriter::ProgramElements().back(); |
| 85 | if (last.is<SkSL::FunctionPrototype>()) { |
| 86 | const SkSL::FunctionPrototype& prototype = last.as<SkSL::FunctionPrototype>(); |
| 87 | if (&prototype.declaration() == fDecl) { |
| 88 | DSLWriter::ProgramElements().pop_back(); |
| 89 | } |
| 90 | } |
| 91 | } |
Ethan Nicholas | c9d65f0 | 2021-09-10 11:57:46 -0400 | [diff] [blame] | 92 | if (fDecl->definition()) { |
| 93 | DSLWriter::ReportError(String::printf("function '%s' was already defined", |
| 94 | fDecl->description().c_str()), pos); |
| 95 | block.release(); |
| 96 | return; |
| 97 | } |
John Stiles | 3b20489 | 2021-08-27 17:35:35 -0400 | [diff] [blame] | 98 | // Append sk_Position fixup to the bottom of main() if this is a vertex program. |
| 99 | DSLWriter::IRGenerator().appendRTAdjustFixupToVertexMain(*fDecl, body.get()); |
| 100 | std::unique_ptr<FunctionDefinition> function = FunctionDefinition::Convert(DSLWriter::Context(), |
Ethan Nicholas | 89cfde1 | 2021-09-27 11:20:34 -0400 | [diff] [blame^] | 101 | pos.line(), |
John Stiles | 3b20489 | 2021-08-27 17:35:35 -0400 | [diff] [blame] | 102 | *fDecl, |
| 103 | std::move(body), |
| 104 | /*builtin=*/false); |
Ethan Nicholas | 600481f | 2021-09-01 09:39:34 -0400 | [diff] [blame] | 105 | DSLWriter::ReportErrors(fPosition); |
Ethan Nicholas | 051aeb7 | 2021-09-24 16:39:19 -0400 | [diff] [blame] | 106 | fDecl->setDefinition(function.get()); |
Ethan Nicholas | e2c0504 | 2021-02-03 10:27:22 -0500 | [diff] [blame] | 107 | DSLWriter::ProgramElements().push_back(std::move(function)); |
Ethan Nicholas | 1ff7609 | 2021-01-28 10:02:43 -0500 | [diff] [blame] | 108 | } |
| 109 | |
Ethan Nicholas | 722cb67 | 2021-05-06 10:47:06 -0400 | [diff] [blame] | 110 | DSLExpression DSLFunction::call(SkTArray<DSLWrapper<DSLExpression>> args) { |
Ethan Nicholas | 80f6235 | 2021-04-09 12:25:03 -0400 | [diff] [blame] | 111 | ExpressionArray released; |
| 112 | released.reserve_back(args.size()); |
Ethan Nicholas | 722cb67 | 2021-05-06 10:47:06 -0400 | [diff] [blame] | 113 | for (DSLWrapper<DSLExpression>& arg : args) { |
| 114 | released.push_back(arg->release()); |
Ethan Nicholas | 80f6235 | 2021-04-09 12:25:03 -0400 | [diff] [blame] | 115 | } |
Ethan Nicholas | 9a1f92e | 2021-09-09 15:03:22 -0400 | [diff] [blame] | 116 | return this->call(std::move(released)); |
| 117 | } |
| 118 | |
| 119 | DSLExpression DSLFunction::call(ExpressionArray args) { |
| 120 | std::unique_ptr<SkSL::Expression> result = DSLWriter::Call(*fDecl, std::move(args)); |
Ethan Nicholas | 292a09d | 2021-07-14 09:52:16 -0400 | [diff] [blame] | 121 | return result ? DSLExpression(std::move(result)) : DSLExpression(); |
Ethan Nicholas | daed259 | 2021-03-04 14:30:25 -0500 | [diff] [blame] | 122 | } |
| 123 | |
Ethan Nicholas | 1ff7609 | 2021-01-28 10:02:43 -0500 | [diff] [blame] | 124 | } // namespace dsl |
| 125 | |
| 126 | } // namespace SkSL |