Returning structs in registers - ARM ABI in GCC
In the ARM ABI documentation I come across functions defined like:
__value_in_regs struct bar foo(int a, int b) {
...
}
but GCC(4.3.3) doesn't allow it and all I could find are references to some RealView compiler. Is there any way of d开发者_C百科oing this from GCC?
I have tried -freg-struct-return but it doesn't make a difference. As it is an ABI I can't change the original programs, and returning a regular struct mangles the stack.
I would rather not using assembly for this if avoidable as it isn't otherwise necessary.
Thanks!
Posting as an answer by request:
If you have to generate a binary that will work with an ABI your compiler doesn't support, you're in for some trouble. There's nothing you can do in C. In this case, you'll need to fall back on assembly language programming and thunk the necessary calls. There are two possibilities:
- Calls from your binary into the other binary's ABI.
- Calls from the other binary into your binary's ABI.
Both of these problems are solved similarly. To call out from your code, you'll need to make shim functions in assembly that swizzle around the calling convention to match the external ABI, and then call the external functions from there. The difference to your C code is that now to make external calls, you call your internal assembly routine, and it does whatever it needs to to call out externally, then puts the return value back in a format your C code will understand, and returns.
To support calls from the external binary into your code, you do the same thing, but in reverse. The entry points to your binary will be little assembly routines that swizzle the external ABI into a format your C code can understand, call your internal function, then put the return values back into a format the external code understands, and return.
Sometimes there's just no good solution, I'm afraid.
You can do it for two registers by using "long long", as specified in "Procedure Call Standard for the ARM Architecture" link given in this page.
long long test(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
long long ret;
ret = a+b;
ret <<= 32;
ret |= c + d;
return ret;
}
will be simply compiled as:
0002dbb8 <test>:
2dbb8: 1841 adds r1, r0, r1
2dbba: 18d0 adds r0, r2, r3
2dbbc: 4770 bx lr
and the ret & 0xFFFFFFFF
and ret >> 32
in your calling function will be replaced seamlessly by r0 and r1.
It is even possible to do it for registers r0 to r3 by using "Containerized vectors":
typedef uint32_t uint32x4_t __attribute__ ((vector_size (16)));
uint32x4_t test2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
uint32x4_t ret = { a + 1, b + 2, c + 3, d + 4};
// to access elements: ret[0], ret[1], ...
return ret;
}
which is compiled as:
0002dbb8 <test2>:
2dbb8: 3001 adds r0, #1
2dbba: 3102 adds r1, #2
2dbbc: 3203 adds r2, #3
2dbbe: 3304 adds r3, #4
2dbc0: 4770 bx lr
Note that it is referenced as a SIMD/NEON feature in the document above, but I just achieved it on a Cortex M0 in Thumb mode, without NEON support.
The "Procedure Call Standard for the ARM Architecture" specifically says (section 5.4: Result Return):
"A Composite Type not larger than 4 bytes is returned in R0."
"A Composite Type larger than 4 bytes ... is stored in memory at an address passed as an extra argument when the function was called ... ."
I know that some CPUs have several different "standard" ABIs. But I was under the impression that practically all compilers for the ARM used this same ABI.
Do you have any evidence that GCC doesn't use this standard ABI?
Would you mind posting a link to any information on a ABI for the ARM that is different from this standard ABI -- the ABI used by the caller, or the called, or both?
I'm not sure if this will work, but you can try using the pcs
function attribute:
struct bar foo(int a, int b) __attribute__((pcs("aapcs")));
struct bar foo(int a, int b) {
...
}
精彩评论