Background and Motivation
In .NET 3.1 we introduced the following intrinsics to expose mulx x86 instruction:
uint MultiplyNoFlags(uint left, uint right, uint* low)ulong MultiplyNoFlags(ulong left, ulong right, ulong* low)
where low is an out parameter that is used to return the lower 32-bit/64-bit part of 64-bit/128-bit result of left * right multiplication while the return value contains the upper part.
When the instrinsics are used the JIT produces sub-optimal code due to the fact that low has "address-taken" attribute.
For example, the following C# methods
staticunsafeuintmulx(uinta,uintb){uintr;returnBmi2.MultiplyNoFlags(a,b,&r)+r;}staticunsafeulongmulx_64(ulonga,ulongb){ulongr;returnBmi2.X64.MultiplyNoFlags(a,b,&r)+r;}will be compiled down to the following code by the current implementation of the JIT
mulx
G_M48748_IG01: ;; offset=0000H50pushrax 33C0 xorrax,rax89442404mov dword ptr [rsp+04H],eax89542418mov dword ptr [rsp+18H],edx ;; bbWeight=1 PerfScore 3.25G_M48748_IG02: ;; offset=000BH 488D442404 learax, bword ptr [rsp+04H] 448B442418 movr8d, dword ptr [rsp+18H] 8BD1 movedx,ecx C4C233F6D0 mulxedx,r9d,r8d448908mov dword ptr [rax],r9d 8BC2 moveax,edx03442404addeax, dword ptr [rsp+04H] ;; bbWeight=1 PerfScore 7.00G_M48748_IG03: ;; offset=0025H 4883C408 addrsp,8 C3 ret
mulx_64
G_M55976_IG01: ;; offset=0000H50pushrax 33C0 xorrax,rax48890424mov qword ptr [rsp],rax4889542418mov qword ptr [rsp+18H],rdx ;; bbWeight=1 PerfScore 3.25G_M55976_IG02: ;; offset=000CH 488D0424 learax, bword ptr [rsp] 4C8B442418 movr8, qword ptr [rsp+18H] 488BD1 movrdx,rcx C4C2B3F6D0 mulxrdx,r9,r8 4C8908 mov qword ptr [rax],r9 488BC2 movrax,rdx48030424addrax, qword ptr [rsp] ;; bbWeight=1 PerfScore 7.00G_M55976_IG03: ;; offset=0027H 4883C408 addrsp,8 C3 ret ;; bbWeight=1 PerfScore 1.25
However, if the Bmi2.MultiplyNoFlags were implemented instead as
publicstaticunsafeuintMultiplyNoFlags(uintleft,uintright,uint*low){varresult=MultiplyNoFlags2(left,right);*low=result.Item1;returnresult.Item2;}publicstaticunsafeulongMultiplyNoFlags(ulongleft,ulongright,ulong*low){varresult=MultiplyNoFlags2(left,right);*low=result.Item1;returnresult.Item2;}the JIT as in #37928 would inline MultiplyNoFlags and be able to remove the address-taken attribute from a local corresponding to low:
mulx
G_M48748_IG01: ;; offset=0000H ;; bbWeight=1 PerfScore 0.00G_M48748_IG02: ;; offset=0000H C4E27BF6D1 mulxedx,eax,ecx 03C2 addeax,edx ;; bbWeight=1 PerfScore 3.25G_M48748_IG03: ;; offset=0007H C3 ret ;; bbWeight=1 PerfScore 1.00
mulx_64
G_M55976_IG01: ;; offset=0000H ;; bbWeight=1 PerfScore 0.00G_M55976_IG02: ;; offset=0000H C4E2FBF6D1 mulxrdx,rax,rcx 4803C2 addrax,rdx ;; bbWeight=1 PerfScore 3.25G_M55976_IG03: ;; offset=0008H C3 ret ;; bbWeight=1 PerfScore 1.00
Proposed API
namespaceSystem.Runtime.Intrinsics.X86{publicabstractclassBmi2:X86Base{publicstatic(uintLower,uintUpper)MultiplyNoFlags2(uintleft,uintright);publicabstractclassX64:X86Base.X64{publicstatic(ulongLower,ulongUpper)MultiplyNoFlags2(ulongleft,ulongright);}}}Based on work Carol did in #37928
cc @CarolEidt@tannergooding
Background and Motivation
In .NET 3.1 we introduced the following intrinsics to expose
mulxx86 instruction:uint MultiplyNoFlags(uint left, uint right, uint* low)ulong MultiplyNoFlags(ulong left, ulong right, ulong* low)where
lowis an out parameter that is used to return the lower 32-bit/64-bit part of 64-bit/128-bit result ofleft * rightmultiplication while the return value contains the upper part.When the instrinsics are used the JIT produces sub-optimal code due to the fact that
lowhas "address-taken" attribute.For example, the following C# methods
will be compiled down to the following code by the current implementation of the JIT
mulx
mulx_64
However, if the
Bmi2.MultiplyNoFlagswere implemented instead asthe JIT as in #37928 would inline
MultiplyNoFlagsand be able to remove the address-taken attribute from a local corresponding tolow:mulx
mulx_64
Proposed API
Based on work Carol did in #37928
cc @CarolEidt@tannergooding