static std::unique_ptr fold_two_constants()

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;
}