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