blob: dbf6da8c681cecdc4e1b8996a0e5c57d581e0a80 [file] [log] [blame]
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkSLAnalysis_DEFINED
#define SkSLAnalysis_DEFINED
#include "include/private/SkSLSampleUsage.h"
#include "src/sksl/SkSLDefines.h"
#include <memory>
namespace SkSL {
class ErrorReporter;
class Expression;
class ForStatement;
class FunctionDeclaration;
class FunctionDefinition;
struct LoadedModule;
struct Program;
class ProgramElement;
class ProgramUsage;
class Statement;
class Variable;
class VariableReference;
enum class VariableRefKind : int8_t;
/**
* Provides utilities for analyzing SkSL statically before it's composed into a full program.
*/
struct Analysis {
static SampleUsage GetSampleUsage(const Program& program, const Variable& fp);
static bool ReferencesBuiltin(const Program& program, int builtin);
static bool ReferencesSampleCoords(const Program& program);
static bool ReferencesFragCoords(const Program& program);
static int NodeCountUpToLimit(const FunctionDefinition& function, int limit);
/**
* Finds unconditional exits from a switch-case. Returns true if this statement unconditionally
* causes an exit from this switch (via continue, break or return).
*/
static bool SwitchCaseContainsUnconditionalExit(Statement& stmt);
/**
* Finds conditional exits from a switch-case. Returns true if this statement contains a
* conditional that wraps a potential exit from the switch (via continue, break or return).
*/
static bool SwitchCaseContainsConditionalExit(Statement& stmt);
static std::unique_ptr<ProgramUsage> GetUsage(const Program& program);
static std::unique_ptr<ProgramUsage> GetUsage(const LoadedModule& module);
static bool StatementWritesToVariable(const Statement& stmt, const Variable& var);
struct AssignmentInfo {
VariableReference* fAssignedVar = nullptr;
};
static bool IsAssignable(Expression& expr, AssignmentInfo* info,
ErrorReporter* errors = nullptr);
// Updates the `refKind` field of exactly one VariableReference inside `expr`.
// `expr` must be `IsAssignable`; returns an error otherwise.
static bool MakeAssignmentExpr(Expression* expr, VariableRefKind kind, ErrorReporter* errors);
// Updates the `refKind` field of every VariableReference found within `expr`.
// `expr` is allowed to have zero, one, or multiple VariableReferences.
static void UpdateRefKind(Expression* expr, VariableRefKind refKind);
// A "trivial" expression is one where we'd feel comfortable cloning it multiple times in
// the code, without worrying about incurring a performance penalty. Examples:
// - true
// - 3.14159265
// - myIntVariable
// - myColor.rgb
// - myArray[123]
// - myStruct.myField
// - half4(0)
//
// Trivial-ness is stackable. Somewhat large expressions can occasionally make the cut:
// - half4(myColor.a)
// - myStruct.myArrayField[7].xyz
static bool IsTrivialExpression(const Expression& expr);
// Returns true if both expression trees are the same. The left side is expected to be an
// lvalue. This only needs to check for trees that can plausibly terminate in a variable, so
// some basic candidates like `FloatLiteral` are missing.
static bool IsSelfAssignment(const Expression& left, const Expression& right);
// Is 'expr' a constant-expression, as defined by GLSL 1.0, section 5.10? A constant expression
// is one of:
//
// - A literal value
// - A global or local variable qualified as 'const', excluding function parameters
// - An expression formed by an operator on operands that are constant expressions, including
// getting an element of a constant vector or a constant matrix, or a field of a constant
// structure
// - A constructor whose arguments are all constant expressions
//
// GLSL (but not SkSL, yet - skbug.com/10835) also provides:
// - A built-in function call whose arguments are all constant expressions, with the exception
// of the texture lookup functions
static bool IsConstantExpression(const Expression& expr);
struct UnrollableLoopInfo {
const Variable* fIndex;
double fStart;
double fDelta;
int fCount;
};
// Ensures that '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(const ForStatement& loop,
UnrollableLoopInfo* outLoopInfo,
ErrorReporter* errors);
static void ValidateIndexingForES2(const ProgramElement& pe, ErrorReporter& errors);
};
/**
* Utility class to visit every element, statement, and expression in an SkSL program IR.
* This is intended for simple analysis and accumulation, where custom visitation behavior is only
* needed for a limited set of expression kinds.
*
* Subclasses should override visitExpression/visitStatement/visitProgramElement as needed and
* intercept elements of interest. They can then invoke the base class's function to visit all
* sub expressions. They can also choose not to call the base function to arrest recursion, or
* implement custom recursion.
*
* The visit functions return a bool that determines how the default implementation recurses. Once
* any visit call returns true, the default behavior stops recursing and propagates true up the
* stack.
*/
template <typename PROG, typename EXPR, typename STMT, typename ELEM>
class TProgramVisitor {
public:
virtual ~TProgramVisitor() = default;
protected:
virtual bool visitExpression(EXPR expression);
virtual bool visitStatement(STMT statement);
virtual bool visitProgramElement(ELEM programElement);
};
// Squelch bogus Clang warning about template vtables: https://bugs.llvm.org/show_bug.cgi?id=18733
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wweak-template-vtables"
#endif
extern template class TProgramVisitor<const Program&, const Expression&,
const Statement&, const ProgramElement&>;
extern template class TProgramVisitor<Program&, Expression&, Statement&, ProgramElement&>;
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
class ProgramVisitor : public TProgramVisitor<const Program&,
const Expression&,
const Statement&,
const ProgramElement&> {
public:
bool visit(const Program& program);
};
using ProgramWriter = TProgramVisitor<Program&, Expression&, Statement&, ProgramElement&>;
} // namespace SkSL
#endif