Skip to content

Conditional mutation in Iterator::next doesn't optimise well #24660

Closed
@huonw

Description

@huonw
#![crate_type = "lib"]

struct R {
    start: i32,
    end: i32
}

impl Iterator for R
{
    type Item = i32;

    fn next(&mut self) -> Option<i32> {
        if self.start < self.end {
            let old = self.start;
            self.start += 1;
            Some(old)
        } else {
            None
        }
    }
}


pub fn test() -> usize {
    R { start: 0, end: 100000}.count()
}

Compiles to:

_ZN4test20h08046f2c32b9b146kaaE:
    .cfi_startproc
    xorl    %ecx, %ecx
    movq    $-1, %rax
    .align  16, 0x90
.LBB0_1:
    cmpl    $100000, %ecx
    setl    %dl
    movzbl  %dl, %edx
    addl    %ecx, %edx
    incq    %rax
    cmpl    $100000, %ecx
    movl    %edx, %ecx
    jl  .LBB0_1
    retq

It should at least look something like:

    xorl    %eax, %eax
    movl    $100000, %ecx
.LBB0_1: 
    incl    %eax
    decl    %ecx
    jne .LBB0_1

But really that case should be constant folded (clang folds the equivalent C for loop).

(NB. count is currently implemented as self.fold(0, |c, _| c + 1), and the bad codegen occurs if that is definition is used directly, and also if one uses the mutating for loop: let mut c = 0; for _ in ... { c += 1 }.)

This previously affected std::ops::Range (i.e. x..y), but #24705 implemented a work-around.

Metadata

Metadata

Assignees

No one assigned

    Labels

    I-slowIssue: Problems and improvements with respect to performance of generated code.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions