[comp.lang.c] No return

pardo@june.cs.washington.edu (David Keppel) (08/27/88)

TURGUT@TREARN.BITNET (Turgut Kalfaoglu) writes:
> f(i){ int tmp; i = i*i; tmp = 222; }
> returns a value!

This kicked my funny bone.  look at the assembly output (or
dissassembled output) of the compiler.  You will see that:

* function return values are always in the same register (e.g, r0)
* temporary calculations are made using the same register

thus

* the "correct" value winds up in the return value accidentally.

The reason this kicked my funny bone is that at times there has been
code that *relied* on this behavior and I have seen at least one style
guide that says "don't depend on this behavior".

Gcc (GNU C compiler) is smart enough (on a VAX, anyway) to perform
temporary computations in the return register, avoiding extra copies,
while pcc (which, in all fairness, is many years older) will
occasionally produce code as shown below, even with the optmizer
turned on (not for the above code, though, it does other entertaining
things):

	cvtbl	-4(fp),r0
	movl	r0,r0		# Could be optimized to "nop" :->
	ret

This is all because of a "phase ordering problem", that you can't
select register use until you know what instructions you're trying to
use and you can't select optimal instructions until you know what
registers you have to play with.

	;-D on  ( More fun than a barrel of cats )  Pardo
-- 
		    pardo@cs.washington.edu
    {rutgers,cornell,ucsd,ubc-cs,tektronix}!uw-beaver!june!pardo

chris@mimsy.UUCP (Chris Torek) (08/27/88)

In article <5571@june.cs.washington.edu> pardo@june.cs.washington.edu
(David Keppel) writes:
>... pcc (which, in all fairness, is many years older) will
>occasionally produce code as shown below, even with the optmizer
>turned on ...:
>
>	cvtbl	-4(fp),r0
>	movl	r0,r0		# Could be optimized to "nop" :->
>	ret

Actually, pcc is smart enough not to move a register to itself, at
least if you are careful in how you write the machine-dependent code
generator.  It does often write temporaries unnecessarily, e.g.,

	f(p) char *p; { return (g() ? h() + i() + j() : *p); }

which generates

		calls	$0,_g
		tstl	r0		# g()?
		jeql	L99999
		calls	$0,_i		# i()
		movl	r0,-4(fp)
		calls	$0,_j		# j()
		movl	r0,-8(fp)
		calls	$0,_h		# h()
		addl2	-4(fp),r0	# +i
		addl2	-8(fp),r0	# +j
		jbr	L99998		# merge ?:
	L99999:
		cvtbl	*4(ap),r0	# *p
	L99998:
		movl	r0,-12(fp)	# was hard; store in temp
		movl	-12(fp),r0	# return result
		ret

The 4.3BSD /lib/c2 is able to remove the second movl:

	L99998:
		movl	r0,-12(fp)
		ret

but the first, while unnecessary, is retained, even though the `hard
expression' (needed too many registers, since function calls clobber
all the temporary registers) that is being computed by ?: is computed
into the return register (via MUSTDO).  We could probably fix this by
checking, in store() or in its callers, to see whether the expression
being stored is a ?:, and if so, whether we only need the result for
a return statement....
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris