in src/sksl/SkSLConstantFolder.cpp [668:808]
static std::unique_ptr<Expression> fold_two_constants(const Context& context,
Position pos,
const Expression* left,
Operator op,
const Expression* right,
const Type& resultType) {
SkASSERT(Analysis::IsCompileTimeConstant(*left));
SkASSERT(Analysis::IsCompileTimeConstant(*right));
const Type& leftType = left->type();
const Type& rightType = right->type();
// Handle pairs of integer literals.
if (left->isIntLiteral() && right->isIntLiteral()) {
using SKSL_UINT = uint64_t;
SKSL_INT leftVal = left->as<Literal>().intValue();
SKSL_INT rightVal = right->as<Literal>().intValue();
// Note that fold_expression returns null if the result would overflow its type.
#define RESULT(Op) fold_expression(pos, (SKSL_INT)(leftVal) Op \
(SKSL_INT)(rightVal), &resultType)
#define URESULT(Op) fold_expression(pos, (SKSL_INT)((SKSL_UINT)(leftVal) Op \
(SKSL_UINT)(rightVal)), &resultType)
switch (op.kind()) {
case Operator::Kind::PLUS: return URESULT(+);
case Operator::Kind::MINUS: return URESULT(-);
case Operator::Kind::STAR: return URESULT(*);
case Operator::Kind::SLASH:
if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
context.fErrors->error(pos, "arithmetic overflow");
return nullptr;
}
return RESULT(/);
case Operator::Kind::PERCENT:
if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
context.fErrors->error(pos, "arithmetic overflow");
return nullptr;
}
return RESULT(%);
case Operator::Kind::BITWISEAND: return RESULT(&);
case Operator::Kind::BITWISEOR: return RESULT(|);
case Operator::Kind::BITWISEXOR: return RESULT(^);
case Operator::Kind::EQEQ: return RESULT(==);
case Operator::Kind::NEQ: return RESULT(!=);
case Operator::Kind::GT: return RESULT(>);
case Operator::Kind::GTEQ: return RESULT(>=);
case Operator::Kind::LT: return RESULT(<);
case Operator::Kind::LTEQ: return RESULT(<=);
case Operator::Kind::SHL:
if (rightVal >= 0 && rightVal <= 31) {
// Left-shifting a negative (or really, any signed) value is undefined behavior
// in C++, but not in GLSL. Do the shift on unsigned values to avoid triggering
// an UBSAN error.
return URESULT(<<);
}
context.fErrors->error(pos, "shift value out of range");
return nullptr;
case Operator::Kind::SHR:
if (rightVal >= 0 && rightVal <= 31) {
return RESULT(>>);
}
context.fErrors->error(pos, "shift value out of range");
return nullptr;
default:
break;
}
#undef RESULT
#undef URESULT
return nullptr;
}
// Handle pairs of floating-point literals.
if (left->isFloatLiteral() && right->isFloatLiteral()) {
SKSL_FLOAT leftVal = left->as<Literal>().floatValue();
SKSL_FLOAT rightVal = right->as<Literal>().floatValue();
#define RESULT(Op) fold_expression(pos, leftVal Op rightVal, &resultType)
switch (op.kind()) {
case Operator::Kind::PLUS: return RESULT(+);
case Operator::Kind::MINUS: return RESULT(-);
case Operator::Kind::STAR: return RESULT(*);
case Operator::Kind::SLASH: return RESULT(/);
case Operator::Kind::EQEQ: return RESULT(==);
case Operator::Kind::NEQ: return RESULT(!=);
case Operator::Kind::GT: return RESULT(>);
case Operator::Kind::GTEQ: return RESULT(>=);
case Operator::Kind::LT: return RESULT(<);
case Operator::Kind::LTEQ: return RESULT(<=);
default: break;
}
#undef RESULT
return nullptr;
}
// Perform matrix multiplication.
if (op.kind() == Operator::Kind::STAR) {
if (leftType.isMatrix() && rightType.isMatrix()) {
return simplify_matrix_times_matrix(context, pos, *left, *right);
}
if (leftType.isVector() && rightType.isMatrix()) {
return simplify_vector_times_matrix(context, pos, *left, *right);
}
if (leftType.isMatrix() && rightType.isVector()) {
return simplify_matrix_times_vector(context, pos, *left, *right);
}
}
// Perform constant folding on pairs of vectors/matrices.
if (is_vec_or_mat(leftType) && leftType.matches(rightType)) {
return simplify_componentwise(context, pos, *left, op, *right);
}
// Perform constant folding on vectors/matrices against scalars, e.g.: half4(2) + 2
if (rightType.isScalar() && is_vec_or_mat(leftType) &&
leftType.componentType().matches(rightType)) {
return simplify_componentwise(context, pos,
*left, op, *splat_scalar(context, *right, left->type()));
}
// Perform constant folding on scalars against vectors/matrices, e.g.: 2 + half4(2)
if (leftType.isScalar() && is_vec_or_mat(rightType) &&
rightType.componentType().matches(leftType)) {
return simplify_componentwise(context, pos,
*splat_scalar(context, *left, right->type()), op, *right);
}
// Perform constant folding on pairs of matrices, arrays or structs.
if ((leftType.isMatrix() && rightType.isMatrix()) ||
(leftType.isArray() && rightType.isArray()) ||
(leftType.isStruct() && rightType.isStruct())) {
return simplify_constant_equality(context, pos, *left, op, *right);
}
// We aren't able to constant-fold these expressions.
return nullptr;
}