-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
As of .NET Core 2.1, JIT produces some code when UInt32 / UInt64 value is compared >= 0 or < 0 even when optimization is turned on.
>= 0 comparisons can be optimized away as they're always evaluated to true since UInt32 / UInt64 values are never negative. (That is, of course, respecting any side effects when obtaining the value to compare with.)
The same applies to < 0 comparisons - they're always evaluated to false in the same manner.
Besides, the codegen (see repro code below) is pointless since TEST instruction always sets CF=0, and both JB and JAE instructions analyse just that CF flag.
This can be confirmed at https://www.felixcloutier.com/x86/Jcc.html , https://www.felixcloutier.com/x86/TEST.html , or any proper x86/x64 ISA manual.
Notes above also apply to enum values having UInt32 / UInt64 as their underlying type.
There is similar behavior when value being compared is of UInt8 or UInt16 types, but I'm not sure whether this should be reported as related to CLR or to C# compiler optimization, since there is intermediate conversion thru Int32 for these types in CLR.
Repro:
enum SEnum : uint { SEnum1 = 1 }
[MethodImpl(MethodImplOptions.NoInlining)]
static void Test(uint u32, ulong u64, SEnum x)
{
if (u32 >= 0) Console.WriteLine("{0}", u32);
if (u64 >= 0) Console.WriteLine("{0}", u64);
if (x >= 0) Console.WriteLine("{0}", x);
if (u32 < 0) Console.WriteLine("{0}", u32);
if (u64 < 0) Console.WriteLine("{0}", u64);
if (x < 0) Console.WriteLine("{0}", x);
}
ILDASM:
.method private hidebysig static void Test(uint32 u32,
uint64 u64,
valuetype _100GbSort.Program2/SEnum x) cil managed noinlining
{
// Code size 123 (0x7b)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: blt.un.s IL_0014
IL_0004: ldstr "{0}"
IL_0009: ldarg.0
IL_000a: box [System.Runtime]System.UInt32
IL_000f: call void [System.Console]System.Console::WriteLine(string,
object)
IL_0014: ldarg.1
IL_0015: ldc.i4.0
IL_0016: conv.i8
IL_0017: blt.un.s IL_0029
IL_0019: ldstr "{0}"
IL_001e: ldarg.1
IL_001f: box [System.Runtime]System.UInt64
IL_0024: call void [System.Console]System.Console::WriteLine(string,
object)
IL_0029: ldarg.2
IL_002a: ldc.i4.0
IL_002b: blt.un.s IL_003d
IL_002d: ldstr "{0}"
IL_0032: ldarg.2
IL_0033: box _100GbSort.Program2/SEnum
IL_0038: call void [System.Console]System.Console::WriteLine(string,
object)
IL_003d: ldarg.0
IL_003e: ldc.i4.0
IL_003f: bge.un.s IL_0051
IL_0041: ldstr "{0}"
IL_0046: ldarg.0
IL_0047: box [System.Runtime]System.UInt32
IL_004c: call void [System.Console]System.Console::WriteLine(string,
object)
IL_0051: ldarg.1
IL_0052: ldc.i4.0
IL_0053: conv.i8
IL_0054: bge.un.s IL_0066
IL_0056: ldstr "{0}"
IL_005b: ldarg.1
IL_005c: box [System.Runtime]System.UInt64
IL_0061: call void [System.Console]System.Console::WriteLine(string,
object)
IL_0066: ldarg.2
IL_0067: ldc.i4.0
IL_0068: bge.un.s IL_007a
IL_006a: ldstr "{0}"
IL_006f: ldarg.2
IL_0070: box _100GbSort.Program2/SEnum
IL_0075: call void [System.Console]System.Console::WriteLine(string,
object)
IL_007a: ret
} // end of method Program2::Test
Disasm:
if (u32 >= 0) Console.WriteLine("{0}", u32);
000007FE74007590 push rdi
000007FE74007591 push rsi
000007FE74007592 push rbx
000007FE74007593 sub rsp,20h
000007FE74007597 mov esi,ecx
000007FE74007599 mov rdi,rdx
000007FE7400759C mov ebx,r8d
000007FE7400759F test esi,esi <=====================
000007FE740075A1 jb 000007FE740075C5 <=====================
000007FE740075A3 mov rcx,7FECA2519E0h
000007FE740075AD call 000007FED3B022B0
000007FE740075B2 mov rdx,rax
000007FE740075B5 mov dword ptr [rdx+8],esi
000007FE740075B8 mov rcx,qword ptr [123B3158h]
000007FE740075C0 call 000007FE74003750
if (u64 >= 0) Console.WriteLine("{0}", u64);
000007FE740075C5 test rdi,rdi <=====================
000007FE740075C8 jb 000007FE740075ED <=====================
000007FE740075CA mov rcx,7FECA251B10h
000007FE740075D4 call 000007FED3B022B0
000007FE740075D9 mov rdx,rax
000007FE740075DC mov qword ptr [rdx+8],rdi
000007FE740075E0 mov rcx,qword ptr [123B3158h]
000007FE740075E8 call 000007FE74003750
if (x >= 0) Console.WriteLine("{0}", x);
000007FE740075ED test ebx,ebx <=====================
000007FE740075EF jb 000007FE74007613 <=====================
000007FE740075F1 mov rcx,7FE73EEB350h
000007FE740075FB call 000007FED3B022B0
000007FE74007600 mov rdx,rax
000007FE74007603 mov dword ptr [rdx+8],ebx
000007FE74007606 mov rcx,qword ptr [123B3158h]
000007FE7400760E call 000007FE74003750
if (u32 < 0) Console.WriteLine("{0}", u32);
000007FE74007613 test esi,esi <=====================
000007FE74007615 jae 000007FE74007639 <=====================
000007FE74007617 mov rcx,7FECA2519E0h
000007FE74007621 call 000007FED3B022B0
000007FE74007626 mov rdx,rax
000007FE74007629 mov dword ptr [rdx+8],esi
000007FE7400762C mov rcx,qword ptr [123B3158h]
000007FE74007634 call 000007FE74003750
if (u64 < 0) Console.WriteLine("{0}", u64);
000007FE74007639 test rdi,rdi <=====================
000007FE7400763C jae 000007FE74007661 <=====================
000007FE7400763E mov rcx,7FECA251B10h
000007FE74007648 call 000007FED3B022B0
000007FE7400764D mov rdx,rax
000007FE74007650 mov qword ptr [rdx+8],rdi
000007FE74007654 mov rcx,qword ptr [123B3158h]
000007FE7400765C call 000007FE74003750
if (x < 0) Console.WriteLine("{0}", x);
000007FE74007661 test ebx,ebx <=====================
000007FE74007663 jae 000007FE74007687 <=====================
000007FE74007665 mov rcx,7FE73EEB350h
000007FE7400766F call 000007FED3B022B0
if (x < 0) Console.WriteLine("{0}", x);
000007FE74007674 mov rdx,rax
000007FE74007677 mov dword ptr [rdx+8],ebx
000007FE7400767A mov rcx,qword ptr [123B3158h]
000007FE74007682 call 000007FE74003750
000007FE74007687 nop
000007FE74007688 add rsp,20h
000007FE7400768C pop rbx
000007FE7400768D pop rsi
000007FE7400768E pop rdi
000007FE7400768F ret
category:cq
theme:range-check
skill-level:intermediate
cost:medium