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

Transforms match into an assignment statement #120614

Merged
merged 7 commits into from
Apr 8, 2024

Conversation

DianQK
Copy link
Member

@DianQK DianQK commented Feb 3, 2024

Fixes #106459.

We should be able to do some similar transformations, like enum to enum.

r? mir-opt

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 3, 2024
@rust-log-analyzer

This comment has been minimized.

@saethlin
Copy link
Member

saethlin commented Feb 4, 2024

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Feb 4, 2024
bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 4, 2024
 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
@bors
Copy link
Contributor

bors commented Feb 4, 2024

⌛ Trying commit 7a47635 with merge eea65f2...

@bors
Copy link
Contributor

bors commented Feb 4, 2024

☀️ Try build successful - checks-actions
Build commit: eea65f2 (eea65f2f4affd04b3b0fd5127c7e0951a2747136)

@rust-timer

This comment has been minimized.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (eea65f2): comparison URL.

Overall result: no relevant changes - no action needed

Benchmarking 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
@rustbot label: -S-waiting-on-perf -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

Results

This 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.

mean range count
Regressions ❌
(primary)
2.1% [1.2%, 3.7%] 7
Regressions ❌
(secondary)
2.9% [2.0%, 4.7%] 20
Improvements ✅
(primary)
-4.1% [-5.0%, -3.1%] 2
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.7% [-5.0%, 3.7%] 9

Cycles

Results

This 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.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-6.2% [-7.2%, -5.2%] 6
All ❌✅ (primary) - - 0

Binary size

Results

This 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.

mean range count
Regressions ❌
(primary)
0.0% [0.0%, 0.0%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-0.0% [-0.0%, -0.0%] 4
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -0.0% [-0.0%, 0.0%] 5

Bootstrap: 659.957s -> 661.541s (0.24%)
Artifact size: 308.08 MiB -> 308.11 MiB (0.01%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Feb 4, 2024
@cjgillot cjgillot self-assigned this Feb 4, 2024
@DianQK DianQK marked this pull request as ready for review February 6, 2024 14:45
@rustbot
Copy link
Collaborator

rustbot commented Feb 6, 2024

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

compiler/rustc_mir_transform/src/match_branches.rs Outdated Show resolved Hide resolved
}

trait SimplifyMatch<'tcx> {
fn simplify(
Copy link
Contributor

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?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

compiler/rustc_mir_transform/src/match_branches.rs Outdated Show resolved Hide resolved
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();
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Member Author

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 {
Copy link
Contributor

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?

Copy link
Member Author

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.

@bors
Copy link
Contributor

bors commented Feb 13, 2024

☔ The latest upstream changes (presumably #121036) made this pull request unmergeable. Please resolve the merge conflicts.

@cjgillot cjgillot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 18, 2024
@DianQK DianQK force-pushed the simplify-switch-int branch 2 times, most recently from 2c51ed1 to f79fd40 Compare February 20, 2024 14:21
@oli-obk oli-obk removed their assignment Feb 20, 2024
@DianQK DianQK force-pushed the simplify-switch-int branch 2 times, most recently from dec8b9d to d47cf55 Compare February 21, 2024 10:54
@DianQK
Copy link
Member Author

DianQK commented Feb 21, 2024

Sorry for my late reply.
@rustbot ready

@rust-log-analyzer

This comment has been minimized.

@DianQK
Copy link
Member Author

DianQK commented Apr 8, 2024

Rebased. :>

@WaffleLapkin
Copy link
Member

@bors r=cjgillot

@bors
Copy link
Contributor

bors commented Apr 8, 2024

📌 Commit f440737 has been approved by cjgillot

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 8, 2024
@bors
Copy link
Contributor

bors commented Apr 8, 2024

⌛ Testing commit f440737 with merge 211518e...

@bors
Copy link
Contributor

bors commented Apr 8, 2024

☀️ Test successful - checks-actions
Approved by: cjgillot
Pushing 211518e to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Apr 8, 2024
@bors bors merged commit 211518e into rust-lang:master Apr 8, 2024
12 checks passed
@rustbot rustbot added this to the 1.79.0 milestone Apr 8, 2024
@DianQK DianQK deleted the simplify-switch-int branch April 8, 2024 20:51
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (211518e): comparison URL.

Overall result: ❌ regressions - no action needed

@rustbot label: -perf-regression

Instruction count

This is a highly reliable metric that was used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
0.3% [0.3%, 0.3%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.3% [0.3%, 0.3%] 1

Max RSS (memory usage)

Results

This 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.

mean range count
Regressions ❌
(primary)
5.4% [5.4%, 5.4%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 5.4% [5.4%, 5.4%] 1

Cycles

This benchmark run did not return any relevant results for this metric.

Binary size

Results

This 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.

mean range count
Regressions ❌
(primary)
0.1% [0.0%, 0.1%] 5
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-0.1% [-0.1%, -0.1%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.0% [-0.1%, 0.1%] 6

Bootstrap: 669.664s -> 668.886s (-0.12%)
Artifact size: 318.23 MiB -> 318.49 MiB (0.08%)

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()
Copy link
Member

@RalfJung RalfJung Apr 17, 2024

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?

Copy link
Member Author

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.

Copy link
Member

@RalfJung RalfJung Apr 18, 2024

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.

Copy link
Member Author

@DianQK DianQK Apr 18, 2024

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())) =>
Copy link
Member

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?

Copy link
Member Author

@DianQK DianQK Apr 18, 2024

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:

#[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,
}
}
.

Copy link
Member

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.

Copy link
Member Author

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:

#[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,
}
}

Copy link
Member

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. via try_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?

Copy link
Member Author

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.

Copy link
Member Author

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.

bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 3, 2024
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missed optimization with match on repr(i8) enum
10 participants