[Mono-list] MCS -cppoutput C# to C++ generator work in progress

Ben Cooley bencooley@cinematix.com
Thu, 4 Jul 2002 20:05:13 +0900


Since porting last may's rev of mono to MSVC , I've been working on the C++
output module for mcs.  Basically, cppoutput.cs is added to the mcs core,
and from the parse tree generated by the main compiler, it generates a
compatible set of .cpp, .h, and .inl files complete with nice #line
directives for .cs debugging, and with the limited set of statements
compiling, it seems to work nicely.

I tweaked mono's and binary vtable representations to coexist with C++
efficiently so that mono could easily marshall and call c++ code as if it
was doing an "internal_call", and c++ could call mono as well using
mono_execute().  The solution I came up with was to put mono's vtable
"before" the vtable structure, and the c++ vtable after it.   When you
inherit in C++ from the Object class, you automatically get a set of 6
vtable entries which reserve space in the C++ vtable for the mono vtable
structure.  That way mono objects are C++ objects, and vica-versa, and each
side conveniently marshals to the other transparently.

This seems to be efficient, and works rather well, with minimal amount of
code change (changes to -vtable offsets in x86.brg, mint.c and object.c, and
changes to object_new_specific(), the mono vtable initializer, and the class
initializer.

The cool bit is how C++ metadata is handled in -cppoutput code.  The idea is
to simply replace the code in each function with "external", with the
interal call attribute, let "msc" compile the resultant code, and then grab
the .exe or .dll file off the disk and incorporate it into the registration
code in the primary .cpp file.  The present version doesn't do this, but I
do it by hand, and add the data into the .cpp files and it seems to work.
This required a change to image.c to allow it to read from a memory buffer.

The way this works is when the .cpp program starts, it initializes a set of
global constructors, which calls each classes "registration" function to
register each of their methods in mono or mint as "internal_calls", then the
main class calls mono's "mono_load_image" method to load the msc compiled
.exe or .dll metadata image buffer for the .cpp code, where each function is
an "internal_call" function.  And whala.. mono thinks your mcs generated
.cpp program is an actual dynamically compiled module, complete with class
metadata and whatnot.  All you know is that you ran "mcs -cppoutput
HelloWorld.cs" and got a nice binary exe.

I plan on adding "raw" mode, which compiles a C# program directly to C++
code without any need to link with the full mono runtime which will give you
a lean and mean C++ program with C# access to the C++ standard lib
(namespace CStdLib I'm thinking), perhaps basic Object, Array, String, and
some of the other basic types linked as needed, but no reflection and no
domain support.  This will allow you to write baremetal C++ code in C# with
no overhead.

Q: BUT WHY?

A: I like C#, and I am tired of C++, but the software I write needs to be
compiled directly to binary and can't be JIT'ed or depend on a complex set
of software/libs that need to be installed and maintained and occasionally
debugged.

I write code for game consoles, which need tightly optimized statically
compiled code, but also need dummy resistant high performance script level
code (which must have bindings to the static code which are always a bitch
to maintain).

I have a little spare time and enjoy hacking a bit.

Q: Why not write a CIL to C/C++ converter?

A: Because you can't source debug in your C++ IDE with the local symbol
information gone.  Plus, converting a C# parse tree to a C++ output is
actually pretty easy.

Q: Why C++, why not C?

A: Because debugging a C# program using a C++ compiler with #line directives
is almost identical to debugging a C# program in .NET.

Q: Does any of this work?

A: The cppoutput.cs module outputs .cpp, .inl, and .h files that can be
compiled and linked with the special mono.lib and mint.lib, and they do run.
I haven't made MCS convert the methods in the file to externs yet, so that
bit I have to do by hand and add to the resulting .cpp file, but that seems
to integrate with mono.  As far as the conversion, I haven't implemented
most statements, exceptions, arrays, or other bits yet, but I compiled a
file that prints "HelloWorld".

Q: Are you serious about this?

A: No.  I'm just hacking a bit.  If anyone wants to turn this into a serious
project and add it to the mono codebase, then be my guest.

Q: Hey, what about the MSVC fixes to make mono compile in Windows without
cygwin??!!

A: Uhh....  yeah.  My May version seems to work pretty well and I use it
exclusively for development since gcc and cygwin have no real decent dev
environments (at least on windows), but it was a pain trying to get a set of
diffs with unix diff that didn't include every damn file in every sub
directory.

If you want a zip of the may snapshot with the changes, just email me.  I've
already sent it to one or two people.