dotnet/runtime

Unnecessary overflow check for checked BigMul on x64

Open

#105.333 aperta il 23 lug 2024

Vedi su GitHub
 (5 commenti) (0 reazioni) (0 assegnatari)C# (5445 fork)batch import
area-CodeGen-coreclrhelp wanted

Metriche repository

Star
 (17.886 star)
Metriche merge PR
 (Merge medio 12g 11h) (661 PR mergiate in 30 g)

Descrizione

For the following code:

        public static long Mul(int a, int b)
        {
            return checked((long)a * (long)b);
        }

Currently on x64 it emits:

; Method CSPlayground.Program:Mul(int,int):long (FullOpts)
G_M63938_IG01:  ;; offset=0x0000
       sub      rsp, 40
						;; size=4 bbWeight=1 PerfScore 0.25

G_M63938_IG02:  ;; offset=0x0004
       movsxd   rax, ecx
       movsxd   rcx, edx
       imul     rax, rcx
       jo       SHORT G_M63938_IG04
						;; size=12 bbWeight=1 PerfScore 3.50

G_M63938_IG03:  ;; offset=0x0010
       add      rsp, 40
       ret      
						;; size=5 bbWeight=1 PerfScore 1.25

G_M63938_IG04:  ;; offset=0x0015
       call     CORINFO_HELP_OVERFLOW
       int3     
						;; size=6 bbWeight=0 PerfScore 0.00
; Total bytes of code: 27

There's an overflow check for the IMUL. However on other architectures, including x86/arm/arm64, there's no overflow check.

ARM64:

; Method CSPlayground.Program:Mul(int,int):long (FullOpts)
G_M63938_IG01:  ;; offset=0x0000
            stp     fp, lr, [sp, #-0x10]!
            mov     fp, sp
						;; size=8 bbWeight=1 PerfScore 1.50

G_M63938_IG02:  ;; offset=0x0008
            smull   x0, w0, w1
						;; size=4 bbWeight=1 PerfScore 2.00

G_M63938_IG03:  ;; offset=0x000C
            ldp     fp, lr, [sp], #0x10
            ret     lr
						;; size=8 bbWeight=1 PerfScore 2.00
; Total bytes of code: 20

ARM32:

; Method CSPlayground.Program:Mul(int,int):long (FullOpts)
G_M63938_IG01:  ;; offset=0x0000
000000      push    {lr}
000002      sub     sp, 12
						;; size=4 bbWeight=1 PerfScore 2.00

G_M63938_IG02:  ;; offset=0x0004
000004      smull   r0, r1, r0, r1
						;; size=4 bbWeight=1 PerfScore 1.00

G_M63938_IG03:  ;; offset=0x0008
000008      add     sp, 12
00000A      pop     {pc}
						;; size=4 bbWeight=1 PerfScore 2.00
; Total bytes of code: 12

x86:

; Method CSPlayground.Program:Mul(int,int):long (FullOpts)
G_M63938_IG01:  ;; offset=0x0000
       sub      esp, 8
						;; size=3 bbWeight=1 PerfScore 0.25

G_M63938_IG02:  ;; offset=0x0003
       mov      eax, ecx
       imul     edx:eax, edx
						;; size=4 bbWeight=1 PerfScore 3.25

G_M63938_IG03:  ;; offset=0x0007
       add      esp, 8
       ret      
						;; size=4 bbWeight=1 PerfScore 1.25
; Total bytes of code: 11

My guess is that x64 is the only platform that supports 64bit*64bit=128bit operations, thus long mul isn't decomposed into MulLow and MulHigh, and fail to discover that the overflow check is unnecessary.

Guida contributor