Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT can reuse a comparison between two locals but fails to do so for two readonly fields #95844

Open
Jorenkv opened this issue Dec 11, 2023 · 4 comments
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI tenet-performance Performance related issue
Milestone

Comments

@Jorenkv
Copy link

Jorenkv commented Dec 11, 2023

Description

For the snippet below, MinMaxA has worse codegen than MinMaxB:

using System;

readonly struct Pair
{
    readonly int U, V;
    
    int MinMaxA() {
        var min = Math.Min(U, V);
        var max = Math.Max(U, V);
        return max ^ min;
    }
    
    int MinMaxB() {
        var (u, v) = (U, V); // Inserting a manual copy to locals improves the codegen
        var min = Math.Min(u, v);
        var max = Math.Max(u, v);
        return max ^ min;
    }
}
; Core CLR 8.0.23.53103 on x64

Pair.MinMaxA()
    L0000: mov eax, [rcx]
    L0002: mov edx, eax
    L0004: mov ecx, [rcx+4]
    L0007: mov r8d, ecx
    L000a: cmp edx, r8d
    L000d: cmovg edx, r8d
    L0011: cmp eax, ecx
    L0013: cmovl eax, ecx
    L0016: xor eax, edx
    L0018: ret

Pair.MinMaxB()
    L0000: mov eax, [rcx]
    L0002: mov ecx, [rcx+4]
    L0005: cmp eax, ecx
    L0007: mov edx, ecx
    L0009: cmovle edx, eax
    L000c: cmovl eax, ecx
    L000f: xor eax, edx
    L0011: ret

In MinMaxA, the JIT performs the comparison between the fields twice, while in MinMaxB it is able to see that the comparison between two locals only needs to happen once.

Configuration

Locally I'm running 8.0.100, Windows x64. The above disasm I took from Sharplab.

@Jorenkv Jorenkv added the tenet-performance Performance related issue label Dec 11, 2023
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 11, 2023
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Dec 11, 2023
@ghost
Copy link

ghost commented Dec 11, 2023

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

For the snippet below, MinMaxA has worse codegen than MinMaxB:

using System;

readonly struct Pair
{
    readonly int U, V;
    
    int MinMaxA() {
        var min = Math.Min(U, V);
        var max = Math.Max(U, V);
        return max ^ min;
    }
    
    int MinMaxB() {
        var (u, v) = (U, V); // Inserting a manual copy to locals improves the codegen
        var min = Math.Min(u, v);
        var max = Math.Max(u, v);
        return max ^ min;
    }
}
; Core CLR 8.0.23.53103 on x64

Pair.MinMaxA()
    L0000: mov eax, [rcx]
    L0002: mov edx, eax
    L0004: mov ecx, [rcx+4]
    L0007: mov r8d, ecx
    L000a: cmp edx, r8d
    L000d: cmovg edx, r8d
    L0011: cmp eax, ecx
    L0013: cmovl eax, ecx
    L0016: xor eax, edx
    L0018: ret

Pair.MinMaxB()
    L0000: mov eax, [rcx]
    L0002: mov ecx, [rcx+4]
    L0005: cmp eax, ecx
    L0007: mov edx, ecx
    L0009: cmovle edx, eax
    L000c: cmovl eax, ecx
    L000f: xor eax, edx
    L0011: ret

In MinMaxA, the JIT performs the comparison between the fields twice, while in MinMaxB it is able to see that the comparison between two locals only needs to happen once.

Configuration

Locally I'm running 8.0.100, Windows x64. The above disasm I took from Sharplab.

Author: Jorenkv
Assignees: -
Labels:

tenet-performance, area-CodeGen-coreclr

Milestone: -

@hez2010
Copy link
Contributor

hez2010 commented Dec 11, 2023

This is expected as a (non-static) readonly field is mutable at runtime.

@huoyaoyuan
Copy link
Member

This is expected as a (non-static) readonly field is mutable at runtime.

No, there's no duplicated reads in generated code. Look at the cmp edx, r8d and cmp eax, ecx instructions.

@jakobbotsch
Copy link
Member

We only get rid of the duplicated loads after CSE, but then we still have multiple locals that we'd need to copy propagate, but copy prop runs before CSE. We do end up with identical codegen if we run copy prop again after CSE (with DOTNET_JitOptRepeat=* in checked builds).

@jakobbotsch jakobbotsch added this to the Future milestone Dec 11, 2023
@jakobbotsch jakobbotsch removed the untriaged New issue has not been triaged by the area owner label Dec 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI tenet-performance Performance related issue
Projects
None yet
Development

No branches or pull requests

4 participants