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

chris@mimsy.UUCP (Chris Torek) (07/18/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,
>but you will get the answer at compile time, not run time :-)
>
>the program [takes the address of variables declared with the `register'
keyword]

>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.

The first statement is false.  The first part of the second statement is
true.  The second part of the second statement is false.

A compiler is free to assign variables to registers without regard to
the `register' keyword.  The programmer is required not to attempt to
use the `&' address-of operator on any variables declared with the
`register' keyword.  That is, you cannot take the address of a register
variable, whether or not it is in fact in a machine register.

The real problem here is that the compiler is free to use none, any or
all machine registers whenever it wants; it is possible (if stupid) for
a compiler to switch from `none' to `all' simply because the code was
compiled in the afternoon rather than the morning.

That said, 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.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

walter@hpclwjm.HP.COM (Walter Murray) (07/19/89)

> That said, 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.

Agreed.  Another way to find out the extent to which the register
specifier will have effect is to read the manual.  When you take
delivery of your first ANSI-conforming  C compiler, it will come
with some sort of a manual.  This is one of the items that are
required to be documented there.

Walter Murray
-------------

johnm@occam.ksr.com (John Martin) (07/20/89)

In article <18603@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>
>... 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.
>-- 
>In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
>Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

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.

On the systems I've tried ( Sun 3/75, 3/60, 3/140, 4/280 ) the
register-variable loops run about 2.2 - 2.5 times faster than the
memory-variable loops.  The SPARC is about twice as fast as a 3/75,
achieving about 1 million Hz for register loops.  For the curious the
register counts are:

    gcc : 10 registers on MC68020, 14 on SPARC.
Sun cc  :  6 registers on MC68020, 14 on SPARC.

Counting address registers, and finding whether address and integer
registers are separate or part of the same general register set, or
being used in a slick way by the compiler, are left as exercises to
the reader.  The answers will have some surprises -- for example, the
gcc MC68020 result of 10 integer registers is interesting, since an
MC68020 only has 8 data registers.  As Chris Torek says, the only way
to be sure what's happening is to look at the assembler.

/* showregs.c   How many registers will your compiler give you?
 *
 *  On a UNIX system:
 * 
 *       % cc showregs.c
 * _or_  % gcc -traditional showregs.c
 * _or_  % gcc -ansi showregs.c
 *
 * ( one flag or the other is needed to make PRINTLINE expand correctly)
 *
 *       % a.out > regs
 *       % graph -b -a -y 0 -g 1 < regs | plot
 */

#include <sys/time.h>
#include <signal.h>

#ifdef __STDC__
#define PRINTLINE(xx)   (void) printf( "%8u %s\n", xx, #xx)
#else          old-style macro substitution inside quotes
#define PRINTLINE(xx)    (void) printf( "%8u xx\n", xx) 
#endif

/* Execute a simple unrolled loop for a fixed time & count iterations.
 */
#define LOOPS(var)				 \
    var = 0;					 \
    timing = 1;					 \
    if ( setitimer( ITIMER_VIRTUAL, &itval, 0) ) \
	perror( "Starting timer");		 \
    while (timing) { var--; var++; var--; var++; var++; } \
    PRINTLINE(var)


unsigned timing;

int
vtalrmhandler()
{
    timing = 0;
    return 0;
}

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

    /* Set up to use the UNIX virtual timer for 200 milliseconds
     * of process time.
     */
    itval.it_value.tv_sec = 0;
    itval.it_value.tv_usec = 200*1000;
    itval.it_interval.tv_sec = itval.it_interval.tv_usec = 0;
    (void) signal( SIGVTALRM, vtalrmhandler);

    LOOPS(r01); LOOPS(r02); LOOPS(r03); LOOPS(r04); 
    LOOPS(r05); LOOPS(r06); LOOPS(r07); LOOPS(r08);
    LOOPS(r09); LOOPS(r10); LOOPS(r11); LOOPS(r12);
    LOOPS(r13); LOOPS(r14); LOOPS(r15); LOOPS(r16);
    LOOPS(r17); LOOPS(r18); LOOPS(r19); LOOPS(r20);
} /* end main */

/* EOF */

John H. Martin					harvard!ksr!johnm
Kendall Square Research Corporation		johnm@ksr.com
170 Tracer Lane
Waltham, MA 02154
"A creative economy is the fuel of magnificence." -- Emerson
Disclaimer:  My only organizationally-endorsed position is prone.

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

In article <547@ksr.UUCP> johnm@ksr.UUCP (John Martin) writes:
| In article <18603@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
| >
| >... 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.
| >-- 
| >In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
| >Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris
| 
| 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.

This method still doesn't work reliably, for a couple of reasons. 
First, the lifetimes of the declared register variables don't overlap,
so a compiler with register coloring could see that they all could be
replaced with a single shared register.  Second, even if a variable was
forced to memory, the LOOP macro test sequence:

     while (timing) { var--; var++; var--; var++; var++; } \

will not necessarily load and store the value to memory at each
increment/decrement.  Rather, the compiler is free to load the value
into a register before entry to the while-loop, computing all values in
that register, and storing the final result back to the variable's
memory address at loop exit.

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

johnm@occam.ksr.com (John Martin) (07/21/89)

In article <26423@amdcad.AMD.COM> tim@amd.com (Tim Olson) writes:
>In article <547@ksr.UUCP> johnm@ksr.UUCP (John Martin) writes:
>>
>> [ A program to time variable access to look for registers.]
>>
>This method still doesn't work reliably, for a couple of reasons. 
>First, the lifetimes of the declared register variables don't overlap,
>so a compiler with register coloring could see that they all could be
>replaced with a single shared register.  Second, even if a variable was
>forced to memory, the LOOP macro test sequence:
>
>     while (timing) { var--; var++; var--; var++; var++; } \
>
>will not necessarily load and store the value to memory at each
>increment/decrement.  Rather, the compiler is free to load the value
>into a register before entry to the while-loop, computing all values in
>that register, and storing the final result back to the variable's
>memory address at loop exit.

If a compiler does coloring with optimization off, initialize the
variables so their lifetimes will be coterminous.  A compiler that
doesn't always store memory variables to memory before their next read
with optimization off would break this test -- and any standard
debugger.

Are there really compilers that do not regard coloring or dynamic
transfer of a variable between register and memory as suppressable
optimizations?  I'd like a list, so I can mark them in my mental map
with the label "Here be monsters."  Unless they come with good
debuggers that can understand them, they'd sure be obnoxious to use
for program development -- the only reliable debugger would be
assembly-level.




John H. Martin					harvard!ksr!johnm
Kendall Square Research Corporation		johnm@ksr.com
170 Tracer Lane
Waltham, MA 02154
"A creative economy is the fuel of magnificence." -- Emerson
Disclaimer:  My only organizationally-endorsed position is prone.