xref: /third_party/skia/src/sksl/dsl/DSLFunction.cpp (revision cb93a386)
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