1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2021 Google LLC
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "src/sksl/SkSLAnalysis.h"
9cb93a386Sopenharmony_ci#include "src/sksl/SkSLContext.h"
10cb93a386Sopenharmony_ci#include "src/sksl/SkSLProgramSettings.h"
11cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLBlock.h"
12cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLExpressionStatement.h"
13cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLForStatement.h"
14cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLNop.h"
15cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLSymbolTable.h"
16cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLType.h"
17cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLVarDeclarations.h"
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_cinamespace SkSL {
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_cistatic bool is_vardecl_block_initializer(const Statement* stmt) {
22cb93a386Sopenharmony_ci    if (!stmt) {
23cb93a386Sopenharmony_ci        return false;
24cb93a386Sopenharmony_ci    }
25cb93a386Sopenharmony_ci    if (!stmt->is<SkSL::Block>()) {
26cb93a386Sopenharmony_ci        return false;
27cb93a386Sopenharmony_ci    }
28cb93a386Sopenharmony_ci    const SkSL::Block& b = stmt->as<SkSL::Block>();
29cb93a386Sopenharmony_ci    if (b.isScope()) {
30cb93a386Sopenharmony_ci        return false;
31cb93a386Sopenharmony_ci    }
32cb93a386Sopenharmony_ci    for (const auto& child : b.children()) {
33cb93a386Sopenharmony_ci        if (!child->is<SkSL::VarDeclaration>()) {
34cb93a386Sopenharmony_ci            return false;
35cb93a386Sopenharmony_ci        }
36cb93a386Sopenharmony_ci    }
37cb93a386Sopenharmony_ci    return true;
38cb93a386Sopenharmony_ci}
39cb93a386Sopenharmony_ci
40cb93a386Sopenharmony_cistatic bool is_simple_initializer(const Statement* stmt) {
41cb93a386Sopenharmony_ci    return !stmt || stmt->isEmpty() || stmt->is<SkSL::VarDeclaration>() ||
42cb93a386Sopenharmony_ci           stmt->is<SkSL::ExpressionStatement>();
43cb93a386Sopenharmony_ci}
44cb93a386Sopenharmony_ci
45cb93a386Sopenharmony_cistd::unique_ptr<Statement> ForStatement::clone() const {
46cb93a386Sopenharmony_ci    std::unique_ptr<LoopUnrollInfo> unrollInfo;
47cb93a386Sopenharmony_ci    if (fUnrollInfo) {
48cb93a386Sopenharmony_ci        unrollInfo = std::make_unique<LoopUnrollInfo>(*fUnrollInfo);
49cb93a386Sopenharmony_ci    }
50cb93a386Sopenharmony_ci
51cb93a386Sopenharmony_ci    return std::make_unique<ForStatement>(
52cb93a386Sopenharmony_ci            fLine,
53cb93a386Sopenharmony_ci            this->initializer() ? this->initializer()->clone() : nullptr,
54cb93a386Sopenharmony_ci            this->test() ? this->test()->clone() : nullptr,
55cb93a386Sopenharmony_ci            this->next() ? this->next()->clone() : nullptr,
56cb93a386Sopenharmony_ci            this->statement()->clone(),
57cb93a386Sopenharmony_ci            std::move(unrollInfo),
58cb93a386Sopenharmony_ci            SymbolTable::WrapIfBuiltin(this->symbols()));
59cb93a386Sopenharmony_ci}
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ciString ForStatement::description() const {
62cb93a386Sopenharmony_ci    String result("for (");
63cb93a386Sopenharmony_ci    if (this->initializer()) {
64cb93a386Sopenharmony_ci        result += this->initializer()->description();
65cb93a386Sopenharmony_ci    } else {
66cb93a386Sopenharmony_ci        result += ";";
67cb93a386Sopenharmony_ci    }
68cb93a386Sopenharmony_ci    result += " ";
69cb93a386Sopenharmony_ci    if (this->test()) {
70cb93a386Sopenharmony_ci        result += this->test()->description();
71cb93a386Sopenharmony_ci    }
72cb93a386Sopenharmony_ci    result += "; ";
73cb93a386Sopenharmony_ci    if (this->next()) {
74cb93a386Sopenharmony_ci        result += this->next()->description();
75cb93a386Sopenharmony_ci    }
76cb93a386Sopenharmony_ci    result += ") " + this->statement()->description();
77cb93a386Sopenharmony_ci    return result;
78cb93a386Sopenharmony_ci}
79cb93a386Sopenharmony_ci
80cb93a386Sopenharmony_cistd::unique_ptr<Statement> ForStatement::Convert(const Context& context, int line,
81cb93a386Sopenharmony_ci                                                 std::unique_ptr<Statement> initializer,
82cb93a386Sopenharmony_ci                                                 std::unique_ptr<Expression> test,
83cb93a386Sopenharmony_ci                                                 std::unique_ptr<Expression> next,
84cb93a386Sopenharmony_ci                                                 std::unique_ptr<Statement> statement,
85cb93a386Sopenharmony_ci                                                 std::shared_ptr<SymbolTable> symbolTable) {
86cb93a386Sopenharmony_ci    bool isSimpleInitializer = is_simple_initializer(initializer.get());
87cb93a386Sopenharmony_ci    bool isVardeclBlockInitializer =
88cb93a386Sopenharmony_ci            !isSimpleInitializer && is_vardecl_block_initializer(initializer.get());
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci    if (!isSimpleInitializer && !isVardeclBlockInitializer) {
91cb93a386Sopenharmony_ci        context.fErrors->error(initializer->fLine, "invalid for loop initializer");
92cb93a386Sopenharmony_ci        return nullptr;
93cb93a386Sopenharmony_ci    }
94cb93a386Sopenharmony_ci
95cb93a386Sopenharmony_ci    if (test) {
96cb93a386Sopenharmony_ci        test = context.fTypes.fBool->coerceExpression(std::move(test), context);
97cb93a386Sopenharmony_ci        if (!test) {
98cb93a386Sopenharmony_ci            return nullptr;
99cb93a386Sopenharmony_ci        }
100cb93a386Sopenharmony_ci    }
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_ci    // The type of the next-expression doesn't matter, but it needs to be a complete expression.
103cb93a386Sopenharmony_ci    // Report an error on intermediate expressions like FunctionReference or TypeReference.
104cb93a386Sopenharmony_ci    if (next && next->isIncomplete(context)) {
105cb93a386Sopenharmony_ci        return nullptr;
106cb93a386Sopenharmony_ci    }
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_ci    std::unique_ptr<LoopUnrollInfo> unrollInfo;
109cb93a386Sopenharmony_ci    if (context.fConfig->strictES2Mode()) {
110cb93a386Sopenharmony_ci        // In strict-ES2, loops must be unrollable or it's an error.
111cb93a386Sopenharmony_ci        unrollInfo = Analysis::GetLoopUnrollInfo(line, initializer.get(), test.get(),
112cb93a386Sopenharmony_ci                                                 next.get(), statement.get(), context.fErrors);
113cb93a386Sopenharmony_ci        if (!unrollInfo) {
114cb93a386Sopenharmony_ci            return nullptr;
115cb93a386Sopenharmony_ci        }
116cb93a386Sopenharmony_ci    } else {
117cb93a386Sopenharmony_ci        // In ES3, loops don't have to be unrollable, but we can use the unroll information for
118cb93a386Sopenharmony_ci        // optimization purposes.
119cb93a386Sopenharmony_ci        unrollInfo = Analysis::GetLoopUnrollInfo(line, initializer.get(), test.get(),
120cb93a386Sopenharmony_ci                                                 next.get(), statement.get(), /*errors=*/nullptr);
121cb93a386Sopenharmony_ci    }
122cb93a386Sopenharmony_ci
123cb93a386Sopenharmony_ci    if (Analysis::DetectVarDeclarationWithoutScope(*statement, context.fErrors)) {
124cb93a386Sopenharmony_ci        return nullptr;
125cb93a386Sopenharmony_ci    }
126cb93a386Sopenharmony_ci
127cb93a386Sopenharmony_ci    if (isVardeclBlockInitializer) {
128cb93a386Sopenharmony_ci        // If the initializer statement of a for loop contains multiple variables, this causes
129cb93a386Sopenharmony_ci        // difficulties for several of our backends; e.g. Metal doesn't have a way to express arrays
130cb93a386Sopenharmony_ci        // of different size in the same decl-stmt, because the array-size is part of the type. It's
131cb93a386Sopenharmony_ci        // conceptually equivalent to synthesize a scope, declare the variables, and then emit a for
132cb93a386Sopenharmony_ci        // statement with an empty init-stmt. (Note that we can't just do this transformation
133cb93a386Sopenharmony_ci        // unilaterally for all for-statements, because the resulting for loop isn't ES2-compliant.)
134cb93a386Sopenharmony_ci        StatementArray scope;
135cb93a386Sopenharmony_ci        scope.push_back(std::move(initializer));
136cb93a386Sopenharmony_ci        scope.push_back(ForStatement::Make(context, line, /*initializer=*/nullptr,
137cb93a386Sopenharmony_ci                                           std::move(test), std::move(next), std::move(statement),
138cb93a386Sopenharmony_ci                                           std::move(unrollInfo), std::move(symbolTable)));
139cb93a386Sopenharmony_ci        return Block::Make(line, std::move(scope));
140cb93a386Sopenharmony_ci    }
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_ci    return ForStatement::Make(context, line, std::move(initializer), std::move(test),
143cb93a386Sopenharmony_ci                              std::move(next), std::move(statement), std::move(unrollInfo),
144cb93a386Sopenharmony_ci                              std::move(symbolTable));
145cb93a386Sopenharmony_ci}
146cb93a386Sopenharmony_ci
147cb93a386Sopenharmony_cistd::unique_ptr<Statement> ForStatement::ConvertWhile(const Context& context, int line,
148cb93a386Sopenharmony_ci                                                      std::unique_ptr<Expression> test,
149cb93a386Sopenharmony_ci                                                      std::unique_ptr<Statement> statement,
150cb93a386Sopenharmony_ci                                                      std::shared_ptr<SymbolTable> symbolTable) {
151cb93a386Sopenharmony_ci    if (context.fConfig->strictES2Mode()) {
152cb93a386Sopenharmony_ci        context.fErrors->error(line, "while loops are not supported");
153cb93a386Sopenharmony_ci        return nullptr;
154cb93a386Sopenharmony_ci    }
155cb93a386Sopenharmony_ci    return ForStatement::Convert(context, line, /*initializer=*/nullptr, std::move(test),
156cb93a386Sopenharmony_ci                                 /*next=*/nullptr, std::move(statement), std::move(symbolTable));
157cb93a386Sopenharmony_ci}
158cb93a386Sopenharmony_ci
159cb93a386Sopenharmony_cistd::unique_ptr<Statement> ForStatement::Make(const Context& context, int line,
160cb93a386Sopenharmony_ci                                              std::unique_ptr<Statement> initializer,
161cb93a386Sopenharmony_ci                                              std::unique_ptr<Expression> test,
162cb93a386Sopenharmony_ci                                              std::unique_ptr<Expression> next,
163cb93a386Sopenharmony_ci                                              std::unique_ptr<Statement> statement,
164cb93a386Sopenharmony_ci                                              std::unique_ptr<LoopUnrollInfo> unrollInfo,
165cb93a386Sopenharmony_ci                                              std::shared_ptr<SymbolTable> symbolTable) {
166cb93a386Sopenharmony_ci    SkASSERT(is_simple_initializer(initializer.get()) ||
167cb93a386Sopenharmony_ci             is_vardecl_block_initializer(initializer.get()));
168cb93a386Sopenharmony_ci    SkASSERT(!test || test->type() == *context.fTypes.fBool);
169cb93a386Sopenharmony_ci    SkASSERT(!Analysis::DetectVarDeclarationWithoutScope(*statement));
170cb93a386Sopenharmony_ci    SkASSERT(unrollInfo || !context.fConfig->strictES2Mode());
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_ci    // Unrollable loops are easy to optimize because we know initializer, test and next don't have
173cb93a386Sopenharmony_ci    // interesting side effects.
174cb93a386Sopenharmony_ci    if (unrollInfo) {
175cb93a386Sopenharmony_ci        // A zero-iteration unrollable loop can be replaced with Nop.
176cb93a386Sopenharmony_ci        // An unrollable loop with an empty body can be replaced with Nop.
177cb93a386Sopenharmony_ci        if (unrollInfo->fCount <= 0 || statement->isEmpty()) {
178cb93a386Sopenharmony_ci            return Nop::Make();
179cb93a386Sopenharmony_ci        }
180cb93a386Sopenharmony_ci    }
181cb93a386Sopenharmony_ci
182cb93a386Sopenharmony_ci    return std::make_unique<ForStatement>(line, std::move(initializer), std::move(test),
183cb93a386Sopenharmony_ci                                          std::move(next), std::move(statement),
184cb93a386Sopenharmony_ci                                          std::move(unrollInfo), std::move(symbolTable));
185cb93a386Sopenharmony_ci}
186cb93a386Sopenharmony_ci
187cb93a386Sopenharmony_ci}  // namespace SkSL
188