Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Static Public Member Functions | List of all members
SkSL::ConstantFolder Class Reference

#include <SkSLConstantFolder.h>

Static Public Member Functions

static bool GetConstantInt (const Expression &value, SKSL_INT *out)
 
static bool GetConstantValue (const Expression &value, double *out)
 
static const ExpressionGetConstantValueForVariable (const Expression &value)
 
static const ExpressionGetConstantValueOrNull (const Expression &value)
 
static bool IsConstantSplat (const Expression &expr, double value)
 
static std::unique_ptr< ExpressionMakeConstantValueForVariable (Position pos, std::unique_ptr< Expression > expr)
 
static std::unique_ptr< ExpressionSimplify (const Context &context, Position pos, const Expression &left, Operator op, const Expression &right, const Type &resultType)
 

Detailed Description

Performs constant folding on IR expressions. This simplifies expressions containing compile-time constants, such as replacing Literal(2) + Literal(2) with Literal(4).

Definition at line 27 of file SkSLConstantFolder.h.

Member Function Documentation

◆ GetConstantInt()

bool SkSL::ConstantFolder::GetConstantInt ( const Expression value,
SKSL_INT out 
)
static

If value is an int literal or const int variable with a known value, returns true and stores the value in out. Otherwise, returns false.

Definition at line 325 of file SkSLConstantFolder.cpp.

325 {
326 const Expression* expr = GetConstantValueForVariable(value);
327 if (!expr->isIntLiteral()) {
328 return false;
329 }
330 *out = expr->as<Literal>().intValue();
331 return true;
332}
static const Expression * GetConstantValueForVariable(const Expression &value)

◆ GetConstantValue()

bool SkSL::ConstantFolder::GetConstantValue ( const Expression value,
double *  out 
)
static

If value is a literal or const scalar variable with a known value, returns true and stores the value in out. Otherwise, returns false.

Definition at line 334 of file SkSLConstantFolder.cpp.

334 {
335 const Expression* expr = GetConstantValueForVariable(value);
336 if (!expr->is<Literal>()) {
337 return false;
338 }
339 *out = expr->as<Literal>().value();
340 return true;
341}
uint8_t value

◆ GetConstantValueForVariable()

const Expression * SkSL::ConstantFolder::GetConstantValueForVariable ( const Expression value)
static

If the expression is a const variable with a known compile-time-constant value, returns that value. If not, returns the original expression as-is.

Definition at line 461 of file SkSLConstantFolder.cpp.

461 {
462 const Expression* expr = GetConstantValueOrNull(inExpr);
463 return expr ? expr : &inExpr;
464}
static const Expression * GetConstantValueOrNull(const Expression &value)

◆ GetConstantValueOrNull()

const Expression * SkSL::ConstantFolder::GetConstantValueOrNull ( const Expression value)
static

If the expression can be replaced by a compile-time-constant value, returns that value. If not, returns null.

Definition at line 440 of file SkSLConstantFolder.cpp.

440 {
441 const Expression* expr = &inExpr;
442 while (expr->is<VariableReference>()) {
443 const VariableReference& varRef = expr->as<VariableReference>();
444 if (varRef.refKind() != VariableRefKind::kRead) {
445 return nullptr;
446 }
447 const Variable& var = *varRef.variable();
448 if (!var.modifierFlags().isConst()) {
449 return nullptr;
450 }
451 expr = var.initialValue();
452 if (!expr) {
453 // Generally, const variables must have initial values. However, function parameters are
454 // an exception; they can be const but won't have an initial value.
455 return nullptr;
456 }
457 }
458 return Analysis::IsCompileTimeConstant(*expr) ? expr : nullptr;
459}
bool IsCompileTimeConstant(const Expression &expr)

◆ IsConstantSplat()

bool SkSL::ConstantFolder::IsConstantSplat ( const Expression expr,
double  value 
)
static

Returns true if the expression contains value in every slot.

Definition at line 354 of file SkSLConstantFolder.cpp.

354 {
355 int numSlots = expr.type().slotCount();
356 for (int index = 0; index < numSlots; ++index) {
357 std::optional<double> slotVal = expr.getConstantValue(index);
358 if (!slotVal.has_value() || *slotVal != value) {
359 return false;
360 }
361 }
362 return true;
363}

◆ MakeConstantValueForVariable()

std::unique_ptr< Expression > SkSL::ConstantFolder::MakeConstantValueForVariable ( Position  pos,
std::unique_ptr< Expression expr 
)
static

If the expression is a const variable with a known compile-time-constant value, returns a clone of that value. If not, returns the original expression as-is.

Definition at line 466 of file SkSLConstantFolder.cpp.

467 {
468 const Expression* expr = GetConstantValueOrNull(*inExpr);
469 return expr ? expr->clone(pos) : std::move(inExpr);
470}
SkPoint pos

◆ Simplify()

std::unique_ptr< Expression > SkSL::ConstantFolder::Simplify ( const Context context,
Position  pos,
const Expression left,
Operator  op,
const Expression right,
const Type resultType 
)
static

Simplifies the binary expression left OP right. Returns null if it can't be simplified.

Definition at line 668 of file SkSLConstantFolder.cpp.

673 {
674 // Replace constant variables with their literal values.
675 const Expression* left = GetConstantValueForVariable(leftExpr);
676 const Expression* right = GetConstantValueForVariable(rightExpr);
677
678 // If this is the assignment operator, and both sides are the same trivial expression, this is
679 // self-assignment (i.e., `var = var`) and can be reduced to just a variable reference (`var`).
680 // This can happen when other parts of the assignment are optimized away.
681 if (op.kind() == Operator::Kind::EQ && Analysis::IsSameExpressionTree(*left, *right)) {
682 return right->clone(pos);
683 }
684
685 // Simplify the expression when both sides are constant Boolean literals.
686 if (left->isBoolLiteral() && right->isBoolLiteral()) {
687 bool leftVal = left->as<Literal>().boolValue();
688 bool rightVal = right->as<Literal>().boolValue();
689 bool result;
690 switch (op.kind()) {
691 case Operator::Kind::LOGICALAND: result = leftVal && rightVal; break;
692 case Operator::Kind::LOGICALOR: result = leftVal || rightVal; break;
693 case Operator::Kind::LOGICALXOR: result = leftVal ^ rightVal; break;
694 case Operator::Kind::EQEQ: result = leftVal == rightVal; break;
695 case Operator::Kind::NEQ: result = leftVal != rightVal; break;
696 default: return nullptr;
697 }
698 return Literal::MakeBool(context, pos, result);
699 }
700
701 // If the left side is a Boolean literal, apply short-circuit optimizations.
702 if (left->isBoolLiteral()) {
703 return short_circuit_boolean(pos, *left, op, *right);
704 }
705
706 // If the right side is a Boolean literal...
707 if (right->isBoolLiteral()) {
708 // ... and the left side has no side effects...
710 // We can reverse the expressions and short-circuit optimizations are still valid.
711 return short_circuit_boolean(pos, *right, op, *left);
712 }
713
714 // We can't use short-circuiting, but we can still optimize away no-op Boolean expressions.
715 return eliminate_no_op_boolean(pos, *left, op, *right);
716 }
717
718 if (op.kind() == Operator::Kind::EQEQ && Analysis::IsSameExpressionTree(*left, *right)) {
719 // With == comparison, if both sides are the same trivial expression, this is self-
720 // comparison and is always true. (We are not concerned with NaN.)
721 return Literal::MakeBool(context, pos, /*value=*/true);
722 }
723
724 if (op.kind() == Operator::Kind::NEQ && Analysis::IsSameExpressionTree(*left, *right)) {
725 // With != comparison, if both sides are the same trivial expression, this is self-
726 // comparison and is always false. (We are not concerned with NaN.)
727 return Literal::MakeBool(context, pos, /*value=*/false);
728 }
729
730 if (error_on_divide_by_zero(context, pos, op, *right)) {
731 return nullptr;
732 }
733
734 // Perform full constant folding when both sides are compile-time constants.
735 const Type& leftType = left->type();
736 const Type& rightType = right->type();
737 bool leftSideIsConstant = Analysis::IsCompileTimeConstant(*left);
738 bool rightSideIsConstant = Analysis::IsCompileTimeConstant(*right);
739
740 if (leftSideIsConstant && rightSideIsConstant) {
741 // Handle pairs of integer literals.
742 if (left->isIntLiteral() && right->isIntLiteral()) {
743 using SKSL_UINT = uint64_t;
744 SKSL_INT leftVal = left->as<Literal>().intValue();
745 SKSL_INT rightVal = right->as<Literal>().intValue();
746
747 // Note that fold_expression returns null if the result would overflow its type.
748 #define RESULT(Op) fold_expression(pos, (SKSL_INT)(leftVal) Op \
749 (SKSL_INT)(rightVal), &resultType)
750 #define URESULT(Op) fold_expression(pos, (SKSL_INT)((SKSL_UINT)(leftVal) Op \
751 (SKSL_UINT)(rightVal)), &resultType)
752 switch (op.kind()) {
753 case Operator::Kind::PLUS: return URESULT(+);
754 case Operator::Kind::MINUS: return URESULT(-);
755 case Operator::Kind::STAR: return URESULT(*);
756 case Operator::Kind::SLASH:
757 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
758 context.fErrors->error(pos, "arithmetic overflow");
759 return nullptr;
760 }
761 return RESULT(/);
762 case Operator::Kind::PERCENT:
763 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
764 context.fErrors->error(pos, "arithmetic overflow");
765 return nullptr;
766 }
767 return RESULT(%);
768 case Operator::Kind::BITWISEAND: return RESULT(&);
769 case Operator::Kind::BITWISEOR: return RESULT(|);
770 case Operator::Kind::BITWISEXOR: return RESULT(^);
771 case Operator::Kind::EQEQ: return RESULT(==);
772 case Operator::Kind::NEQ: return RESULT(!=);
773 case Operator::Kind::GT: return RESULT(>);
774 case Operator::Kind::GTEQ: return RESULT(>=);
775 case Operator::Kind::LT: return RESULT(<);
776 case Operator::Kind::LTEQ: return RESULT(<=);
777 case Operator::Kind::SHL:
778 if (rightVal >= 0 && rightVal <= 31) {
779 // Left-shifting a negative (or really, any signed) value is undefined
780 // behavior in C++, but not in GLSL. Do the shift on unsigned values to avoid
781 // triggering an UBSAN error.
782 return URESULT(<<);
783 }
784 context.fErrors->error(pos, "shift value out of range");
785 return nullptr;
786 case Operator::Kind::SHR:
787 if (rightVal >= 0 && rightVal <= 31) {
788 return RESULT(>>);
789 }
790 context.fErrors->error(pos, "shift value out of range");
791 return nullptr;
792
793 default:
794 return nullptr;
795 }
796 #undef RESULT
797 #undef URESULT
798 }
799
800 // Handle pairs of floating-point literals.
801 if (left->isFloatLiteral() && right->isFloatLiteral()) {
802 SKSL_FLOAT leftVal = left->as<Literal>().floatValue();
803 SKSL_FLOAT rightVal = right->as<Literal>().floatValue();
804
805 #define RESULT(Op) fold_expression(pos, leftVal Op rightVal, &resultType)
806 switch (op.kind()) {
807 case Operator::Kind::PLUS: return RESULT(+);
808 case Operator::Kind::MINUS: return RESULT(-);
809 case Operator::Kind::STAR: return RESULT(*);
810 case Operator::Kind::SLASH: return RESULT(/);
811 case Operator::Kind::EQEQ: return RESULT(==);
812 case Operator::Kind::NEQ: return RESULT(!=);
813 case Operator::Kind::GT: return RESULT(>);
814 case Operator::Kind::GTEQ: return RESULT(>=);
815 case Operator::Kind::LT: return RESULT(<);
816 case Operator::Kind::LTEQ: return RESULT(<=);
817 default: return nullptr;
818 }
819 #undef RESULT
820 }
821
822 // Perform matrix multiplication.
823 if (op.kind() == Operator::Kind::STAR) {
824 if (leftType.isMatrix() && rightType.isMatrix()) {
825 return simplify_matrix_times_matrix(context, pos, *left, *right);
826 }
827 if (leftType.isVector() && rightType.isMatrix()) {
828 return simplify_vector_times_matrix(context, pos, *left, *right);
829 }
830 if (leftType.isMatrix() && rightType.isVector()) {
831 return simplify_matrix_times_vector(context, pos, *left, *right);
832 }
833 }
834
835 // Perform constant folding on pairs of vectors/matrices.
836 if (is_vec_or_mat(leftType) && leftType.matches(rightType)) {
837 return simplify_componentwise(context, pos, *left, op, *right);
838 }
839
840 // Perform constant folding on vectors/matrices against scalars, e.g.: half4(2) + 2
841 if (rightType.isScalar() && is_vec_or_mat(leftType) &&
842 leftType.componentType().matches(rightType)) {
843 return simplify_componentwise(context, pos,
844 *left, op, *splat_scalar(context, *right, left->type()));
845 }
846
847 // Perform constant folding on scalars against vectors/matrices, e.g.: 2 + half4(2)
848 if (leftType.isScalar() && is_vec_or_mat(rightType) &&
849 rightType.componentType().matches(leftType)) {
850 return simplify_componentwise(context, pos,
851 *splat_scalar(context, *left, right->type()), op, *right);
852 }
853
854 // Perform constant folding on pairs of matrices, arrays or structs.
855 if ((leftType.isMatrix() && rightType.isMatrix()) ||
856 (leftType.isArray() && rightType.isArray()) ||
857 (leftType.isStruct() && rightType.isStruct())) {
858 return simplify_constant_equality(context, pos, *left, op, *right);
859 }
860 }
861
862 if (context.fConfig->fSettings.fOptimize) {
863 // If just one side is constant, we might still be able to simplify arithmetic expressions
864 // like `x * 1`, `x *= 1`, `x + 0`, `x * 0`, `0 / x`, etc.
865 if (leftSideIsConstant || rightSideIsConstant) {
866 if (std::unique_ptr<Expression> expr = simplify_arithmetic(context, pos, *left, op,
867 *right, resultType)) {
868 return expr;
869 }
870 }
871
872 // We can simplify some forms of matrix division even when neither side is constant.
873 if (std::unique_ptr<Expression> expr = simplify_matrix_division(context, pos, *left, op,
874 *right, resultType)) {
875 return expr;
876 }
877 }
878
879 // We aren't able to constant-fold.
880 return nullptr;
881}
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
#define RESULT(Op)
#define URESULT(Op)
int64_t SKSL_INT
Definition SkSLDefines.h:16
float SKSL_FLOAT
Definition SkSLDefines.h:17
static std::unique_ptr< Literal > MakeBool(const Context &context, Position pos, bool value)
Definition SkSLLiteral.h:69
GAsyncResult * result
bool IsSameExpressionTree(const Expression &left, const Expression &right)
bool HasSideEffects(const Expression &expr)
static std::unique_ptr< Expression > simplify_matrix_division(const Context &context, Position pos, const Expression &left, Operator op, const Expression &right, const Type &resultType)
static bool error_on_divide_by_zero(const Context &context, Position pos, Operator op, const Expression &right)
static std::unique_ptr< Expression > eliminate_no_op_boolean(Position pos, const Expression &left, Operator op, const Expression &right)
static std::unique_ptr< Expression > simplify_matrix_times_matrix(const Context &context, Position pos, const Expression &left, const Expression &right)
static bool is_vec_or_mat(const Type &type)
static std::unique_ptr< Expression > simplify_matrix_times_vector(const Context &context, Position pos, const Expression &left, const Expression &right)
static std::unique_ptr< Expression > short_circuit_boolean(Position pos, const Expression &left, Operator op, const Expression &right)
static std::unique_ptr< Expression > simplify_constant_equality(const Context &context, Position pos, const Expression &left, Operator op, const Expression &right)
static std::unique_ptr< Expression > simplify_vector_times_matrix(const Context &context, Position pos, const Expression &left, const Expression &right)
static std::unique_ptr< Expression > simplify_componentwise(const Context &context, Position pos, const Expression &left, Operator op, const Expression &right)
static std::unique_ptr< Expression > splat_scalar(const Context &context, const Expression &scalar, const Type &type)
static std::unique_ptr< Expression > simplify_arithmetic(const Context &context, Position pos, const Expression &left, Operator op, const Expression &right, const Type &resultType)

The documentation for this class was generated from the following files: