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