Skip to content

Regression: iter_mut().nth().next() retains unwrap panic path and extra branch vs split_at_mut in optimized code (Rust 1.82+) #150235

@camc314

Description

@camc314

Code

I tried this code:

#![crate_type = "lib"]
use std::hint::unreachable_unchecked;
use std::collections::HashMap;
type Map = HashMap<String, Vec<u32>>;
pub struct Stack {
    stack: Vec<Map>,
    depth: usize,
}
// ============================================================================
// BEFORE: Using iterator with nth() and next()
// - Generates unwrap panic path even with assert_unchecked hints
// - More instructions, conditional branch
// ============================================================================
#[no_mangle]
pub fn use_iter(s: &mut Stack) -> (&mut Map, &mut Map) {
    // Assert invariants
    if s.depth == 0 {
        unsafe { unreachable_unchecked() };
    }
    if s.stack.len() <= s.depth {
        unsafe { unreachable_unchecked() };
    }
    
    let mut iter = s.stack.iter_mut();
    let parent = iter.nth(s.depth - 1).unwrap();
    let current = iter.next().unwrap();
    (current, parent)
}
// ============================================================================
// AFTER: Using split_at_mut
// - No panic path, fully inlined
// - Fewer instructions, no branches
// ============================================================================
#[no_mangle]
pub fn use_split(s: &mut Stack) -> (&mut Map, &mut Map) {
    // Assert invariants
    if s.depth == 0 {
        unsafe { unreachable_unchecked() };
    }
    if s.stack.len() <= s.depth {
        unsafe { unreachable_unchecked() };
    }
    
    let (head, tail) = s.stack.split_at_mut(s.depth);
    let parent = &mut head[s.depth - 1];
    let current = &mut tail[0];
    (current, parent)
}

I expected to see this happen:

use_iter:
        mov     rcx, qword ptr [rdi + 8]
        mov     rax, qword ptr [rdi + 24]
        lea     rdx, [rax + 2*rax]
        shl     rdx, 4
        lea     rax, [rcx + rdx]
        add     rdx, rcx
        add     rdx, -48
        ret

use_split:
        mov     rcx, qword ptr [rdi + 8]
        mov     rax, qword ptr [rdi + 24]
        lea     rdx, [rax + 2*rax]
        shl     rdx, 4
        lea     rax, [rcx + rdx]
        add     rdx, rcx
        add     rdx, -48
        ret

Instead, this happened:

use_iter:
        mov     rax, qword ptr [rdi + 24]
        movabs  rcx, 1152921504606846975
        and     rcx, rax
        cmp     rcx, qword ptr [rdi + 16]
        je      .LBB0_2
        lea     rcx, [rax + 2*rax]
        shl     rcx, 4
        mov     rdx, qword ptr [rdi + 8]
        lea     rax, [rdx + rcx]
        add     rdx, rcx
        add     rdx, -48
        ret
.LBB0_2:
        push    rax
        lea     rdi, [rip + .Lanon.621aa17d7ebd209918a0c2e574629b83.1]
        call    qword ptr [rip + core::option::unwrap_failed::he1a8284b5a1e2496@GOTPCREL]

use_split:
        mov     rcx, qword ptr [rdi + 8]
        mov     rax, qword ptr [rdi + 24]
        lea     rdx, [rax + 2*rax]
        shl     rdx, 4
        lea     rax, [rcx + rdx]
        add     rdx, rcx
        add     rdx, -48
        ret

.Lanon.621aa17d7ebd209918a0c2e574629b83.0:
        .asciz  "/app/example.rs"

.Lanon.621aa17d7ebd209918a0c2e574629b83.1:
        .quad   .Lanon.621aa17d7ebd209918a0c2e574629b83.0
        .asciz  "\017\000\000\000\000\000\000\000 \000\000\000\037\000\000"

Version it worked on

It most recently worked on: Rust 1.81
It broke in 1.82

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchI-prioritizeIssue: Indicates that prioritization has been requested for this issue.I-slowIssue: Problems and improvements with respect to performance of generated code.needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions