Well, I have been watching this thread to see what comes up. Tonight I took
a shot at it and after about 45min of playing around this is what I have
come up with. Unfortunately there is nothing conclusive other than it does
appear to be a JIT bug, but hey everyone guessed that already :). And my
evaluation of the code might be off since it was just a quick scan of what
is happening.
Below is the non-optimized JIT code generated for the expression. I have
included the IL instuctions that map to the JIT code and you will notice
that the ldloc instructions which load the variable b onto the evaluation
stack move data from the esi to the dsi. Now notice the ldloc at label
IL_000c in this case the JIT code loads the same value into the edi and esi
registers, this throws the evaluation stack out and causing the chaos.
For the other expressions the only difference is that this particular
section of the code is not executed and the problem is hidden, but still
exists. The bug does appear to be with the JIT, and it would appear that the
short circuit evaluation is in face hidding the problem rather than causing
it, I say this because of the short circuit evaluation the erroneous code is
not executed in cases where the variable is true. This is also true for the
case where the brackets are removed from the expression.
It would be very interesting to isolate the exact senarios where this
erroneous code is generated, but I have confirmed that it is when a
non-primitive value type (ie. Decimal, DateTime or User Defined value types)
exists on the evaluation stack prior to the expression that this code is
generated.
IL_0003: ldc.i4.3
0000001c mov esi,3
IL_0004: newobj instance void
[mscorlib]System.Decimal::.ctor(int32)
00000021 lea ecx,[ebp-18h]
00000024 mov edx,esi
00000026 call dword ptr ds:[79C41A78h]
IL_0009: ldloc.0
0000002c mov dword ptr [ebp-1Ch],ebx
0000002f mov dword ptr [ebp-20h],edi
00000032 lea edi,[ebp-30h]
00000035 lea esi,[ebp-18h]
00000038 movs dword ptr [edi],dword ptr [esi]
00000039 movs dword ptr [edi],dword ptr [esi]
0000003a movs dword ptr [edi],dword ptr [esi]
0000003b movs dword ptr [edi],dword ptr [esi]
IL_000a: brtrue.s IL_000f
0000003c cmp dword ptr [ebp-1Ch],0
00000040 jne 0000005B
//------------Here is here I believe the problem is in the JIT code
IL_000c: ldloc.0
00000042 mov dword ptr [ebp-64h],ebx
00000045 mov eax,dword ptr [ebp-20h]
00000048 mov dword ptr [ebp-20h],eax
0000004b lea edi,[ebp-30h] //<------- Prblem on this and next line,
edi/esi are loaded with same value so
0000004e lea esi,[ebp-30h] // evaluation stack is not adjusted
00000051 movs dword ptr [edi],dword ptr [esi]
00000052 movs dword ptr [edi],dword ptr [esi]
00000053 movs dword ptr [edi],dword ptr [esi]
00000054 movs dword ptr [edi],dword ptr [esi]
//---------------------------------------------------------------
IL_000d: brfalse.s IL_0018
00000055 cmp dword ptr [ebp-64h],0
00000059 je 000000CC
IL_000f: ldloc.0
0000005b mov dword ptr [ebp-34h],ebx
0000005e mov eax,dword ptr [ebp-20h]
00000061 mov dword ptr [ebp-38h],eax
00000064 lea edi,[ebp-48h]
00000067 lea esi,[ebp-30h]
0000006a movs dword ptr [edi],dword ptr [esi]
0000006b movs dword ptr [edi],dword ptr [esi]
0000006c movs dword ptr [edi],dword ptr [esi]
0000006d movs dword ptr [edi],dword ptr [esi]
IL_0010: brfalse.s IL_0018
--
Chris Taylor
http://dotnetjunkies.com/weblog/chris.taylor
"Frans Bouma [C# MVP]" <perseus.usenetNOSPAM@xs4all.nl> wrote in message
news:#CcXc8hUFHA.2124@TK2MSFTNGP14.phx.gbl...[color=blue]
> Gianluca wrote:[color=green]
> > the optmizer cannot know that b will never be true, the same code[/color][/color]
doesn't[color=blue][color=green]
> > work if b is evaluated at runtime.[/color]
>
> it knows b is false, it's set to false at the start. (b || b) &&...
> will then evaluate to false, no matter what. If the code sets b to true,
> no bug appears, which IMHO shows that the bug is likely in the shortcut
> code to evaluate boolean expressions.
>[color=green]
> > actually it's enough to remove the parenthesis and the code works![/color]
>
> Hmm... that's indeed strange.
> you could argue that
> b || b && exp
> is different from
> (b || b) && exp
>
> as the JIT could see it as:
> b || (b && exp)
> vs.
> (b || b) && exp
>
> though that would trigger the question: why does it resolve (b || b)
> but not b || b ?
>
> FB
>[color=green]
> >
> >
> > "Frans Bouma [C# MVP]" <perseus.usenetNOSPAM@xs4all.nl> wrote in message
> > news:OlC78UVUFHA.2420@TK2MSFTNGP12.phx.gbl...
> >[color=darkred]
> >>Daniel O'Connell [C# MVP] wrote:
> >>
> >>>"Gianluca" <gianluca@nospam.com> wrote in message
> >>>news:uX8ee.26042$lZ.22675@trnddc04...
> >>>
> >>>
> >>>>No, you are right. I was looking at a bool cast that my code applies.[/color][/color][/color]
I[color=blue][color=green][color=darkred]
> >>>>was
> >>>>checking the MSIL from the original code and it's more complex that[/color][/color][/color]
the[color=blue][color=green][color=darkred]
> >>>>sample expression. I just checked the MSIL from the test code and[/color][/color][/color]
there[color=blue][color=green][color=darkred]
> >>>>is
> >>>>no visible difference (other than the assigment) between the b = false
> >>>>(bug)
> >>>>and the b = true (no bug) calls. Looking at the MSIL below is it[/color]
> >
> > possible
> >[color=darkred]
> >>>>that when L_0019 is reached the first argument on the stack has been
> >>>>truncated?
> >>>>
> >>>>How can it be the JIT if the same code works when b = true?
> >>>
> >>>
> >>>I would guess its an optimization bug where the JIT spits out code[/color][/color][/color]
based[color=blue][color=green]
> >
> > on
> >[color=darkred]
> >>>a stack value it already knows(its possible to determine what code will
> >>>execute statically after all), but the bug could just be somewhere on[/color]
> >
> > the
> >[color=darkred]
> >>>path that is followed when the boolean is
> >>
> >>After reading this post, I think indeed it's the JIT and not the
> >>compiler optimization (first thought the compiler was it)
> >>
> >>I now also think it's jit optimization related where it knows that the
> >>boolean expression can never be true, because b is false. It therefore
> >>pops operators and operands from the stack (that's a guess) and it does
> >>that wrong, so too less data is left on the stack.
> >>
> >>making b is true fixes it, which means that the optimization trick '(b
> >>|| b) &&.. can never be true' can't be used, and hte complete expression
> >>has to be evaluated, avoiding the popping and therefore will result in
> >>working code.
> >>
> >>workaround: evaluate the expression into a variable, then pass that
> >>variable.[/color][/color]
>
> --
> ------------------------------------------------------------------------
> Get LLBLGen Pro, productive O/R mapping for .NET:
http://www.llblgen.com
> My .NET blog:
http://weblogs.asp.net/fbouma
> Microsoft MVP (C#)
> ------------------------------------------------------------------------[/color]