SkSL IR normalization: Convert while loops to for loops

Bug: skia:11095
Change-Id: Icd69df40675e5ecde5004e04a7dcd78eedf8343c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/344765
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
This commit is contained in:
Brian Osman 2020-12-15 17:08:59 -05:00 committed by Skia Commit-Bot
parent ba158b9daf
commit d6f2338ab1
21 changed files with 24 additions and 222 deletions

View File

@ -116,7 +116,6 @@ skia_sksl_sources = [
"$_src/sksl/ir/SkSLVariable.h",
"$_src/sksl/ir/SkSLVariableReference.cpp",
"$_src/sksl/ir/SkSLVariableReference.h",
"$_src/sksl/ir/SkSLWhileStatement.h",
]
skia_sksl_gpu_sources = [

View File

@ -35,7 +35,6 @@
#include "src/sksl/ir/SkSLNop.h"
#include "src/sksl/ir/SkSLReturnStatement.h"
#include "src/sksl/ir/SkSLSwitchStatement.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
// Expressions
#include "src/sksl/ir/SkSLBinaryExpression.h"
@ -616,10 +615,6 @@ bool TProgramVisitor<PROG, EXPR, STMT, ELEM>::visitStatement(STMT s) {
auto& v = s.template as<VarDeclaration>();
return v.value() && this->visitExpression(*v.value());
}
case Statement::Kind::kWhile: {
auto& w = s.template as<WhileStatement>();
return this->visitExpression(*w.test()) || this->visitStatement(*w.statement());
}
default:
SkUNREACHABLE;
}

View File

@ -1912,21 +1912,6 @@ void ByteCodeGenerator::writeVarDeclaration(const VarDeclaration& decl) {
}
}
void ByteCodeGenerator::writeWhileStatement(const WhileStatement& w) {
this->write(ByteCodeInstruction::kLoopBegin);
size_t cond = fCode->size();
this->writeExpression(*w.test());
this->write(ByteCodeInstruction::kLoopMask);
this->write(ByteCodeInstruction::kBranchIfAllFalse);
DeferredLocation endLocation(this);
this->writeStatement(*w.statement());
this->write(ByteCodeInstruction::kLoopNext);
this->write(ByteCodeInstruction::kBranch);
this->write16(cond);
endLocation.set();
this->write(ByteCodeInstruction::kLoopEnd);
}
void ByteCodeGenerator::writeStatement(const Statement& s) {
switch (s.kind()) {
case Statement::Kind::kBlock:
@ -1962,9 +1947,6 @@ void ByteCodeGenerator::writeStatement(const Statement& s) {
case Statement::Kind::kVarDeclaration:
this->writeVarDeclaration(s.as<VarDeclaration>());
break;
case Statement::Kind::kWhile:
this->writeWhileStatement(s.as<WhileStatement>());
break;
case Statement::Kind::kInlineMarker:
case Statement::Kind::kNop:
break;

View File

@ -45,7 +45,6 @@
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariableReference.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
#include "src/sksl/spirv.h"
namespace SkSL {
@ -282,7 +281,6 @@ private:
void writeContinueStatement(const ContinueStatement& c);
void writeIfStatement(const IfStatement& stmt);
void writeForStatement(const ForStatement& f);
void writeWhileStatement(const WhileStatement& w);
void writeDoStatement(const DoStatement& d);
void writeSwitchStatement(const SwitchStatement& s);
void writeReturnStatement(const ReturnStatement& r);

View File

@ -23,7 +23,6 @@
#include "src/sksl/ir/SkSLSwitchStatement.h"
#include "src/sksl/ir/SkSLSwizzle.h"
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
namespace SkSL {
@ -487,10 +486,6 @@ void CFGGenerator::addLValue(CFG& cfg, std::unique_ptr<Expression>* e) {
}
}
static bool is_true(Expression& expr) {
return expr.is<BoolLiteral>() && expr.as<BoolLiteral>().value();
}
void CFGGenerator::addStatement(CFG& cfg, std::unique_ptr<Statement>* s) {
switch ((*s)->kind()) {
case Statement::Kind::kBlock: {
@ -556,25 +551,6 @@ void CFGGenerator::addStatement(CFG& cfg, std::unique_ptr<Statement>* s) {
cfg.addExit(cfg.fCurrent, fLoopContinues.top());
cfg.fCurrent = cfg.newIsolatedBlock();
break;
case Statement::Kind::kWhile: {
WhileStatement& w = (*s)->as<WhileStatement>();
BlockId loopStart = cfg.newBlock();
fLoopContinues.push(loopStart);
BlockId loopExit = cfg.newIsolatedBlock();
fLoopExits.push(loopExit);
this->addExpression(cfg, &w.test(), /*constantPropagate=*/true);
BlockId test = cfg.fCurrent;
if (!is_true(*w.test())) {
cfg.addExit(test, loopExit);
}
cfg.newBlock();
this->addStatement(cfg, &w.statement());
cfg.addExit(cfg.fCurrent, loopStart);
fLoopContinues.pop();
fLoopExits.pop();
cfg.fCurrent = loopExit;
break;
}
case Statement::Kind::kDo: {
DoStatement& d = (*s)->as<DoStatement>();
BlockId loopStart = cfg.newBlock();

View File

@ -47,7 +47,6 @@
#include "src/sksl/ir/SkSLUnresolvedFunction.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariable.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
#ifdef SKSL_STANDALONE
@ -494,13 +493,6 @@ void Dehydrator::write(const Statement* s) {
this->write(v.value().get());
break;
}
case Statement::Kind::kWhile: {
const WhileStatement& w = s->as<WhileStatement>();
this->writeCommand(Rehydrator::kWhile_Command);
this->write(w.test().get());
this->write(w.statement().get());
break;
}
}
} else {
this->writeCommand(Rehydrator::kVoid_Command);

View File

@ -1308,9 +1308,6 @@ void GLSLCodeGenerator::writeStatement(const Statement& s) {
case Statement::Kind::kFor:
this->writeForStatement(s.as<ForStatement>());
break;
case Statement::Kind::kWhile:
this->writeWhileStatement(s.as<WhileStatement>());
break;
case Statement::Kind::kDo:
this->writeDoStatement(s.as<DoStatement>());
break;
@ -1368,6 +1365,15 @@ void GLSLCodeGenerator::writeIfStatement(const IfStatement& stmt) {
}
void GLSLCodeGenerator::writeForStatement(const ForStatement& f) {
// Emit loops of the form 'for(;test;)' as 'while(test)', which is probably how they started
if (!f.initializer() && f.test() && !f.next()) {
this->write("while (");
this->writeExpression(*f.test(), kTopLevel_Precedence);
this->write(") ");
this->writeStatement(*f.statement());
return;
}
this->write("for (");
if (f.initializer() && !f.initializer()->isEmpty()) {
this->writeStatement(*f.initializer());
@ -1393,13 +1399,6 @@ void GLSLCodeGenerator::writeForStatement(const ForStatement& f) {
this->writeStatement(*f.statement());
}
void GLSLCodeGenerator::writeWhileStatement(const WhileStatement& w) {
this->write("while (");
this->writeExpression(*w.test(), kTopLevel_Precedence);
this->write(") ");
this->writeStatement(*w.statement());
}
void GLSLCodeGenerator::writeDoStatement(const DoStatement& d) {
if (!fProgram.fCaps->rewriteDoWhileLoops()) {
this->write("do ");

View File

@ -41,7 +41,6 @@
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariableReference.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
namespace SkSL {
@ -189,8 +188,6 @@ protected:
void writeForStatement(const ForStatement& f);
void writeWhileStatement(const WhileStatement& w);
void writeDoStatement(const DoStatement& d);
virtual void writeSwitchStatement(const SwitchStatement& s);

View File

@ -57,7 +57,6 @@
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariable.h"
#include "src/sksl/ir/SkSLVariableReference.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
namespace SkSL {
@ -559,7 +558,8 @@ std::unique_ptr<Statement> IRGenerator::convertWhile(const ASTNode& w) {
if (!statement) {
return nullptr;
}
return std::make_unique<WhileStatement>(w.fOffset, std::move(test), std::move(statement));
return std::make_unique<ForStatement>(w.fOffset, /*initializer=*/nullptr, std::move(test),
/*next=*/nullptr, std::move(statement), fSymbolTable);
}
std::unique_ptr<Statement> IRGenerator::convertDo(const ASTNode& d) {

View File

@ -51,7 +51,6 @@
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariable.h"
#include "src/sksl/ir/SkSLVariableReference.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
namespace SkSL {
namespace {
@ -100,7 +99,6 @@ static int count_returns_at_end_of_control_flow(const FunctionDefinition& funcDe
this->visitStatement(*block.children().back());
}
case Statement::Kind::kSwitch:
case Statement::Kind::kWhile:
case Statement::Kind::kDo:
case Statement::Kind::kFor:
// Don't introspect switches or loop structures at all.
@ -132,7 +130,6 @@ static int count_returns_in_breakable_constructs(const FunctionDefinition& funcD
bool visitStatement(const Statement& stmt) override {
switch (stmt.kind()) {
case Statement::Kind::kSwitch:
case Statement::Kind::kWhile:
case Statement::Kind::kDo:
case Statement::Kind::kFor: {
++fInsideBreakableConstruct;
@ -257,7 +254,7 @@ void Inliner::ensureScopedBlocks(Statement* inlinedBody, Statement* parentStmt)
// No changes necessary if the parent statement doesn't require a scope.
if (!parentStmt || !(parentStmt->is<IfStatement>() || parentStmt->is<ForStatement>() ||
parentStmt->is<DoStatement>() || parentStmt->is<WhileStatement>())) {
parentStmt->is<DoStatement>())) {
return;
}
@ -574,10 +571,6 @@ std::unique_ptr<Statement> Inliner::inlineStatement(int offset,
return std::make_unique<VarDeclaration>(clone, baseTypePtr, arraySize,
std::move(initialValue));
}
case Statement::Kind::kWhile: {
const WhileStatement& w = statement.as<WhileStatement>();
return std::make_unique<WhileStatement>(offset, expr(w.test()), stmt(w.statement()));
}
default:
SkASSERT(false);
return nullptr;
@ -956,20 +949,6 @@ public:
this->visitExpression(&varDeclStmt.value());
break;
}
case Statement::Kind::kWhile: {
WhileStatement& whileStmt = (*stmt)->as<WhileStatement>();
// The loop body is a candidate for inlining.
this->visitStatement(&whileStmt.statement());
// The inliner isn't smart enough to inline the test-expression for a while loop at
// this time. There are two limitations:
// - We would need to insert the inlined-body block at the very beginning of the
// while loop's inner fStatement. We don't support that today, but it's doable.
// - The while-loop's built-in test-expression would need to be replaced with a
// `true` BoolLiteral, and the loop would be halted via a break statement at the
// end of the inlined test-expression. This is again something we don't support
// today, but it could be implemented.
break;
}
default:
SkUNREACHABLE;
}

View File

@ -1670,9 +1670,6 @@ void MetalCodeGenerator::writeStatement(const Statement& s) {
case Statement::Kind::kFor:
this->writeForStatement(s.as<ForStatement>());
break;
case Statement::Kind::kWhile:
this->writeWhileStatement(s.as<WhileStatement>());
break;
case Statement::Kind::kDo:
this->writeDoStatement(s.as<DoStatement>());
break;
@ -1730,6 +1727,15 @@ void MetalCodeGenerator::writeIfStatement(const IfStatement& stmt) {
}
void MetalCodeGenerator::writeForStatement(const ForStatement& f) {
// Emit loops of the form 'for(;test;)' as 'while(test)', which is probably how they started
if (!f.initializer() && f.test() && !f.next()) {
this->write("while (");
this->writeExpression(*f.test(), kTopLevel_Precedence);
this->write(") ");
this->writeStatement(*f.statement());
return;
}
this->write("for (");
if (f.initializer() && !f.initializer()->isEmpty()) {
this->writeStatement(*f.initializer());
@ -1747,13 +1753,6 @@ void MetalCodeGenerator::writeForStatement(const ForStatement& f) {
this->writeStatement(*f.statement());
}
void MetalCodeGenerator::writeWhileStatement(const WhileStatement& w) {
this->write("while (");
this->writeExpression(*w.test(), kTopLevel_Precedence);
this->write(") ");
this->writeStatement(*w.statement());
}
void MetalCodeGenerator::writeDoStatement(const DoStatement& d) {
this->write("do ");
this->writeStatement(*d.statement());
@ -2247,11 +2246,6 @@ MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Statemen
this->requirements(f.next().get()) |
this->requirements(f.statement().get());
}
case Statement::Kind::kWhile: {
const WhileStatement& w = s->as<WhileStatement>();
return this->requirements(w.test().get()) |
this->requirements(w.statement().get());
}
case Statement::Kind::kDo: {
const DoStatement& d = s->as<DoStatement>();
return this->requirements(d.test().get()) |

View File

@ -43,7 +43,6 @@
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariableReference.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
namespace SkSL {
@ -274,8 +273,6 @@ protected:
void writeForStatement(const ForStatement& f);
void writeWhileStatement(const WhileStatement& w);
void writeDoStatement(const DoStatement& d);
void writeSwitchStatement(const SwitchStatement& s);

View File

@ -49,7 +49,6 @@
#include "src/sksl/ir/SkSLUnresolvedFunction.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariable.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
namespace SkSL {
@ -445,12 +444,6 @@ std::unique_ptr<Statement> Rehydrator::statement() {
}
case Rehydrator::kVoid_Command:
return nullptr;
case Rehydrator::kWhile_Command: {
std::unique_ptr<Expression> expr = this->expression();
std::unique_ptr<Statement> stmt = this->statement();
return std::unique_ptr<Statement>(new WhileStatement(-1, std::move(expr),
std::move(stmt)));
}
default:
printf("unsupported statement %d\n", kind);
SkASSERT(false);

View File

@ -144,8 +144,6 @@ public:
// uint16 varId, uint8 refKind
kVariableReference_Command,
kVoid_Command,
// Expression test, Statement body
kWhile_Command,
};
// src must remain in memory as long as the objects created from it do

View File

@ -2927,9 +2927,6 @@ void SPIRVCodeGenerator::writeStatement(const Statement& s, OutputStream& out) {
case Statement::Kind::kFor:
this->writeForStatement(s.as<ForStatement>(), out);
break;
case Statement::Kind::kWhile:
this->writeWhileStatement(s.as<WhileStatement>(), out);
break;
case Statement::Kind::kDo:
this->writeDoStatement(s.as<DoStatement>(), out);
break;
@ -3027,33 +3024,6 @@ void SPIRVCodeGenerator::writeForStatement(const ForStatement& f, OutputStream&
fContinueTarget.pop();
}
void SPIRVCodeGenerator::writeWhileStatement(const WhileStatement& w, OutputStream& out) {
SpvId header = this->nextId();
SpvId start = this->nextId();
SpvId body = this->nextId();
SpvId continueTarget = this->nextId();
fContinueTarget.push(continueTarget);
SpvId end = this->nextId();
fBreakTarget.push(end);
this->writeInstruction(SpvOpBranch, header, out);
this->writeLabel(header, out);
this->writeInstruction(SpvOpLoopMerge, end, continueTarget, SpvLoopControlMaskNone, out);
this->writeInstruction(SpvOpBranch, start, out);
this->writeLabel(start, out);
SpvId test = this->writeExpression(*w.test(), out);
this->writeInstruction(SpvOpBranchConditional, test, body, end, out);
this->writeLabel(body, out);
this->writeStatement(*w.statement(), out);
if (fCurrentBlock) {
this->writeInstruction(SpvOpBranch, continueTarget, out);
}
this->writeLabel(continueTarget, out);
this->writeInstruction(SpvOpBranch, header, out);
this->writeLabel(end, out);
fBreakTarget.pop();
fContinueTarget.pop();
}
void SPIRVCodeGenerator::writeDoStatement(const DoStatement& d, OutputStream& out) {
SpvId header = this->nextId();
SpvId start = this->nextId();

View File

@ -39,7 +39,6 @@
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariableReference.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
#include "src/sksl/spirv.h"
union ConstantValue {
@ -314,8 +313,6 @@ private:
void writeForStatement(const ForStatement& f, OutputStream& out);
void writeWhileStatement(const WhileStatement& w, OutputStream& out);
void writeDoStatement(const DoStatement& d, OutputStream& out);
void writeSwitchStatement(const SwitchStatement& s, OutputStream& out);

View File

@ -25,7 +25,6 @@
#include "src/sksl/ir/SkSLSwizzle.h"
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLVariable.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
namespace SkSL {

View File

@ -21,7 +21,6 @@
#include "src/sksl/ir/SkSLSwitchStatement.h"
#include "src/sksl/ir/SkSLSwizzle.h"
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLWhileStatement.h"
namespace SkSL {

View File

@ -33,10 +33,9 @@ public:
kSwitch,
kSwitchCase,
kVarDeclaration,
kWhile,
kFirst = kBlock,
kLast = kWhile
kLast = kVarDeclaration,
};
Statement(int offset, Kind kind)

View File

@ -1,62 +0,0 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_WHILESTATEMENT
#define SKSL_WHILESTATEMENT
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLStatement.h"
namespace SkSL {
/**
* A 'while' loop.
*/
struct WhileStatement final : public Statement {
static constexpr Kind kStatementKind = Kind::kWhile;
WhileStatement(int offset, std::unique_ptr<Expression> test,
std::unique_ptr<Statement> statement)
: INHERITED(offset, kStatementKind)
, fTest(std::move(test))
, fStatement(std::move(statement)) {}
std::unique_ptr<Expression>& test() {
return fTest;
}
const std::unique_ptr<Expression>& test() const {
return fTest;
}
std::unique_ptr<Statement>& statement() {
return fStatement;
}
const std::unique_ptr<Statement>& statement() const {
return fStatement;
}
std::unique_ptr<Statement> clone() const override {
return std::unique_ptr<Statement>(new WhileStatement(fOffset, this->test()->clone(),
this->statement()->clone()));
}
String description() const override {
return "while (" + this->test()->description() + ") " + this->statement()->description();
}
private:
std::unique_ptr<Expression> fTest;
std::unique_ptr<Statement> fStatement;
using INHERITED = Statement;
};
} // namespace SkSL
#endif

View File

@ -733,7 +733,8 @@ DEF_TEST(SkSLInterpreterRestrictFunctionCalls, r) {
expect_failure(r, "float foo(); float bar() { return foo(); } float foo() { return bar(); }");
// returns are not allowed inside loops
expect_failure(r, "float main(float x) { while (x > 1) { return x; } return 0; }");
expect_failure(r, "float main(float x)"
"{ while (x > 1) { if (x > 2) { return x; } } return 0; }");
}
DEF_TEST(SkSLInterpreterEarlyReturn, r) {