-
Notifications
You must be signed in to change notification settings - Fork 830
Simplify signed reminders which compared with zero #3153
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
Conversation
|
Fuzzed: |
tlively
left a comment
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.
Why do you need the eqz version of this pattern? It looks like it doesn't do anything that the binary version doesn't already do.
src/passes/OptimizeInstructions.cpp
Outdated
| return ret; | ||
| } | ||
| } else if (auto* unary = curr->dynCast<Unary>()) { | ||
| if (unary->op == EqZInt32 || unary->op == EqZInt64) { |
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.
Could you move this up to the top of this function with the other matcher-based transformations? The more we can avoid putting new logic in this highly-nested part of the function, the better. It would also be good to start the match with the top-level expression, even though we already know it is a unary expression. Moving this will also let you use the binary matcher without the Match:: prefix.
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 decide use slightly different approach due to input wasm could contain mixed eqz and x == 0 operations and due to x == 0 --> eqz x transform apply in early stage exactly after canonicalization I guess better just remove x == 0 matching and always check only x != 0. So x == 0 --> eqz x rule may be make sense even move to canonicalization routine.
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 still think it would be good to move these up to where the other matcher-based transformations are. It's ok if you have to move other code as well to make it work out. I would also be willing to do a non-functional change to port most of this code to use matchers first if that would make it easier for you.
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.
Done!
src/passes/OptimizeInstructions.cpp
Outdated
| using namespace Match; | ||
| Const* c; | ||
| Binary* inner; | ||
| Expression* left; |
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.
It looks like left is only used to get the child type of the binary expression. You should be able to save a variable by using c->type for that instead.
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.
Done!
src/passes/OptimizeInstructions.cpp
Outdated
| // eqz((signed)x % C_pot) ==> eqz(x & (C_pot - 1)) | ||
| if (matches(unary->value, | ||
| Match::binary( | ||
| &inner, Abstract::RemS, any(&left), constant(&c)))) { |
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.
You could use ival instead of constant to make this more compact.
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.
It will be impossible after this I guess
Initially it was without |
|
refuzzed |
|
same issue with CI as yesterday |
tlively
left a comment
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.
Looks good with these final changes applied!
src/passes/OptimizeInstructions.cpp
Outdated
| if (IsPowerOf2((uint64_t)c->value.getInteger())) { | ||
| inner->op = Abstract::getBinary(c->type, Abstract::And); | ||
| c->value = c->value.sub(Literal::makeFromInt32(1, c->type)); | ||
| return eqz; |
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.
This can just be return curr; and the eqz variable can be removed. Also you can use ival instead of constant and rename inner to bin to make the pattern more compact.
src/passes/OptimizeInstructions.cpp
Outdated
| // (signed)x % C_pot != 0 ==> x & (C_pot - 1) != 0 | ||
| if (matches(curr, | ||
| binary(Abstract::Ne, | ||
| binary(&inner, Abstract::RemS, any(&left), constant(&c)), |
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.
| binary(&inner, Abstract::RemS, any(&left), constant(&c)), | |
| binary(&inner, Abstract::RemS, any(&left), ival()), |
You actually don't need c here because it is already captured in right.
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
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.
Hmm, I got regression:
https://github.com/WebAssembly/binaryen/runs/1184925427#step:15:6398
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.
Yes, right and c are different constants. right is outer constant and c is inner for this expression:
(signed)x % c != 0 ==> x & (c - 1) != 0
where 0 is right.
So I reverted changes)
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.
Oh right, my bad!
Co-authored-by: Thomas Lively <7121787+tlively@users.noreply.github.com>
src/passes/OptimizeInstructions.cpp
Outdated
| if (matches( | ||
| curr, | ||
| binary(Abstract::Ne, | ||
| binary(&inner, Abstract::RemS, any(&left), constant(&c)), |
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.
constant can still be renamed ival here for compactness. In general, other than removing c, I think all the comments still apply.
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.
But I should modify this constant's value as:
c->value = c->value.sub(Literal::makeFromInt32(1, left->type));How I can do this without access to Const* expression?
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.
ival(&c) should work fine. constant is for when the constant can be either an integer or a floating point constant.
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 just wondering how about this approach:
int64_t c;
Binary* inner;
if (matches(
curr,
binary(Abstract::Ne,
binary(&inner, Abstract::RemS, any(&left), ival(&c)),
ival(0)))) {
if (IsPowerOf2((uint64_t)c)) {
if (left->type == Type::i64) {
optimizePowerOf2URem(inner, (uint64_t)c);
} else {
optimizePowerOf2URem(inner, (uint32_t)c);
}
return curr;
}
}Is it make sense?
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.
constant is for when the constant can be either an integer or a floating point constant.
I see. Thanks!
I refactored
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.
What about rename constant to val or xval / cval?
i32()
i64()
f32()
f64()
ival() ;; i32() | i64()
fval() ;; f32() | f64()
val() ;; ival() | fval()|
Fast refuzzing stats: |
tlively
left a comment
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.
Thanks, this looks great!
| if (matches(curr, | ||
| unary(Abstract::EqZ, | ||
| binary(&inner, Abstract::RemS, any(), ival(&c)))) && | ||
| IsPowerOf2((uint64_t)c->value.getInteger())) { |
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.
why is the cast to uint64_t needed?
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.
IsPowerOf2 allow only unsigned types but getInteger() return int64_t
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.
Btw negative dividers like x % -4 == 0 currently rejected and not optimized which is ok but it seems it could be handle as (x & 3) == 0 as well.
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.
Wouldn't it work without the cast, then? The function is
template<typename T> bool isPowerOf2(T v) {
return v != 0 && (v & (v - 1)) == 0;
}which just cares about bits anyhow. There should be no compiler warning or error here? Maybe I'm not sure what you mean by "allow only".
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'm not sure signed type does make sense for isPowerOf2 due to negative values always return false. But you're right casting to uint64_t here unnecessary.
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.
Logically, I agree, it's odd to ask about a negative number. But we just have bits here, really. A Literal just stores the bits, without a sign (just like wasm and LLVM IR, etc.).
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'll create PR which handle also negative dividers: x % -C_pot == 0 -> (x & (abs(C_pot) - 1)) == 0 and remove this cast as well
Implement a more general (additional) version of #3153 which also handles negative constant divisors: `(int32)x % -4 == 0` --> `(x & 3) == 0` `x % -C_pot == 0` --> `(x & (abs(C_pot) - 1)) == 0` and special two-complement values as well: `(int32)x % 0x80000000 == 0` --> `(x & 0x7fffffff) == 0` `(int64)x % 0x8000000000000000 == 0` --> `(x & 0x7fffffffffffffff) == 0` as separete rules: `(int32)x % 0x80000000` --> `x & 0x7fffffff` `(int64)x % 0x8000000000000000` --> `x & 0x7fffffffffffffff` The [previous pr](#3153) didn't use these possibilities.
eqz((signed)x % C_pot)->eqz(x & (C_pot - 1))(signed)x % C_pot == 0->x & (C_pot - 1) == 0(signed)x % C_pot != 0->x & (C_pot - 1) != 0