@@ -41,6 +41,37 @@ using std::string;
41
41
using std::vector;
42
42
43
43
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
+
44
75
int static_sign(const Expr &x) {
45
76
if (is_positive_const(x)) {
46
77
return 1;
@@ -56,6 +87,7 @@ int static_sign(const Expr &x) {
56
87
}
57
88
return 0;
58
89
}
90
+
59
91
} // anonymous namespace
60
92
61
93
const FuncValueBounds &empty_func_value_bounds() {
@@ -1195,6 +1227,15 @@ class Bounds : public IRVisitor {
1195
1227
// else fall thru and continue
1196
1228
}
1197
1229
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
+
1198
1239
if (op->is_intrinsic(Call::abs)) {
1199
1240
Interval a = arg_bounds.get(0);
1200
1241
interval.min = make_zero(t);
@@ -1468,6 +1509,7 @@ class Bounds : public IRVisitor {
1468
1509
}
1469
1510
} else if (op->args.size() == 1 &&
1470
1511
(op->is_intrinsic(Call::round) ||
1512
+ op->is_intrinsic(Call::strict_float) ||
1471
1513
op->name == "ceil_f32" || op->name == "ceil_f64" ||
1472
1514
op->name == "floor_f32" || op->name == "floor_f64" ||
1473
1515
op->name == "exp_f32" || op->name == "exp_f64" ||
@@ -1518,14 +1560,107 @@ class Bounds : public IRVisitor {
1518
1560
}
1519
1561
interval = result;
1520
1562
} 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 );
1526
1568
} 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);
1529
1664
} else if (op->call_type == Call::Halide) {
1530
1665
bounds_of_func(op->name, op->value_index, op->type);
1531
1666
} else {
0 commit comments