Your idea about the structure per function call is interesting but it is going to bloat the code a lot (or at least the source files) since I'll need to create a unique structure for each call.
My IL to C++ converter is coming along nicely. I have implemented roughly 70% of all IL instructions, enough to get a very good idea about performance. I tested it with a method that calls into another method with various parameters that then loads a string and does some basic math. It ran at about 80% - 90% of the C# version. It was able to complete ~71M calls/sec. Not too bad, considering I have not tried to optimize the code yet.
The following have been implemented:
Function calls (static, instance, virtual). P/Invoke should be easy to add.
"Proxy" functions, used to handle static and instance calls on native types, like Int32, bool, etc. I chose not to create structs for these because I think it would be faster to just treat them as native types and use proxy functions (like Int32 Int32_Parse("123")).
Strings. This includes the static (internal) strings, which are really fast to load (IL ldstr). I just pre-create an array of all of the static strings and specify the index to the array in the C++ code.
The first part of the GC which includes making all ref types accessible in order to mark them before the sweep operations, and allowing updating of any pointers that have changed due to compacting. Classes are movable in theory, but I have not tested this yet.
About 70% of all IL instructions.
Some other stuff I forgot about.
I did run into a bizarre problem though, maybe someone can help me out.
I have the following C# method:
public class VirtualString : VirtualObject
private UInt16 m_length;
private unsafe char* m_buffer;
public unsafe VirtualString(char* pStr)
m_buffer = pStr;
m_length = 0;
if (m_buffer == null)
while (*pStr++ != 0)
The IL looks like this (I modified ILSpy to show the instruction operand as well):
The problem is that the code does something different from what the original one does. It always sets m_length to one less than what it should be. The C# version works correctly (pass in "123", and m_length is set to 3), but the C++ version leaves m_length one too small. I'm confused as to how that can happen (pass in "123" and m_length is set to 2). As far as I can see, my C++ code does exactly what the IL tells it to do. Any ideas?
Note I can turn the IL labels and instruction comments on/off but turned both on to see the relationship to the original IL better.