Migrate VerifyStaticTestsAndExpressions analysis pass out.
A followup CL will add new checks to this analysis pass (specifically, error reporting for never-assigned out params). In preparation for this, the code has been renamed to `DoFinalizationChecks` and migrated out of SkSLAnalysis.cpp and into its own file in sksl/analysis/. No logic is changing in this CL. Change-Id: I7a474269be34350b3adfdd13fc1f384df143fde9 Bug: skia:12867 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/499755 Commit-Queue: John Stiles <johnstiles@google.com> Commit-Queue: Brian Osman <brianosman@google.com> Auto-Submit: John Stiles <johnstiles@google.com> Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
9eb1679b77
commit
03e7350111
@ -83,6 +83,7 @@ skia_sksl_sources = [
|
||||
"$_src/sksl/SkSLUtil.h",
|
||||
"$_src/sksl/analysis/SkSLCanExitWithoutReturningValue.cpp",
|
||||
"$_src/sksl/analysis/SkSLCheckProgramUnrolledSize.cpp",
|
||||
"$_src/sksl/analysis/SkSLFinalizationChecks.cpp",
|
||||
"$_src/sksl/analysis/SkSLGetLoopUnrollInfo.cpp",
|
||||
"$_src/sksl/analysis/SkSLIsConstantExpression.cpp",
|
||||
"$_src/sksl/analysis/SkSLProgramUsage.cpp",
|
||||
|
@ -530,90 +530,6 @@ void Analysis::ValidateIndexingForES2(const ProgramElement& pe, ErrorReporter& e
|
||||
visitor.visitProgramElement(pe);
|
||||
}
|
||||
|
||||
void Analysis::VerifyStaticTestsAndExpressions(const Program& program) {
|
||||
class TestsAndExpressions : public ProgramVisitor {
|
||||
public:
|
||||
TestsAndExpressions(const Context& ctx) : fContext(ctx) {}
|
||||
|
||||
bool visitProgramElement(const ProgramElement& pe) override {
|
||||
if (pe.kind() == ProgramElement::Kind::kGlobalVar) {
|
||||
const VarDeclaration& decl =
|
||||
pe.as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>();
|
||||
|
||||
size_t prevSlotsUsed = fGlobalSlotsUsed;
|
||||
fGlobalSlotsUsed = SkSafeMath::Add(fGlobalSlotsUsed, decl.var().type().slotCount());
|
||||
// To avoid overzealous error reporting, only trigger the error at the first
|
||||
// place where the global limit is exceeded.
|
||||
if (prevSlotsUsed < kVariableSlotLimit && fGlobalSlotsUsed >= kVariableSlotLimit) {
|
||||
fContext.fErrors->error(pe.fLine, "global variable '" + decl.var().name() +
|
||||
"' exceeds the size limit");
|
||||
}
|
||||
}
|
||||
return INHERITED::visitProgramElement(pe);
|
||||
}
|
||||
|
||||
bool visitStatement(const Statement& stmt) override {
|
||||
if (!fContext.fConfig->fSettings.fPermitInvalidStaticTests) {
|
||||
switch (stmt.kind()) {
|
||||
case Statement::Kind::kIf:
|
||||
if (stmt.as<IfStatement>().isStatic()) {
|
||||
fContext.fErrors->error(stmt.fLine, "static if has non-static test");
|
||||
}
|
||||
break;
|
||||
|
||||
case Statement::Kind::kSwitch:
|
||||
if (stmt.as<SwitchStatement>().isStatic()) {
|
||||
fContext.fErrors->error(stmt.fLine,
|
||||
"static switch has non-static test");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return INHERITED::visitStatement(stmt);
|
||||
}
|
||||
|
||||
bool visitExpression(const Expression& expr) override {
|
||||
switch (expr.kind()) {
|
||||
case Expression::Kind::kFunctionCall: {
|
||||
const FunctionDeclaration& decl = expr.as<FunctionCall>().function();
|
||||
if (!decl.isBuiltin() && !decl.definition()) {
|
||||
fContext.fErrors->error(expr.fLine, "function '" + decl.description() +
|
||||
"' is not defined");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Expression::Kind::kExternalFunctionReference:
|
||||
case Expression::Kind::kFunctionReference:
|
||||
case Expression::Kind::kMethodReference:
|
||||
case Expression::Kind::kTypeReference:
|
||||
SkDEBUGFAIL("invalid reference-expr, should have been reported by coerce()");
|
||||
fContext.fErrors->error(expr.fLine, "invalid expression");
|
||||
break;
|
||||
default:
|
||||
if (expr.type().matches(*fContext.fTypes.fInvalid)) {
|
||||
fContext.fErrors->error(expr.fLine, "invalid expression");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return INHERITED::visitExpression(expr);
|
||||
}
|
||||
|
||||
private:
|
||||
using INHERITED = ProgramVisitor;
|
||||
size_t fGlobalSlotsUsed = 0;
|
||||
const Context& fContext;
|
||||
};
|
||||
|
||||
// Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
|
||||
TestsAndExpressions visitor{*program.fContext};
|
||||
for (const std::unique_ptr<ProgramElement>& element : program.fOwnedElements) {
|
||||
visitor.visitProgramElement(*element);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ProgramVisitor
|
||||
|
||||
|
@ -172,10 +172,11 @@ void ValidateIndexingForES2(const ProgramElement& pe, ErrorReporter& errors);
|
||||
bool CanExitWithoutReturningValue(const FunctionDeclaration& funcDecl, const Statement& body);
|
||||
|
||||
/**
|
||||
* Searches for @if/@switch statements that didn't optimize away, or dangling
|
||||
* FunctionReference or TypeReference expressions, and reports them as errors.
|
||||
* Runs at finalization time to perform any last-minute correctness checks:
|
||||
* - Reports @if/@switch statements that didn't optimize away
|
||||
* - Reports dangling FunctionReference or TypeReference expressions
|
||||
*/
|
||||
void VerifyStaticTestsAndExpressions(const Program& program);
|
||||
void DoFinalizationChecks(const Program& program);
|
||||
|
||||
} // namespace Analysis
|
||||
} // namespace SkSL
|
||||
|
@ -585,9 +585,10 @@ bool Compiler::runInliner(const std::vector<std::unique_ptr<ProgramElement>>& el
|
||||
}
|
||||
|
||||
bool Compiler::finalize(Program& program) {
|
||||
// Do a pass looking for @if/@switch statements that didn't optimize away, or dangling
|
||||
// FunctionReference or TypeReference expressions. Report these as errors.
|
||||
Analysis::VerifyStaticTestsAndExpressions(program);
|
||||
// Do one last correctness-check pass. This looks for @if/@switch statements that didn't
|
||||
// optimize away, or dangling FunctionReference or TypeReference expressions, and reports them
|
||||
// as errors.
|
||||
Analysis::DoFinalizationChecks(program);
|
||||
|
||||
// Verify that the program conforms to ES2 limitations.
|
||||
if (fContext->fConfig->strictES2Mode() && this->errorCount() == 0) {
|
||||
|
110
src/sksl/analysis/SkSLFinalizationChecks.cpp
Normal file
110
src/sksl/analysis/SkSLFinalizationChecks.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "include/private/SkSLStatement.h"
|
||||
#include "src/core/SkSafeMath.h"
|
||||
#include "src/sksl/SkSLAnalysis.h"
|
||||
#include "src/sksl/SkSLContext.h"
|
||||
#include "src/sksl/SkSLProgramSettings.h"
|
||||
#include "src/sksl/analysis/SkSLProgramVisitor.h"
|
||||
#include "src/sksl/ir/SkSLFunctionCall.h"
|
||||
#include "src/sksl/ir/SkSLFunctionDeclaration.h"
|
||||
#include "src/sksl/ir/SkSLIfStatement.h"
|
||||
#include "src/sksl/ir/SkSLProgram.h"
|
||||
#include "src/sksl/ir/SkSLSwitchStatement.h"
|
||||
#include "src/sksl/ir/SkSLVarDeclarations.h"
|
||||
|
||||
namespace SkSL {
|
||||
namespace {
|
||||
|
||||
class FinalizationVisitor : public ProgramVisitor {
|
||||
public:
|
||||
FinalizationVisitor(const Context& ctx) : fContext(ctx) {}
|
||||
|
||||
bool visitProgramElement(const ProgramElement& pe) override {
|
||||
if (pe.kind() == ProgramElement::Kind::kGlobalVar) {
|
||||
const VarDeclaration& decl =
|
||||
pe.as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>();
|
||||
|
||||
size_t prevSlotsUsed = fGlobalSlotsUsed;
|
||||
fGlobalSlotsUsed = SkSafeMath::Add(fGlobalSlotsUsed, decl.var().type().slotCount());
|
||||
// To avoid overzealous error reporting, only trigger the error at the first
|
||||
// place where the global limit is exceeded.
|
||||
if (prevSlotsUsed < kVariableSlotLimit && fGlobalSlotsUsed >= kVariableSlotLimit) {
|
||||
fContext.fErrors->error(pe.fLine, "global variable '" + decl.var().name() +
|
||||
"' exceeds the size limit");
|
||||
}
|
||||
}
|
||||
return INHERITED::visitProgramElement(pe);
|
||||
}
|
||||
|
||||
bool visitStatement(const Statement& stmt) override {
|
||||
if (!fContext.fConfig->fSettings.fPermitInvalidStaticTests) {
|
||||
switch (stmt.kind()) {
|
||||
case Statement::Kind::kIf:
|
||||
if (stmt.as<IfStatement>().isStatic()) {
|
||||
fContext.fErrors->error(stmt.fLine, "static if has non-static test");
|
||||
}
|
||||
break;
|
||||
|
||||
case Statement::Kind::kSwitch:
|
||||
if (stmt.as<SwitchStatement>().isStatic()) {
|
||||
fContext.fErrors->error(stmt.fLine,
|
||||
"static switch has non-static test");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return INHERITED::visitStatement(stmt);
|
||||
}
|
||||
|
||||
bool visitExpression(const Expression& expr) override {
|
||||
switch (expr.kind()) {
|
||||
case Expression::Kind::kFunctionCall: {
|
||||
const FunctionDeclaration& decl = expr.as<FunctionCall>().function();
|
||||
if (!decl.isBuiltin() && !decl.definition()) {
|
||||
fContext.fErrors->error(expr.fLine, "function '" + decl.description() +
|
||||
"' is not defined");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Expression::Kind::kExternalFunctionReference:
|
||||
case Expression::Kind::kFunctionReference:
|
||||
case Expression::Kind::kMethodReference:
|
||||
case Expression::Kind::kTypeReference:
|
||||
SkDEBUGFAIL("invalid reference-expr, should have been reported by coerce()");
|
||||
fContext.fErrors->error(expr.fLine, "invalid expression");
|
||||
break;
|
||||
default:
|
||||
if (expr.type().matches(*fContext.fTypes.fInvalid)) {
|
||||
fContext.fErrors->error(expr.fLine, "invalid expression");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return INHERITED::visitExpression(expr);
|
||||
}
|
||||
|
||||
private:
|
||||
using INHERITED = ProgramVisitor;
|
||||
size_t fGlobalSlotsUsed = 0;
|
||||
const Context& fContext;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void Analysis::DoFinalizationChecks(const Program& program) {
|
||||
// Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
|
||||
FinalizationVisitor visitor{*program.fContext};
|
||||
for (const std::unique_ptr<ProgramElement>& element : program.fOwnedElements) {
|
||||
visitor.visitProgramElement(*element);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace SkSL
|
Loading…
Reference in New Issue
Block a user