[comp.lang.c] re-using registers

roy@phri.UUCP (Roy Smith) (07/20/87)

	Imagine the following (rather silly) function:

struct foo {struct foo *next;};
f(s, p)
char *s;
struct foo *p
{
	register char *rs;
	register struct foo *rp;

	for (rs = s; *rs != NULL; rs++);
	for (rp = p; rp->next != NULL; rp = rp->next);
}

	Are there any C compilers (or other languages, for that matter)
which are smart enough to realize that rs and rp could be put in the same
register?  In Fortran you would write this as "EQUIVALENCE (RS, RP)" and
the compiler wouldn't have to be smart at all, but that's cheating.

	Actually, I guess a really smart compiler might optimize the whole
function down to:

f() { return; }
-- 
Roy Smith, {allegra,cmcl2,philabs}!phri!roy
System Administrator, Public Health Research Institute
455 First Avenue, New York, NY 10016

bright@dataio.Data-IO.COM (Walter Bright) (07/20/87)

In article <2803@phri.UUCP< roy@phri.UUCP (Roy Smith) writes:
<	Imagine the following (rather silly) function:
<struct foo {struct foo *next;};
<f(s, p) char *s; struct foo *p
<{	register char *rs;
<	register struct foo *rp;
<
<	for (rs = s; *rs != NULL; rs++);
<	for (rp = p; rp-<next != NULL; rp = rp-<next);
<}
<	Are there any C compilers (or other languages, for that matter)
<which are smart enough to realize that rs and rp could be put in the same
<register?

The Datalight C compiler uses register allocation by coloring in order
to put rs and rp into the same register. I should know, I wrote it.

henry@utzoo.UUCP (Henry Spencer) (07/20/87)

> 	Are there any C compilers (or other languages, for that matter)
> which are smart enough to realize that rs and rp could be put in the same
> register?...

Any optimizing compiler worth its salt, for C or most anything else, will
recognize that one.  Good register allocation is a major area of concern
for serious optimizers.
-- 
Support sustained spaceflight: fight |  Henry Spencer @ U of Toronto Zoology
the soi-disant "Planetary Society"!  | {allegra,ihnp4,decvax,utai}!utzoo!henry

devine@vianet.UUCP (Bob Devine) (07/20/87)

In article <2803@phri.UUCP>, roy@phri.UUCP (Roy Smith) writes:
> 	Are there any C compilers (or other languages, for that matter)
> which are smart enough to realize that rs and rp could be put in the same
> register?  In Fortran you would write this as "EQUIVALENCE (RS, RP)" and
> the compiler wouldn't have to be smart at all, but that's cheating.

  You can increase the likelihood of register re-use if you break up
the function into blocks that each declare their own "local" variables.
I don't know which compilers will re-use (I believe that PCC descendants 
will) but this is strictly an implementators choice.

Bob Devine

[[ Here is the posted code rewritten into blocks. ]]
{
	{
	    register char *rs;
	    for (rs = s; *rs != NULL; rs++);
	}

	{
	    register struct foo *rp;
	    for (rp = p; rp->next != NULL; rp = rp->next);
	}
}

ron@topaz.rutgers.edu (Ron Natalie) (07/21/87)

> f(s, p)
> char *s;
> struct foo *p
> {
>	register char *rs;
>	register struct foo *rp;
>
>	for (rs = s; *rs != NULL; rs++);
>	for (rp = p; rp->next != NULL; rp = rp->next);
> }

Most C compilers are trully one pass jobs (note the frequent "jump to
the end of the function to allocate the stack because by then we'll
know how much to allocate hack" that many compilers use).  They have
no way of knowing that after the first "for" loop that "rs" will not
be used again.  Why not give it some help...

    f(s,p)...{

	{
	    register char *rs;
	    for(rs = s; *rs != NULL; rs++);
	}{
	    register struct foo *rp;
	    for( rp = p ...
	}
    }

-Ron

ka@hropus.UUCP (Kenneth Almquist) (07/21/87)

> Imagine the following (rather silly) [code]:

>	register char *rs;
>	register struct foo *rp;
>
>	for (rs = s; *rs != NULL; rs++);
>	for (rp = p; rp->next != NULL; rp = rp->next);
>	return;

>	Are there any C compilers (or other languages, for that matter)
> which are smart enough to realize that rs and rp could be put in the same
> register?

There are certainly a lot of compilers for languages other than C which
are that smart, IBM's PL/1 Optimizing Compiler being just one example.

> In Fortran you would write this as "EQUIVALENCE (RS, RP)" and
> the compiler wouldn't have to be smart at all, but that's cheating.

In C, you should write:

	{
		register char *rs;
		for (rs = s; *rs != NULL; rs++);
	}
	{
		register struct foo *rp;
		for (rp = p; rp->next != NULL; rp = rp->next);
	}

The standard joke is that in the C world, you don't have optimizing
compilers, you have optimizing programmers.

By the way, the FORTRAN equivalence statement may very well scare
the optimizer out of putting either RS or RP in registers.
			Kenneth Almquist

grk@sfsup.UUCP (G.R.Kuntz) (07/22/87)

In article <1331@dataio.Data-IO.COM>, bright@dataio.UUCP writes:
< In article <2803@phri.UUCP< roy@phri.UUCP (Roy Smith) writes:
< <	Imagine the following (rather silly) function:
< <struct foo {struct foo *next;};
< <f(s, p) char *s; struct foo *p
< <{	register char *rs;
< <	register struct foo *rp;
< <
< <	for (rs = s; *rs != NULL; rs++);
< <	for (rp = p; rp-<next != NULL; rp = rp-<next);
< <}
< <	Are there any C compilers (or other languages, for that matter)
< <which are smart enough to realize that rs and rp could be put in the same
< <register?
< 
< The Datalight C compiler uses register allocation by coloring in order
< to put rs and rp into the same register. I should know, I wrote it.

On my master's thesis compiler I too did register allocation by graph coloring
and so would put both rs and rp into the same register.
-- 
	G. Ralph Kuntz N2HBN	UUCP: {ihnp4,allegra}!attunix!grk
				ARPA: rutgers.rutgers.edu!pisc2b!grk

stuart@bms-at.UUCP (Stuart D. Gathman) (07/22/87)

The GNU compiler reuses registers.  It is free from the Free Software
Foundation in source form.  Machine descriptions for vax and 680?0 are
included.
-- 
Stuart D. Gathman	<stuart@bms-at.uucp>
			<..!seismo!dgis!bms-at!stuart>

fnf@mcdsun.UUCP (Fred Fish) (07/23/87)

In article <2803@phri.UUCP> roy@phri.UUCP (Roy Smith) writes:
>
>	Imagine the following (rather silly) function:
>		[...deleted...]
>	Are there any C compilers (or other languages, for that matter)
>which are smart enough to realize that rs and rp could be put in the same
>register?  In Fortran you would write this as "EQUIVALENCE (RS, RP)" and

The GNU C compiler emits:

	.globl _f
	.text
		.even
	_f:
		link a6,#0
		movl a6@(12),d0
		movl a6@(8),a0
		tstb a0@		<--- use a0 for first pointer
		jeq L13
	L2:
		addql #1,a0
		tstb a0@
		jne L2
	L13:
		movl d0,a0
		tstl a0@		<--- reuse a0 for second pointer
		jeq L12
	L7:
		movl a0@,a0
		tstl a0@
		jne L7
	L12:
		unlk a6
		rts

So the answer is yes, and it won't even cost you an arm and a leg...

-Fred
-- 
= Drug tests; just say *NO*!
= Fred Fish  Motorola Computer Division, 3013 S 52nd St, Tempe, Az 85282  USA
= seismo!noao!mcdsun!fnf    (602) 438-3614

john@rufus.molly.UUCP (John Franks) (07/24/87)

In article <1668@sfsup.UUCP>, grk@sfsup.UUCP (G.R.Kuntz) writes:
> On my master's thesis compiler I too did register allocation by graph coloring

Anybody want to explain what is register allocation by coloring?

-- 
John Franks 	Dept of Math. Northwestern University
		UUCP		john@molly.uucp	
		Internet	john%molly.uucp@eecs.nwu.edu

bright@dataio.Data-IO.COM (Walter Bright) (07/27/87)

In article <358@rufus.molly.UUCP> john@rufus.molly.UUCP (John Franks) writes:
>In article <1668@sfsup.UUCP>, grk@sfsup.UUCP (G.R.Kuntz) writes:
>> On my master's thesis compiler I too did register allocation by graph coloring
>Anybody want to explain what is register allocation by coloring?

For register AX, you color in the top part of the A... :-)
But seriously,

Imagine an x-y graph. The each point on the horizontal axis corresponds
to one statement in the program. Each point on the vertical axis corresponds
to a variable that could be allocated to a register. Now graph with a
pencil each x-y point where the value of variable y is required at point x.
(This is called the 'live range' of each variable.)

Now aquire a crayon for each register you have, a different color for
each crayon. Find a way of assigning a crayon to each variable such that
when the graph is redrawn with the crayons, at no statement x do any
two variables have the same color.

In other words, you can never have two variables assigned to the same
register at the same time, and two variable can occupy the same register
if their usages do not overlap.

grk@sfsup.UUCP (08/07/87)

WARNING! LONG! SKIP IF YOU SUFFER FROM EXTREME BOREDOM!

In article <1334@dataio.Data-IO.COM>, bright@dataio.Data-IO.COM (Walter Bright) writes:
> In article <358@rufus.molly.UUCP> john@rufus.molly.UUCP (John Franks) writes:
> >In article <1668@sfsup.UUCP>, grk@sfsup.UUCP (G.R.Kuntz) writes:
> >> On my master's thesis compiler I too did register allocation by graph coloring
> >Anybody want to explain what is register allocation by coloring?

While doing code generation we imagine that we have an ideal machine with an
unlimited number of registers.  For example, lets say we want to evaluate the
following expression:

		auto short a[4], i, b;

		a[i + 1] = a[i + 1] * i + b;

with the following frame layout:

		a[0]:	0(fp)
		a[1]:	2(fp)
		a[2]:	4(fp)
		a[3]:	6(fp)
		i:	8(fp)
		b:	10(fp)

The sample expression might generate the following pseudo-assembly:

	1:	t0 <- *8(fp)		/* contents of i */
	2:	t1 <- 1			/* guess :-) */
	3:	t2 <- t0 + t1		/* i + 1 */
	4:	t3 <- t2 * 2		/* offset by size of one element */
	5:	t4 <- adr[0(fp)]	/* address of a[0] */
	6:	t5 <- t3 + t4		/* address of a[i + 1] */
	7:	t6 <- *t5		/* fetch a[i + 1] */
	8:	t7 <- t6 * t0		/* a[i + 1] * i */
	9:	t8 <- *10(fp)		/* contents of b */
	10:	t9 <- t7 + t8		/* a[i + 1] * i + b */
	11:	*t5 <- t9		/* a[i + 1] = a[i + 1] * i + b */

Now we've got to assign all of the various t? to real registers.  We first
decide the live-ness of each pRegister (pseudo-register) as follows:

		first used		last used
		----------		---------
	t0	    1			    8
	t1	    2			    3
	t2	    3			    4
	t3	    4			    6
	t4	    5			    6
	t5	    6			    11
	t6	    7			    8
	t7	    8			    10
	t8	    9			    10
	t9	    10			    11

We now draw a graph in which the nodes are the pRegisters and an edge between
two nodes indicates that both of those pRegisters are in use at the same time:

			  ______ 1
			 /
		6 _____ 0 ______ 2
		|      /|\
	  7 ___ 5 ____/ | \_____ 3
	  |    /|       \        |
	  8 __/ 9        \______ 4

Assume our real machine has 3 registers.  We begin removing nodes from the
graph that contain 3 or less edges, and remember the order that we remove
them:

	remove 8:

			  ______ 1
			 /
		6 _____ 0 ______ 2
		|      /|\
	  7 ___ 5 ____/ | \_____ 3
	        |       \        |
	        9        \______ 4

	remove 6:
			  ______ 1
			 /
		        0 ______ 2
		       /|\
	  7 ___ 5 ____/ | \_____ 3
	        |       \        |
	        9        \______ 4

	remove 5:
			  ______ 1
			 /
		        0 ______ 2
		        |\
	  7             | \_____ 3
	                \        |
	        9        \______ 4

	remove 3:
			  ______ 1
			 /
		        0 ______ 2
		        |
	  7             |
	                \
	        9        \______ 4

	remove 0:
			         1
			 
		                 2

	  7

	        9                4

	remove the rest in arbitrary order 1, 2, 4, 7, 9

Let's assign "colors" to our real registers as follows:

	r0:	red (R)
	r1:	green (G)
	r2:	blue (B)

We removed the nodes from the graph in the following order:

	8, 6, 5, 3, 0, 1, 2, 4, 7, 9

Reverse them:

	9, 7, 4, 2, 1, 0, 3, 5, 6, 8

and we assign the colors by referring to the original graph and making
sure that there is no overlap:

	9, 7, 4, 2, 1 can all get red since they do not touch:

			  ______ R
			 /
		6 _____ 0 ______ R
		|      /|\
	  R ___ 5 ____/ | \_____ 3
	  |    /|       \        |
	  8 __/ R        \______ R

	0 gets an unused color, say green:

			  ______ R
			 /
		6 _____ G ______ R
		|      /|\
	  R ___ 5 ____/ | \_____ 3
	  |    /|       \        |
	  8 __/ R        \______ R

	3 and 5 get blue:

			  ______ R
			 /
		6 _____ G ______ R
		|      /|\
	  R ___ B ____/ | \_____ B
	  |    /|       \        |
	  8 __/ R        \______ R

	6 becomes red:

			  ______ R
			 /
		R _____ G ______ R
		|      /|\
	  R ___ B ____/ | \_____ B
	  |    /|       \        |
	  8 __/ R        \______ R

	finally, 8 becomes green:

			  ______ R
			 /
		R _____ G ______ R
		|      /|\
	  R ___ B ____/ | \_____ B
	  |    /|       \        |
	  G __/ R        \______ R

Now our original code looks like the following:

	1:	r1 <- *8(fp)		/* contents of i */
	2:	r0 <- 1			/* guess :-) */
	3:	r0 <- r1 + r0		/* i + 1 */
	4:	r2 <- r0 * 2		/* offset by size of one element */
	5:	r0 <- adr[0(fp)]	/* address of a[0] */
	6:	r2 <- r2 + r0		/* address of a[i + 1] */
	7:	r0 <- *r2		/* fetch a[i + 1] */
	8:	r0 <- r0 * r1		/* a[i + 1] * i */
	9:	r1 <- *10(fp)		/* contents of b */
	10:	r0 <- r0 + r1		/* a[i + 1] * i + b */
	11:	*r2 <- r0		/* a[i + 1] = a[i + 1] * i + b */

and in real code:

	move	8(fp),r1
	move	1,r0		/* could have been better */
	add2	r1,r0
	mul3	r0,2,r2		/* peephole changes to shift left 1 */
	movadr	0(fp),r0
	add2	r0,r2
	move	*r2,r0
	mul2	r1,r0
	move	10(fp),r1
	add2	r1,r0
	move	r0,*r1

Hope this long-winded discussion helps.
-- 
	G. Ralph Kuntz N2HBN	UUCP: {ihnp4,allegra}!attunix!grk
				ARPA: rutgers.rutgers.edu!pisc2b!grk