Skip to content

split_at fails to optimize bounds check #74938

Closed

Description

@lcnr

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

const N: usize = 3;
const T = u8;

pub fn split_mutiple(slice: &[T]) -> (&[T], &[T]) {
    let len = slice.len() / N;
    slice.split_at(len * N)
}

results in the following assembly

example::split_mutiple:
        push    rax
        mov     r8, rdx
        mov     rcx, rdi
        movabs  rdx, -6148914691236517205
        mov     rax, r8
        mul     rdx
        shr     rdx
        lea     rdx, [rdx + 2*rdx]
        mov     rax, r8
        sub     rax, rdx
        mov     rdi, r8
        sub     rdi, rax
        jb      .LBB0_1  ; Note that this check can never be actually hit
        mov     qword ptr [rcx], rsi
        add     rsi, rdi
        mov     qword ptr [rcx + 8], rdi
        mov     qword ptr [rcx + 16], rsi
        mov     qword ptr [rcx + 24], rax
        mov     rax, rcx
        pop     rcx
        ret
.LBB0_1:
        lea     rdx, [rip + .L__unnamed_1]
        mov     rsi, r8
        call    qword ptr [rip + core::slice::slice_index_len_fail@GOTPCREL]
        ud2

When using const N: usize = 4 the check is correctly optimized away:

example::split_mutiple:
        mov     rax, rdi
        mov     rcx, rdx
        and     rcx, -4
        and     edx, 3
        mov     qword ptr [rdi], rsi
        add     rsi, rcx
        mov     qword ptr [rdi + 8], rcx
        mov     qword ptr [rdi + 16], rsi
        mov     qword ptr [rdi + 24], rdx
        ret

This happens both on all stable versions I have tested and on the most recent nightly.

Activity

  1. added
    C-enhancementCategory: An issue proposing an enhancement or a PR with one.
    I-slowIssue: Problems and improvements with respect to performance of generated code.
    on Jul 30, 2020
  2. bugadani commented on Jul 31, 2020

    @bugadani
    Contributor

    In fact, this has nothing to do with slices: https://rust.godbolt.org/z/7oG6qE change from 3 to 4 (or other powers of 2) to see everything optimized away.

    Edit: reduced it some more: https://rust.godbolt.org/z/bEoj8x

  3. tesuji commented on Jul 31, 2020

    @tesuji
    Contributor

    There are no optimization in GCC and Clang either: https://godbolt.org/z/M88ndP

    #include <stddef.h>
    
    const size_t N = 3;
    
    int foo(size_t len) {
    	size_t newlen = (len / N) * N;
    	return newlen <= len;
    }

    Should foo be optimized to only return true?

  4. tesuji commented on Jul 31, 2020

    @tesuji
    Contributor

    So this is more as LLVM part: @rustbot modify labels: +A-LLVM +A-mir-opt
    Of course this might be improved with MIR-opt. But how hard it would be? I don't know.

  5. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    on Jul 31, 2020
  6. bugadani commented on Jul 31, 2020

    @bugadani
    Contributor

    So this is more as LLVM part

    What's interesting is that the power of 2 optimization is done by rustc somewhere - the same MIR is translated to radically different LLVM-IR when N is 2^n.

    Trying to find where this happens without knowledge of the compiler is rather difficult, though :)

  7. tesuji commented on Jul 31, 2020

    @tesuji
    Contributor

    Clang could do that, change N=4 in my snippet about to see. But still GCC doesn't do that.

  8. nikic commented on Jul 31, 2020

    @nikic
    Contributor
  9. bugadani commented on Jul 31, 2020

    @bugadani
    Contributor

    Cool. So if I understand correctly, the following is happening:

    The bounds check boils down to basically (length / N) * N <= length.
    This can be transformed to length - length % N <= length (fair enough)
    This is further transformed to (as the llvm ir shows) length % N <= length; (I don't see how this transform is equivalent, but OK)

    And for some reason, LLVM can't seem to deduce that this comparison will always be false. Adding this as an assumption will make the check be optimized:

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

  10. xldenis commented on Jul 31, 2020

    @xldenis
    Contributor

    @bugadani, what's weird is that if you feed this IR straight into llc it does optimize it away: https://rust.godbolt.org/z/o6d6nM. Are we not calling a magic pass on the IR in rustc?

    Ignore me, I missed that the assume caused the IR to have ret true

    However, it does optimize if a udiv is inserted instead of a urem, InstructionSimplify has the following opt: x udiv y <=u x I don't know why the same doesn't exist for urem? maybe no one considered it until now?

    LLVM doesn't know about this optimization but it is apparently valid (thanks John Regehr) so I'll submit a patch to LLVM.

  11. xldenis commented on Aug 2, 2020

    @xldenis
    Contributor

    I've submitted a patch to LLVM adding this optimization: https://reviews.llvm.org/D85092

  12. tesuji commented on Aug 2, 2020

    @tesuji
    Contributor

    Does anyone want to file a bug against GCC? I registered an account two days ago but they hasn't accepted my account yet.

  13. 11 remaining items

  14. added
    E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.
    on Jan 14, 2022
  15. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Apr 5, 2023
  16. added
    C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing such
    on Oct 8, 2023
  17. added 5 commits that reference this issue on Jun 9, 2024
    b7edafe
    a1b4415
    b1761a2
    4936869
    7ac6c2f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.A-mir-optArea: MIR optimizationsC-enhancementCategory: An issue proposing an enhancement or a PR with one.C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchE-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.I-slowIssue: Problems and improvements with respect to performance of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions