-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle many more intrinsics in Bounds.cpp #7823
Changes from 2 commits
0e64045
fd0b475
0d5b3e4
32eb37d
c788294
afa7feb
1888074
7d958f6
2bcb879
13745f8
a42428f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,17 @@ using std::string; | |
using std::vector; | ||
|
||
namespace { | ||
|
||
Expr widen(Expr a) { | ||
Type result_type = a.type().widen(); | ||
return Cast::make(result_type, std::move(a)); | ||
} | ||
|
||
Expr narrow(Expr a) { | ||
Type result_type = a.type().narrow(); | ||
return Cast::make(result_type, std::move(a)); | ||
} | ||
|
||
int static_sign(const Expr &x) { | ||
if (is_positive_const(x)) { | ||
return 1; | ||
|
@@ -56,6 +67,7 @@ int static_sign(const Expr &x) { | |
} | ||
return 0; | ||
} | ||
|
||
} // anonymous namespace | ||
|
||
const FuncValueBounds &empty_func_value_bounds() { | ||
|
@@ -1468,6 +1480,7 @@ class Bounds : public IRVisitor { | |
} | ||
} else if (op->args.size() == 1 && | ||
(op->is_intrinsic(Call::round) || | ||
op->is_intrinsic(Call::strict_float) || | ||
op->name == "ceil_f32" || op->name == "ceil_f64" || | ||
op->name == "floor_f32" || op->name == "floor_f64" || | ||
op->name == "exp_f32" || op->name == "exp_f64" || | ||
|
@@ -1517,15 +1530,24 @@ class Bounds : public IRVisitor { | |
result.include(arg_bounds.get(i)); | ||
} | ||
interval = result; | ||
} else if (op->is_intrinsic(Call::widen_right_add)) { | ||
Expr add = Add::make(op->args[0], cast(op->args[0].type(), op->args[1])); | ||
add.accept(this); | ||
} else if (op->is_intrinsic(Call::widen_right_sub)) { | ||
Expr sub = Sub::make(op->args[0], cast(op->args[0].type(), op->args[1])); | ||
sub.accept(this); | ||
} else if (op->is_intrinsic(Call::widen_right_mul)) { | ||
Expr mul = Mul::make(op->args[0], cast(op->args[0].type(), op->args[1])); | ||
mul.accept(this); | ||
} else if (op->is_intrinsic(Call::halving_add)) { | ||
// lower_halving_add() uses bitwise tricks that are hard to reason | ||
// about; let's do this instead: | ||
Expr e = narrow((widen(op->args[0]) + widen(op->args[1])) / 2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at the bot failure, I suspect this is trying to widen a 64-bit input There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, well, ok, but this is literally the fallback implementation for it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (I don't know how to make the bitwise op handling robust enough to handle this correctly) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess the right way to handle this is to special-case 64-bit and use the min/max possible There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a branch that (among other things) tacked on bounds inference for many of these intrinsics - I did have to special-case any intrinsic that semantically widens if the arguments are 64 bit, and there were a few that would produce a double-widening so had to be even further special-cases (I think rounding_mul_shift_right lowers to a widening mul followed by a rounding shift right that lowers to a widening add or something like that, so it would double-widen There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
please share! These changes make for much better bounds inference in some cases (esp pipelines with fixed-point math); if your fixes are better than these we should take yours. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need to severely clean it up - that's part of why I never opened a PR. I can try to clean it up and share, might take me a few days unfortunately - I am about to be traveling for a funding thing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even if it's ugly, feel free to put it somewhere I can look at it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the change in Bounds.cpp (note the TODOs I have there are out-of-date): https://github.com/halide/Halide/blob/7a497fd4369f278c16abb9790beabf40514ae22f/src/Bounds.cpp#L1522-#L1546 |
||
e.accept(this); | ||
} else if (op->is_intrinsic(Call::rounding_halving_add)) { | ||
// lower_rounding_halving_add() uses bitwise tricks that are hard to reason | ||
// about; let's do this instead: | ||
Expr e = narrow((widen(op->args[0]) + widen(op->args[1]) + 1) / 2); | ||
e.accept(this); | ||
} else if (op->call_type == Call::PureIntrinsic) { | ||
Expr e = lower_intrinsic(op); | ||
if (e.defined()) { | ||
e.accept(this); | ||
} else { | ||
// Just use the bounds of the type | ||
bounds_of_type(t); | ||
} | ||
} else if (op->call_type == Call::Halide) { | ||
bounds_of_func(op->name, op->value_index, op->type); | ||
} else { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's going to be a merge conflict here because Call::saturating_cast is in the same category. Probably should add it in this PR in case the other one doesn't go in and we revert the u32 -> i32 cast change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I take it back! saturating_cast doesn't belong here.