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

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

Activity

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.C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchI-slowIssue: Problems and improvements with respect to performance of generated code.P-lowLow priorityneeds-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