will multi-register returns get fixed?
David Mosberger
davidm@napali.hpl.hp.com
Fri Jun 6 19:10:00 GMT 2003
A Linux-kernel related discussion brought up the topic of
multi-register returns. This in turn motivated me to look, once
again, at how various compilers are doing. I used the attached test
program on ia64 and x86. Results:
platform
compiler: x86 ia64
gcc atrocious code atrocious code
Intel compiler (icc/ecc): near optimal code near optimal code
Is there any hope that GCC will get fixed to handle multi-register
returns in a way that it actually becomes a usable feature? Right
now, it keeps pushing everything through memory, with all the bad
effects that entails.
For the sake of concreteness, here is the code that ecc generates
for foo() on ia64:
foo: alloc r3=ar.pfs,1,0,0,0
add sp=-32,sp // alloc stack frame (bogus)
add r10=-1,r0 // set .ret2 = -1
add r8=13,r0 // set .ret0 = 13
cmp.le.unc p6,p0=r32,r0
(p6) br.cond.dpnt.many .b1_1
mov r10=r0 // set .ret2 = 0
mov r8=r32 // set .ret0 = arg
mov r9=r0 // set .ret1 = 0
add sp=32,sp // free stack frame (bogus)
br.ret.sptk.many b0
.b1_1: mov r9=r0 // set .ret1 = 0
add sp=32,sp // free stack frame (bogus)
br.ret.sptk.many b0
Apart from the needless stack-frame allocation and return-path
duplication, this is nice and clean. In contrast, the gcc-generated
code almost makes you go blind (ditto for x86).
While, personally, I would like to take advantage of multi-register
returns on ia64, I believe the situation is actually significantly
worse on x86. Nowadays, it's not all that uncommon in the Linux
kernel to return 64-bit values even on x86 and since that is a
multi-register return, it triggers all the bad effects.
--david
#ifdef __ia64
typedef struct {
long ret0; /* r8 */
long ret1; /* r9 */
long ret2; /* r10 = -1 or 0 */
} sysret_t;
#define sysval(val) ((sysret_t) { .ret0 = (val), .ret2 = 0 })
#define syserr(err) ((sysret_t) { .ret0 = (err), .ret2 = -1 })
#define sysiserr(ret) ((ret).ret2 == -1)
#define sysgetval(ret) ((ret).ret0)
#else
typedef long long sysret_t;
#define sysval(val) ((unsigned int) (val))
#define syserr(err) (-(unsigned int) (err))
#define sysiserr(ret) ((ret) < 0)
#define sysgetval(ret) ((ret))
#endif
extern sysret_t
foo (long arg)
{
if (arg > 0)
return sysval(arg);
return syserr(13);
}
sysret_t
bar (long arg)
{
sysret_t ret;
if (sysiserr(ret = foo(arg)))
return ret;
if (sysiserr(ret = foo(arg + 1)))
return ret;
return sysval(sysgetval(ret) + 1);
}
More information about the Gcc
mailing list