-
Notifications
You must be signed in to change notification settings - Fork 787
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:
|
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
@@ -570,6 +570,22 @@ struct OptimizeInstructions | |||
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 |
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:
|
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