Hello! Today I wanted to experiment on a CIL to C compiler... so I did it. It doesn't support much right now, but I'm making progress. (I'm writing this at 2 AM please forgive me) https://github.com/AnErrupTion/CIL2C
#CIL2C - CIL to C compiler
1 messages ยท Page 1 of 1 (latest)
nice!
Thanks!

๐
A lot of work has gone towards CIL2C since then! I've just done a huge type system refactoring, and a bunch of opcodes have been implemented. For example, you can now allocate structs (on the stack, not on the heap though), do all sorts of loops and jumps, methods, fields, etc!

Now you can import C functions inside your CIL code! For example, with C#:
[CFunctionImport(IncludeFile = "utils.h", FunctionName = "outb")]
private static extern void Out8(ushort port, byte value);
why not use a normal DllImporter or whatever its called in C#
Because I plan to use that for actual DLL imports
you can use that with both static and dynamic linking
Also that attribute only has one string, I need two
like, staticly link your c code and statically link the outputed C#->C code and then link em together into a final binary
I plan to have a CStructImportAttribute as well so that you can use C structs in your CIL code
That's already what I do on my test project
Each C file is generated to an ELF object file which is then assembled into a final ELF binary
wait, so in theory, you don't even need an attribute, just an extern
you can just have it as a prototype
Yes but the function name may not be in accordance with your CIL language's naming scheme or whatever
and keep it as is
Like private static extern void outb() is ugly
well, you don't need the IncludeFile
also coreclr does alot of ugly naming for the sake of imports
Yeah I know, I could just add an extern declaration to the function name
Buuuut I don't like that, it requires adding more code to handle the CIL type => C type conversions and stuff
hmmmm
Plus that could be achieved with DllImport anyway
you don't need to put an extern in c function prototype
I mean, why would you add an extern declaration to a function that's in your C code when you can just specify the header file
just don't add a body
Same thing
Doesn't change that
because then its generic for both static libraries already compiled and normal code you link it
or for example if you want to import something from another native language
like, you don't need the duplication of code here
since you would need it for DllImport anyways
(the code to have a prorotype)
and then you can also more easily import C++ stuff code
I already generate function prototypes (I wish I didn't have to, fuck C for that and I'm also having issues with structs as well but anyway)
(Because of forward declarations and stuff)
yeah it makes sense you have to do that anyways
alot of time when I generate C code I just forward declare everything in the top of the file so it will sort itself out lol
Except you can't do that with structs apparently
I mean you can but if you need to specify that struct in another struct the compiler explodes
And I'm having that exact problem
well, you can forward declare structs, but if you want to have them inlined you need that
Especially when my code is multi-threaded
The structs are generated pretty much in random order
that is now a graph problem :)
is it actually worth it having that part multithreaded?
it would simplify alot if you just did it recursively
Well even if it isn't
Sometimes a struct in the type system at index 0 will refer to a struct at index 2
If you see what I mean
Huh? How would I do that?
I mean I'm loading a lot of the dnlib module into my own type system
I do the struct size calculation as a separate pass from metadata loading
and then when I want to fill the size of a type I recursive go over its fields and fill them as well
and I have separate passes for heap size and stack size
(in structs its the same, in objects its different)
But that's the thing: I already theoretically know the size of the structs at compile-time
If only I could, for example, specify it in the struct prototype...
So that the compiler is happy and it can compile the code correctly
yeah but you still have the struct ordering, since C does it the simple way, if it doesn't know the size yet you can't use it
so what you would do when declaring structs is recursively go over the fields of the struct you define and emit them as well
That's why I said to specifiy it in the struct prototype
depth first
Just like when you specify arguments in a function prototype
That's already what I do
When I emit the struct I emit its fields at the same time
that would be too error prone
so all you need to do is delay the actual emitting, concat all the struct content until you are done with it
if you do like that then it will naturally go in the correct order
something like
I'm not sure how that would solve my issue, which is that structs are used in other structs before they're declared
that is when a two pass system is needed
you first parse all of the metadata
and only then you emit stuff
Well that's technically already what I do
dnlib => TypeSystem => Emit types (with non-static fields) => Emit function prototypes => Emit static fields => Emit methods => Emit main method
That's what's happening, in the specified order
so now you just need to emit it recursively, go over all the types you want to emit, concat into a string all the contents, generating the dependant structs on the way
Each emittion you see here is its own separate loop
you would want for struct types that Emit fields will call into Emit type essentially
and as long as you only dump into the final buffer at the end of each emit field iteration you will have the order of structs in c be correct
By Emit fields I mean the static fields
The non-static fields go inside the struct itself
I am talking about non-static fields yeah
ok so you want to essentially have Emit type call itself to emit the field types of value types
So, if I understand correctly, I just go:
EmitType:
for each field in type.Fields:
EmitTypeAtTop(field.FieldType)
Or something like this
essentially yeah
just make sure to not do non-value types
because otherwise you can get into nasty cyclic loops
So I just do this for CIL structs and not CIL classes?
so ```py
EmitType:
if already emitted:
return
s = 'struct name {'
for each field in type.Fields:
s += field.type + ' ' + field.name + ';'
if field.type is value type:
EmitType(field.type)
s += '};'
emit_text(s)
But then how do I start the recursion? I mean, how to get all types emitted at the end?
Oh wait nevermind
I can just loop over them all since there's a condition at the start
exactly
Well now, I'm running into the issue but with non-value types as well :v
Oh wait
Awesome it works!
I just forgot to apply the original recursion to non-value types as well
I wonder if I can also apply the recursion to function prototypes and static fields as well
nice!
static fields maybe
function prototypes not really
recursion is possible for functions
like, you can do it lazy
Yeah but I'd need to make that code single-threaded and it's really not worth it since it also emits the method bodies
for functions I would forward declare everything
Yeah
I was adding comments before each struct declaration to indicate which type it was, well now it's all a bit backwards ๐
However I'll probably remove those before they clutter quite a lot the code for not much reason
Yeah it's much more readable without them
Well, thanks @stark elk for this worthy help! ๐
glad I could help :)
Just a note for the DllImport stuff- that attribute would only serve for linking to other external DLLs, whatever they may be, while CFunctionImportAttribute (and soon CStructImportAttribute) only serve for including other external C code
So the use cases are different
personally I just don't see it as a difference since both are library, be it static or dynamic 
but you do you
But then, how would you do it? I'm not sure I fully understood it
I would use extern + DllImport optionally and let the linker do the rest of the work
so extern without anything just matches the function name as is, DllImport allows to change the name
So basically have the method name as the function name you want to import?
But DllImport normally serves for, well, importing a function from a DLL
I guess that could work
You know what I'll try to implement that
Though my code would be more complex
yeah using DllImport is a bit of meh, so you could still keep the FunctionImport attribute
but I don't think the IncludeFile is needed
since you can let the linker do the job
and then you don't depend on external headers
that is a cool feature tbf, so no need for codegen
even tho you still need the prototype in C# so the C# compiler will be happy
For now actually I'll leave it as is, I'll focus on making my compiler generate a bit less code first
yeah no need to really change it if you already have it lel
just make stuff compile and work
Yeah
good luck when you get to try-catch 
I still need to handle the heap, abstracted classes, etc
For the heap I only need to implement newobj and newarr though
(I think)
Of course the GC
I just told myself I will only handle the GC when I run out of memory
and use malloc until then
with no freeing
I mean... that's one way of doing it
Lol I was thinking about how technically now you could use C# to program an Arduino...
Yeah right
if you don't use too much of the corelib it will be finneee
xD
Yeah