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

Use matches by None in is_none method on Option #110456

Closed
wants to merge 1 commit into from

Conversation

heckad
Copy link
Contributor

@heckad heckad commented Apr 17, 2023

assume don't understand inversion. Code like

pub fn f(a: Option<i32>) -> i32 {
    unsafe { assume(a.is_some()) }

    return a.unwrap();
}

compiles into

example::f:
        mov     eax, esi
        ret

but

pub fn f(a: Option<i32>) -> i32 {
    unsafe { assume(a.is_none()) }

    return a.unwrap();
}

compiles into

example::f:
        test    edi, edi
        je      .LBB0_1
        mov     eax, esi
        ret
.LBB0_1:
        push    rax
        lea     rdi, [rip + .L__unnamed_1]
        lea     rdx, [rip + .L__unnamed_2]
        mov     esi, 43
        call    qword ptr [rip + core::panicking::panic@GOTPCREL]
        ud2

This patch solves that problem.

@rustbot
Copy link
Collaborator

rustbot commented Apr 17, 2023

r? @m-ou-se

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Apr 17, 2023
@rustbot
Copy link
Collaborator

rustbot commented Apr 17, 2023

Hey! It looks like you've submitted a new PR for the library teams!

If this PR contains changes to any rust-lang/rust public library APIs then please comment with @rustbot label +T-libs-api -T-libs to tag it appropriately. If this PR contains changes to any unstable APIs please edit the PR description to add a link to the relevant API Change Proposal or create one if you haven't already. If you're unsure where your change falls no worries, just leave it as is and the reviewer will take a look and make a decision to forward on if necessary.

Examples of T-libs-api changes:

  • Stabilizing library features
  • Introducing insta-stable changes such as new implementations of existing stable traits on existing stable types
  • Introducing new or changing existing unstable library APIs (excluding permanently unstable features / features without a tracking issue)
  • Changing public documentation in ways that create new stability guarantees
  • Changing observable runtime behavior of library APIs

@matthiaskrgr
Copy link
Member

@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 Apr 17, 2023
@bors
Copy link
Contributor

bors commented Apr 17, 2023

⌛ Trying commit f8e6844 with merge bd51390ff492a775ea7ad9d80516043a5a7627a6...

@WaffleLapkin
Copy link
Member

WaffleLapkin commented Apr 17, 2023

Can we actually solve this on the compiler/llvm side instead?

This looks like a bug in the compiler — we forget somewhere that Option's discriminant can only be 0 or 1. See this godbolt comparison, for the OPs code (assume(is_none) and unwrap) we generate the following LLVM-IR:

define noundef i32 @_ZN7example1f17h05bced40beed593aE(i32 noundef %0, i32 returned %1) unnamed_addr #0 !dbg !6 {
start:
  %2 = icmp ne i32 %0, 1, !dbg !11
  tail call void @llvm.assume(i1 %2), !dbg !23
  %switch = icmp eq i32 %0, 0, !dbg !24
  br i1 %switch, label %bb4, label %bb6, !dbg !24

bb4:                                              ; preds = %start
  tail call void @_ZN4core9panicking5panic17h9caa3ddd0cccb1d2E(ptr noalias noundef nonnull readonly align 1 @alloc_5f55955de67e57c79064b537689facea, i64 noundef 43, ptr noalias noundef nonnull readonly align 8 dereferenceable(24) @alloc_b5761c36d4207a96188a90095c0f36d6) #3, !dbg !27
  unreachable, !dbg !27

bb6:                                              ; preds = %start
  ret i32 %1, !dbg !28
}

Note: we assume %0 != 1, and then check if %0 == 0, since %0 is i32, after the assumption there are 4294967295
possible values, most of which are not 0, so the check must stay.

Adding a unsafe { assume(discriminant_value(&a) <= 1) } or changing the assumption to matches!(a, None) allows us to optimize even before LLVM, in MIR.

I'm not sure how easy it is to fix this (cc @rust-lang/wg-mir-opt?), but IMO we should really try to fix this in the compiler, as it should have an effect on more code.

@bors
Copy link
Contributor

bors commented Apr 17, 2023

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

@rust-timer

This comment has been minimized.

@heckad
Copy link
Contributor Author

heckad commented Apr 17, 2023

@WaffleLapkin. I like it more when the method explicitly says what it does without delegate to other code. I also wanted to offer a patch for Result, but the problem was not reproduced there, so I left it only for Option. I would like to add this patch too if there is no fundamental reason not to add it.

@WaffleLapkin
Copy link
Member

@heckad I think there is nothing particularly wrong with this change, I just want to make sure we don't forget to fix the compiler :)

btw you can write matches!(self, None)/matches!(self, Some(_)) the dereference there is an artifact of old times

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (bd51390ff492a775ea7ad9d80516043a5a7627a6): comparison URL.

Overall result: ❌ regressions - 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 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
Regressions ❌
(secondary)
1.0% [0.2%, 1.7%] 8
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

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.2% [1.7%, 2.7%] 2
Regressions ❌
(secondary)
2.9% [2.9%, 2.9%] 1
Improvements ✅
(primary)
-2.4% [-2.4%, -2.4%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.7% [-2.4%, 2.7%] 3

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)
3.1% [0.9%, 6.2%] 12
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 3.1% [0.9%, 6.2%] 12

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Apr 17, 2023
@bugadani
Copy link
Contributor

The problem seems to be a regression. While assume doesn't work on stable, here's a comparison with unreachable_unchecked: https://godbolt.org/z/K9x8eY6o8

1.68 gives just about what you'd expect, while nightly (and beta, too, if you can coerce it to not die) doesn't.

@WaffleLapkin
Copy link
Member

I've opened an issue to track the regression: #110551

@jyn514
Copy link
Member

jyn514 commented Apr 26, 2023

#110551 was fixed in #110569. Does that mean we can close this PR?

@heckad
Copy link
Contributor Author

heckad commented Apr 26, 2023

Now it's just a stylistic change. If you don't need it, you can close it.

@jyn514 jyn514 closed this Apr 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants