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