diff --git a/src/IROperator.cpp b/src/IROperator.cpp index ffd33122d741..a4007c7a8762 100644 --- a/src/IROperator.cpp +++ b/src/IROperator.cpp @@ -129,7 +129,8 @@ bool is_const(const Expr &e, int64_t value) { } else if (const FloatImm *i = e.as()) { return i->value == value; } else if (const Cast *c = e.as()) { - return is_const(c->value, value); + return c->type.can_represent(value) && + is_const(c->value, value); } else if (const Broadcast *b = e.as()) { return is_const(b->value, value); } else { diff --git a/test/correctness/solve.cpp b/test/correctness/solve.cpp index b356d4a1bfe5..69d2c72051a4 100644 --- a/test/correctness/solve.cpp +++ b/test/correctness/solve.cpp @@ -164,6 +164,28 @@ void test_positive_const_multiplier_still_rewritten() { (x != 2) || (Mod::make(seven, three) != 0)); } +// IROperator::is_const(e, -1) used to ignore signedness in casts. When `c` +// was a cast from `-1` to uint8, Solve.cpp's `f(x) * c cmp b` rewrite for +// `c == -1` treated `c` as -1 rather than the correct value (255). +void test_cast_signedness_respected() { + // int32(uint8(-1_i8)) == 255, not -1. + Expr fake_negative_one = Cast::make(Int(32), Cast::make(UInt(8), -1)); + + // Let N = fake_negative_one. + // Broken: y < N * x ~> x < -y ~> 1 < -100 ~> false + // Fixed: y < N * x ~> x * N > y ~> 1 * 255 > 100 ~> true + std::map vars{ + {"x", Expr(1)}, + {"y", Expr(100)}, + }; + check_solve_equivalent(y < fake_negative_one * x, vars); + check_solve_equivalent(y <= fake_negative_one * x, vars); + check_solve_equivalent(y > fake_negative_one * x, vars); + check_solve_equivalent(y >= fake_negative_one * x, vars); + check_solve_equivalent(y == fake_negative_one * x, vars); + check_solve_equivalent(y != fake_negative_one * x, vars); +} + // Solver used to rewrite `f(x) + f(x) -> f(x) * 2` via `operator*(Expr, int)`, // which rejects constants that don't fit in the expression type. For UInt(1), // the literal 2 isn't representable, aborting the whole solve. Use Mul::make @@ -610,6 +632,7 @@ int main(int argc, char **argv) { test_unsigned_equality_still_rearranged(); test_nonconstant_multiplier_not_rewritten(); test_positive_const_multiplier_still_rewritten(); + test_cast_signedness_respected(); test_solve_does_not_abort_on_narrow_self_add(); test_narrow_div_add_equivalence(); test_simplify_preserves_float_to_uint_cast_chain();