[comp.lang.c] Short code to determine compiler's

mcdonald@uxe.cso.uiuc.edu (07/15/89)

>Some students here had to determine the number of registers (data 
>and address, we use 680x0's) the C compiler uses.  A friend and
>I wrote the following code to show to some students having trouble.
>It is very short and simple, but it seems to work.  The only logical
>next step is to post it to comp.lang.c and have it torn apart!



>#include <stdio.h>

>main()
>{
>    {
>        int count1; /* Base of stackframe */

>        register x1, x2, x3, x4, x5, x6, x7, x8, x9, x10,
>                 x11, x12, x13, x14, x15, x16, x17, x18, x19;
>        {
>            int count2;
>            printf("Number of data registers = %d\n", 19 - abs(&count1 - &count2) + 1);
>        }
>    }

(code for int * deleted)

Oh my!!!! What on earth makes you think that this will give the
number of register variables actually used? If tried on Microsoft C 5.1
it gives 9 and 9. But, actually Microsoft C uses at most 2 register
variables. Just look at the assembler output of the compiler:

(header removed)
_DATA      SEGMENT
$SG180	DB	'Number of data registers = %d',  0aH,  00H
$SG202	DB	'Number of address registers = %d',  0aH,  00H
_DATA      ENDS
_TEXT      SEGMENT
	ASSUME	CS: _TEXT
; Line 16
	PUBLIC	_main
_main	PROC NEAR
	push	bp
	mov	bp,sp
	mov	ax,42
	call	__chkstk
; Line 17
;	count1 = -20   (These are the offsets from bp of the local variables)
;	x1 = -14
;	x2 = -18
;	x3 = -24
;	x4 = -28
;	x5 = -32
;	x6 = -36
;	x7 = -40
;	x8 = -4
;	x9 = -8
;	x10 = -12
;	x11 = -16
;	x12 = -22
;	x13 = -26
;	x14 = -30
;	x15 = -34
;	x16 = -38
;	x17 = -2
;	x18 = -6
;	x19 = -10
; Line 22
;	count2 = -42
; Line 25
	mov	ax,11
	push	ax
	call	_abs
	add	sp,2
	sub	ax,20
	neg	ax
	push	ax
	mov	ax,OFFSET DGROUP:$SG180
	push	ax
	call	_printf
	add	sp,4

Nothing in the C language says where variables have to go - putting
an & in front of two different variables (not in an array or struct)
and subtracting the resulting pointers
is a meaningless exercise. Somebody might
assign variables random addresses!!!!! Somebody (i.e. Microsoft) DID!!!!!

If the original writer has "students", that seems to imply that he
is a "teacher". I would hope that anyone teaching C would know this.

In fact, the really interesting question is, in legal C, is it
even POSSIBLE to write a program to see how many registers are used?

Doug McDonald

andre@targon.UUCP (andre) (07/17/89)

In article <225800197@uxe.cso.uiuc.edu> mcdonald@uxe.cso.uiuc.edu writes:
>
>>Some students here had to determine the number of registers (data 
>>and address, we use 680x0's) the C compiler uses.  A friend and
>>I wrote the following code to show to some students having trouble.
>>It is very short and simple, but it seems to work.  The only logical
>>next step is to post it to comp.lang.c and have it torn apart!

[ text explaining why this doesn't always work ]

>In fact, the really interesting question is, in legal C, is it
>even POSSIBLE to write a program to see how many registers are used?

Yes, I think you can write a program that checks the nr of registers,
but you will get the answer at compile time, not run time :-)

the program looks like this:

/* test register usage of compiler */

main()
{
	register n1, n2, n3, n4, n5, n6, n7, n8; /* etc. */
	int *a;

	a = &n8;
	a = &n7;

	/* repeat n6 - n2 */
	a = &n1;
}

/* end */

The compiler will assing n1 to n{x} to the registers it has available and
the rest will be normal variables. You can take the address of a variable
but not of a register, so the compiler will start to complain at the first
line that tries to take the address of a register. That's why the a = &n{x};
lines must count backwards.

	I would like to know if this is also possible at run-time,
		Andre

-- 
    \---|    AAA         DDDD  It's not the kill, but the thrill of the chase.
     \  |   AA AAvv   vvDD  DD        Ketchup is a vegetable.
  /\  \ |  AAAAAAAvv vvDD  DD                    {nixbur|nixtor}!adalen.via
_/__\__\| AAA   AAAvvvDDDDDD    Andre van Dalen, uunet!hp4nl!targon!andre

plocher%sally@Sun.COM (John Plocher) (07/18/89)

>>>Some students here had to determine the number of registers (data 
>>[ text explaining why this doesn't always work ]
>Yes, I think you can write a program that checks the nr of registers,
>but you will get the answer at compile time, not run time :-)
> [ code that relies on a compiler barfing on &registervar ]

This fails if your compiler notes that you did a & of a register var and 
removes the register modifier from the declaration for you.  It also fails
if your compiler is nice enuf to make a temp var for you and copy the value
from a reg to that var so you can take the addr of it.  I have seen compilers
that do both (the latter is only really doable in a data flow analyzing compiler
where modification points and aliasing can be taken into account).

There is no compiler/OS/CPU portable way to do this, but on a specific cpu
with a specific compiler it can be done.  But if the CPU and compiler are already
known in that much detail, why determine this dynamically?  Actually, what
is the use of this knowledge anyways? The new family* of compilers do a better
job of optimization if *they* can do the register declarations for you.  

  -John Plocher

* The non-pcc based compilers from Greenhills, The Free Software Foundation,
Silicon Valley Systems, Metaware and others all do a better job of speed
optimization if you do NOT specify explicit register variables!

Horne-Scott@cs.yale.edu (Scott Horne) (07/18/89)

In article <579@targon.UUCP>, andre@targon (andre) writes:
> 
> Yes, I think you can write a program that checks the nr of registers,
> but you will get the answer at compile time, not run time :-)
> 
> the program looks like this:
> 
> /* test register usage of compiler */
> 
> main()
> {
> 	register n1, n2, n3, n4, n5, n6, n7, n8; /* etc. */
> 	int *a;
> 
> 	a = &n8;
> 	a = &n7;
> 
> 	/* repeat n6 - n2 */
> 	a = &n1;
> }
> 
> /* end */
> 
> The compiler will assing n1 to n{x} to the registers it has available and
> the rest will be normal variables. You can take the address of a variable
> but not of a register, so the compiler will start to complain at the first
> line that tries to take the address of a register. That's why the a = &n{x};
> lines must count backwards.

But C doesn't guarantee the order of allocation of registers.  How do you know,
for example, that n8, n7, ..., n{x} won't be put into registers and n{x-1}..n1
made automatic?  Your program depends on the order of allocation and therefore
is not reliable.

Besides, the rule that the address of a register variable cannot be taken is
new in K&R 2; some old compilers might return an ``address''.

Nice try, though....  :-)

					--Scott

Scott Horne                              Hacker-in-Chief, Yale CS Dept Facility
horne@cs.Yale.edu                         ...!{harvard,cmcl2,decvax}!yale!horne
Home: 203 789-0877     SnailMail:  Box 7196 Yale Station, New Haven, CT   06520
Work: 203 432-1260              Summer residence:  175 Dwight St, New Haven, CT
Dare I speak for the amorphous gallimaufry of intellectual thought called Yale?

bet@orion.mc.duke.edu (Bennett Todd) (07/19/89)

In article <579@targon.UUCP>, andre@targon (andre) writes:
>/* test register usage of compiler */
>
>main()
>{
>	register n1, n2, n3, n4, n5, n6, n7, n8; /* etc. */
>	int *a;
>
>	a = &n8;
>	a = &n7;
>
>	/* repeat n6 - n2 */
>	a = &n1;
>}
>
>/* end */

Compilers are completely within their rights to give error messages for
all these "&reg" expressions, whether or not they put any of them in
registers. In fact, the "register" modifier is routinely ignored by both
the stupidest and the smartest compilers; *very* *very* smart compilers
could if they wished note ONLY that such variables may not have their
address taken (a vaguely similar optimization hint to the unlamented
"noalias"). In fact, as far as I know, the prohibition on taking the
address of a variable flagged as "register" is the only language defined
effect it has.

Now, to check a specific, real compiler. Using gcc 1.34, I compiled the
following:

	int main()
	{
	       register n1, n2, n3, n4, n5, n6, n7, n8; /* etc. */
	       register n9, n10,n11,n12,n13,n14,n15,n16,n17;
	       int *a;
	       a = &n17;
	       a = &n16;
	       a = &n15;
	       a = &n14;
	       a = &n13;
	       a = &n12;
	       a = &n11;
	       a = &n10;
	       a = &n9;
	       a = &n8;
	       a = &n7;
	       a = &n6;
	       a = &n5;
	       a = &n4;
	       a = &n3;
	       a = &n2;
	       a = &n1;
	       return(0);
	}

Calling that file "junk.c" I then ran the command

	make junk.o >& output

which, because of how I define CC and CFLAGS, produced this in "output"
(manually edited to break the gcc command line in two):

	gcc -O -g -Wall -Wwrite-strings -msoft-float -fstrength-reduce \
	    -finline-functions -I/usr/local/include -c junk.c -o junk.o
	junk.c: In function main:
	junk.c:6: address of global register variable requested
	junk.c:7: address of global register variable requested
	junk.c:8: address of global register variable requested
	junk.c:9: address of global register variable requested
	junk.c:10: address of global register variable requested
	junk.c:11: address of global register variable requested
	junk.c:12: address of global register variable requested
	junk.c:13: address of global register variable requested
	junk.c:14: address of global register variable requested
	junk.c:15: address of global register variable requested
	junk.c:16: address of global register variable requested
	junk.c:17: address of global register variable requested
	junk.c:18: address of global register variable requested
	junk.c:19: address of global register variable requested
	junk.c:20: address of global register variable requested
	junk.c:21: address of global register variable requested
	junk.c:22: address of global register variable requested
	make: *** Error 1

Look, ma, 17 registers allocated for integers on a 68020!

I would conclude that a teacher giving his students the assignment of
determining how many register variables are actively supported by their
compiler had better be teaching compiler internals, and not the C
programming language.

-Bennett
bet@orion.mc.duke.edu

stuart@bms-at.UUCP (Stuart Gathman) (07/19/89)

In article <579@targon.UUCP>, andre@targon.UUCP (andre) writes:

> 	register n1, n2, n3, n4, n5, n6, n7, n8; /* etc. */
> 	int *a;

> 	a = &n8;
> 	a = &n7;

> 	/* repeat n6 - n2 */
> 	a = &n1;

Taking the address of a register variable is not allowed even when it
is not actually in a register.  (When not in a register, banning addressof
guarrantees no aliases for that variable.  Anyone for rekindling the "noalias"
debate :-)
-- 
Stuart D. Gathman	<stuart@bms-at.uucp>
			<..!{vrdxhq|daitc}!bms-at!stuart>

paul@moncam.co.uk (Paul Hudson) (07/19/89)

In article <579@targon.UUCP> andre@targon.UUCP (andre) writes:
   In article <225800197@uxe.cso.uiuc.edu> mcdonald@uxe.cso.uiuc.edu writes:
   >
   >>Some students here had to determine the number of registers (data 
   >>and address, we use 680x0's) the C compiler uses.  A friend and
   >>I wrote the following code to show to some students having trouble.
   >>It is very short and simple, but it seems to work.  The only logical
   >>next step is to post it to comp.lang.c and have it torn apart!

   [ text explaining why this doesn't always work ]

   >In fact, the really interesting question is, in legal C, is it
   >even POSSIBLE to write a program to see how many registers are used?

   Yes, I think you can write a program that checks the nr of registers,
   but you will get the answer at compile time, not run time :-)

   the program looks like this:

   /* test register usage of compiler */

   main()
   {
	   register n1, n2, n3, n4, n5, n6, n7, n8; /* etc. */
	   int *a;

	   a = &n8;
	   a = &n7;

	   /* repeat n6 - n2 */
	   a = &n1;
   }

   /* end */

   The compiler will assing n1 to n{x} to the registers it has available and
   the rest will be normal variables. You can take the address of a variable
   but not of a register, so the compiler will start to complain at the first
   line that tries to take the address of a register. That's why the a = &n{x};
   lines must count backwards.


I would hope this doesn't work either. I would expect the compiler to
complain if I took the address of a variable I had declared register,
regardless of whether it was in a register or not. At the time the
compiler emits such errors it may not even know whether the variable
will get a register or not.


--
Paul Hudson	 MAIL: Monotype ADG, Science Park, Cambridge, CB4 4FQ, UK.
		PHONE: +44 (223) 420018	  EMAIL: paul@moncam.co.uk,
	;"	  FAX: +44 (223) 420911		 ...!ukc!acorn!moncam!paul
 `"";";"        These opinions void where prohibited by law.

hjm@cernvax.UUCP (Hubert Matthews) (07/22/89)

I have a question for the authors of smart C compilers: is the
register declaration useful at all for such compilers?  It indicates
that a variable is non-volatile, noalias and that taking its address
is illegal, all of which should make things easier for the optimiser.

So, does "register" help or hinder?  Has it become a source-code
"no-op"?  Can I merrily leave it out of my programs and let the
compiler do the guessing for me :-).

(Please, no wars, just *informed* replies, preferably from the authors
of said compilers...)-- 
Hubert Matthews      ...helping make the world a quote-free zone...

hjm@cernvax.cern.ch   hjm@vxomeg.decnet.cern.ch    ...!mcvax!cernvax!hjm

mcdonald@uxe.cso.uiuc.edu (07/23/89)

>>
>>... the way to find out how many registers the compiler will
>>use on your code is to compile your code and count how many registers
>>were used.  This is completely reliable, although not necessarily
>>repeatable.  It is also likely to be a useless statistic.
>>-- 

Yes, that's true.

>All true.  For amusement's sake though, here is a C program that finds
>out what register variables one can have by looking at their effect on
>execution time, which presumably is why one might want to know in the
>first place.  The following example works with gcc and cc on Sun
>3's and 4's running SunOS 3.5, and should work on most UNIX machines
>with little change.   As is frequently noted, timing routines are not
>very portable across OS's; sorry about that.

Except that his program was truly hopelessly non-portable. Here is
one that, I sincerely hope, is 100%  portable, unless you have on
obsolete compiler. It is not, unfortunately, 100% going to work
right on multitasking machines. There you will have to either go
single user and/or run it lots of times and take the maximum 
iterations.


#include <time.h>
#include <stdio.h>

#define T(A)      t = time(NULL); \
    while (t == time(NULL));\
    t = time(NULL);\
    for(i = 0; t == time(NULL); i++)\
       for(A = 0; A < 1000; A++) j |= 1; /* do something in loop */ \
    printf("register %s did %d loops\n", #A , i);

/* making these static and volatile will hopefully prevent putting
them in registers */

    volatile unsigned i,j;
    volatile time_t t;

int
main()
{
    register unsigned r01, r02, r03, r04, r05, r06, r07, r08, r09, r10,
                      r11, r12, r13, r14, r15, r16, r17, r18, r19, r20;

    if( time(NULL) == (time_t)-1) {
        puts("Sorry, the time function returned a -1.");
        exit(1);
    }


    T(r01);
    T(r02);
    T(r03);
    T(r04);
    T(r05);
    T(r06);
    T(r07);
    T(r08);
    T(r09);
    T(r10);
    T(r11);
    T(r12);
    T(r13);
    T(r14);
    T(r15);
    T(r16);
    T(r17);
    T(r18);
    T(r19);
    T(r20);

} 

It correctly reports two registers on my IBM PC.

Doug McDonald (mcdonald@uxe.cso.uiuc.edu)

flint@gistdev.UUCP (07/23/89)

I think you're gonna come back to "you have to look at the assembler
output." For one thing, you really can't know that the compiler hasn't
decided (possibly stupidly) to reserve a register for something else, such
as a stack pointer, which would make your count too low.  Some other things
nobody has taken into account here is that there are some architectures
with multiple levels of register performance: such as machines that have X
registers, and then a second (or more) bank(s) of X alternate registers,
with a fast instruction to exchange the contents of the registers with the
contents of the alternate registers.  A timing loop is likely to see 4
levels of speed: when using only the first bank of registers, when you have
to switch to the other bank and use a register, when you have to switch to
the other bank, use a register, and then switch back, and when you have to
use memory. 

Flint Pellett, Global Information Systems Technology, Inc.
1800 Woodfield Drive, Savoy, IL  61874     (217) 352-1165
INTERNET: flint%gistdev@uxc.cso.uiuc.edu
UUCP:     {uunet,pur-ee,convex}!uiucuxc!gistdev!flint

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/23/89)

In article <1038@cernvax.UUCP> hjm@cernvax.UUCP (Hubert Matthews) writes:
>So, does "register" help or hinder?

You already answered your own question.  The appearance of the "register"
storage-class specifier allows the compiler to know of certain constraints
on the variable's usage, which can never hurt optimization.  There are
already compilers that ignore "register" (except for diagnosing attempts
to take its address), showing that it can't really hinder optimization.
There are also a large number of compilers in daily use that do take
the "register" specification into account when generating code.  For such
compilers it obviously helps, assuming that the programmer has applied
"register" wisely.

mball@cod.NOSC.MIL (Michael S. Ball) (07/23/89)

In article <1038@cernvax.UUCP> hjm@cernvax.UUCP (Hubert Matthews) writes:
>
>I have a question for the authors of smart C compilers: is the
>register declaration useful at all for such compilers?

I can only speak for the three compilers with which I have been intimate,
but in them the "register" declaration had no effect except to prohibit
the application of "&".  In one of them you could force the compiler
to take your advice and pay attention to the "register" declaration.  It
usually only slowed the code a little bit.

Personally, I think compilers which require register declarations should
be referred to as "dumb C compilers", rather than referring to modern
compilers as "smart C compilers".

Michael S. Ball
TauMetric Corporation
1094 Cudahy Place, Ste 302
San Diego, CA 92110
(619)275-6381
mball@cod.nosc.mil

tim@crackle.amd.com (Tim Olson) (07/24/89)

In article <1038@cernvax.UUCP> hjm@cernvax.UUCP (Hubert Matthews) writes:
| I have a question for the authors of smart C compilers: is the
| register declaration useful at all for such compilers?  It indicates
| that a variable is non-volatile, noalias and that taking its address
| is illegal, all of which should make things easier for the optimiser.

Yes, the register keyword does indicate such things, but they can all be
determined by flow analysis anyway.  If a local variable does not have
its address taken, and it is non-volatile, then it is equivalent to
"register".

| So, does "register" help or hinder?  Has it become a source-code
| "no-op"?  Can I merrily leave it out of my programs and let the
| compiler do the guessing for me :-).

It's a no-op, except for error messages that must be reported if a
register variable's address is taken.  In other words, it's more for
diagnostic purposes than performance in "smart" C compilers. 

	-- Tim Olson
	Advanced Micro Devices
	(tim@amd.com)

hascall@atanasoff.cs.iastate.edu (John Hascall) (07/24/89)

In article <26470@amdcad.AMD.COM> tim@amd.com (Tim Olson) writes:
>In article <1038@cernvax.UUCP> hjm@cernvax.UUCP (Hubert Matthews) writes:
>| I have a question for the authors of smart C compilers: is the
>| register declaration useful at all for such compilers?
>
>Yes, the register keyword does indicate such things, but they can all be
>determined by flow analysis anyway.

    There are times that the programmer has more information than the
    compiler, consider this code fragment for example:

	for(i=0; i<foo; i++) ...
	for(j=0; j<bar; j++) ...

    and suppose the programmer knows `foo' will be large and `bar' will be
    small, then hopefully `register int i' could be a hint to the
    compiler that the first loop will be executed many times more than
    the second--something it has no clue about.

    John Hascall
    ISU Comp Center

mark@adec23.UUCP (Mark Salyzyn) (07/25/89)

In article <579@targon.UUCP>, andre@targon.UUCP (andre) writes:
>Yes, I think you can write a program that checks the nr of registers,
> ...
>main()
>{
>	register n1, n2, n3, n4, n5, n6, n7, n8; /* etc. */
>	int *a;
>	a = &n8;
>	/* repeat n6 - n2 */
>	a = &n1;
>}
>You can take the address of a variable but not of a register.

I have several compilers (Aztec C-80, MSC3.0, SYSV cc) that will complain with
ALL of the address taking and only assign a pair of them to registers. I'm
sorry but this is not the answer. I can't even find in the source code of the
C compiler how many registers are used :-).

As far as runtime is concerned, just do a decrement loop on each of the
register variables and check the time. The non-register variables will
take longer to do the loop (probably???). In my next posting I will
divulge the secrets of the Universe :-) :-).

Ciao,
-- Mark Salyzyn

bright@Data-IO.COM (Walter Bright) (07/25/89)

In article <1038@cernvax.UUCP> hjm@cernvax.UUCP (Hubert Matthews) writes:
<I have a question for the authors of smart C compilers: is the
<register declaration useful at all for such compilers?  It indicates
<that a variable is non-volatile, noalias and that taking its address
<is illegal, all of which should make things easier for the optimiser.
For Zortech C:

No, it isn't useful. Non-volatile is the default anyway. Variables are
assumed to be noalias unless:
	1. their address is taken
	2. they are static or global (in which case they can't be register
	   anyway)
The compiler trivially figures out for itself if the address is taken
or not.

<So, does "register" help or hinder?  Has it become a source-code
<"no-op"?  Can I merrily leave it out of my programs and let the
<compiler do the guessing for me :-).

The only effect "register" has is to cause a syntax error if you
took the address of a register variable. "register" should be listed
as an anachronism in the ANSI C spec.

I've seen a lot of effort expended by programmers trying to use the "register"
keyword most effectively. The time they spent doing this would usually pay
for a modern compiler which would do this automatically, and usually with
better results.

ari@eleazar.dartmouth.edu (Ari Halberstadt) (08/15/89)

In article <171@bms-at.UUCP> stuart@bms-at.UUCP (Stuart Gathman) writes:
#>In article <579@targon.UUCP>, andre@targon.UUCP (andre) writes:
#>
#>> 	register n1, n2, n3, n4, n5, n6, n7, n8; /* etc. */
#>> 	int *a;
#>
#>> 	a = &n8;
#>> 	a = &n7;
#>
#>> 	/* repeat n6 - n2 */
#>> 	a = &n1;
#>
#>Taking the address of a register variable is not allowed even when it
#>is not actually in a register. 

It should be illegal, but not all compilers enforce the rule! I tried the
program on our VAX running 4.3BSD, and it compiled it very happily.
--
Ari Halberstadt '91, "Long live short signatures"