-
Notifications
You must be signed in to change notification settings - Fork 51
Comparison patterns for PPC #289
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
base: master
Are you sure you want to change the base?
Conversation
I'll add a comment about the version info and flags. |
That's just a case of "try all the patterns one at a time", first fold_divmod, then replace_clz_shift, then (newly added) replace_or_shift. The way you've added it looks good to me. |
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. Happy to merge this now and do the other comparisons in a follow-up if you feel like it, but also ok with awaiting those
Oh, right. Should I leave it like this then or can I chain it one more time? I think the other new patterns will go to the same place, so I'm not sure. |
I won't take long, so I'd like to do it here to avoid cluttering PRs |
Co-authored-by: Simon Lindholm <simon.lindholm10@gmail.com>
Either way is fine. Probably best to be consistent, i.e. either lower_bits = replace_or_shift(replace_clz_shift(fold_divmod(lower_bits))) or lower_bits = fold_divmod(lower_bits)
lower_bits = replace_clz_shift(lower_bits)
lower_bits = replace_or_shift(lower_bits) Maybe the latter is a bit more readable? |
Yes, especially if considering that I'll have to add more. |
a < b and b > a generate the exact same code, I wonder which one I should go for. |
Hmm, if you look at a <= b in the comparison2 test, it's decompiled perfectly. But a >= b has casts, even though it's the exact same thing in the asm. This is how the expressions look like: Do you think we can do something about that safely? |
m2c/evaluate.py
Outdated
left_expr = early_unwrap_ints(expr.left) | ||
if not (isinstance(left_expr, BinaryOp) and left_expr.op == "+"): | ||
return expr | ||
# We call this function only from carry_add_to so it's not necessary to check the right side |
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.
Now that I think about it, the expression might be changed by fold_divmod by this point. I think I should either change the order or just add an extra check here.
Btw, it would be worth adding a test for unsigned comparisons as well. |
Oh wow, that generates code that uses an instruction that's not even implemented yet: orc. I'll take a look at the patterns from the old comparison test and leave the orc and the pattern using it for other PRs. |
Actually, let's look at the patterns from the old comparison test some other time. Looking forward to your feedback to merge this :) |
I'll take a look tomorrow! |
Thanks! I just had this idea: if we know that a certain pattern is only generated if the args are u32 for example, can't we use this information to guess the types better? 🤔 |
Yes, that would be ideal! Comparisons have different semantics between signed and unsigned so it's natural that they would give different codegen, and it's certainly useful to expose the types to the user. |
May I ask how to do that? :D Though the problem is that in the a < b case we don't know which one from the two is u32. It generates this if either of them is u32. |
I feel like
|
I ran this through the whole Mario Party 4 decomp and these new patterns caused 30 files to be changed. Only one of them is a false positive, but that function is a mess anyways. I noticed that the order in a < b actually matters when matching, because of the order the values are loaded in, we've sadly lost this info by the point we handle the expression as a whole. |
I would suggest using as_uintish() on both operands, which will try to coerce them both to unsigned, falling back to a cast if it fails. It might be wrong (as you say only one has to be u32 for C to auto-convert to u32) but it still feels like a better guess than nothing at all?
Well, sure, sounds good to me. Do you want to do it in the same PR or another? (I'm already taking too long to get around to reviewing this one I feel like...) |
Actually, for comparisons specifically you can use BinaryOp.ucmp() for uint compares, which is shorthand for doing as_uintish() on each argument. |
|
||
def replace_shift_add_carry(expr: BinaryOp) -> BinaryOp: | ||
""" | ||
Simplify the expressions matching `((b >> 31) + (a >> 31U)) + M2C_CARRY` |
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.
(Hm, I wish M2C_CARRY expressions kept the addition/subtraction that resulted in the carry, it's tricky to understand why the pattern makes sense right now)
I added a test case for how MWCC 1.3.2/2.6 handles a != 0 and a != b and code to handle it. In this PR I plan to fix all possible combinations from the previous and new test.
Feel free to give feedback to the commits in the meantime! As I wrote on Discord, I'm still learning when to use
early_unwrap_ints
overearly_unwrap
. I have a TODO comment and I'm also not sure why the input ofreplace_clz_shift
is first passed tofold_divmod
, should we do that here too?