Your .NET applications can start being more native

I was interested in trying out the dotnet command line interface and seeing how it all works. Microsoft have after all just told us that they will be delaying the release of DNX to allow the integration into the CLI model. You can download a build of the comand line tools from here and it’s really easy to get going.
You can generate a demonstration “hello world” project using
dotnet new
You then get all of the packages it depends on using
dotnet restore
And then build it using
dotnet compile
Now it’s business as usual and you can run it as normal
cd bin\Debug\dnxcore50
dotnet2.exe [named as this because I was in a folder named dotnet2 when I created the project]
What excited me more about the command line tools though, is that they now have started offering the opportunity to compile .NET applications to native code. Be warned though that this is very early functionality and they only guarantee support for small hello world applications.
You can choose to compile using an ahead of time version of the normal JIT compiler [on x64] or can choose to go via generated C++ code. I wanted to see the kind of native code that can be produced and therefore chose the latter option.
dotnet compile –native –cpp
[Note that you have to be in a VS2015 x64 Native Tools command window to get the right tools available on the PATH]
This generates an executable in a native subdirectory of bin\debug\dnxcore50\native
which runs very quickly – there’s no jitting in order for the application to get running, and it is noticeably quicker on my fairly old laptop.
The demo application is a very simple hello world, and you can find the emitted c++ in the directory
I was interested in how the GC got linked into the project, particularly as I had heard of
CoreRT and couldn’t see any appropriately named dll when I attached windbg to the running executable.
I therefore modified the code to generate garbage [and built it in a folder named dotnet so ended up with an application named dotnet.exe]
public static void Main(string[] args)
Console.WriteLine(“Hello World!”);
for (int i =0; i < 100000; i++)
var x = new object();
The generated Main method takes the form
#line 8 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
void dotnet::ConsoleApplication::Program::Main(System_Private_CoreLib::System::String__Array* args){int32_t i=0; System_Private_CoreLib::System::Object* x=0; uint8_t _l2=0; _bb0: {
#line 8 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
#line 9 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
void* _1=__load_string_literal(“Hello World!”); System_Console::System::Console::WriteLine_13((System_Private_CoreLib::System::String*)_1);
#line 10 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
i=0; { goto _bb28; }; } _bb16: {
#line 11 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
#line 12 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
void* _8=__allocate_object(System_Private_CoreLib::System::Object::__getMethodTable()); System_Private_CoreLib::System::Object::_ctor((System_Private_CoreLib::System::Object*)_8); x=(System_Private_CoreLib::System::Object*)_8;
#line 13 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
#line 10 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
int32_t _10=i; int32_t _11=_10+1; i=_11; } _bb28: {
#line 10 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
int32_t _3=i; int32_t _4=_3<100000; _l2=_4; int32_t _6=_l2; if (_6!=0) { goto _bb16; }; } _bb40: {
#line 14 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
void* _7=System_Console::System::Console::ReadLine();
#line 15 “C:\\Users\\clive.tong\\Desktop\\dotnet\\Program.cs”
return; } }
Unfortunately, running the application with a debugger attached I got an access violation
00007ff6`c2a4a460 4d8b01          mov     r8,qword ptr [r9] ds:0000003c`00032000=????????????????
0:000> k
# Child-SP          RetAddr           Call Site
00 0000003c`7715f3c0 00007ff6`c2a4ae60 dotnet!WKS::gc_heap::mark_object_simple1+0x180
01 0000003c`7715f430 00007ff6`c2a2e850 dotnet!WKS::gc_heap::mark_object_simple+0x1e0
02 0000003c`7715f480 00007ff6`c2a283be dotnet!WKS::GCHeap::Promote+0x90
03 0000003c`7715f4b0 00007ff6`c2a21aa9 dotnet!GcBulkEnumObjects+0x2e
04 0000003c`7715f4e0 00007ff6`c2a14c6c dotnet!Module::EnumStaticGCRefs+0x69
05 0000003c`7715f540 00007ff6`c2a4b233 dotnet!RuntimeInstance::EnumAllStaticGCRefs+0x6c
06 0000003c`7715f5a0 00007ff6`c2a43606 dotnet!WKS::gc_heap::mark_phase+0x193
07 0000003c`7715f630 00007ff6`c2a432e3 dotnet!WKS::gc_heap::gc1+0xd6
08 0000003c`7715f690 00007ff6`c2a2d823 dotnet!WKS::gc_heap::garbage_collect+0x753
09 0000003c`7715f6f0 00007ff6`c2a5a629 dotnet!WKS::GCHeap::GarbageCollectGeneration+0x303
0a 0000003c`7715f740 00007ff6`c2a2c4ee dotnet!WKS::gc_heap::try_allocate_more_space+0x1b9
0b 0000003c`7715f780 00007ff6`c2a194ac dotnet!WKS::GCHeap::Alloc+0x5e
0c 0000003c`7715f7b0 00007ff6`c29d9b53 dotnet!RhpNewFast+0x5c
0d 0000003c`7715f7e0 00007ff6`c29d3132 dotnet!System_Private_CoreLib::System::Runtime::InternalCalls::RhpNewFast+0x13 [c:\users\clive.tong\desktop\dotnet\obj\debug\dnxcore50\native\dotnet.cpp @ 39893]
0e 0000003c`7715f810 00007ff6`c29ee993 dotnet!System_Private_CoreLib::System::Runtime::RuntimeExports::RhNewObject+0x92 [c:\users\clive.tong\desktop\dotnet\obj\debug\dnxcore50\native\dotnet.cpp @ 37660]
0f 0000003c`7715f890 00007ff6`c29dc783 dotnet!RhNewObject+0x13 [c:\users\clive.tong\desktop\dotnet\obj\debug\dnxcore50\native\dotnet.cpp @ 37666]
10 0000003c`7715f8c0 00007ff6`c29d26fe dotnet!dotnet::ConsoleApplication::Program::Main+0x53 [c:\users\clive.tong\desktop\dotnet\program.cs @ 12]
11 0000003c`7715f930 00007ff6`c29ef1ea dotnet!dotnet::_Module_::StartupCodeMain+0x4e [c:\users\clive.tong\desktop\dotnet\obj\debug\dnxcore50\native\dotnet.cpp @ 37467]
12 0000003c`7715f980 00007ff6`c2a62718 dotnet!main+0x4a [c:\users\clive.tong\desktop\dotnet\program.cs @ 5676]
13 (Inline Function) ——–`——– dotnet!invoke_main+0x22 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 74]
14 0000003c`7715f9d0 00007ff9`d5872d92 dotnet!__scrt_common_main_seh+0x124 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 264]
15 0000003c`7715fa10 00007ff9`d5b39f64 KERNEL32!BaseThreadInitThunk+0x22
16 0000003c`7715fa40 00000000`00000000 ntdll!RtlUserThreadStart+0x34
Notice that the source locations show that many of the frames are compiled versions of the C++ code that we found in the obj directory, but the other parts of the runtime are linked in and there is no source location.
Running the command line with the -v option, you can see the files that are passed into the cpp compiler, and you can see that lots of them come from the dotnet sdk directory
Running C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\..\..\VC\bin\amd64\link.exe “/NOLOGO” “/DEBUG” “/MANIFEST:NO” “/IGNORE:4099” “/out:C:\Users\clive.tong\Desktop\dotnet\bin\Debug\dnxcore50\native\dotnet.exe” “kernel32.lib” “user32.lib” “gdi32.lib” “winspool.lib” “comdlg32.lib” “advapi32.lib” “shell32.lib” “ole32.lib” “oleaut32.lib” “uuid.lib” “odbc32.lib” “odbccp32.lib” “C:\Program Files\dotnet\bin\sdk\PortableRuntime.lib” “C:\Program Files\dotnet\bin\sdk\bootstrappercpp.lib” “/MACHINE:x64” “C:\Users\clive.tong\Desktop\dotnet\obj\Debug\dnxcore50\native\dotnet.obj”
The PortableRuntime here is the actual CoreRT code. If you clone the CoreRT project and build using the build.cmd script, you can indeed make your own version of this SDK.
It includes code such as a garbage collector and a runtime that is fairly sophisticated, containing such features as thread hijacking which is implemented in this file . There are loads of interesting comments if you browse the code.
It will be interesting to see how this portable runtime turns out… it’s certainly true that no jitting makes start up time much better, but it isn’t clear to me how features like Reflection are going to be supported. It will also be interesting to see how the debugging experience works when debugging AOT compiled code.
While we are on the subject of .NET, I’d like to recommend these talks from NDC London. A talk about the new CLI and a talk on the history of ASP.NET which goes into some detail about how things have changed over the years. There is also this talk on the implementation of SignalR from several years ago.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s