Skip to content

Conversation

@EgorBo
Copy link
Member

@EgorBo EgorBo commented Dec 29, 2025

Closes #122684

bool Test<T>(T a, T b) => a.Equals(b);

Codegen for Test<MethodImplOptions> (before):

; Assembly listing for method Prog:Test[int](int,int):bool:this (FullOpts)
       push     rdi
       push     rsi
       push     rbp
       push     rbx
       sub      rsp, 40
       mov      ebx, edx
       mov      esi, r8d
       mov      rdi, 0x7FF955DF2D68 ; System.Runtime.CompilerServices.MethodImplOptions
       mov      rcx, rdi
       call     CORINFO_HELP_NEWSFAST
       mov      rbp, rax
       mov      dword ptr [rbp+0x08], esi
       mov      rcx, rdi
       call     CORINFO_HELP_NEWSFAST
       mov      dword ptr [rax+0x08], ebx
       mov      rcx, rax
       mov      rdx, rbp
       call     [System.Enum:Equals(System.Object):bool:this]
       nop      
       add      rsp, 40
       pop      rbx
       pop      rbp
       pop      rsi
       pop      rdi
       ret      
; Total bytes of code 70

Codegen for Test<MethodImplOptions> (after):

; Assembly listing for method Prog:Test[int](int,int):bool:this (FullOpts)
       cmp      edx, r8d
       sete     al
       movzx    rax, al
       ret      
; Total bytes of code 10

Technically, we could also make Enum.Equals inlineable and intrinsify InternalGetCorElementType. but that seems to be more fragile.

@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 29, 2025
@EgorBo
Copy link
Member Author

EgorBo commented Dec 29, 2025

@MihuBot

}

CORINFO_CLASS_HANDLE underlyingEnumCls;
if (info.compCompHnd->isEnum(cls1, &underlyingEnumCls) != TypeCompareState::Must)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this check needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this check needed?

Not really needed judging by the managed impl, although, it does have a Debug.Assert(rt.IsActualEnum) so I converted the check to an assert.

@hez2010
Copy link
Contributor

hez2010 commented Dec 30, 2025

Technically, we could also make Enum.Equals inlineable and intrinsify InternalGetCorElementType. but that seems to be more fragile.

Intrinsifying InternalGetCorElementType seems more beneficial to me, as it also covers GetHashCode, GetNames, GetUnderlyingValue etc. See https://github.com/search?q=repo%3Adotnet%2Fruntime%20InternalGetCorElementType&type=code

@EgorBo
Copy link
Member Author

EgorBo commented Dec 30, 2025

Technically, we could also make Enum.Equals inlineable and intrinsify InternalGetCorElementType. but that seems to be more fragile.

Intrinsifying InternalGetCorElementType seems more beneficial to me, as it also covers GetHashCode, GetNames, GetUnderlyingValue etc. See https://github.com/search?q=repo%3Adotnet%2Fruntime%20InternalGetCorElementType&type=code

GetHashCode has no CQ issues it seems. Other use-cases seem to be in huge methods which require inlining to benefit from it and rely on escape analysis heavily. My initial impl was InternalGetCorElementType and the codegen was not pretty.

@EgorBo EgorBo marked this pull request as ready for review December 30, 2025 12:10
Copilot AI review requested due to automatic review settings December 30, 2025 12:10
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR intrinsifies Enum.Equals to avoid boxing and dramatically improve performance. When comparing enums, the JIT now recognizes calls to Enum.Equals and generates optimized code that directly compares the underlying integral values without boxing. The PR demonstrates a reduction from 70 bytes of assembly with boxing allocations to just 10 bytes of efficient comparison code.

Key changes:

  • Added JIT intrinsic recognition for Enum.Equals method
  • Implemented optimization pass that unboxes enum arguments and compares underlying values directly
  • Added comprehensive test suite covering all enum underlying types, generic scenarios, and edge cases

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/libraries/System.Private.CoreLib/src/System/Enum.cs Marks Enum.Equals method with [Intrinsic] attribute to enable JIT recognition
src/coreclr/jit/namedintrinsiclist.h Adds NI_System_Enum_Equals to the named intrinsic enumeration
src/coreclr/jit/compiler.h Declares impFoldEnumEquals function for the optimization implementation
src/coreclr/jit/importercalls.cpp Implements the intrinsic optimization logic including type checking, unboxing, and comparison generation; also fixes a typo in an existing comment
src/tests/JIT/Intrinsics/EnumIntrinsics.cs Comprehensive test suite covering all enum types (sbyte through ulong), generics, different underlying types, flags, boxing scenarios, and null handling
src/tests/JIT/Intrinsics/EnumIntrinsics.csproj Test project configuration for the new intrinsic tests

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enum.Equals boxes its arguments and is not inlined

3 participants