Repository: rust
Issue: Weird codegen (missed optimization) with str::strip_suffix
Labels: A-LLVM,I-slow,T-compiler,C-bug

The following rust code:

```rust
pub fn strip_lf(s: &str) -> &str {
    s.strip_suffix('\n').unwrap_or(s)
}
```

compiles to the following (courtesy of godbolt):
```asm
example::strip_lf:
        mov     rax, rdi
        test    rsi, rsi
        je      .LBB0_1
        lea     rdx, [rsi - 1]
        xor     ecx, ecx
        cmp     byte ptr [rax + rsi - 1], 10
        cmove   rcx, rax
        jmp     .LBB0_3
.LBB0_1:
        xor     ecx, ecx
.LBB0_3:
        test    rcx, rcx
        cmovne  rax, rcx
        cmove   rdx, rsi
        ret
```

The code path "string is empty" is ridiculous:
```asm
        mov     rax, rdi    // set rax (returned pointer) to the value of rdi (pointer to the string)
        // skip the test + jump
        xor     ecx, ecx    // sets rcx to 0 (ops on e* regs zero-extends into r* regs)
        test    rcx, rcx    // if rcx == 0
        cmovne  rax, rcx    // don't do this
        cmove   rdx, rsi    // set rdx (returned length) to the value of rsi (original length, which was 0).
        ret
```

Technically, all that code path needs to do is:
```asm
        mov     rax, rdi    // set rax (returned pointer) to the value of rdi (pointer to the string)
        xor     edx, edx    // set rdx (returned length) to 0
        ret
```

Now, let's look at the "string is not empty" code path, it's better, but not great either:
```asm
        mov     rax, rdi       // set rax (returned pointer) to the value of rdi (pointer to the string)
        // skip the test + jump
        lea     rdx, [rsi - 1] // set rdx to the length of the string - 1
        xor     ecx, ecx       // set rcx to 0
        cmp     byte ptr [rax + rsi - 1], 10  // compare the last byte with '\n'
        cmove   rcx, rax       // set rcx to the value of rax (pointer to the string) if it matched
        // skip the jump
        test    rcx, rcx       // if rcx != 0
        cmovne  rax, rcx       // then set rax (returned pointer) to rcx, which was set to rax, which was already the same value
        cmove   rdx, rsi       // else set rdx (returned length) to the value of rsi (original length)
        ret
```
It's pointlessly setting rax to the value it already had.

Compare with the manual equivalent:
```rust
pub fn strip_lf2(s: &str) -> &str {
    let l = s.len();
    if l > 0 && s.as_bytes()[l - 1] == b'\n' {
        &s[..l-1]
    } else {
        s
    }
}
```

... which compiles to:
```asm
example::strip_lf2:
        mov     rax, rdi
        test    rsi, rsi
        je      .LBB1_1
        lea     rdx, [rsi - 1]
        cmp     byte ptr [rax + rsi - 1], 10
        cmovne  rdx, rsi
        ret
.LBB1_1:
        xor     edx, edx
        ret
```

Which is simpler and more straightforward: the code path for "string is empty" is the ideal minimum, and the code path for "string is not empty" looks like:
```asm
        mov     rax, rdi       // set rax (returned pointer) to the value of rdi (pointer to the string)
        // skip the test + jump
        lea     rdx, [rsi - 1] // set rdx (returned length) to the length of the string - 1
        cmp     byte ptr [rax + rsi - 1], 10  // compare the last byte with '\n'
        cmovne  rdx, rsi       // if that doesn't match, set rdx (returned length) to the value of rsi (original length)
        ret
```

Ideally, the strip_suffix version should be able to compile to that.

`rustc --version --verbose`:
```
rustc 1.56.0 (09c42c458 2021-10-18)
binary: rustc
commit-hash: 09c42c45858d5f3aedfa670698275303a3d19afa
commit-date: 2021-10-18
host: x86_64-unknown-linux-gnu
release: 1.56.0
LLVM version: 13.0.0
```

godbolt says this also happens on nightly.