#CIL2C - CIL to C compiler

1 messages ยท Page 1 of 1 (latest)

modest crescent
foggy hedge
#

nice!

kindred sorrel
#

Woah

#

Looks nice

modest crescent
#

Thanks!

stark elk
modest crescent
modest crescent
#

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!

stark elk
modest crescent
#

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);
stark elk
#

why not use a normal DllImporter or whatever its called in C#

modest crescent
stark elk
#

you can use that with both static and dynamic linking

modest crescent
#

Also that attribute only has one string, I need two

stark elk
#

like, staticly link your c code and statically link the outputed C#->C code and then link em together into a final binary

modest crescent
#

I plan to have a CStructImportAttribute as well so that you can use C structs in your CIL code

modest crescent
#

Each C file is generated to an ELF object file which is then assembled into a final ELF binary

stark elk
#

wait, so in theory, you don't even need an attribute, just an extern

#

you can just have it as a prototype

modest crescent
#

Yes but the function name may not be in accordance with your CIL language's naming scheme or whatever

stark elk
#

and keep it as is

modest crescent
#

Like private static extern void outb() is ugly

stark elk
#

well, you don't need the IncludeFile

#

also coreclr does alot of ugly naming for the sake of imports

modest crescent
#

Buuuut I don't like that, it requires adding more code to handle the CIL type => C type conversions and stuff

stark elk
#

hmmmm

modest crescent
#

Plus that could be achieved with DllImport anyway

stark elk
#

you don't need to put an extern in c function prototype

modest crescent
#

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

stark elk
#

just don't add a body

modest crescent
#

Same thing

stark elk
modest crescent
#

But why

#

Just why

#

You could simply change to a DllImportAttribute

stark elk
#

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

modest crescent
#

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)

stark elk
#

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

modest crescent
#

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

stark elk
#

well, you can forward declare structs, but if you want to have them inlined you need that

modest crescent
#

Especially when my code is multi-threaded

#

The structs are generated pretty much in random order

stark elk
#

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

modest crescent
#

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

modest crescent
#

I mean I'm loading a lot of the dnlib module into my own type system

stark elk
#

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)

modest crescent
#

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

stark elk
#

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

modest crescent
#

That's why I said to specifiy it in the struct prototype

stark elk
#

depth first

modest crescent
#

Just like when you specify arguments in a function prototype

modest crescent
#

When I emit the struct I emit its fields at the same time

stark elk
stark elk
#

if you do like that then it will naturally go in the correct order

modest crescent
#

Huh?

#

I don't get it

stark elk
#

something like

modest crescent
#

I'm not sure how that would solve my issue, which is that structs are used in other structs before they're declared

stark elk
#

that is when a two pass system is needed

#

you first parse all of the metadata

#

and only then you emit stuff

modest crescent
#

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

stark elk
#

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

modest crescent
stark elk
#

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

modest crescent
#

By Emit fields I mean the static fields

#

The non-static fields go inside the struct itself

stark elk
#

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

modest crescent
#

So, if I understand correctly, I just go:

EmitType:
    for each field in type.Fields:
        EmitTypeAtTop(field.FieldType)
#

Or something like this

stark elk
#

essentially yeah

#

just make sure to not do non-value types

#

because otherwise you can get into nasty cyclic loops

modest crescent
#

So I just do this for CIL structs and not CIL classes?

stark elk
#

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)

modest crescent
#

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

stark elk
#

exactly

modest crescent
#

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

stark elk
#

nice!

#

static fields maybe

#

function prototypes not really

#

recursion is possible for functions

#

like, you can do it lazy

modest crescent
#

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

stark elk
#

for functions I would forward declare everything

modest crescent
#

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! ๐Ÿ˜„

stark elk
#

glad I could help :)

modest crescent
#

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

stark elk
#

personally I just don't see it as a difference since both are library, be it static or dynamic 3sGudaEhehe

#

but you do you

modest crescent
#

But then, how would you do it? I'm not sure I fully understood it

stark elk
#

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

modest crescent
#

So basically have the method name as the function name you want to import?

modest crescent
modest crescent
#

You know what I'll try to implement that

#

Though my code would be more complex

stark elk
#

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

modest crescent
#

Fair

#

My idea is to be also able to import C structs directly

stark elk
#

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

modest crescent
#

For now actually I'll leave it as is, I'll focus on making my compiler generate a bit less code first

stark elk
#

yeah no need to really change it if you already have it lel

#

just make stuff compile and work

modest crescent
#

Yeah

stark elk
#

good luck when you get to try-catch smug_face_FB

modest crescent
#

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

stark elk
#

I just told myself I will only handle the GC when I run out of memory

#

and use malloc until then

#

with no freeing

modest crescent
#

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...

stark elk
#

arduino has 2kb of ram

#

and like 2kb of rom too

modest crescent
#

But if I compile with -Ofast

#

It should eliminate most of the CIL "bloat"

stark elk
#

you want Oz and Os

#

but yeah

modest crescent
#

Yeah right

stark elk
#

if you don't use too much of the corelib it will be finneee

modest crescent
#

xD

grand stag
#

its means what we can write os without tysila?

#

hmm

modest crescent