noise@eneevax.UUCP (Johnson Noise) (02/13/88)
While testing the availaility of register variables on several machines and compilers, I thought of this possibility: typedef union { char b; short w; long l; } dreg; typedef union { char *b; short *w; long *l; } areg; register dreg d0, d1, d2; register areg a0, a1; which would effectively allow full use of cpu registers. Note the similarity to 68k assembly. Unfortunately, all the compilers I have tried (including pcc) will not allocate unions in registers. My initial suspicion is that most compilers simply do not allow anything other than basic types to be allocated in registers. I realize that combining pointers and non-pointers in 68k systems is not possible, but I don't intend to do that. I don't see any immediate problems in allowing such a declaration (with checks on 68k's). Has anyone else considered this possibility? Is there a compiler which allows this? If it were possible, one could almost write exact assembly in C. It probably is not neccessary for the most part, but it would further reduce the neccessity of assembly (not to mention tearing up benchmarks :-). Comments, flames? If there aren't any problems, I would like to incorporate these and other concepts into my copy of gcc.
gwyn@brl-smoke.ARPA (Doug Gwyn ) (02/13/88)
I think it's a valid notion. Notice that the compiler would have to determine whether or not to assign a genuine register variable for the union based on what the member types are (they must all be compatible with the register). Also, only non-scratch registers could be allocated, so for example on a 68000 register D0, used as a scratch register in the generated code, would not be assigned to such a union. Which registers you got would of course depend on the register allocation strategy of the compiler. (You may get stack storage instead, if it runs out of registers.) You may need to add a check for & being applied to such a union, which should be illegal by normal C rules but may not be checked for in a compiler that didn't think unions would ever be in real registers. On the other hand, I don't think this is nearly as useful as you seem to think it would be. Why would you want to twiddle specific registers when not having to be concerned with such matters is the whole point of using a higher-level language?
kgregory@bbn.COM (Keith D. Gregory) (02/14/88)
In article <1229@eneevax.UUCP> noise@eneevax.umd.edu.UUCP (Johnson Noise) writes: > [code examples deleted] > >which would effectively allow full use of cpu registers. Note the >similarity to 68k assembly. Unfortunately, all the compilers I have >tried (including pcc) will not allocate unions in registers. My initial >suspicion is that most compilers simply do not allow anything other than >basic types to be allocated in registers. I realize that combining >pointers and non-pointers in 68k systems is not possible, but I don't >intend to do that. I don't see any immediate problems in allowing >such a declaration (with checks on 68k's). I think that the answer becomes apparent with some more code examples :-) struct mystruct { char field1, field2, field3, field4; }; union myunion { int field1; char field2[2]; }; The first example of course, would fail on the 68000 right away - you can't arbitrarily split a register into 4 bytes. The second is simply more of the same, and the key point is that structures and unions are manipulated using pointer arithmetic (to access the fields therein), and pointers to registers can not exist. -kdg
noise@eneevax.UUCP (Johnson Noise) (02/14/88)
In article <6831@ccv.bbn.COM> kgregory@ccv.bbn.com (Keith D. Gregory) writes: >In article <1229@eneevax.UUCP> noise@eneevax.umd.edu.UUCP >(Johnson Noise) writes: >> [code examples deleted] >> > >struct mystruct { > char field1, field2, field3, field4; > }; > >union myunion { > int field1; > char field2[2]; > }; > > >The first example of course, would fail on the 68000 right away - you >can't arbitrarily split a register into 4 bytes. The second is simply >more of the same, and the key point is that structures and unions are >manipulated using pointer arithmetic (to access the fields therein), and >pointers to registers can not exist. > I understand now, but for slightly different reasons. In the code examples I gave in my previous posting, pointer arithmetic was not necessary to access the different fields. The problem is with most/least significant byte/word. move.b _blah, d0 /* and */ move.w _blah, d0 move data into the least significant part of the register. Where move.b _blah, _there /* and */ move.w _blah, _there move data into the most significant part of _there (assuming _there is aligned to a longword address). This is obviously inconsistent. So what about address registers (pointers)? Pointers and adresses are always 32 bits and occupy the same position whether in memory or registers. Some examples: 68k assembly C source movea _i, a0 a0.l = &i; i must be long movea _j, a0 a0.w = &j; j must be short or long movea _k, a0 a0.b = &k; k may be char, short, or long move.b (a0)+, (a1)+ *a1.b++ = *a0.b++; move.l -(a0), -(a1) *--a1.w = *--a0.w; etc. Again, comments, flames?
tanner@ki4pv.uucp (Dr. T. Andrews) (02/14/88)
In article <6831@ccv.bbn.COM>, kgregory@bbn.COM (Keith D. Gregory) writes:
) In article <1229@eneevax.UUCP> noise@eneevax.umd.edu.UUCP
) > [code examples deleted]
) I think that the answer becomes apparent with some more code
) examples :-)
) [code examples where a register is not appropriate on machine 'X' ]
You miss the point. In certain cases, which cases will vary from
machine to machine, it is desirable to put a union in a register.
For instance, to preserve registers or (more likely) to walk through
more than one kind of structure, you might put a union of several
pointers in a register.
The compiler should, when appropriate, put these unions into
registers. Oh, do I need the standard code examples?
union _ptrs { struct _foo *foo_ptr; struct _bar *bar_ptr; }
/* .. now, within a function */
register union _ptrs my_ptr;
if (dealing_with_foo)
my_ptr.foo_ptr = &foo_struct;
else
my_ptr.bar_ptr = &bar_struct;
A "good" compiler should know that it can put my_ptr into a register
(unless using some architecture where pointers don't fit into
registers; names omitted here to protect intel).
I suspect that few compilers are actually smart enough to put my_ptr
into a register, however. The obligatory chorus of compiler vendors
bragging on \fItheir\fP compilers goes in messages immediately
following.
--
{allegra clyde!codas decvax!ucf-cs ihnp4!codas killer}!ki4pv!tanner
cjc@ulysses.homer.nj.att.com (Chris Calabrese[rs]) (02/16/88)
In article <1229@eneevax.UUCP>, noise@eneevax.UUCP (Johnson Noise) writes: > > similarity to 68k assembly. Unfortunately, all the compilers I have > tried (including pcc) will not allocate unions in registers. My initial > suspicion is that most compilers simply do not allow anything other than > basic types to be allocated in registers. I realize that combining > pointers and non-pointers in 68k systems is not possible, but I don't > intend to do that. I don't see any immediate problems in allowing > such a declaration (with checks on 68k's). The official AT&T version of the K&R portable C compiler only supports register allocation of int's. All other types are silently assigned to core. This, however, is something which is supposed to change in the near future, at leas according to my documentation. Chris Calabrese AT&T Bell Labs ulysses!cjc
guy@gorodish.Sun.COM (Guy Harris) (02/16/88)
> The official AT&T version of the K&R portable C compiler only > supports register allocation of int's. All other types are > silently assigned to core. I presume you mean "integral types", not "int"s. The version of PCC2 that comes with S5R3.1 on 3B2s, for instance, quite cheerfully puts "short"s, "char"s, and "long"s into registers as well.
daveb@laidbak.UUCP (Dave Burton) (02/16/88)
In article <1229@eneevax.UUCP> noise@eneevax.umd.edu.UUCP (Johnson Noise) writes: | typedef union | { | char b; | short w; | long l; | } dreg; | | typedef union | { | char *b; | short *w; | long *l; | } areg; | | register dreg d0, d1, d2; | register areg a0, a1; | |which would effectively allow full use of cpu registers. Note the |similarity to 68k assembly. Though not specifically disallowed in the original specification of C, this declaration is not useful in the manner you propose. K&R (pg 197) and H&S (pg 109) state that storage is allocated from the beginning of the union. The 68k series put the char type in the LSB, while the union declarations want the char type in the MSB. -- --------------------"Well, it looked good when I wrote it"--------------------- Verbal: Dave Burton Net: ...!ihnp4!laidbak!daveb V-MAIL: (312) 505-9100 x325 USSnail: 1901 N. Naper Blvd. #include <disclaimer.h> Naperville, IL 60540
hankd@pur-ee.UUCP (Hank Dietz) (02/18/88)
According to K&R (page 193), a key constraint on objects of storage class register is that: "the address-of operator & cannot be applied to them" Now, I certainly admit that the original intent was simply to give the compiler a hint as to which auto variables should be placed in registers (if you've got 'em), but the lack of an address for an object in C means that the object NAME HAS NO ALIASES, and that is far more important to a good optimizing or parallelizing compiler. Consider code like: *p = b * c; a = b * c; In this code, unless the compiler can prove that b and c cannot be pointed-to by p, b * c is NOT a common subexpression. Proving that p can't point at b or c is, however, very difficult under many circumstances -- particularly if b, c, and p are globals accessed in a separately compiled module. If b and c are declared as register variables, then the address can't be taken, hence the proof is given without need to examine separately compiled modules, or anything else for that matter. One might argue that removing a common subexpression like b * c is not that important an optimization, but similar constraints apply for nearly all optimizations and in automatic parallelization. In summary, for the most efficient and effective compilation, register OUGHT TO MEAN "HAS NO ALIAS" and nothing else. Hence, it makes sense to say register anything -- any type, any storage class (not just register implies auto, as K&R suggest) -- and let the compiler assign registers as it sees fit. In fact, this is how at least a few optimizing C compilers treat register declarations anyway. More details as to treatment of register as a "noalias"-like storage-class modifier appear in my PhD Thesis, "The Refined-Language Approach to Compiling for Parallel Supercomputers," Polytechnic University, Brooklyn NY. I'm currently an assistant prof. of EE at Purdue and can supply reprints of the thesis or related materials directly, if you prefer (so long as there are not too many of you). -Hank Dietz
tainter@ihlpg.ATT.COM (Tainter) (02/20/88)
In article <6831@ccv.bbn.COM>, kgregory@bbn.COM (Keith D. Gregory) writes: > In article <1229@eneevax.UUCP> noise@eneevax.umd.edu.UUCP > (Johnson Noise) writes: > > [ A STATEMENT THAT REGISTERIZING A SUBSET OF UNIONS WOULD BE USEFUL ] > I think that the answer becomes apparent with some more code examples :-) This is a specious argument. One cannot give examples of problems when the compiler is free to pick and choose which of your register declarations to ignore and which to implement. > struct mystruct { > char field1, field2, field3, field4; > }; I don't see anything prohibiting this. To access a field you do byte swaps or copy to another register and mask etc, depending on what's available in your instruction set. This might be less expensive than offset calculation and memory referencing. On something without byte addressability it might implement it with register loads, shifts and masks anyway. In which case a register to register copy is bound to be quicker than fecthing from memory. > union myunion { > int field1; > char field2[2]; > }; What problems does this have? field one obviously presents no problems. field2 is very easy to handle with byte swaps and byte instructions (assuming the machine has such). Now, if you intend to pull bytes out of that int it isn't required that the compiler provide such capability anyway. > -kdg --j.a.tainter
jejones@mcrware.UUCP (James Jones) (02/20/88)
In article <7258@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes: > On the other hand, I don't think this is nearly as useful as > you seem to think it would be. Why would you want to twiddle > specific registers when not having to be concerned with such > matters is the whole point of using a higher-level language? The place I can see people wanting unions to live in registers is something like register union { woof *wp; char *cp; ... } mumble; (Actually, this isn't great insight on my part; I've heard people say, "Gee, it would be nice if a compiler let you do this...") There are some low-level cases in which one would like to add a byte offset to a pointer that usually points at something bigger than one byte, and still keep the pointer in a register. (Of course, compilers should all be smart enough to figure out what should go in registers themselves, right? We don't need no steenkin' register declara- tions! :-) (I *do* recognize the advantages of letting the compiler figure out what can go into a register, what the lifetimes of such variables are, etc.--it's just that if the compiler *can* really put such a union in a register itself, it seems to me that the programmer should be able to specify explicitly that he thinks it's important that the union be in a register.) James Jones
gnu@hoptoad.uucp (John Gilmore) (02/21/88)
noise@eneevax.UUCP (Johnson Noise) wrote: > Unfortunately, all the compilers I have > tried (including pcc) will not allocate unions in registers. > If there aren't any problems, I would like to incorporate these > and other concepts into my copy of gcc. I think gcc (the GNU C Compiler) will put unions into registers. I have seen it put a struct into a register. I couldn't get the simple examples Johnson used to allocate a register -- the optimizer was smart enough to notice that it was only used a few times or was constant, so why waste a register on it! While compiling most of Berkeley Unix with gcc I ran across two programs that declared "register struct foo bar" and then took the address of bar! (You cain't do that to register variables). PCC never complained because it forgets that you called something "register" if it doesn't actually put it in a register. Doug Gwyn noted that you wouldn't get a variable allocated to d0 on the 68000 anyway, since it's a scratch register. Gcc is smart enough to put variables in d0 that aren't used across a function call (or any variable, in a "leaf" routine that makes no function calls). It's fun having the fastest 68K C compiler be the free one, though that can't last forever. If you notice that GCC is generating slow code though, you could help by improving it, or at least send a note to bug-gcc@prep to encourage others to do so. -- {pyramid,ptsfa,amdahl,sun,ihnp4}!hoptoad!gnu gnu@toad.com "Watch me change my world..." -- Liquid Theatre
gnu@l nptoad.uucp (John Gilmore) (02/21/88)
noise@eneevax.UUCP (Johnson Noise) wrote: > Unfortunately, all the compilers I have > tried (including pcc) will not allocate unions in registers. > If there aren't any problems, I would like to incorporate these > and other concepts into my copy of gcc. I think gcc (the GNU C Compiler) will put unions into registers. I have seen it put a struct into a register. I couldn't get the simple examples Johnson used to allocate a register -- the optimizer was smart enough to notice that it was only used a few times or was constant, so why waste a register on it! While compiling most of Berkeley Unix with gcc I ran across two programs that declared "register struct foo bar" and then took the address of bar! (You cain't do that to register variables). PCC never complained because it forgets that you called something "register" if it doesn't actually put it in a register. Doug Gwyn noted that you wouldn't get a variable allocated to d0 on the 68000 anyway, since it's a scratch register. Gcc is smart enough to put variables in d0 that aren't used across a function call (or any variable, in a "leaf" routine that makes no function calls). It's fun having the fastest 68K C compiler be the free one, though that can't last forever. If you notice that GCC is generating slow code though, you could help by improving it, or at least send a note to bug-gcc@prep to encourage others to do so. -- {pyramid,ptsfa,amdahl,sun,ihnp4}!l nptoad!gnu gnu@toad.com "Watch me change my world..." -- Liquid Theat
ok@quintus.UUCP (Richard A. O'Keefe) (02/22/88)
In article <604@mcrware.UUCP>, jejones@mcrware.UUCP (James Jones) writes: > The place I can see people wanting unions to live in registers is something > like > register union { > woof *wp; > char *cp; > ... > } mumble; Last time I looked at a PR1ME C manual, most pointers were 32 bits, but character pointers were 48 bits. The P400 (the machine I used) has 32-bit registers. {There are also the "Field Address" and "Field Length" registers, one set of which overlaps a floating-point register...} If you really need to do this, you can manage it with casts. The cast version is a wee bit better than the union version, because Lint will complain about the casts, but the union code will just quietly do something surprising. for (times = 100; --times >= 0; ) { fprintf(stderr, "Byte addressing is not universal.\n"); fprintf(stderr, "Pointers may come in different sizes.\n\n"); }
mwm@eris (Mike (My watch has windows) Meyer) (02/23/88)
In article <677@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes: <In article <604@mcrware.UUCP>, jejones@mcrware.UUCP (James Jones) writes: <> The place I can see people wanting unions to live in registers is something <> like <> register union { <> woof *wp; <> char *cp; <> ... <> } mumble; < < for (times = 100; --times >= 0; ) { < fprintf(stderr, "Byte addressing is not universal.\n"); < fprintf(stderr, "Pointers may come in different sizes.\n\n"); < } True statements, but they have nothing to do with people wanting to put unions in registers. <If you really need to do this, you can manage it with casts. Do what? You don't know why the union was wanted. Maybe I just want to reuse that register for multiple things, and have no intention of using it to copy bits from a (char *) to a (woof *). <The cast version is a wee bit better than the union version, Actually, the cast version is a lot better. It gives the compiler the chance to do something intelligent with the bits before putting it in the other type of pointer. Doing that kind of thing with a union means you get raw bits. Then again, that may be what you want. <Last time I looked at a PR1ME C manual, most pointers were 32 bits, <but character pointers were 48 bits. The P400 (the machine I used) <has 32-bit registers. So, because the prime won't let you put (char *) objects in a register, you think people shouldn't be allowed to declare register union objects? It'd make more sense to disallow register char * objects, and any union that they wouldn't fit in. But there are to many machines where register char * makes sense, so that probably won't fly. From the old dpANS, I'd guess you can declare register unions. You just can't take the address of the union, or any part of it. Machines that won't support what you're trying to put into the union in the register type probably ignore the register - just like they do for a register object decleration where the object won't fit in a register. <mike -- Estant assis, de nuit secrette estude, Mike Meyer Seul, repose sur la selle d'airain, mwm@berkeley.edu Flambe exigue, sortant de solitude, ucbvax!mwm Fait proferer qui n'est a croire vain. mwm@ucbjade.BITNET
ok@quintus.UUCP (Richard A. O'Keefe) (02/24/88)
Someone (I have lost the original posting) suggested that
'register union { foo* x; baz *y; ... }'
would be a useful construct in C.
Well, it's already legal. According to the Oct '86 dpANS,
"A declaration with storage-class specifier 'register' is an 'auto'
declaration, with a suggestion that the objects declared be stored
in fast-access machine registers if possible. The types of objects
that are stored in such registers and the number of such
declaratiopns in each block that are effective are implementation-
defined [footnote: The implementation may treat any 'register'
declaration simply as an 'auto' declarations. However, ... the
unary & (address-of) operator may not be applied to an object
declared with storage-class specifier 'register', whether or not
a machine register is actually used.]"
The System V Programmer's Guide explicitly says
"excess or invalid 'register' declarations are ignored."
Similar statements appear in other C manuals.
Interestingly enough, DEC's VAX C manual explicitly says
"If the variable requires storage (for example, arrays or structures),
the object of the variable is not placed in a register."
It seems that any C compiler which rejects 'register union foo X'
as an error has always been broken: this is legal K&R C. But a
compiler has always been within its rights to ignore 'register' in
this or any other case, though better-quality compilers print a
warning message.
The construct thus already being legal, I assumed the original poster
to be urging that compilers SHOULD put unions in registers if it is
possible for them to do so, and to be alleging that this was particularly
important for pointers.
I posted a message pointing out that this simply doesn't make sense on
some machines (specifically including PR1MEs), and employed a familiar
humourous device to stress this. I have received some flaming messages
from people who took exception to this commonplace observation. Oddly
enough, no-one using a PR1ME has complained to me yet...
Here's why I feel strongly about issues like this:
(1) I did a couple of days consulting once for a company who had found that
the only practical way of porting 4.2BSD to their machine was to change
the microcode so that *(char*0) == 0.
(2) I have had the unpleasant experience of porting a program which used
pointers heavily to a machine where 'int' and 'char*' were not the
same size. Even changing the definition of NULL to 0L (which is not,
strictly speaking, correct) didn't help. I had to go through more
lines of code than I care to remember changing 0 to (char*)NULL.
(3) I had to port a program which assumed that, given the declaration
union two {int a; char *b;} jim;
the calls
harry(jim);
and
harry(jim.b);
were identical. Suffice it to say that they weren't.
(4) I ported a middle-sized program to a machine, and watched someone else
port a much large program to the same machine, where although character
pointers and word pointers were both the same size as an integer, they
had different representations. So, for example,
int data[50];
fwrite(data, sizeof *data, (sizeof data)/(sizeof *data), output);
wasn't just badly typed (it has always been that), it gave the wrong
answers. Again, one had to go though changing things like this to
fwrite((char*)data, ....);
(5) I had to advise someone that a very large (and *very* useful) program
of theirs would be too expensive to port to a PR1ME because they had
assumed throughout that word pointers and character pointers were both
the same size as an integer. What was really tragic about this was
that the program in question had very little real use for character
pointers, but things had been converted to this "common currency".
What is the point of something like this:
union ptr { char *c; int *i; long *l; int (*f)(); };
register union ptr fred;
Surely the point is to say
fred.c = /* something */;
... fred.i ...
and have it go fast.
But this is going to give you major porting headaches in the future.
Or more plausibly, it is going to give someone else major porting
headaches. Too bad that it is already legal...
Is there something comparable which is less trouble for porting? YES.
Use casts. Do something like
#if ....
typedef int UsualStorageUnit;
#elif ...
typedef short UsualStorageUnit;
#elif ...
typedef char UsualStorageUnit;
#else
/* if case not handled, syntax error in next declaration */
#endif
typedef UsualStorageUnit *UsualPointer;
(void*) is close, but not quite identical. (void*) has to handle the
worst case, and is usually much the same as (char*). UsualPointer is to
be the "native" pointer type, just as int is the "native" integer type.
#define AsUsualPtr(x) ((UsualPointer)(x))
#define AsShortPtr(x) ((short*)(x))
#define AsIfuncPtr(x) ((int (*)())(x))
and so on.
Then you can declare routines like
void apply1(Fn, Arg)
register UsualPointer Fn, Arg;
{
(*AsIfuncPtr(Fn))(*AsShortPtr(Arg));
}
What's the difference? Well, apart from the fact that the compiler is
more likely to put UsualPointers into registers than unions (though
putting both, and putting neither, are both ALREADY legal), the compiler
can now spot each type change, and can tell you about the ones that
aren't going to work.
Look *very* carefully at **any** union in your programs which is not
#ifdeffed by machine or implementation; bugs breed in them like
mosquitos in a swamp. Using different members of a union at different
times is fine, but putting something into one member of a union and
picking it up again from another member is bad practice in any programming
language. (My first introduction to the problem was trying to port a
Pascal program from a CDC machine to a B6700. The Pascal programmer had
assumed that 10 characters = 1 integer, and not only was that not the
case, but the bit pattern of the first N characters often wasn't valid
as an integer.)
Frankly, I am not impressed by people who say
"don't be so condescending, this is a useful construct on MY machine."
I do not use a PR1ME (or any of the other machines I hinted at above)
myself. I have done, and hope never to do so again. Life is difficult
enough for these people without going out of our way to make things worse.
Oh yes, another porting problem: sizeof *main. On at least three machines
that I can think of, the function pointers that C programs pass around is
actually a pointer to a control block, *not* a pointer to the code...
Don't expect the bit value C has to be equal to what you see in a load map.
gwyn@brl-smoke.ARPA (Doug Gwyn ) (02/24/88)
In article <686@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes: >(void*) has to handle the worst case, and is usually much the same as (char*). As of the last draft proposed ANSI C standard, (void *) and (char *) are required to have the same representation. This is to grandfather in existing programs that use routines like fwrite() that really should always have received generic pointers, but historically have (correctly) been fed (char *)s.
mike@hpfclq.HP.COM (Mike McNelly) (02/25/88)
Register unions are indeed useful and implementable. I implemented them
in a pcc based compiler a couple of years ago primarily for our internal
use. Many programs show negligible speed improvement but some,
particularly those using yacc output gained about 10% speed and space
improvement. As a consequence we use register unions to generate our
compilers and our assembler, which is also yacc based.
There are difficulties that I never bothered to resolve in our
implementation. The most obvious one involves multiple assignment.
e.g.
register union foo
{
int i;
float f;
int *ip;
} a,b,c;
a.i = b.f = (int)c.ip = ...;
After a series of multiple assignments the internal representation
within pcc becomes very messy. Since the implementation was for an
internal tool only it was easier to avoid the problem than add lots of
code to fix it. The situation is very rare and not worth slowing the
compiler down to handle an occasional occurrence.
You may also have problems deciding which register class in which to put
the union. For the above example, it may be wiser to locate the union
in a data register (MC68000 family) unless the pointer member is to be
accessed frequently in which case an address register may be more
appropriate. If you guess wrong the resulting code can actually be
somewhat slower than if the union is in memory. We chose to use the
first union member type to be a clue.
Mike McNelly hplabs!hpfcla!mike
Hewlett Packard Company
blarson@skat.usc.edu (Bob Larson) (02/25/88)
In article <686@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes: >I posted a message pointing out that this simply doesn't make sense on >some machines (specifically including PR1MEs), and employed a familiar >humourous device to stress this. I have received some flaming messages >from people who took exception to this commonplace observation. Oddly >enough, no-one using a PR1ME has complained to me yet... It was quite obvious that you were trying to use Prime as an example without actually having used the C compiler on one. The C compiler designers made some strange desisions (for people used to the machines) to make porting incorrect code eaiser in some cases. 64v mode: sizeof (int) == sizeof (long) == 4 sizeof (char *) == sizeof (int *) == sizeof (whatever *) == 6 (last 16 bits of pointers other than char * are unused) If the first 32 bits of a pointer are 0, it is considered a NULL pointer, not a pointer to the valid address 0 in kernal space. (Other Prime langues use a pointer to segment 7777 with the fault bit set for NULL pointers.) Casts from pointers to integers are non-trivial conversions, producing the number of bytes from address 0, and loosing auxilary information such as ring and fault bits. Since 64 v mode C doesn't support 32 bit pointers, calling some other language routines is difficult. stdin, stdout, and stderr may only be used other than as arguments to functions in main. ("FILE *f = stdin;" will not work other than in main.) No variables are ever kept in registers. Register function paramaters are copied to local memory for faster access. 32ix mode: sizeof (char *) == sizeof (int *) == sizeof (whatever *) == 4 Register variables are supported. The -ignoreregister option allows the compiler to choose which variables should go in registers. Only works on newer primes. (4-digit except 2250.) -- Bob Larson Arpa: Blarson@Ecla.Usc.Edu blarson@skat.usc.edu Uucp: {sdcrdcf,cit-vax}!oberon!skat!blarson Prime mailing list: info-prime-request%fns1@ecla.usc.edu oberon!fns1!info-prime-request
wes@obie.UUCP (Barnacle Wes) (02/26/88)
In article <41964@sun.uucp>, guy@gorodish.Sun.COM (Guy Harris) writes: > > The official AT&T version of the K&R portable C compiler only > > supports register allocation of int's. All other types are > > silently assigned to core. > > I presume you mean "integral types", not "int"s. The version of PCC2 that > comes with S5R3.1 on 3B2s, for instance, quite cheerfully puts "short"s, > "char"s, and "long"s into registers as well. A lot of this type of problem is caused by the underlying machine architecture. On the M68000, you usually have 5 or 6 address registers available, so most 68K C compilers let you put pointers in registers as well as integral types. The one I use the most (MWC for Atari ST) does not allow floats of any flavor to be put in registers. On Intel chips, a pointer in a register isn't really defined; there are no real general-purpose registers, and there aren't any registers big enough to hold a full address. I guess you could probably put an address is ES:DI or something like that, but I don't know of any compilers that do. MicroPort's pcc surely doesn't! ---- "Segments are for worms!" -- Landon Dyer -- /\ - "Against Stupidity, - {backbones}! /\/\ . /\ - The Gods Themselves - utah-cs!utah-gr! / \/ \/\/ \ - Contend in Vain." - uplherc!sp7040! / U i n T e c h \ - Schiller - obie!wes
gwyn@smoke.BRL.MIL (Doug Gwyn ) (10/24/88)
>> In article <976@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes: >> >One complaint that I have about the C compilers I have used is that they >> >do not support register unions. The point is, on many architectures not all basic data types could actually share a register. Your original example was of an integer and a floating type sharing a register, which doesn't work on any machines I'm aware of. And obviously a union of large types would not fit into a register. Simply permitting "register" to be crammed on the front of "union" would do no good; the hardware would have to actually support the usage for this to be worthwhile. If the hardware DOES support this, its C implementation is allowed to use an actual register (so long as the address is not taken). Good optimizing C compilers ignore the "register" specifiers and do their own register allocation anyway.
cik@l.cc.purdue.edu (Herman Rubin) (10/24/88)
In article <8741@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes: > >> In article <976@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes: > >> >One complaint that I have about the C compilers I have used is that they > >> >do not support register unions. > > The point is, on many architectures not all basic data types could actually > share a register. Your original example was of an integer and a floating > type sharing a register, which doesn't work on any machines I'm aware of. > And obviously a union of large types would not fit into a register. On the VAX and PYRAMID, single precision floating point and integer both take up exactly one register. But that is not the example I gave. I have used the other in a situation where it obviously saved time. I put down the address of an integer and the address of a floating point number. There are very few machines where these take up different amounts of space. Of course, if something cannot fit into the registers available, it cannot be made register. But this is no reason to ignore the programmer. And there is even less reason not to put a union into a register because it is a union when any other pointer would have been put into a register. > Simply permitting "register" to be crammed on the front of "union" would > do no good; the hardware would have to actually support the usage for > this to be worthwhile. If the hardware DOES support this, its C > implementation is allowed to use an actual register (so long as the > address is not taken). Good optimizing C compilers ignore the "register" > specifiers and do their own register allocation anyway. I have not even seen a fair optimizing C compiler. In a university environ- ment, one has little say in this. I can say that the 4.xBSD compiler does not allow certain registers to be used by the programmer, even if they are available. It does not allow the use of symbolic registers in asm line. There are too many other things it does not allow, and the simple idea of register unions is one of them. BTW, in the book, whose title and authors slip my mind, on the use of lex and yacc in constructing a C compiler, the authors use register unions. I have seen register unions used in widely published C sources. -- Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907 Phone: (317)494-6054 hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)
renglish@hpisod1.HP.COM (Robert English) (10/26/88)
> / gwyn@smoke.BRL.MIL (Doug Gwyn ) / 2:16 pm Oct 23, 1988 / > The point is, on many architectures not all basic data types could actually > share a register. Your original example was of an integer and a floating > type sharing a register, which doesn't work on any machines I'm aware of. > And obviously a union of large types would not fit into a register. > Simply permitting "register" to be crammed on the front of "union" would > do no good; the hardware would have to actually support the usage for > this to be worthwhile. If the hardware DOES support this, its C > implementation is allowed to use an actual register (so long as the > address is not taken). Good optimizing C compilers ignore the "register" > specifiers and do their own register allocation anyway. Even if the hardware could not support a register union of incompatible data types, the information would be valuable to an optimizer. "Register," in addition to expressing a storage preference, informs the compiler that the variable is non-addressable, and that the compiler needn't worry about the contents changing. --bob--
guy@auspex.UUCP (Guy Harris) (10/28/88)
>Even if the hardware could not support a register union of incompatible >data types, the information would be valuable to an optimizer. >"Register," in addition to expressing a storage preference, informs the >compiler that the variable is non-addressable, and that the compiler >needn't worry about the contents changing. Well, optimizers of that level of sophistication often completely ignore "register" declarations and decide for themselves what should go into registers.... (Wasn't there some other keyword people were talking about at one point for saying that there wouldn't be any aliases for some location? :-) :-) :-) :-) :-) :-) :-) :-) :-)) The compiler can generally figure out for itself whether you're taking the address of some local variable or not; it tends not to need the assistance of a "register" declaration.