Decimal
constructor source with an int
parameter from the .NET Reference Source: // Constructs a Decimal from an integer value. // public Decimal(int value) { // JIT today can't inline methods that contains "starg" opcode. // For more details, see DevDiv Bugs 81184: x86 JIT CQ: Removing the inline striction of "starg". int value_copy = value; if (value_copy >= 0) { flags = 0; } else { flags = SignMask; value_copy = -value_copy; } lo = value_copy; mid = 0; hi = 0; }
starg
or ldarga
. The decimal constructor is very desirable to be inlined, so the developers of the standard class went to the trick: they copied the parameter to a local variable to avoid the “bad” instruction. In JIT-x64, this feature was removed. For those interested, it is recommended to study:step=1
? private int bar; public void Foo(int step) { for (int i = 0; i < step; i++) { bar = i + 10; for (int j = 0; j < 2 * step; j += step) Console.WriteLine(j + 10); } }
10 11
, but a bug in optimizing JIT-x64 will ruin everything and give us 10 21
. In JIT-x86 and RyuJIT, everything works well. With the bug will have to accept, Microsoft does not want to fix it. The example is very fragile, stumble upon it in real life is extremely problematic. Someone will ask: but if this is a rare bug, then why know about it? Why bother with such things at all? If you are a funny person, you can use the bug for your own purposes. For example, determine which version of JIT is currently used in runtime: public enum JitVersion { Mono, MsX86, MsX64, RyuJit } public class JitVersionInfo { public JitVersion GetJitVersion() { if (IsMono()) return JitVersion.Mono; if (IsMsX86()) return JitVersion.MsX86; if (IsMsX64()) return JitVersion.MsX64; return JitVersion.RyuJit; } private int bar; private bool IsMsX64(int step = 1) { var value = 0; for (int i = 0; i < step; i++) { bar = i + 10; for (int j = 0; j < 2 * step; j += step) value = j + 10; } return value == 20 + step; } public static bool IsMono() { return Type.GetType("Mono.Runtime") != null; } public static bool IsMsX86() { return !IsMono() && IntPtr.Size == 4; } }
for (int i = 0; i < 1024; i++) Foo(i);
for (int i = 0; i < 1024; i += 4) { Foo(i); Foo(i + 1); Foo(i + 2); Foo(i + 3); }
int sum = 0; for (int i = 0; i < 1024; i++) sum += i; Console.WriteLine(sum);
; int sum = 0; 00007FFCC8710090 sub rsp,28h ; for (int i = 0; i < 1024; i++) 00007FFCC8710094 xor ecx,ecx 00007FFCC8710096 mov edx,1 ; edx = i + 1 00007FFCC871009B nop dword ptr [rax+rax] 00007FFCC87100A0 lea eax,[rdx-1] ; eax = i ; sum += i; 00007FFCC87100A3 add ecx,eax ; sum += i 00007FFCC87100A5 add ecx,edx ; sum += i + 1 00007FFCC87100A7 lea eax,[rdx+1] ; eax = i + 2 00007FFCC87100AA add ecx,eax ; sum += i + 2; 00007FFCC87100AC lea eax,[rdx+2] ; eax = i + 3 00007FFCC87100AF add ecx,eax ; sum += i + 3; 00007FFCC87100B1 add edx,4 ; i += 4 ; for (int i = 0; i < 1024; i++) 00007FFCC87100B4 cmp edx,401h 00007FFCC87100BA jl 00007FFCC87100A0
struct Point { public int X; public int Y; } static void Print(Point p) { Console.WriteLine(pX + " " + pY); } static void Main() { var p = new Point(); for (pX = 0; pX < 2; p.X++) Print(p); }
0 1 1 0
produced 2 0 2 0
. Stumble upon it is not so difficult. Fortunately, in the CLR 4 it was corrected, and in other versions of JIT it was not at all. Keep in mind that if you are working under .NET Framework 3.5 (yes, some still have to), this implies CLR2. You need to be prepared that such a simple code will turn into ; var p = new Point(); 05C5178C push esi 05C5178D xor esi,esi ; pY = 0 ; for (pX = 0; pX < 2; p.X++) 05C5178F lea edi,[esi+2] ; pX = 2 ; Print(p); 05C51792 push esi ; push pY 05C51793 push edi ; push pX 05C51794 call dword ptr ds:[54607F4h] ; Print(p) 05C5179A push esi ; push pY 05C5179B push edi ; push pX 05C5179C call dword ptr ds:[54607F4h] ; Print(p) 05C517A2 pop esi 05C517A3 pop edi 05C517A4 pop ebp 05C517A5 ret
int sum = 0; for (int i = 0; i < 4; i++) sum += i; Console.WriteLine(sum);
; int sum = 0; 00007FFCC86F3EC0 sub rsp,28h ; Console.WriteLine(sum); 00007FFCC86F3EC4 mov ecx,6 ; sum = 6 00007FFCC86F3EC9 call 00007FFD273DCF10 00007FFCC86F3ECE nop 00007FFCC86F3ECF add rsp,28h 00007FFCC86F3ED3 ret
Source: https://habr.com/ru/post/252105/
All Articles