Skip to content
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

Bug fix for lossless_cast with minor additions #5459

Merged
merged 9 commits into from Dec 14, 2020

Conversation

aankit-ca
Copy link
Contributor

The bug can seen for types where lossless_cast type can represent
cast->value.type() but not cast->type. For eg:

lossless_cast(UInt(16), cast(Int(8), Variable::make(UInt(16), e))) returns
(uint16)e which is incorrect.

The patch also adds lossless_cast of Mod and Ramp expressions.

The bug can seen for types where lossless_cast type can represent
cast->value.type() but not cast->type. For eg:

lossless_cast(UInt(16), cast(Int(8), Variable::make(UInt(16), e))) returns
(uint16)e which is incorrect.

The patch also adds lossless_cast of Mod and Ramp expressions.
@aankit-ca
Copy link
Contributor Author

@dsharletg @abadams PTAL

@@ -566,6 +618,32 @@ Expr lossless_cast(Type t, Expr e) {
return Shuffle::make(vecs, shuf->indices);
}

if (const Mod *mod = e.as<Mod>()) {
if (lossless_cast(t, mod->b).defined()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making some changes for handling negative numbers here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

@dsharletg dsharletg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abadams any opinion on a new internal test vs. a new/modified correctness test? I lean towards a correctness test, especially because lossless_cast is already visible to tests (in IROperator.h).

@@ -438,6 +439,80 @@ Expr const_false(int w) {
return make_zero(UInt(1, w));
}

void check(const Type &t, const Expr &in, const Expr &correct) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename check_lossless_cast ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

<< correct << "\n";
}

void lossless_cast_test() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think our convention would be to put this at the bottom of the file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@aankit-ca
Copy link
Contributor Author

Ping for review!

@@ -2365,4 +2404,83 @@ Expr undef(Type t) {
Internal::Call::PureIntrinsic);
}

namespace Internal {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a general rule, if it can be an external correctness test, it should be, to keep libHalide smaller. I think this code can be.

if (const Mod *mod = e.as<Mod>()) {
Expr val;
if (e.type().is_uint()) {
val = simplify(mod->b + make_const(e.type(), -1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens here and below if mod->b is zero?

val = simplify(mod->b + make_const(e.type(), -1));
} else if (e.type().is_int()) {
// 0 <= a%b < |b|
Type wide_ty = e.type().with_bits(e.type().bits() * 2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the cases where you return cast(t, e), could you just do this:

Expr x = cast(t, e);
if (can_prove(cast(e.type(), x) == e) {
  return x;
}

?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe

if (e.type().can_represent(t.min()) && 
    e.type().can_represent(t.max()) && 
    can_prove(e >= cast(e.type(), t.min()) && 
    e <= cast(e.type(), t.max())) 

Basically I'm wondering if we can do something that appeals to verified code to reason about the bounds of mods and ramps, rather than having to worry about this method being correct. It's hard to get bounds right.

// We can recurse into widening casts.
// We can recurse into widening casts.
if (c->type.can_represent(c->value.type()) &&
t.can_represent(c->value.type())) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can get rid of this second part of the condition, and leave that to the recursion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right

@aankit-ca
Copy link
Contributor Author

I got rid of Ramp and Mod for lossless_cast as per discussion with Dillon.

@alexreinking alexreinking added this to the v12.0.0 milestone Dec 11, 2020
@dsharletg dsharletg merged commit 9fea59f into halide:master Dec 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants