Skip to content

JIT: Faster Math.Max/Min for x64 #65625

Closed

Description

@EgorBo

Optimize Math.Max/Min to a single instruction on x64 when one of the arguments is a constant (not NaN and whoever will be implementing it has to be careful around -/+0.0)

float Test(float a) => Math.Max(a, 10);

Currently emits:

; Method Tests4:Test(float):float:this
G_M55200_IG01:
       vzeroupper 
G_M55200_IG02:
       vmovss   xmm0, dword ptr [reloc @RWD00]
       vucomiss xmm1, xmm0
       jp       SHORT G_M55200_IG03
       je       SHORT G_M55200_IG06
G_M55200_IG03:
       vucomiss xmm1, xmm1
       jp       SHORT G_M55200_IG05
       vucomiss xmm1, xmm0
       ja       SHORT G_M55200_IG04
       jmp      SHORT G_M55200_IG08
G_M55200_IG04:
       vmovaps  xmm0, xmm1
       jmp      SHORT G_M55200_IG08
G_M55200_IG05:
       vmovaps  xmm0, xmm1
       jmp      SHORT G_M55200_IG08
G_M55200_IG06:
       vmovaps  xmm1, xmm0
       vmovd    eax, xmm1
       test     eax, eax
       jl       SHORT G_M55200_IG07
       jmp      SHORT G_M55200_IG08
G_M55200_IG07:
       vmovss   xmm0, dword ptr [reloc @RWD00]
G_M55200_IG08:
       ret      
RWD00  	dd	41200000h		;        10
; Total bytes of code: 68

Expected codegen:

; Method Tests4:Test(float):float:this
       vzeroupper 
       vmaxss   xmm0, xmm1, dword ptr [reloc @RWD00]
       ret      
RWD00  	dd	41200000h ; 10.0
; Total bytes of code: 12

#65584 did it for ARM where we could do it even for both non-constants

Activity

  1. ghost added
    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
    on Feb 19, 2022
  2. ghost added
    untriagedNew issue has not been triaged by the area owner
    on Feb 19, 2022
  3. added
    good first issueIssue should be easy to implement, good for first-time contributors
    help wanted[up-for-grabs] Good issue for external contributors
    on Feb 19, 2022
  4. added this to the Future milestone on Feb 19, 2022
  5. removed
    untriagedNew issue has not been triaged by the area owner
    on Feb 19, 2022
  6. tannergooding commented on Feb 19, 2022

    @tannergooding
    Member

    In particular, Math.Max and Math.Min for floating-point have a requirement, from the IEEE 754 spec, that -0.0 be the minimum of it and +0.0. Likewise, they have a requirement that NaN is propagated (if both inputs are NaN, then the exact NaN returned doesn't matter).

    x86/x64 provide maxss and minss. These instructions will return the second operand if both operands are 0 (same or differing signs). Likewise, if either input is NaN, the second operand is returned. Otherwise, the correct max/min is returned.

    What this functionally means is that if both inputs are unknown, we can't really "optimize" and instead have to use maxss/minss and then some follow up computations. However, if either input is constant then we actually can optimize it down to a single instruction in all cases:

    • In the case both are constant, we simply constant fold and it doesn't matter.
    • If either input is known to be NaN, it is used as the second argument (ensuring NaN is propagated since either input being NaN means the second argument is returned)
    • For maxss, if either input is +0.0 it takes the second argument (ensuring +0.0 is returned if both inputs are zero since both inputs being zero, of either sign, means the second argument is returned)
    • For minss, if either input is -0.0 it takes the second argument (ensuring -0.0 is returned if both inputs are zero since both inputs being zero, of either sign, means the second argument is returned)
    • Otherwise, for any other known constant input it is the first argument
      • If the unknown is NaN, this ensures it is returned since either input being NaN means the second argument is returned
      • If the unknown is +0.0 for maxss and the known was -0.0, it would ensure +0.0 is returned since both inputs being zero, of either sign, means the second argument is returned
      • If the unknown is -0.0 for minss and the known was +0.0, it would ensure -0.0 is returned since both inputs being zero, of either sign, means the second argument is returned
      • For any other inputs known or unknown; the default handling is already correct

    This ensures that Max returns NaN and +0.0 and likewise that Min returns NaN and -0.0 for their respective special cases.

  7. SkiFoD commented on Feb 21, 2022

    @SkiFoD
    Contributor

    Hey, everyone. I would like to take a look at the issue. @EgorBo Could you please tell me how you made "Math.Max" call to get inlined?

  8. EgorBo commented on Feb 21, 2022

    @EgorBo
    MemberAuthor

    Hey, everyone. I would like to take a look at the issue. @EgorBo Could you please tell me how you made "Math.Max" call to get inlined?

    Hey, sure! What do you mean inlined? Intrinsic?

  9. SkiFoD commented on Feb 21, 2022

    @SkiFoD
    Contributor

    I'm trying to get the same emit, but I always get something like this:
    image
    So I can't look inside the Math.Max call to invistigate it.

  10. EgorBo commented on Feb 21, 2022

    @EgorBo
    MemberAuthor

    @SkiFoD see #65584, it ifdefed special import for Max for x86/64, you need to enable it, most likely you will also have to modify lsraxarch and the xarch emitter

  11. SkiFoD commented on Feb 21, 2022

    @SkiFoD
    Contributor

    @EgorBo Thank you, so basically you are changing the call to instrinsic use.

  12. SkiFoD commented on Feb 21, 2022

    @SkiFoD
    Contributor

    @EgorBo Seems like I figured out how to make the changes, but I got a few questions.
    Is it all right that the call transforms to the intrinsic use only under a pressure (like if the Math.Max/Min is called in a huge cycle) or if there are envs "COMPlus_TieredCompilation=0" and "COMPlus_JITMinOpts=0" set up?
    p.s. For some reason if I used the envs along with "COMPlus_JitDump/JitDisasm" then there are not output in the console :( Have you ever come across with such a behaviour?

  13. 10 remaining items

  14. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Jun 22, 2022
  15. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Jun 23, 2022
  16. ghost locked as resolved and limited conversation to collaborators on Jul 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMItenet-performancePerformance related issue

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions