-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Transforms match into an assignment statement #120614
Conversation
This comment has been minimized.
This comment has been minimized.
f21eda9
to
7a47635
Compare
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
Transforms match into an assignment statement Fixes rust-lang#106459. We should be able to do some similar transformations, like `enum` to `enum`. r? mir-opt
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
Finished benchmarking commit (eea65f2): comparison URL. Overall result: no relevant changes - no action neededBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. @bors rollup=never Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 659.957s -> 661.541s (0.24%) |
7a47635
to
a578b6f
Compare
Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt |
} | ||
|
||
trait SimplifyMatch<'tcx> { | ||
fn simplify( |
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 comment what this function does?
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.
let def_id = body.source.def_id(); | ||
let param_env = tcx.param_env_reveal_all_normalized(def_id); | ||
|
||
let bbs = body.basic_blocks.as_mut(); |
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.
Calling as_mut()
clears the CFG caches unconditionally. Is there a way to delay this invalidation?
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 submitted a separate commit. I invalidate the cache only after modifying the CFG. If the cache is used later in a loop, we need to invalidate it after each modification.
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.
With MirPatch
we don't need to worry about this.
/// goto -> bb5; | ||
/// } | ||
/// ``` | ||
impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { |
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.
IIUC, this transform is a strict superset of SimplifyToIf
, isn't it? Can we only keep this one?
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.
Most of their code can't be shared, and if they were put together, I'd be worried that it would be difficult to maintain later. A major difference is that the otherwise target of SimplifyToIf
is reachable, while SimplifyToExp
is unreachable.
☔ The latest upstream changes (presumably #121036) made this pull request unmergeable. Please resolve the merge conflicts. |
2c51ed1
to
f79fd40
Compare
dec8b9d
to
d47cf55
Compare
Sorry for my late reply. |
755d9e5
to
6d577fc
Compare
This comment has been minimized.
This comment has been minimized.
6d577fc
to
f440737
Compare
Rebased. :> |
@bors r=cjgillot |
☀️ Test successful - checks-actions |
Finished benchmarking commit (211518e): comparison URL. Overall result: ❌ regressions - no action needed@rustbot label: -perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesThis benchmark run did not return any relevant results for this metric. Binary sizeResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 669.664s -> 668.886s (-0.12%) |
let (from, first, second) = bbs.pick3_mut(bb_idx, first, second); | ||
fn int_equal(l: ScalarInt, r: impl Into<u128>, size: Size) -> bool { | ||
l.try_to_int(l.size()).unwrap() | ||
== ScalarInt::try_from_uint(r, size).unwrap().try_to_int(size).unwrap() |
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 is the point of doing the comparison in such a complicated way? Why turn r
into a ScalarInt
and then back into a i128
?
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.
Because SwitchTargets
only saves the value of the corresponding bit value by u128
, it lacks information on bit-width and sign.
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 doesn't need the sign, as I said: ==
is sign-independent, so we can figure out the switch target without knowing the sign. And it has the width, it is determined by the type of the match operand.
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.
Ah, you're right. This code is used to compare signed integers of different widths, such as i8
and i16
. We must add additional conversions for this scenario.
Hmm, it's been just a month, and I have to carefully review the code to respond correctly. It seems I indeed wrote some hard-to-understand code. :>
I'll add some comments. I should also move the unsigned comparison to the front, as I expect this might make the code a bit faster.
&& int_equal(s, second_val, discr_size)) | ||
|| (Some(f) == ScalarInt::try_from_uint(first_val, f.size()) | ||
&& Some(s) | ||
== ScalarInt::try_from_uint(second_val, s.size())) => |
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 is very strange. ==
is sign-independent, so I don't understand why both cases need to be considered here. Furthermore, if the sign mattered, then surely it makes no sense to check they_are_equal_signed || they_are_equal_unsigned
; instead you have to check if they_are_signed { they_are_equal_signed } else { they_are_equal_unsigned }
. Finally, f_c.const_.ty().is_signed() || discr_ty.is_signed()
sounds like you are mixing signed and unsigned values (as in, LHS and RHS can have different sign), which should never happen.
What is going on here?
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 is used to handle conversions such as from enum(u32)
to i32
or from enum(i32)
to u32
:
rust/tests/mir-opt/matches_reduce_branches.rs
Lines 69 to 85 in e752af7
#[repr(u8)] | |
enum EnumAu8 { | |
A = 1, | |
B = 2, | |
} | |
// EMIT_MIR matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff | |
fn match_u8_i16(i: EnumAu8) -> i16 { | |
// CHECK-LABEL: fn match_u8_i16( | |
// CHECK-NOT: switchInt | |
// CHECK: _0 = _3 as i16 (IntToInt); | |
// CHECH: return | |
match i { | |
EnumAu8::A => 1, | |
EnumAu8::B => 2, | |
} | |
} |
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.
Aha... I have no idea what this means. ;)
But it makes no sense to compare things twice here. ==
is entirely based on being equal bitwise, so the sign doesn't matter.
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.
Same as above (different width), corresponding test case:
rust/tests/mir-opt/matches_reduce_branches.rs
Lines 197 to 215 in e752af7
#[repr(i8)] | |
enum EnumAi8 { | |
A = -1, | |
B = 2, | |
C = -3, | |
} | |
// EMIT_MIR matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff | |
fn match_i8_i16(i: EnumAi8) -> i16 { | |
// CHECK-LABEL: fn match_i8_i16( | |
// CHECK-NOT: switchInt | |
// CHECK: _0 = _3 as i16 (IntToInt); | |
// CHECH: return | |
match i { | |
EnumAi8::A => -1, | |
EnumAi8::B => 2, | |
EnumAi8::C => -3, | |
} | |
} |
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 you are comparing both signed and unsigned representation completely disregarding whether the value is actually signed or unsigned. It sounds like you want two cases
- value is signed; then convert everything to
i128
(with sign extension, e.g. viatry_to_int
) and compare there - value is unsigned; the convert everything to
u128
and compare there
But currently you're interpreting the number both ways and then checking if either comparison succeeds. It seems to me that can sometimes lead to blatantly wrong results, e.g. when two numbers are equal unsigned but different after sign extension, and they are actually signed -- you code will treat them as equal, I think?
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.
Ah, I'm fixing it right now.
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.
Fixed in #124122, thanks for pointing it out.
Simplify match based on the cast result of `IntToInt` Continue to complete rust-lang#124150. The condition in rust-lang#120614 is wrong, e.g. `-1i8` cannot be converted to `255i16`. I've rethought the issue and simplified the conditional judgment for a more straightforward approach. The new approach is to check **if the case value after the `IntToInt` conversion equals the target value**. In different types, `IntToInt` uses different casting methods. This rule is as follows: - `i8`/`u8` to `i8`/`u8`: do nothing. - `i8` to `i16`/`u16`: sign extension. - `u8` to `i16`/`u16`: zero extension. - `i16`/`u16` to `i8`/`u8`: truncate to the target size. The previous error was a mix of zext and sext. r? mir-opt
Fixes #106459.
We should be able to do some similar transformations, like
enum
toenum
.r? mir-opt