stuart@bms-at.UUCP (Stuart Gathman) (12/24/88)
With all this talk about 'volatile' being necessary for good optimization, I wonder why compilers don't do the optimizations already available without it? Examples, mov.l d5,d0 mov.l d0,d4 ; typical "optimized" code ! mov.l &0,d0 or mov.w &1234,d0 ext.l d0 sub.l d2,d0 ; "optimized" code . . . mov.w d0,&5678 or (for Intel fans) register char far *p; ... p->a = 1; p->b = 2; becomes (on MSC & Turbo C) les bx,[bp-4] mov byte ptr es:[bx],1 les bx,[bp-4] ; "optimized" code ! mov byte ptr es:[bx+1],2 I could go on and on. I have used Aztec, Turbo, MS, Motorola, GCC (better than most), Convergent Technologies (the worst), and many other compilers. When compilers don't take advantage of existing optimizations, what's the big deal about "volatile"? Furthermore, all "auto" and "register" variables are subject to the same optimizations as (absence of) "volatile" gives you if their address is never taken (and "noalias" to boot). If you optimize "static" data as well, only "extern" data gets volatile treatment. In my programs, this is precisely the data requiring the "volatile" keyword! (And device drivers would make all magic addresses "extern".) If compilers would simply use the optimization opportunities already available, the improvement with "volatile" would be marginal. P.S. don't post counter examples like: int a,b, *p = &a; p[1] = 2; this is not portable 'C' (or even good programming). -- Stuart D. Gathman <stuart@bms-at.uucp> <..!{vrdxhq|daitc}!bms-at!stuart>
bill@twwells.uucp (T. William Wells) (12/25/88)
In article <141@bms-at.UUCP> stuart@bms-at.UUCP (Stuart Gathman) writes: : I could go on and on. I have used Aztec, Turbo, MS, Motorola, : GCC (better than most), Convergent Technologies (the worst), : and many other compilers. : : When compilers don't take advantage of existing optimizations, : what's the big deal about "volatile"? While many compilers suck wind, many others are very good. Also, remember that some optimizations are easier than others. The examples you gave were failures in the peephole optimizer; most such do their thing by handling special cases. What you found were special cases not handled by particular peephole optimizers. Tightening up the peephole optimizer, past a certain point, is a waste of time: all additional optimizations will be marginal. If I had my 'druthers, I'd rather take advantage of volatile instead of trying to tighten up a peephole optimizer. Why? Because the absence of volatile permits me to make wholesale optimizations. In other words, if I assume that all variables are implicitly volatile, I must not assume that the variable retains its values between accesses; nor may I assume that a write to a variable which can be determined to be unnecessary (for the algorithm) can be deleted. This means that all memory references implied by the code must remain there; this prohibits some of the most effective optimizations from being done. Another advantage of this kind of optimization is that it is machine independent. Many optimizers have two parts, one for machine independent optimizations and one for machine dependent optimizations. Compilers that have such often can have the code generators changed, resulting in a compiler that generates code for a different machine. Optimizations done that are machine independent will then apply to all versions of the compiler, not just one. : Furthermore, all "auto" and "register" variables are subject : to the same optimizations as (absence of) "volatile" gives you if their : address is never taken (and "noalias" to boot). If you optimize : "static" data as well, only "extern" data gets volatile treatment. Not true. Consider that a static variable might be accessed from a function called by a signal handler. In other words, in static int Var; int foo() { ++Var; } Since foo() can be called from a signal handler in another translation unit, Var is also "volatile". : In my programs, this is precisely the data requiring the "volatile" keyword! : (And device drivers would make all magic addresses "extern".) But you're ignoring an important point: volatile does not only apply to variables, it also applies to, e.g., things pointed to. If I were writing a device driver to handle several devices, I might well declare a structure containing volatile elements and pass the address of the structure to some control routine. Or a pointer might well point to a variable which is changed in a signal handler. Because of this, any pointed to variable, absent the possibility of using the volatile keyword, must be considered volatile. : If compilers would simply use the optimization opportunities already : available, the improvement with "volatile" would be marginal. On what evidence do you base that? --- Bill {uunet|novavax}!proxftl!twwells!bill
chris@mimsy.UUCP (Chris Torek) (12/26/88)
In article <275@twwells.uucp> bill@twwells.uucp (T. William Wells) writes: >... Because of this, any pointed to variable, absent the possibility of >using the volatile keyword, must be considered volatile. Or, you can put up with a long, slow, tortuous (and perhaps torturous) inter-module analysis phase that picks up those cases that it can, and assumes the worst (volatility) for those it cannot. We do not know how to do this well (nor fast) now, but I predict that in a number of years, we will---just as compilers are (only just now) starting to do decent register allocation. Incidentally, Mr. Grandi% has a point (which I might note that I myself made not too long ago) about `register': It implies not-volatile and not-aliased; and it does so in a way that the compiler can reasonably enforce. It is conceivable that one could make use of the existing keyword (and overload C keywords yet further) for optimisations it does not now allow. [Please note that I make no judgement as to tastefulness, desirability, etc.! I got into this last time because someone mistook a hypothetical argument for a real one.] ----- % I do hope I spelled your last name right. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/27/88)
In article <15166@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >... ["register"] implies not-volatile and >not-aliased; and it does so in a way that the compiler can reasonably >enforce. No, I reject this claim. A STORAGE CLASS by no means obviates the need for type qualifiers. You could conceivably invent a language with only type qualifiers (one of which you may choose to name "register") and no storage classes, but it wouldn't be reasonable to call that language "C".
chris@mimsy.UUCP (Chris Torek) (12/29/88)
>In article <15166@mimsy.UUCP> I suggested that >>... ["register"] implies not-volatile and not-aliased; and it does so >>in a way that the compiler can reasonably enforce. In article <9236@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes: >No, I reject this claim. You mean to say that a register could be aliased? Not in C! How about a volatile register? It is a possibility, but it seems entirely unnecessary. >A STORAGE CLASS by no means obviates the need for type qualifiers. I did not say that. I said `register implies not-volatile, not- aliased': that `volatile register' and `aliased register' are nonsensical combinations. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/02/89)
>[I wrote:] >>A STORAGE CLASS by no means obviates the need for type qualifiers. In article <15171@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >I did not say that. I said `register implies not-volatile, not- >aliased': that `volatile register' and `aliased register' are >nonsensical combinations. Of course there are nonsensical expressions in ANY version of C that are not ruled "illegal" just because they're useless. But I thought you were trying to claim that "volatile" could have been dispensed with and the necessary additional semantics invested in "register", and THAT is what I dispute, because type qualifiers can apply at any of the "levels" of a type declaration, unlike storage class specifiers (which apply to the identifier, in effect). Thus register const int * volatile * const ptr = SOMETHING; has a well-defined and possibly useful meaning, which would be impossible (I think) to mimic correctly by stretching "register" to serve as a type qualifier while not breaking existing usage of "register". In any case the switch from a storage class specifier to a type qualifier would be pretty drastic!
geoff@endor.harvard.edu (Geoff Clemm) (01/03/89)
In article <15171@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >>In article <15166@mimsy.UUCP> I suggested that >>>... ["register"] implies not-volatile and not-aliased; and it does so >>>in a way that the compiler can reasonably enforce. > >In article <9236@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes: >>No, I reject this claim. I would have to second this rejection. There is nothing in the reference manual that lets you count on register variables not being volatile. >You mean to say that a register could be aliased? Not in C! How >about a volatile register? It is a possibility, but it seems entirely >unnecessary. Now Chris, this is a total waffle. For folks that are seriously concerned about portability, one needs a stronger argument than that "it seems entirely unnecessary" for a compiler writer to do something -- especially since many compiler writers ignore register declarations, which means register variables will be as volatile or non-volatile as any other variable. Geoff Clemm
chris@mimsy.UUCP (Chris Torek) (01/04/89)
>>>In article <15166@mimsy.UUCP> I suggested that >>>>... ["register"] implies not-volatile and not-aliased; and it does so >>>>in a way that the compiler can reasonably enforce. (by the way, the word `implies' is key.) >>In article <9236@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes: >>>No, I reject this claim. >In article <15171@mimsy.UUCP> I answered with: >>You mean to say that a register could be aliased? Not in C! How >>about a volatile register? It is a possibility, but it seems entirely >>unnecessary. In article <903@husc6.harvard.edu> geoff@endor.harvard.edu (Geoff Clemm) replies: >I would have to second this rejection. There is nothing in the >reference manual that lets you count on register variables not being >volatile. You misinterpret (everyone always misinterprets! :-) ). The dpANS allows you to write register const k = 4; and register volatile v; but, I ask, what do these mean? The answer is that they mean the same thing as const k = 4; and volatile v; except that you may not take the address of either variable---which therefore means that the compiler need not even assign an address to either variable. This makes the effect (if any) of volatile on a register declaration dubious at best. >Now Chris, this is a total waffle. For folks that are seriously concerned >about portability, one needs a stronger argument than that "it seems entirely >unnecessary" for a compiler writer to do something -- especially since many >compiler writers ignore register declarations, which means register variables >will be as volatile or non-volatile as any other variable. For folks concerned with portability, the word `volatile' does not exist in the language (with one exception), since the actual effect of the volatile qualifier is compiler-dependent. The exception is in functions using setjmp. Optimising such functions well is hard, and the dpANS simply grants license to ignore the effect of longjmp on program flow during optimisation. This was, in my opinion, the wrong thing to do; but while we are stuck with it, remember that I was not saying that `volatile' and `register' are opposites, but merely that `register' *ought* (in some more perfect world where the dpANS were different) to imply not-aliased (it does) and not-volatile (it does not). Actually, again in my opinion, in a still more perfect world, both the dpANS and `old C' would not have the `register' keyword anyway. Moreover, for those again concerned with portability, it is necessary never to use `register volatile' even in functions using setjmp, since in old compilers---where one would `#define volatile /*empty*/'--- registers are the *only* non-volatile variables. In particular, the PDP-11 V7 C runtime system, the 3.0, 4.0, and 4.1BSD runtime systems, and most if not all SunOS runtime systems, effectively restore register variables to their state at the time of the *setjmp* call: f() { register int r = 1; jmp_buf j; if (setjmp(j)) { (void) printf("r is now %d\n", r); return; } r = 2; longjmp(j, 1); /* NOTREACHED */ } This program prints `1', not `2', in a number of systems. This is precisely the effect produced by an optimising compiler given a non-volatile variable `r' and ignoring the implicit `goto' produced by the setjmp/longjmp pair. The variable life analysis goes like this: register int r = 1 | r = constant 1 jmp_buf j | r = constant 1, j = constant <uninit> if (setjmp(j)) | r = constant 1, j = <unknown 1> /* (remember, j is passed by address) */ printf(...) | r = constant 1, j = <unknown 1> return r = 2 | r = constant 2, j = <unknown 1> longjmp(j, 1) | r = constant 2, j = <unknown 2> /* note that <unknown 1> != <unknown 2> */ implicit return | r = dead, j = dead At this point, the compiler should replace known constants (the one instance of r) with their values. It can then delete both writes (r=1 and r=2) since they are to dead variables. The optimised function is thus if (setjmp(j)) { (void) printf("r is now %d\n", 1); return; } longjmp(j, 1); We can merrily write `register volatile int r = 1', and a dpANS- conformant compiler will do the `right thing', but the program is not portable to some widespread versions of `old C' (including SunOS 3.2, where I just tested it). The important thing is not that these implementations are not dpANS conformant (they are not), but rather that `volatile int r = 1' *will* work. Then too, I doubt that `register volatile v' will do anything different from `volatile v' in any worthwhile compiler. (Note key words `I doubt': this is an opinion, not a fact.) Anyway, the point is that `register volatile v' (but not `register volatile *v') is effectively useless. It is therefore possible---but not necessarily wise---to make `register' mean `not volatile', and to declare the volatile qualifier and the register storage class incompatible. I do not claim that this encompasses all uses for `volatile'. I claim only what I said: register implies not-aliased, and does so in a way the compiler can enforce (`&v' not allowed); and register implies (through effective uselessness) not-volatile, again in a way that the compiler can enforce (since volatility is purely the compiler's notion anyway, as far as code generation is concerned). -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/04/89)
In article <15248@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >For folks concerned with portability, the word `volatile' does not >exist in the language (with one exception), since the actual effect of >the volatile qualifier is compiler-dependent. No, that's wrong. A conforming compiler is obliged to generate code for "volatile" object accesses strictly in accordance with the virtual machine model (no caching). It is OTHER mechanisms for accessing a volatile object during execution that are not specified.
chris@mimsy.UUCP (Chris Torek) (01/04/89)
>In article <15248@mimsy.UUCP> I claimed that >>For folks concerned with portability, the word `volatile' does not >>exist in the language (with one exception), since the actual effect of >>the volatile qualifier is compiler-dependent. In article <9273@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes: >No, that's wrong. A conforming compiler is obliged to generate code >for "volatile" object accesses strictly in accordance with the >virtual machine model (no caching). It is OTHER mechanisms for >accessing a volatile object during execution that are not specified. This is a difference that makes no difference. It can therefore be ignored with impunity. Or do you have some way to tell, within strictly conforming code, that the compiler generated code that is not strictly in accordance with the virtual machine model? When it comes to figuring out what `register volatile v' means (versus simply `register v'), consider the following: - `register' may not be applied to global and static variables. - The compiler (not the linker, nor anything else) chooses the location of all automatic (stack) variables. This therefore implies that the compiler can easily tell whether the register or memory it chooses for that variable is in fact volatile, and can (and in all compilers that now exist---if you can find one where this is false, let me know---does) choose storage that is not volatile. [By the way, using mmap on your entire stack segment after using up all the local machine registers is cheating. :-) ] A rather more shaky claim, yet one that is partly believable, is that `auto volatile' is also never needed (with that one exception). The major difference is that one can take the address of a non-register automatic; it *is* conceivable that one can feed the address of a program variable to a device and have that device make use of it. But since the compiler chooses the address of a register variable AND DOES NOT EVER LET YOU KNOW WHAT IT IS, there is no way ever to get a handle on that variable. The compiler can assign it `real memory' space and rest assured that that space is indeed not volatile. None of the above applies to `register volatile *' (where the pointer itself is not the volatile object, the volatility being in the object to which it points). -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
w-colinp@microsoft.UUCP (Colin Plumb) (01/05/89)
I can think of at least one example of a register volatile variable, if you're playing with setjmp/longjmp. We all know a non-volatile variable can't be trusted across a setjmp, so given register int x, y; jmp_buf jbuf; x = 0; y = setjmp(jbuf); x++; if (y = 0) longjmp(jbuf, 1); printf("x = %d\n", x); This "should" print 2, but a compiler could legitimately print x = 1. Obviously, x isn't aliased (register), and flow analysis (which isn't required to understand setjmp and longjmp) shows it is set to 0, incremented once, and printed. "Register volatile x" tells the compiler to try and store it somewhere quick, but safe from setjmp/longjmp. In most compilers, this would basically turn off the register declaration, but in transputer compilers I've used, "register" tells the compiler to keep the value in the 16 words nearest the stack pointer, which are especially quick to access. Here, register and volatile both mean something. Can anyone poke holes in this example? -- -Colin (uunet!microsof!w-colinp)