Skip to content

Commit

Permalink
Store loop-unroll information inside ForStatement.
Browse files Browse the repository at this point in the history
This will be useful when trying to determine the flattened size of a
program, and it is expensive to compute on demand.

Change-Id: I232d9189511502d4783e5542a9bfe0dff8ea8c4a
Bug: skia:12396
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/443883
Commit-Queue: John Stiles <[email protected]>
Commit-Queue: Brian Osman <[email protected]>
Auto-Submit: John Stiles <[email protected]>
Reviewed-by: Brian Osman <[email protected]>
  • Loading branch information
johnstiles-google authored and SkCQ committed Aug 31, 2021
1 parent aa008bc commit 9c975c5
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 55 deletions.
26 changes: 12 additions & 14 deletions src/sksl/SkSLAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,7 @@ static const char* invalid_for_ES2(int offset,
const Expression* loopTest,
const Expression* loopNext,
const Statement* loopStatement,
Analysis::UnrollableLoopInfo& loopInfo) {
LoopUnrollInfo& loopInfo) {
//
// init_declaration has the form: type_specifier identifier = constant_expression
//
Expand Down Expand Up @@ -1093,23 +1093,21 @@ static const char* invalid_for_ES2(int offset,
return nullptr; // All checks pass
}

bool Analysis::ForLoopIsValidForES2(int offset,
const Statement* loopInitializer,
const Expression* loopTest,
const Expression* loopNext,
const Statement* loopStatement,
Analysis::UnrollableLoopInfo* outLoopInfo,
ErrorReporter* errors) {
UnrollableLoopInfo ignored,
*loopInfo = outLoopInfo ? outLoopInfo : &ignored;
if (const char* msg = invalid_for_ES2(
offset, loopInitializer, loopTest, loopNext, loopStatement, *loopInfo)) {
std::unique_ptr<LoopUnrollInfo> Analysis::GetLoopUnrollInfo(int offset,
const Statement* loopInitializer,
const Expression* loopTest,
const Expression* loopNext,
const Statement* loopStatement,
ErrorReporter* errors) {
auto result = std::make_unique<LoopUnrollInfo>();
if (const char* msg = invalid_for_ES2(offset, loopInitializer, loopTest, loopNext,
loopStatement, *result)) {
result = nullptr;
if (errors) {
errors->error(offset, msg);
}
return false;
}
return true;
return result;
}

// Checks for ES2 constant-expression rules, and (optionally) constant-index-expression rules
Expand Down
26 changes: 10 additions & 16 deletions src/sksl/SkSLAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct Program;
class ProgramElement;
class ProgramUsage;
class Statement;
struct LoopUnrollInfo;
class Variable;
class VariableReference;
enum class VariableRefKind : int8_t;
Expand Down Expand Up @@ -132,24 +133,17 @@ struct Analysis {
// of the texture lookup functions
static bool IsConstantExpression(const Expression& expr);

struct UnrollableLoopInfo {
const Variable* fIndex;
double fStart;
double fDelta;
int fCount;
};

// Ensures that a for-loop meets the strict requirements of The OpenGL ES Shading Language 1.00,
// Appendix A, Section 4.
// Information about the loop's structure are placed in outLoopInfo (if not nullptr).
// If the function returns false, specific reasons are reported via errors (if not nullptr).
static bool ForLoopIsValidForES2(int offset,
const Statement* loopInitializer,
const Expression* loopTest,
const Expression* loopNext,
const Statement* loopStatement,
UnrollableLoopInfo* outLoopInfo,
ErrorReporter* errors);
// If the requirements are met, information about the loop's structure is returned.
// If the requirements are not met, the problem is reported via `errors` (if not nullptr), and
// null is returned.
static std::unique_ptr<LoopUnrollInfo> GetLoopUnrollInfo(int offset,
const Statement* loopInitializer,
const Expression* loopTest,
const Expression* loopNext,
const Statement* loopStatement,
ErrorReporter* errors);

static void ValidateIndexingForES2(const ProgramElement& pe, ErrorReporter& errors);

Expand Down
4 changes: 3 additions & 1 deletion src/sksl/SkSLInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,10 @@ std::unique_ptr<Statement> Inliner::inlineStatement(int offset,
// need to ensure initializer is evaluated first so that we've already remapped its
// declarations by the time we evaluate test & next
std::unique_ptr<Statement> initializer = stmt(f.initializer());
// We can't reuse the unroll info from the original for loop, because it uses a
// different induction variable. Ours is a clone.
return ForStatement::Make(*fContext, offset, std::move(initializer), expr(f.test()),
expr(f.next()), stmt(f.statement()),
expr(f.next()), stmt(f.statement()), /*unrollInfo=*/nullptr,
SymbolTable::WrapIfBuiltin(f.symbols()));
}
case Statement::Kind::kIf: {
Expand Down
2 changes: 1 addition & 1 deletion src/sksl/SkSLRehydrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ std::unique_ptr<Statement> Rehydrator::statement() {
std::shared_ptr<SymbolTable> symbols = this->symbolTable();
return ForStatement::Make(fContext, /*offset=*/-1, std::move(initializer),
std::move(test), std::move(next), std::move(body),
std::move(symbols));
/*unrollInfo=*/nullptr, std::move(symbols));
}
case Rehydrator::kIf_Command: {
bool isStatic = this->readU8();
Expand Down
6 changes: 2 additions & 4 deletions src/sksl/codegen/SkSLVMCodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1459,10 +1459,8 @@ void SkVMGenerator::writeContinueStatement() {

void SkVMGenerator::writeForStatement(const ForStatement& f) {
// We require that all loops be ES2-compliant (unrollable), and actually unroll them here
Analysis::UnrollableLoopInfo loop;
SkAssertResult(Analysis::ForLoopIsValidForES2(f.fOffset, f.initializer().get(), f.test().get(),
f.next().get(), f.statement().get(), &loop,
/*errors=*/nullptr));
SkASSERT(f.unrollInfo());
const LoopUnrollInfo& loop = *f.unrollInfo();
SkASSERT(loop.fIndex->type().slotCount() == 1);

size_t indexSlot = this->getSlot(*loop.fIndex);
Expand Down
32 changes: 22 additions & 10 deletions src/sksl/ir/SkSLForStatement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,18 @@ static bool is_simple_initializer(const Statement* stmt) {
}

std::unique_ptr<Statement> ForStatement::clone() const {
std::unique_ptr<LoopUnrollInfo> unrollInfo;
if (fUnrollInfo) {
unrollInfo = std::make_unique<LoopUnrollInfo>(*fUnrollInfo);
}

return std::make_unique<ForStatement>(
fOffset,
this->initializer() ? this->initializer()->clone() : nullptr,
this->test() ? this->test()->clone() : nullptr,
this->next() ? this->next()->clone() : nullptr,
this->statement()->clone(),
std::move(unrollInfo),
SymbolTable::WrapIfBuiltin(this->symbols()));
}

Expand Down Expand Up @@ -102,10 +108,11 @@ std::unique_ptr<Statement> ForStatement::Convert(const Context& context, int off
}
}

std::unique_ptr<LoopUnrollInfo> unrollInfo;
if (context.fConfig->strictES2Mode()) {
if (!Analysis::ForLoopIsValidForES2(offset, initializer.get(), test.get(), next.get(),
statement.get(), /*outLoopInfo=*/nullptr,
context.fErrors)) {
unrollInfo = Analysis::GetLoopUnrollInfo(offset, initializer.get(), test.get(),
next.get(), statement.get(), context.fErrors);
if (!unrollInfo) {
return nullptr;
}
}
Expand All @@ -121,12 +128,13 @@ std::unique_ptr<Statement> ForStatement::Convert(const Context& context, int off
scope.push_back(std::move(initializer));
scope.push_back(ForStatement::Make(context, offset, /*initializer=*/nullptr,
std::move(test), std::move(next), std::move(statement),
std::move(symbolTable)));
std::move(unrollInfo), std::move(symbolTable)));
return Block::Make(offset, std::move(scope));
}

return ForStatement::Make(context, offset, std::move(initializer), std::move(test),
std::move(next), std::move(statement), std::move(symbolTable));
std::move(next), std::move(statement), std::move(unrollInfo),
std::move(symbolTable));
}

std::unique_ptr<Statement> ForStatement::ConvertWhile(const Context& context, int offset,
Expand All @@ -146,18 +154,22 @@ std::unique_ptr<Statement> ForStatement::Make(const Context& context, int offset
std::unique_ptr<Expression> test,
std::unique_ptr<Expression> next,
std::unique_ptr<Statement> statement,
std::unique_ptr<LoopUnrollInfo> unrollInfo,
std::shared_ptr<SymbolTable> symbolTable) {
SkASSERT(is_simple_initializer(initializer.get()) ||
is_vardecl_block_initializer(initializer.get()));
SkASSERT(!test || test->type() == *context.fTypes.fBool);
SkASSERT(!context.fConfig->strictES2Mode() ||
Analysis::ForLoopIsValidForES2(offset, initializer.get(), test.get(), next.get(),
statement.get(), /*outLoopInfo=*/nullptr,
/*errors=*/nullptr));

// If the caller didn't provide us with unroll info, we can compute it here if needed.
if (!unrollInfo && context.fConfig->strictES2Mode()) {
unrollInfo = Analysis::GetLoopUnrollInfo(offset, initializer.get(), test.get(),
next.get(), statement.get(), /*errors=*/nullptr);
SkASSERT(unrollInfo);
}

return std::make_unique<ForStatement>(offset, std::move(initializer), std::move(test),
std::move(next), std::move(statement),
std::move(symbolTable));
std::move(unrollInfo), std::move(symbolTable));
}

} // namespace SkSL
40 changes: 31 additions & 9 deletions src/sksl/ir/SkSLForStatement.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,37 @@

namespace SkSL {

/**
* The unrollability information for an ES2-compatible loop.
*/
struct LoopUnrollInfo {
const Variable* fIndex;
double fStart;
double fDelta;
int fCount;
};

/**
* A 'for' statement.
*/
class ForStatement final : public Statement {
public:
static constexpr Kind kStatementKind = Kind::kFor;

ForStatement(int offset, std::unique_ptr<Statement> initializer,
std::unique_ptr<Expression> test, std::unique_ptr<Expression> next,
std::unique_ptr<Statement> statement, std::shared_ptr<SymbolTable> symbols)
: INHERITED(offset, kStatementKind)
, fSymbolTable(std::move(symbols))
, fInitializer(std::move(initializer))
, fTest(std::move(test))
, fNext(std::move(next))
, fStatement(std::move(statement)) {}
ForStatement(int offset,
std::unique_ptr<Statement> initializer,
std::unique_ptr<Expression> test,
std::unique_ptr<Expression> next,
std::unique_ptr<Statement> statement,
std::unique_ptr<LoopUnrollInfo> unrollInfo,
std::shared_ptr<SymbolTable> symbols)
: INHERITED(offset, kStatementKind)
, fSymbolTable(std::move(symbols))
, fInitializer(std::move(initializer))
, fTest(std::move(test))
, fNext(std::move(next))
, fStatement(std::move(statement))
, fUnrollInfo(std::move(unrollInfo)) {}

// Creates an SkSL for loop; handles type-coercion and uses the ErrorReporter to report errors.
static std::unique_ptr<Statement> Convert(const Context& context, int offset,
Expand All @@ -51,6 +66,7 @@ class ForStatement final : public Statement {
std::unique_ptr<Expression> test,
std::unique_ptr<Expression> next,
std::unique_ptr<Statement> statement,
std::unique_ptr<LoopUnrollInfo> unrollInfo,
std::shared_ptr<SymbolTable> symbolTable);

std::unique_ptr<Statement>& initializer() {
Expand Down Expand Up @@ -89,6 +105,11 @@ class ForStatement final : public Statement {
return fSymbolTable;
}

/** Loop-unroll information is only supported in strict-ES2 code. Null is returned in ES3+. */
const LoopUnrollInfo* unrollInfo() const {
return fUnrollInfo.get();
}

std::unique_ptr<Statement> clone() const override;

String description() const override;
Expand All @@ -99,6 +120,7 @@ class ForStatement final : public Statement {
std::unique_ptr<Expression> fTest;
std::unique_ptr<Expression> fNext;
std::unique_ptr<Statement> fStatement;
std::unique_ptr<LoopUnrollInfo> fUnrollInfo;

using INHERITED = Statement;
};
Expand Down

0 comments on commit 9c975c5

Please sign in to comment.