llvm / llvm/llvm-project

[s390x] optimisation missing

Open
#208,712 1 comment 0 reactions 0 assignees View on GitHub
backend:SystemZ llvm:instcombine missed-optimization
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

# Summary

LLVM optimizes away a redundant checked integer conversion inside a loop on x86_64, but not on s390x.

This reproduces with the exact reduced shape from Rust’s codegen-llvm test `tests/codegen-llvm/range-len-try-from.rs`, compiled with optimization enabled. The call to `unwrap_failed` for the loop-local conversion `u32::try_from(i).unwrap()` is proven redundant on x86_64, but remains as an extra path on s390x.

The same Rust source produces the same high-level checked-conversion structure before optimization, and the divergence only appears after target-specific LLVM optimization (x86_64 removes the inner `unwrap_failed` path while s390x does not), which strongly suggests the missed optimization is in LLVM rather than Rust.

# Targets

- affected: s390x-unknown-linux-gnu
- optimized as expected: x86_64-unknown-linux-gnu

# Observed behavior

With `-Copt-level=3`:

- x86_64: the function contains only one `unwrap_failed` call, corresponding to the expected outer check:
- `u32::try_from(candidates.len()).unwrap()`
- s390x: the function contains two `unwrap_failed` calls:
- the expected outer length check
- an extra path from the loop-local `u32::try_from(i).unwrap()`

This suggests LLVM is failing on s390x to prove that the loop index is bounded by the already-checked slice length.

# Reproducer

```rust
use std::convert::TryFrom;
use std::hint::black_box;

#[no_mangle]
pub fn score_round_enumerate(candidates: &[bool]) {
u32::try_from(candidates.len()).unwrap();

for (i, _) in candidates.iter().enumerate() {
u32::try_from(i).unwrap();
black_box(42);
}
}
```

## Compile:

```bash
rustc -Copt-level=3 --emit=llvm-ir --crate-type=lib reproduce.rs --target s390x-unknown-linux-gnu
rustc -Copt-level=3 --emit=llvm-ir --crate-type=lib reproduce.rs --target x86_64-unknown-linux-gnu
```

## Expected result

Both targets should retain only the single unwrap_failed associated with: `u32::try_from(candidates.len()).unwrap();`
The loop-local conversion `unwrap_failed` call (`u32::try_from(i).unwrap();`) should be optimized away on both targets.

## Actual result

On s390x the function retain an extra unwrap_failed from the loop-local checked conversion. On x86_64, that extra `unwrap_failed` path is removed.

# Attachments:
[reproduce.rs.txt](https://github.com/user-attachments/files/29889613/reproduce.rs.txt)
[reproduce_s390x_noopt.ll.txt](https://github.com/user-attachments/files/29889612/reproduce_s390x_noopt.ll.txt)
[reproduce_s390x_o3.ll.txt](https://github.com/user-attachments/files/29889615/reproduce_s390x_o3.ll.txt)
[reproduce_x86_64_noopt.ll.txt](https://github.com/user-attachments/files/29889616/reproduce_x86_64_noopt.ll.txt)
[reproduce_x86_64_o3.ll.txt](https://github.com/user-attachments/files/29889614/reproduce_x86_64_o3.ll.txt)

Contributor guide

Open the contributing guide

Research direction

Start with the reduced Rust reproducer in reproduce.rs and compile it at -Copt-level=3 for s390x-unknown-linux-gnu and x86_64-unknown-linux-gnu as shown. Compare the optimized LLVM IR attachments, then trace the s390x optimization path responsible for retaining the loop-local unwrap_failed call; done means s390x retains only the outer check, matching x86_64.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.