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 ®istervar ] 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 "®" 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"