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

Missed optimization with match on repr(i8) enum #106459

Closed
jrose-signal opened this issue Jan 4, 2023 · 9 comments · Fixed by #120614
Closed

Missed optimization with match on repr(i8) enum #106459

jrose-signal opened this issue Jan 4, 2023 · 9 comments · Fixed by #120614
Assignees
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. A-mir-opt Area: MIR optimizations C-bug Category: This is a bug. I-slow Issue: Problems and improvements with respect to performance of generated code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@jrose-signal
Copy link

jrose-signal commented Jan 4, 2023

The following functions ought to be equivalent:

use core::cmp::Ordering;

pub fn test1(call: fn() -> Ordering) -> i32 {
    match call() {
        Ordering::Less => -1,
        Ordering::Equal => 0,
        Ordering::Greater => 1,
    }
}

pub fn test2(call: fn() -> Ordering) -> i32 {
    call() as i32
}

but compile to the following x86_64 assembly on play.rust-lang.org:

playground::test1:
	pushq	%rax
	callq	*%rdi
	incb	%al
	movzbl	%al, %eax
	decl	%eax
	popq	%rcx
	retq

playground::test2:
	pushq	%rax
	callq	*%rdi
	movsbl	%al, %eax
	popq	%rcx
	retq

for both stable (1.66.0) and nightly (1.68.0-nightly 2023-01-03 c757267).

@nikic
Copy link
Contributor

nikic commented Jan 5, 2023

Godbolt: https://rust.godbolt.org/z/3qqrhehTr
Missing this fold: https://alive2.llvm.org/ce/z/7p4sQg

We do handle the general pattern: https://llvm.godbolt.org/z/7MndEd3an The problem here is that the zext could also be an sext due to the range restriction.

@nikic
Copy link
Contributor

nikic commented Jan 5, 2023

Though this one could also be solved by converting the phi into sext in InstCombine already -- pretty sure we needed that to solve some more general cases of this problem.

@nikic nikic added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. I-slow Issue: Problems and improvements with respect to performance of generated code. labels Jan 5, 2023
@nikic
Copy link
Contributor

nikic commented Jan 5, 2023

Though this one could also be solved by converting the phi into sext in InstCombine already -- pretty sure we needed that to solve some more general cases of this problem.

In particular the case where we don't have range metadata can't be solved by just folding the add/zext/add pattern. We either need to recognize sext in simplifyUsingControlFlow(), or directly produce sext when SimplifyCFG removes the switch.

@nikic
Copy link
Contributor

nikic commented Jan 5, 2023

We have llvm/llvm-project#54561 for this case and llvm/llvm-project#59671 for a more complicated case with an extra * -1.

@scottmcm scottmcm added the A-mir-opt Area: MIR optimizations label Jan 6, 2023
@scottmcm
Copy link
Member

scottmcm commented Jan 6, 2023

Added mir-opt because we could consider recognizing simple matches like this in MIR as well, replacing them with https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.Rvalue.html#variant.Discriminant.

That might have the advantage that we have a Rust type, and thus can tell whether to look for sext or zext of the values.

@Noratrieb Noratrieb added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Apr 5, 2023
bors added a commit to rust-lang-ci/rust that referenced this issue 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
@nikic
Copy link
Contributor

nikic commented Feb 4, 2024

We do handle the general pattern: https://llvm.godbolt.org/z/7MndEd3an The problem here is that the zext could also be an sext due to the range restriction.

This should have said "sext could also be a zext". This should be very easy to fix now, because it will be a zext nneg, so the pattern to handle would be https://alive2.llvm.org/ce/z/2iCmDs. Just need to find where the SExt variant is folded and check for SExtLike instead.

@DianQK
Copy link
Member

DianQK commented Feb 5, 2024

We do handle the general pattern: https://llvm.godbolt.org/z/7MndEd3an The problem here is that the zext could also be an sext due to the range restriction.

This should have said "sext could also be a zext". This should be very easy to fix now, because it will be a zext nneg, so the pattern to handle would be https://alive2.llvm.org/ce/z/2iCmDs. Just need to find where the SExt variant is folded and check for SExtLike instead.

Although, we need to fix that. But I found that transformation on switch is also appropriate. I can expand the original issue into the following kinds.

#![crate_type = "lib"]

#[no_mangle]
pub unsafe fn foo(a: i32) -> i8 {
    match a {
        1 => 1,
        9 => 9,
        _ => std::hint::unreachable_unchecked()
    }
}

#[no_mangle]
pub unsafe fn bar(a: i32) -> i8 {
    match a {
        -1 => -1,
        3 => 3,
        9 => 9,
        _ => std::hint::unreachable_unchecked()
    }
}

https://rust.godbolt.org/z/KTsc53bqz

The first transformation did not make better use of the UB and could not continue the transformation after it became a compare instruction. Although I can handle both scenarios in LLVM, but I can make the transformation easier in Rust by utilizing the sign information of the integers.

bors added a commit to rust-lang-ci/rust that referenced this issue Apr 7, 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 bors closed this as completed in 211518e Apr 8, 2024
@DianQK
Copy link
Member

DianQK commented Apr 11, 2024

We do handle the general pattern: https://llvm.godbolt.org/z/7MndEd3an The problem here is that the zext could also be an sext due to the range restriction.

This should have said "sext could also be a zext". This should be very easy to fix now, because it will be a zext nneg, so the pattern to handle would be https://alive2.llvm.org/ce/z/2iCmDs. Just need to find where the SExt variant is folded and check for SExtLike instead.

I created an issue for this: llvm/llvm-project#88348.

@DianQK
Copy link
Member

DianQK commented Apr 11, 2024

@rustbot claim

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. A-mir-opt Area: MIR optimizations C-bug Category: This is a bug. I-slow Issue: Problems and improvements with respect to performance of generated code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants