Skip to content

PartialOrd derived for C-like enum isn't properly optimized for the <= operator #73338

Closed
@pubfnbar

Description

@pubfnbar

The manual version:

use std::{
    cmp::Ordering,
    time::SystemTime,
};

#[repr(u32)]
#[derive(Copy, Clone, Eq, PartialEq)]
enum Foo {
    Zero,
    One,
    Two,
}

impl PartialOrd for Foo {
    fn partial_cmp(&self, _rhs: &Foo) -> Option<Ordering> {
        panic!()
    }

    fn le(&self, rhs: &Foo) -> bool {
        *self as u32 <= *rhs as u32
    }
}

fn main() {
    let opaque = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_secs() as u32
        % 3;

    let foo = unsafe { *(&opaque as *const _ as *const Foo) };

    if foo <= Foo::One {
        println!("here");
    }
}

The manual version ASM:

playground::main:
	subq	$88, %rsp
	callq	*std::time::SystemTime::now@GOTPCREL(%rip)
	movq	%rax, 56(%rsp)
	movq	%rdx, 64(%rsp)
	leaq	8(%rsp), %rdi
	leaq	56(%rsp), %rsi
	xorl	%edx, %edx
	xorl	%ecx, %ecx
	callq	*std::time::SystemTime::duration_since@GOTPCREL(%rip)
	cmpq	$1, 8(%rsp)
	je	.LBB4_4
	movl	16(%rsp), %eax
	movl	$2863311531, %ecx
	imulq	%rax, %rcx
	shrq	$33, %rcx
	leal	(%rcx,%rcx,2), %ecx
	subl	%ecx, %eax
	cmpl	$1, %eax
	ja	.LBB4_3
	leaq	.L__unnamed_2(%rip), %rax
	movq	%rax, 8(%rsp)
	movq	$1, 16(%rsp)
	movq	$0, 24(%rsp)
	leaq	.L__unnamed_3(%rip), %rax
	movq	%rax, 40(%rsp)
	movq	$0, 48(%rsp)
	leaq	8(%rsp), %rdi
	callq	*std::io::stdio::_print@GOTPCREL(%rip)

The derived version:

use std::{
    cmp::Ordering,
    time::SystemTime,
};

#[repr(u32)]
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd)]
enum Foo {
    Zero,
    One,
    Two,
}

fn main() {
    let opaque = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_secs() as u32
        % 3;

    let foo = unsafe { *(&opaque as *const _ as *const Foo) };

    if foo <= Foo::One {
        println!("here");
    }
}

The derived version ASM:

playground::main:
	subq	$88, %rsp
	callq	*std::time::SystemTime::now@GOTPCREL(%rip)
	movq	%rax, 56(%rsp)
	movq	%rdx, 64(%rsp)
	leaq	8(%rsp), %rdi
	leaq	56(%rsp), %rsi
	xorl	%edx, %edx
	xorl	%ecx, %ecx
	callq	*std::time::SystemTime::duration_since@GOTPCREL(%rip)
	cmpq	$1, 8(%rsp)
	je	.LBB4_4
	movl	16(%rsp), %eax
	movl	$2863311531, %ecx
	imulq	%rax, %rcx
	shrq	$33, %rcx
	leal	(%rcx,%rcx,2), %ecx
	xorl	%edx, %edx
	subl	%ecx, %eax
	setne	%dl
	xorl	%ecx, %ecx
	cmpl	$1, %eax
	leaq	-1(%rdx,%rdx), %rax
	cmovneq	%rax, %rcx
	addq	$1, %rcx
	cmpq	$1, %rcx
	ja	.LBB4_3
	leaq	.L__unnamed_2(%rip), %rax
	movq	%rax, 8(%rsp)
	movq	$1, 16(%rsp)
	movq	$0, 24(%rsp)
	leaq	.L__unnamed_3(%rip), %rax
	movq	%rax, 40(%rsp)
	movq	$0, 48(%rsp)
	leaq	8(%rsp), %rdi
	callq	*std::io::stdio::_print@GOTPCREL(%rip)

The diff is that while the manual version does:

	subl	%ecx, %eax
	cmpl	$1, %eax

...the derived version does:

	xorl	%edx, %edx
	subl	%ecx, %eax
	setne	%dl
	xorl	%ecx, %ecx
	cmpl	$1, %eax
	leaq	-1(%rdx,%rdx), %rax
	cmovneq	%rax, %rcx
	addq	$1, %rcx
	cmpq	$1, %rcx

All the other PartialOrd operators are optimized properly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-enhancementCategory: An issue proposing an enhancement or a PR with one.I-slowIssue: Problems and improvements with respect to performance of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions