none
Bug in .Net CF Just-In-Time-Compiler with Roslyn generated IL-Code [Windows Embedded Compact 2013 with Visual Studio 2015]? RRS feed

  • Question

  • We have a C# application running under Windows CE 6.0 (Compact Framework .Net 3.5) on an Intel Atom Z510 x86 1.1 GHz 512MB RAM.
    After porting this application to Windows Embedded Compact 2013 (Compact Framework .Net 3.9) on an iMX6 (ARM Cortex ™ -A9) 800MHz 2GB RAM, we get an incorrect evaluation with two long variables:

    long v1 = 1L;
    long v2 = 1L;
    bool result = (v1 == 0L) || (v2 == 0L);
    // Old C# Compiler + Compact Framework: result == false ==> CORRECT!!!
    // New Roslyn C# Compiler + Compact Framework: result == true ==> WRONG!!!

    We can recreate the behavior with the following code (if the variables are from type int32 there is no problem):

    // File: Program.cs
    using System;
    
    namespace TestLongOr
    {
    	class Program
    	{
    		static void Main(string[] args)
    		{
    			TestLongOr_Faulty();
    			TestIntOr_Good();
    			Console.ReadLine();
    		}
    		
    		private static void TestLongOr_Faulty()
    		{
    			long v1 = 1L;
    			long v2 = 1L;
    			bool result = (v1 == 0L) || (v2 == 0L);
    			Console.WriteLine("long v1 = {0}; long v2 = {1};", v1, v2);
    			Console.WriteLine("(v1 == 0L) || (v2 == 0L): {0}", result);
    			// Old C# Compiler + Compact Framework: result == false
    			// New Roslyn C# Compiler + Compact Framework: result == true
    		}
    
    		private static void TestIntOr_Good()
    		{
    			int v1 = 1;
    			int v2 = 1;
    			bool result = (v1 == 0) || (v2 == 0);
    			Console.WriteLine("int v1 = {0}; int v2 = {1};", v1, v2);
    			Console.WriteLine("(v1 == 0) || (v2 == 0): {0}", result);
    			// Old C# Compiler + Compact Framework: result == false
    			// New Roslyn C# Compiler + Compact Framework: result == false
    		}
    	}
    }

    We could limit the problem to the generated IL code from the new Rosyln compiler used by Visual Studio 2015 for Windows Embedded Compact 2013 C# projects.

    More accurate:

    Visual Studio 2008 is used to build the Windows CE 6.0 project with the old C# compiler like "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe".
    Visual Studio 2015 is used to build the Windows Embedded Compact 2013 project with the new Roslyn C# compiler "C:\Program Files (x86)\MSBuild\14.0\bin\csc.exe".

    You can create an application that runs on all platforms (Windows CE 6.0, Windows Embedded Compact 2013 and Windows 7 Desktop)
    as long as you specify /reference:"C:\Program Files (x86)\Microsoft.NET\SDK\CompactFramework\v3.5\WindowsCE\mscorlib.dll" (Installed with Visual Studio 2008 and Smart Device Development). 

    If the code is built with the old C# compiler "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe", it runs on all platforms (Windows CE 6.0, Windows Embedded Compact 2013, Windows 7 Desktop) correctly.

    Run command in Command Prompt:

    "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe" /noconfig /nowarn:1701,1702,2008 /nostdlib+ /platform:AnyCPU /errorreport:prompt /warn:4 /define:DEBUG;TRACE /errorendlocation /preferreduilang:en-US /reference:"C:\Program Files (x86)\Microsoft.NET\SDK\CompactFramework\v3.5\WindowsCE\mscorlib.dll" /filealign:512 /optimize- /out:TestLongOr35_csc4.exe /target:exe /utf8output Program.cs

    The error occurs under Windows CE 6.0 and Windows Embedded Compact 2013 with the new Roslyn C# compiler ("C:\Program Files (x86)\MSBuild\14.0\bin\csc.exe"). 
    On Windows 7 Desktop, the error with the identical exe does not occur.

    Run command in Command Prompt (Visual Studio 2015 required):

    "C:\Program Files (x86)\MSBuild\14.0\bin\csc.exe" /noconfig /nowarn:1701,1702,2008 /nostdlib+ /platform:AnyCPU /errorreport:prompt /warn:4 /define:DEBUG;TRACE /errorendlocation /preferreduilang:en-US /reference:"C:\Program Files (x86)\Microsoft.NET\SDK\CompactFramework\v3.5\WindowsCE\mscorlib.dll" /filealign:512 /optimize- /out:TestLongOr35_csc14.exe /target:exe /utf8output Program.cs

    The only differnces we found in the generated IL code is "brfalse.s" in line IL_0008:

    .method private hidebysig static 
    	void TestLongOr_Faulty () cil managed 
    {
    	// Method begins at RVA 0x2068
    	// Code size 63 (0x3f)
    	.maxstack 3
    	.locals init (
    		[0] int64,
    		[1] int64,
    		[2] bool
    	)
    
    	IL_0000: nop
    	IL_0001: ldc.i4.1
    	IL_0002: conv.i8
    	IL_0003: stloc.0
    	IL_0004: ldc.i4.1
    	IL_0005: conv.i8
    	IL_0006: stloc.1
    	IL_0007: ldloc.0
    	IL_0008: ldc.i4.0 // <== old compiler
    	IL_0009: conv.i8  // <== old compiler
    	IL_000a: beq.s IL_0013 // <== old compiler
    
    	IL_000c: ldloc.1
    	IL_000d: ldc.i4.0
    	IL_000e: conv.i8
    	IL_000f: ceq
    	IL_0011: br.s IL_0014
    
    	IL_0013: ldc.i4.1
    
    	IL_0014: nop
    	IL_0015: stloc.2
    	IL_0016: ldstr "long v1 = {0}; long v2 = {1};"
    	IL_001b: ldloc.0
    	IL_001c: box [mscorlib]System.Int64
    	IL_0021: ldloc.1
    	IL_0022: box [mscorlib]System.Int64
    	IL_0027: call void [mscorlib]System.Console::WriteLine(string, object, object)
    	IL_002c: nop
    	IL_002d: ldstr "(v1 == 0L) || (v2 == 0L): {0}"
    	IL_0032: ldloc.2
    	IL_0033: box [mscorlib]System.Boolean
    	IL_0038: call void [mscorlib]System.Console::WriteLine(string, object)
    	IL_003d: nop
    	IL_003e: ret
    } // end of method Program::TestLongOr_Faulty
    .method private hidebysig static 
    	void TestLongOr_Faulty () cil managed 
    {
    	// Method begins at RVA 0x2068
    	// Code size 60 (0x3c)
    	.maxstack 3
    	.locals init (
    		[0] int64,
    		[1] int64,
    		[2] bool
    	)
    
    	IL_0000: nop
    	IL_0001: ldc.i4.1
    	IL_0002: conv.i8
    	IL_0003: stloc.0
    	IL_0004: ldc.i4.1
    	IL_0005: conv.i8
    	IL_0006: stloc.1
    	IL_0007: ldloc.0
    	IL_0008: brfalse.s IL_0011 // new Roslyn compiler
    
    	IL_000a: ldloc.1
    	IL_000b: ldc.i4.0
    	IL_000c: conv.i8
    	IL_000d: ceq
    	IL_000f: br.s IL_0012
    
    	IL_0011: ldc.i4.1
    
    	IL_0012: stloc.2
    	IL_0013: ldstr "long v1 = {0}; long v2 = {1};"
    	IL_0018: ldloc.0
    	IL_0019: box [mscorlib]System.Int64
    	IL_001e: ldloc.1
    	IL_001f: box [mscorlib]System.Int64
    	IL_0024: call void [mscorlib]System.Console::WriteLine(string, object, object)
    	IL_0029: nop
    	IL_002a: ldstr "(v1 == 0L) || (v2 == 0L): {0}"
    	IL_002f: ldloc.2
    	IL_0030: box [mscorlib]System.Boolean
    	IL_0035: call void [mscorlib]System.Console::WriteLine(string, object)
    	IL_003a: nop
    	IL_003b: ret
    } // end of method Program::TestLongOr_Faulty


    Can this be confirmed on other CF .Net Platforms?

    Current update 19.04.2017:
    Microsoft was able to reproduce the problem: "[...]it’s generated by an exception in .NET CF code during comparison.[...]"
    • Edited by GKindler Wednesday, April 19, 2017 11:28 AM New information
    Wednesday, March 29, 2017 1:24 PM

Answers

  • Hi Maik,

    see Windows Embedded Compact 2013 Update 44 (July 2017)

    https://support.microsoft.com/en-us/help/4034055/fix-incorrect-evaluation-of-long-variables-64-bit-in-windows-embedded

    Microsoft has a patch, but you have to get the patch from Microsoft. Our reference was:

    [REG:117033115536280] Possible bug in .Net CF Just-In-Time-Compiler with Roslyn generated IL-Code [Windows Embedded Compact 2013 with Visual Studio 2015]

    Background information:

    This issue occurs in only a very specific case where all the below conditions are true:

    - Variable is of type long int.
    - It is checked for equality with constant 0
    - Based on the check it must branch to another instruction
    - The check statement is of shorthand type

    Alternatively you can perform syntax changes in C#:

    1. Use 'Equals()' function to check equality instead of '==' operator.

    E.g.
    long v1 = 1L;
    long v2 = 1L;
    bool result = v1.Equals(0L) || v2.Equals(0L);

    2. Assign 0L to a variable and then use that variable in the comparison, instead of directly comparing with 0L.

    E.g.
    readonly long zero = 0L;
    long v1 = 1L;
    long v2 = 1L;
    bool result = (v1 == zero) || (v2 == zero);

    Sincerely,

    GKindler


    • Marked as answer by GKindler Wednesday, September 12, 2018 6:31 AM
    • Edited by GKindler Tuesday, September 25, 2018 8:55 AM Link to bugfix added
    Wednesday, September 12, 2018 6:31 AM

All replies

  • Hi GKindler,

    I have forwarded this question to the Sustained Engineering team but recommend that you report this through your license reseller as well to enable appropriate tracking.

    Sincerely,

    IoTGirl

    Wednesday, March 29, 2017 7:50 PM
    Moderator
  • Hi IotGirl,

    Thanks for forwarding!

    For a workaround we replaced the roslyn compiler with the old compiler with following changes to MSBuild:

    1.) Add msbuildToolsets  in C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe.config

    <!-- To define one or more new toolsets, add an 'msbuildToolsets' element in this file. -->
    <!-- see: https://msdn.microsoft.com/de-de/library/bb397428(v=vs.110).aspx -->
    <msbuildToolsets>
        <toolset toolsVersion="14.0">
            <property name="MSBuildToolsPath" value="C:\Windows\Microsoft.NET\Framework\v4.0.30319\" />
        </toolset>
    </msbuildToolsets>

    2.) Add following properties to a PropertyGroup in all project files:

    <UseSharedCompilation>False</UseSharedCompilation>
    <CodeAnalysisRuleset>X.ruleset</CodeAnalysisRuleset>

    OR

    We added this lines to C:\Program Files (x86)\MSBuild\Microsoft\WindowsEmbeddedCompact\v8.0\Microsoft.WindowsEmbeddedCompact.Common.targets

    so we didn't need to change all our project files.

    But of course we can no longer use the new language features for C# :-(

    Friday, March 31, 2017 7:07 AM
  • Hi GKindler,

    Have you reported this through your reseller? They can actually create an incident with Microsoft and track the progress for you.

    Sincerely,

    IoTGirl

    Friday, March 31, 2017 7:10 AM
    Moderator
  • Hi IoTGirl,

    yes we have forwarded it now. I also want to report this through my MSDN subscription support. However, I am still waiting for activation for RegForm@msdirectservices.com.

    Sincerely,

    GKindler

    Friday, March 31, 2017 7:32 AM
  • Hi GKinder,

    The reseller is your best option for tracking.  They represent not only you but all of the other consumers of Compact 2013 through their channel.

    Many thanks for reporting through your reseller and please do continue to ask them to update you on their progress on the issue.

    Sincerely,

    IoTGirl

    PS: Please do ensure all updates are applied to all tools you are leveraging!

    Friday, March 31, 2017 3:58 PM
    Moderator
  • Hi GKinder,

    I would like you to potentially try another reporting option, Visual Studio.  http://connect.microsoft.com/VisualStudio/content/content.aspx?ContentID=31113

    Please give the feedback focusing on the different JIT compiler results for ARM between the versions of Visual Studio, in this case VS 2012/2013 and VS 2015 rather than Focusing on the Windows Embedded Compact target.

    This method should allow the VS team to contact you directly.

    Sincerely,

    IoTGirl

    Tuesday, April 4, 2017 3:17 PM
    Moderator
  • Hi IoTGirl,

    after getting the confirmation from microsoft (it's a bug in CF), I gave feedback over Visual Studio (with mention of the open ticket).

    The bug has nothing to do with ARM or Intel processors. If I build a sample application for CF 3.5 or 3.9, the wrong result is the same for ARM and Intel hardware.

    Now I am waiting for a correction of the bug. I will report again when I hear something new about the issue.

    Sincerely,

    GKindler


    Wednesday, April 19, 2017 12:32 PM
  • Hi GKinder,

    The SE team has not received the request.  Do you have a reference I could leverage to track it down?

    Sincerely,

    IoTGirl

    Thursday, April 20, 2017 5:06 PM
    Moderator
  • Hi IoTGirl,

    the reference is:

    RE: [REG:117033115536280] Possible bug in .Net CF Just-In-Time-Compiler with Roslyn generated IL-Code [Windows Embedded Compact 2013 with Visual Studio 2015]

    Sincerely,

    GKindler


    Friday, April 21, 2017 5:27 AM
  • Thanks GKinder. I will see if I can find that reference internally.
    Friday, April 21, 2017 6:23 AM
    Moderator
  • Hi IoTGirl,

    is there any update on the issue? Is a fix for roslyn available?

    I have the same issues as the thread opener. We are porting our .NET CF 3.5 application to a new device using windows embedded compact 2013.

    Today i found a strange issue with our synchronization mechanism. The version of local data is compared against 0 which returns true even if the version variable is above zero. The variable is of type long which brought me here.

    Would be good to get some help

    Thanks

    Maik

    Tuesday, September 11, 2018 3:58 PM
  • Hi Maik,

    As direction was given to the previous poster, you will need to go through your license reseller to log an issue with Microsoft.  The Windows CE Team doesn't support the Roslyn Compiler but VS does.  If you get a case logged through your reseller, you can get tracking for your issue.

    Note: The compiler is from VS 2012 so if you have an installed version of 2012 with updates it should have the latest compiler.

    Sincerely,

    IoTGirl

    Tuesday, September 11, 2018 4:12 PM
    Moderator
  • Hi Maik,

    see Windows Embedded Compact 2013 Update 44 (July 2017)

    https://support.microsoft.com/en-us/help/4034055/fix-incorrect-evaluation-of-long-variables-64-bit-in-windows-embedded

    Microsoft has a patch, but you have to get the patch from Microsoft. Our reference was:

    [REG:117033115536280] Possible bug in .Net CF Just-In-Time-Compiler with Roslyn generated IL-Code [Windows Embedded Compact 2013 with Visual Studio 2015]

    Background information:

    This issue occurs in only a very specific case where all the below conditions are true:

    - Variable is of type long int.
    - It is checked for equality with constant 0
    - Based on the check it must branch to another instruction
    - The check statement is of shorthand type

    Alternatively you can perform syntax changes in C#:

    1. Use 'Equals()' function to check equality instead of '==' operator.

    E.g.
    long v1 = 1L;
    long v2 = 1L;
    bool result = v1.Equals(0L) || v2.Equals(0L);

    2. Assign 0L to a variable and then use that variable in the comparison, instead of directly comparing with 0L.

    E.g.
    readonly long zero = 0L;
    long v1 = 1L;
    long v2 = 1L;
    bool result = (v1 == zero) || (v2 == zero);

    Sincerely,

    GKindler


    • Marked as answer by GKindler Wednesday, September 12, 2018 6:31 AM
    • Edited by GKindler Tuesday, September 25, 2018 8:55 AM Link to bugfix added
    Wednesday, September 12, 2018 6:31 AM