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