Documentation of MIR_T_BLK
epitavy opened this issue · comments
I want to generate a function that takes a C struct as parameter and returns another C struct.
As far as I understand, the argument can be of type MIR_T_BLK. Can we have more documentation on the usage of MIR_T_BLK, MIR_T_RBLK and some examples in the mir-tests folder ?
Also the documentation is not clear on what is the difference between MIR_T_BLK and MIR_T_BLK + xxx.
I want to generate a function that takes a C struct as parameter and returns another C struct. As far as I understand, the argument can be of type MIR_T_BLK. Can we have more documentation on the usage of MIR_T_BLK, MIR_T_RBLK and some examples in the mir-tests folder ?
Also the documentation is not clear on what is the difference between MIR_T_BLK and MIR_T_BLK + xxx.
Unfortunately, it is complicated because ABIs can be complicated too. For example. structure with two int members is passed through 2 integer regs on x86-64 and structure with one integer and one fp member are passed through 1 int reg and fp reg (nested structures can complicate passing even more). MIR_T_BLK+xxx are used as enumeration of all possible methods of passing structures and they described different passing methods heavily depended on a target.
I decided to use MIR_T_BLK+xxx approach instead of introducing structure/union types as in LLVM IR. It is a simpler solution, imho.
I'll try to document MIR_T_BLK+xxx for different targets for the next MIR release which will be probably be at the end of summer or begging of fall.
Ok, thank you for the answer. Can you tell me which MIR_T_BLK should I use if I generate code for x86_64 ? I would also appreciate a location to look in the code where the passing argument method depends on the BLK type.
You can find how to use blk types for x86-64 in mir-gen-x8_64.c
. Here are the brief descriptions for SYSV ABI:
o MIR_BLK + 1 is used for passing unions/structures (of size <= 16) with integer members
o MIR_BLK + 2 is used for passing unions/structures (of size <= 16) with fp members
o MIR_BLK + 3 is used for passing structures whose 1st member is of int type and 2nd one is of fp type
o MIR_BLK + 4 is used for passing structures whose 1st member is of fp type and 2nd one is of int type
o MIR_BLK is for all other cases
Depending on MIR_BLK type, the args can be passed (partially or fully) through integer or fp regs or on the stack. Please look at SYSV ABI for this.
You can also find how blk type are generated by c2m
in cx86_64-ABI-code.c
file.
@vnmakarov would using MIR_BLK for all cases work even though it would be sub-optimal?
@vnmakarov would using MIR_BLK for all cases work even though it would be sub-optimal?
Yes. I believe it should work. The problem will be only when you try to call external C functions (unfortunately there is no way to force a C compiler even with all implemented GCC extensions to accept MIR_BLK parameter passing for any type parameter).
Alright. This is exactly the use case I'm trying to solve unfortunately.
So if i understand this: i have to change what type of BLK instruction i'm using depending on the architecture?
@vnmakarov several question around passing struct/union by value:
- How do I return a struct/union?
MIR_T_RBLK
is not accepted as return type (then what is that type compared to MIR_T_BLK) ? - I suppose only argument registers can hold a struct and I then operate on its address in the function body?
- aarch64 seems to only need a simple MIR_T_BLK, am I correct?
- For x86_64, it's more complicated, what I understand is that I have to flatten the struct (in the event that it contains another struct value), and follow the rules you layed out in the earlier comment?
@vnmakarov several question around passing struct/union by value:
* How do I return a struct/union? `MIR_T_RBLK` is not accepted as return type (then what is that type compared to MIR_T_BLK) ?
Actually returning structures is less complicated than passing. Address of the function result is set up as the first argument. So if func returns 16-byte structure, the func will be one arg function and look like
f: func rblk:16(Ret_Addr)
The return from f
can be implemented by
call memcpy_p, memcpy, memcpy_res, Ret_Addr, address of structure to return, 16
or something analogous. The call of f
will look like
call f_p, f, rblk:16(addr)
Here f_p
is prototype of the func f
, addr
is where the result of call should be.
How the Ret_Addr passed is machine dependent (defined by the target C ABI). It can be really passed through a reg or calculated from the stack pointer.
* I suppose only argument registers can hold a struct and I then operate on its address in the function body? * aarch64 seems to only need a simple MIR_T_BLK, am I correct?
I don't remember exactly C ABI on aarch64 but it seems so. Generally speaking MIR_T_BLK
is the most generic structure arg passing. It should work for any size struct passing although for some small sizes it may be not conformant to target C ABI.
* For x86_64, it's more complicated, what I understand is that I have to flatten the struct (in the event that it contains another struct value), and follow the rules you layed out in the earlier comment?
Yes, it is complicated. You should read x86-64 SYSV ABI. It was designed by Jan Hibicka ( a GCC developer) when AMD introduced its 64-bit architecture. The major goal was performance. I hope recent Intel extension APX (adding another 16 general purpose registers) will not result in changing SYSV ABI.