Skip to content

Commit 4fc2a7d

Browse files
Handle many more intrinsics in Bounds.cpp (#7823)
* Handle many more intrinsics in Bounds.cpp This addresses many (but not all) of the `signed integer overflow` issues we're seeing in Google due to #7814 -- a lot of the issues seems to be in code that uses intrinsics that had no handling in value bounds checking, so the bounds were naively large and overflowed. - Most of the intrinsics from FindIntrinsics.h weren't handled; now they all are (most by lowering to other IR, though the halving_add variants were modeled directly because the bitwise ops don't mesh well) - strict_float() is just a pass-through - round() is a best guess (basically, if bounds exist, expand by one as a worst-case) There are definitely others we should handle here... trunc/floor/ceil probably? * Fix round() and strict_float() handling * Update Bounds.cpp * Fixes? * trigger buildbots * Revert saturating_cast handling * Update Bounds.cpp --------- Co-authored-by: Andrew Adams <[email protected]>
1 parent 3136819 commit 4fc2a7d

File tree

2 files changed

+143
-7
lines changed

2 files changed

+143
-7
lines changed

src/Bounds.cpp

+142-7
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,37 @@ using std::string;
4141
using std::vector;
4242

4343
namespace {
44+
45+
bool can_widen(const Expr &e) {
46+
// We don't want to widen Xtensa 48-bit integers
47+
return e.type().bits() <= 32;
48+
}
49+
50+
bool can_widen_all(const std::vector<Expr> &args) {
51+
for (const auto &e : args) {
52+
if (!can_widen(e)) {
53+
return false;
54+
}
55+
}
56+
return true;
57+
}
58+
59+
Expr widen(Expr a) {
60+
internal_assert(can_widen(a));
61+
Type result_type = a.type().widen();
62+
return Cast::make(result_type, std::move(a));
63+
}
64+
65+
Expr narrow(Expr a) {
66+
Type result_type = a.type().narrow();
67+
return Cast::make(result_type, std::move(a));
68+
}
69+
70+
Expr saturating_narrow(const Expr &a) {
71+
Type narrow = a.type().narrow();
72+
return saturating_cast(narrow, a);
73+
}
74+
4475
int static_sign(const Expr &x) {
4576
if (is_positive_const(x)) {
4677
return 1;
@@ -56,6 +87,7 @@ int static_sign(const Expr &x) {
5687
}
5788
return 0;
5889
}
90+
5991
} // anonymous namespace
6092

6193
const FuncValueBounds &empty_func_value_bounds() {
@@ -1195,6 +1227,15 @@ class Bounds : public IRVisitor {
11951227
// else fall thru and continue
11961228
}
11971229

1230+
const auto handle_expr_bounds = [this, t](const Expr &e) -> void {
1231+
if (e.defined()) {
1232+
e.accept(this);
1233+
} else {
1234+
// Just use the bounds of the type
1235+
this->bounds_of_type(t);
1236+
}
1237+
};
1238+
11981239
if (op->is_intrinsic(Call::abs)) {
11991240
Interval a = arg_bounds.get(0);
12001241
interval.min = make_zero(t);
@@ -1468,6 +1509,7 @@ class Bounds : public IRVisitor {
14681509
}
14691510
} else if (op->args.size() == 1 &&
14701511
(op->is_intrinsic(Call::round) ||
1512+
op->is_intrinsic(Call::strict_float) ||
14711513
op->name == "ceil_f32" || op->name == "ceil_f64" ||
14721514
op->name == "floor_f32" || op->name == "floor_f64" ||
14731515
op->name == "exp_f32" || op->name == "exp_f64" ||
@@ -1518,14 +1560,107 @@ class Bounds : public IRVisitor {
15181560
}
15191561
interval = result;
15201562
} else if (op->is_intrinsic(Call::widen_right_add)) {
1521-
Expr add = Add::make(op->args[0], cast(op->args[0].type(), op->args[1]));
1522-
add.accept(this);
1523-
} else if (op->is_intrinsic(Call::widen_right_sub)) {
1524-
Expr sub = Sub::make(op->args[0], cast(op->args[0].type(), op->args[1]));
1525-
sub.accept(this);
1563+
internal_assert(op->args.size() == 2);
1564+
Expr e = can_widen(op->args[1]) ?
1565+
lower_widen_right_add(op->args[0], op->args[1]) :
1566+
Expr();
1567+
handle_expr_bounds(e);
15261568
} else if (op->is_intrinsic(Call::widen_right_mul)) {
1527-
Expr mul = Mul::make(op->args[0], cast(op->args[0].type(), op->args[1]));
1528-
mul.accept(this);
1569+
internal_assert(op->args.size() == 2);
1570+
Expr e = can_widen(op->args[1]) ?
1571+
lower_widen_right_mul(op->args[0], op->args[1]) :
1572+
Expr();
1573+
handle_expr_bounds(e);
1574+
} else if (op->is_intrinsic(Call::widen_right_sub)) {
1575+
internal_assert(op->args.size() == 2);
1576+
Expr e = can_widen(op->args[1]) ?
1577+
lower_widen_right_sub(op->args[0], op->args[1]) :
1578+
Expr();
1579+
handle_expr_bounds(e);
1580+
} else if (op->is_intrinsic(Call::widening_add)) {
1581+
internal_assert(op->args.size() == 2);
1582+
Expr e = can_widen_all(op->args) ?
1583+
lower_widening_add(op->args[0], op->args[1]) :
1584+
Expr();
1585+
handle_expr_bounds(e);
1586+
} else if (op->is_intrinsic(Call::widening_mul)) {
1587+
internal_assert(op->args.size() == 2);
1588+
Expr e = can_widen_all(op->args) ?
1589+
lower_widening_mul(op->args[0], op->args[1]) :
1590+
Expr();
1591+
handle_expr_bounds(e);
1592+
} else if (op->is_intrinsic(Call::widening_sub)) {
1593+
internal_assert(op->args.size() == 2);
1594+
Expr e = can_widen_all(op->args) ?
1595+
lower_widening_sub(op->args[0], op->args[1]) :
1596+
Expr();
1597+
handle_expr_bounds(e);
1598+
} else if (op->is_intrinsic(Call::saturating_add)) {
1599+
internal_assert(op->args.size() == 2);
1600+
Expr e = can_widen_all(op->args) ?
1601+
narrow(clamp(widen(op->args[0]) + widen(op->args[1]), t.min(), t.max())) :
1602+
Expr();
1603+
handle_expr_bounds(e);
1604+
} else if (op->is_intrinsic(Call::saturating_sub)) {
1605+
internal_assert(op->args.size() == 2);
1606+
Expr e = can_widen_all(op->args) ?
1607+
narrow(clamp(widen(op->args[0]) - widen(op->args[1]), t.min(), t.max())) :
1608+
Expr();
1609+
handle_expr_bounds(e);
1610+
} else if (op->is_intrinsic(Call::widening_shift_left)) {
1611+
internal_assert(op->args.size() == 2);
1612+
Expr e = can_widen(op->args[0]) ?
1613+
lower_widening_shift_left(op->args[0], op->args[1]) :
1614+
Expr();
1615+
handle_expr_bounds(e);
1616+
} else if (op->is_intrinsic(Call::widening_shift_right)) {
1617+
internal_assert(op->args.size() == 2);
1618+
Expr e = can_widen(op->args[0]) ?
1619+
lower_widening_shift_right(op->args[0], op->args[1]) :
1620+
Expr();
1621+
handle_expr_bounds(e);
1622+
} else if (op->is_intrinsic(Call::rounding_shift_right)) {
1623+
internal_assert(op->args.size() == 2);
1624+
// TODO: uses bitwise ops we may not handle well
1625+
handle_expr_bounds(lower_rounding_shift_right(op->args[0], op->args[1]));
1626+
} else if (op->is_intrinsic(Call::rounding_shift_left)) {
1627+
internal_assert(op->args.size() == 2);
1628+
// TODO: uses bitwise ops we may not handle well
1629+
handle_expr_bounds(lower_rounding_shift_left(op->args[0], op->args[1]));
1630+
} else if (op->is_intrinsic(Call::halving_add)) {
1631+
internal_assert(op->args.size() == 2);
1632+
Expr e = can_widen_all(op->args) ?
1633+
narrow((widen(op->args[0]) + widen(op->args[1])) / 2) :
1634+
Expr();
1635+
handle_expr_bounds(e);
1636+
} else if (op->is_intrinsic(Call::halving_sub)) {
1637+
internal_assert(op->args.size() == 2);
1638+
Expr e = can_widen_all(op->args) ?
1639+
narrow((widen(op->args[0]) - widen(op->args[1])) / 2) :
1640+
Expr();
1641+
handle_expr_bounds(e);
1642+
} else if (op->is_intrinsic(Call::rounding_halving_add)) {
1643+
internal_assert(op->args.size() == 2);
1644+
Expr e = can_widen_all(op->args) ?
1645+
narrow((widen(op->args[0]) + widen(op->args[1]) + 1) / 2) :
1646+
Expr();
1647+
handle_expr_bounds(e);
1648+
} else if (op->is_intrinsic(Call::rounding_mul_shift_right)) {
1649+
internal_assert(op->args.size() == 3);
1650+
Expr e = can_widen_all(op->args) ?
1651+
saturating_narrow(rounding_shift_right(widening_mul(op->args[0], op->args[1]), op->args[2])) :
1652+
Expr();
1653+
handle_expr_bounds(e);
1654+
} else if (op->is_intrinsic(Call::mul_shift_right)) {
1655+
internal_assert(op->args.size() == 3);
1656+
Expr e = can_widen_all(op->args) ?
1657+
saturating_narrow(widening_mul(op->args[0], op->args[1]) >> op->args[2]) :
1658+
Expr();
1659+
handle_expr_bounds(e);
1660+
} else if (op->is_intrinsic(Call::sorted_avg)) {
1661+
internal_assert(op->args.size() == 2);
1662+
Expr e = lower_sorted_avg(op->args[0], op->args[1]);
1663+
handle_expr_bounds(e);
15291664
} else if (op->call_type == Call::Halide) {
15301665
bounds_of_func(op->name, op->value_index, op->type);
15311666
} else {

src/FindIntrinsics.h

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Expr lower_saturating_cast(const Type &t, const Expr &a);
3030
Expr lower_halving_add(const Expr &a, const Expr &b);
3131
Expr lower_halving_sub(const Expr &a, const Expr &b);
3232
Expr lower_rounding_halving_add(const Expr &a, const Expr &b);
33+
Expr lower_sorted_avg(const Expr &a, const Expr &b);
3334

3435
Expr lower_mul_shift_right(const Expr &a, const Expr &b, const Expr &q);
3536
Expr lower_rounding_mul_shift_right(const Expr &a, const Expr &b, const Expr &q);

0 commit comments

Comments
 (0)