hankd@pur-ee.UUCP (Hank Dietz) (04/26/88)
In article <2003@rtech.UUCP>, jas@llama.rtech.UUCP (Jim Shankland) writes: > In article <13074@brl-adm.ARPA> dsill@NSWC-OAS.arpa (Dave Sill) writes: > >Terry Lambert writes: > >>Basically, if it works without -O, it should work with -O, regardless of what > >>the compiler writer's optimization does to achieve its goal. If this makes > >>writing compilers harder, so what? > > > >This bears repeating. There should be no circumstances under which > >the semantics of the language are changed by a flag to the compiler. I agree. The programmer should explicitly request "magic" which might change the results of running a program; further, this request should be within the language, not an ad-hoc command-line option. > The alternatives that have been mentioned include: > > (1) "Just don't do all that silly optimization". Then, as Dennis Actually, you can still do a lot of that optimization... it just takes more compile time to do the better flow/dependence analysis to support it. > (2) "If you need `volatile', don't use C." I agree, it's The un-solution. > (3) "Let implementers of optimizing compilers come up with their > own pragmas that can turn off certain optimizations where they > will be destructive." In other words, let each implementer Another un-solution... this is the "programmer's job security enhancement" choice. Further, there is no guarantee that two compilers will not use the exact same #pragma to mean two subtly different things. Instead, why not use "register" to solve the problem.... Ever since K&R, code which takes the address of a register variable has been non-portable in the best sense of the phrase: where the code wouldn't work, it would be flagged as a compile-time error. Now, X3J11 says that it is ALWAYS a compile-time error to take the address of a register variable. In other words, if code is broken by register assignment, the compiler can easily flag this condition at compile time. Now, look at volatile. Volatile says that an object either can change value while we are not looking at it or that somebody else may notice when we change the object's value. This can only happen if two or more "processes" (perhaps across multiple processors or DMA channels) simultaneously have access to the same object. It is easy for one process to know where an object is (by simply declaring it), but I know of NO EXISTING C MECHANISM for letting another process access that object OTHER THAN to pass the explicitly-taken address of the object. The same is true of DMA locations: they are specific places in memory, and the only general way to overlay a specific place is to set a pointer to that address. In other words, AN OBJECT WHICH HAS NO ADDRESS CANNOT BE VOLATILE: A REGISTER OBJECT CANNOT BE VOLATILE! Why not make use of this SAFE, prior-art, compiler hint? Currently, a C compiler is correct iff it treats ALL non-register variables as volatile (unless flow analysis can prove that the object address is never taken: i.e., unless flow analysis can prove that it would have been safe to place the variable in a register :-). I propose that we simply extend register so that, instead of merely being a flavor of auto storage class, it is an attribute which can be applied to static and global data as well as to expressions... the meaning is simply "this could be in a register" or, phrasing it slightly differently, "this is NOT volatile." This usage not only breaks no (reasonable :-) existing code, but it also results in compile-time errors where code would be broken, rather than resulting in the mysterious execution caused by lack of a now-required-by-X3J11 volatile declaration. Oddly enough, the extended notion of register IS "noalias" with a somewhat more flexible structure; an object has no aliases if one can only have one name for it, and the inability to take the address of a register object provides exactly this guarantee. The semantics proposed are: 1. Use of "register" as the storage class specifier in a declaration indicates that the object being allocated is of the default storage class, but cannot have its address taken. In other words, the declared items are non-volatile and have the noalias property. 2. Use of "register" as an attribute of a storage class ("register" appearing immediately after the storage class specifier in a declaration) forces the exact same constraint: all thus declared objects carry the "register" property. 3. Use of "register" as an attribute within a declaration after the type specifier indicates that the appropriate typed entity has the register property. (This rule actually is how register declarations *should* have been done, but it does not conflict with supporting the old-style register declaration syntax for a while :-). 4. Use of "register" as an attribute cast on an expression, or as an attribute cast within a declaration, forces the interpretation that the expression value object has the "register" property. 5. When used as a cast or in a function argument list, the "register" attribute does not propagate, whereas it does propagate when used as part of a declaration. Some simple examples: /* a register global defined elsewhere */ register extern int a; /* an array of 100 doubles, all conceptually accessible ONLY by use of the name d, with or without subscripts, but of static storage class. */ register static double d[100]; /* a pointer to a char object for which no other name exists... a pointer to a non-volatile, noalias, object. */ char register *p; /* how the function strcpy should be declared (but need not be)... */ char * strcpy(char register *p, register *q;) { ... } /* how strcpy might be used... including a cast on malloc()... */ char register *p = (char register *) malloc(6); p = strcpy(p, "hello"); Now, I'm not saying that the above syntax is perfect, and as DMR pointed-out to me, having all those "register"s is certainly noisy, but by the rule of "if it ain't broken, don't fix it" it seems to me that the above extension of register -- especially rules 1. and 2. -- is much more reasonable. Further, although I have nothing against volatile, per se, isn't it better to go with the more aggressive compiler optimization only when a person has specifically hinted that it's ok, as opposed to the "volatile" scheme of aggressively optimizing unless you're told no? Besides, by now we all know how to spell "register." The views expressed above are overly written and under thought-out, but there they are, so what is the consensus? Is register as useful as I think it is? -Prof. Hank Dietz School of EE Purdue University West Lafayette, IN 47907
gwyn@brl-smoke.ARPA (Doug Gwyn ) (04/26/88)
In article <7996@pur-ee.UUCP> hankd@pur-ee.UUCP (Hank Dietz) writes: >Why not make use of this SAFE, prior-art, compiler hint? Any attempt to make "register" a type qualifier rather than a storage class is likely to conflict with existing practice. Otherwise I more or less agree with the notion. In any case, it's too late to change this in the forthcoming Standard.
christiansen@chewi.che.wisc.EDU (REED CHRISTIANSEN) (04/27/88)
> >From: Hank Dietz <hankd@pur-ee.uucp> >Subject: A different view of volatile >Message-ID: <7996@pur-ee.UUCP> >Date: 26 Apr 88 00:29:37 GMT > > [Text Deleted. Professor Dietz proposes the use of REGISTER to denote > data objects that cannot possibly be VOLATILE, since a REGISTER item > does not have an address.] > >although I have nothing against volatile, per se, isn't it better to go with >the more aggressive compiler optimization only when a person has specifically >hinted that it's ok, as opposed to the "volatile" scheme of aggressively >optimizing unless you're told no? Besides, by now we all know how to spell >"register." My observation is that the number of data items in a program that are VOLATILE (that is, whose value(s) can change asynchronously due to action by another process or by hardware action) is very small. Even in implementing real-time process control systems, the D/A and A/D handlers are a small portion of the entire package. It seems much easier to declare these few variables as special cases than to declare all non-special variables to be REGISTER. I have no problem with aggressive optimization--I would prefer that the compiler do as much optimization as it can, as long as I can point out the VERY FEW data items that are non-deterministic. In fact, our never-ending problem is to squeeze more complicated mathematical models into our real-time packages in order to do state-estimation, etc. The faster these computational loops run, the more realistic our models can become. If you hate VOLATILE and need to do real-time work, you have these options: (1) Optimize and use REGISTER, which is a burdensome but workable alternative, requiring one to modify 90+% of declarations in one's code from conventional C technique [TEDIOUS BUT SUITABLE CHOICE], (2) Don't optimize, which will limit your ability to do computations in real-time loops [VERY BAD CHOICE], (3) Optimize computational modules; don't optimize the control loops; make sure there is no module with both, document what you're doing, and hope that the next fool to work on your project understands what is going on [POTENTIALLY DANGEROUS CHOICE]. I prefer VOLATILE. ------
karl@haddock.ISC.COM (Karl Heuer) (04/27/88)
In article <7996@pur-ee.UUCP> hankd@pur-ee.UUCP (Hank Dietz) writes: >Instead, why not use "register" to solve the [volatile] problem.... Currently, "break" has two (or maybe one and a half) meanings, "static" has two meanings, and "void" has three. I'd rather not see "register" achieve a similar overloading, which is what I see in your proposal. >[a register object cannot be volatile] But the converse is false. I use non-volatile non-register objects all the time. Also, your proposal breaks the rule of defaulting to the most common case. Most programs don't need *any* volatile declarations, and of those that do, most objects are *not* so qualified. This is why X3J11 invented "volatile" rather than "novolatile". >register static double d[100]; How does the register-nonaddressibility rule interact with the automatic decay of the array expression "d" into the pointer "&d[0]"? You'd need to define the rules very carefully to make this declaration useful. >char *strcpy(char register *p, register *q;) { ... } Minor nit: types don't propagate in a function prototype; you need "char" in both declarations. And no semicolon. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
nevin1@ihlpf.ATT.COM (00704a-Liber) (04/27/88)
In article <7996@pur-ee.UUCP> hankd@pur-ee.UUCP (Hank Dietz) writes: >Why not make use of this SAFE, prior-art, compiler hint? Currently, a C >compiler is correct iff it treats ALL non-register variables as volatile >(unless flow analysis can prove that the object address is never taken: >i.e., unless flow analysis can prove that it would have been safe to place >the variable in a register :-). I don't like this for a number of reasons. If a compiler assumes that all variables are volatile unless otherwise stated (such as by a register declaration), then I do not think that it is possible to do *ANY* type of optimization involving those variables. Even simple peephole optimizations such as 'a=b;a=b' cannot be legally optimized out, and 'x=2*y' and 'x=y+y' can yield two different results. What kind of optimization (or non-optimization code generation, for that matter) is legal and what is illegal?? Most of the semantics of C become ill-defined at best, if it has to use volatile variables. This is my main problem with using 'volatile' as it is defined now. Suppose a variable 'i' is declared 'volatile' because every time the memory location is referenced its' value is automatically incremented. What does '++i' or 'i <<= 1' mean? In these examples, is the memory location for i referenced once or twice? Since this is *implementation-dependent* (it is not addressed in the Standard as far as I could see), using these operations for any kind of portable application are meaningless. But try programming without them!! Try programming making the same assumptions that you want the compiler to make (ie, that all variables, unless otherwise stated, are volatile). Good luck! Unless you are *very* careful, you shouldn't use statements like tmp = a * b; c = tmp + a; since 'a' could have changed between the two assignments. (If you use this construct with the assumption that a,b,c,tmp are volatile and you really meant this as the outcome, this is still very hard to read/maintain.) Since you make assumptions about your environment when you program (it is probably impossible to program otherwise), why not let the compiler in on the secret? [I was also going to talk about how 'register' would now have too many meanings, but what I stated above is what I very strongly object to. This posting is long enough as it is.] Any variable which is 'volatile' automatically becomes non-portable and implementation-dependent. Therefore, assuming that all variables, unless otherwise stated, are volatile leads to machine-dependent unoptimizable code (given the current definition of volatile). This is not desirable and it defeats the purpose of a Standard. -- _ __ NEVIN J. LIBER ..!ihnp4!ihlpf!nevin1 (312) 510-6194 ' ) ) "The secret compartment of my ring I fill / / _ , __o ____ with an Underdog super-energy pill." / (_</_\/ <__/ / <_ These are solely MY opinions, not AT&T's, blah blah blah
hankd@pur-ee.UUCP (Hank Dietz) (04/28/88)
In article <4548@ihlpf.ATT.COM>, nevin1@ihlpf.ATT.COM (00704a-Liber) writes: > In article <7996@pur-ee.UUCP> hankd@pur-ee.UUCP (Hank Dietz) writes: > >Why not make use of this SAFE, prior-art, compiler hint? Currently, a C > >compiler is correct iff it treats ALL non-register variables as volatile > >(unless flow analysis can prove that the object address is never taken: > >i.e., unless flow analysis can prove that it would have been safe to place > >the variable in a register :-). ... > If a compiler assumes that all variables are volatile unless otherwise > stated (such as by a register declaration), then I do not think that it is > possible to do *ANY* type of optimization involving those variables. > Even simple peephole optimizations such as 'a=b;a=b' cannot be > legally optimized out, and 'x=2*y' and 'x=y+y' can yield two different > results. What kind of optimization (or non-optimization code generation, > for that matter) is legal and what is illegal?? Not correct: converting "a=b;a=b" into "a=b" often can be KNOWN SAFE. You only have to use register when the compiler wouldn't otherwise have noticed. Suppose that a is declared "int a;" and that ALL references to a can be seen. If the address of "a" is never taken, then all references to "a" can be safely removed; likewise, if "&a" is taken, only references to "a" where "&a" reaches cannot be removed. I'm oversimplifying a bit, but the basic point is that the analysis to determine this for most LOCAL NON-POINTER variables is quite easy, hence "register" helps only a little for local variables. It would be a big help for externs, statics, and for function interfaces because it is very expensive (at least) for the compiler to see all references when some references might be in other compilation units (other files). Even if we want to keep volatile, which I agree is probably a good idea given that X3J11 has assumed it's there, we should at least permit register and volatile to be used in very similar fashion, because they are conceptually very closely related. -hankd
bts@sas.UUCP (Brian T. Schellenberger) (05/03/88)
In article <8013@pur-ee.UUCP> hankd@pur-ee.UUCP (Hank Dietz) writes: | |converting "a=b;a=b" into "a=b" often can be KNOWN SAFE. You |only have to use register when the compiler wouldn't otherwise have noticed. | |Suppose that a is declared "int a;" and that ALL references to a can be |seen. If the address of "a" is never taken, then all references to "a" can |be safely removed; likewise, if "&a" is taken, only references to "a" where |"&a" reaches cannot be removed. I'm oversimplifying a bit, but the basic |point is that the analysis to determine this for most LOCAL NON-POINTER |variables is quite easy, hence "register" helps only a little for local |variables. It would be a big help for externs, statics, and for function |interfaces because it is very expensive (at least) for the compiler to see |all references when some references might be in other compilation units |(other files). | No, no. If you assume that it is "volatile" in the absense of of "register", you can't do this at all. "a=b;a=b" can't be optimized in this case because the value of "b" may change between the two statement due to an asynchronus process. THAT is what "volatile" means. What you are talking about--and what "register" implies--is NOALIAS, that much-maligned and now defunct keyword. It might be good to suggest for the C-92 standard that the next ANSI committee add semantic meaning to "register" allowing it to apply to other situations than it currently does in order to provide the same functionality as NOALIAS in a keyword everybody feels more comfortable with. Of course, as Mr. Dietz points out, "register" is redundant for this purpose for simple local variables anyway, since the compiler can usually figure it out. Which is probably just as well, since "register" already means something else for simple local variables. The proposal to reuse "register" in this way seems very much in the "spirit of C", but I consider this to be the worst single aspect of the language; I'd rather see NOALIAS brought back than overload "register" in this way meself. Understood that this is probably a minority position; please skip the non- informative flames (flames with actual information are ok). -- --Brian. (Brian T. Schellenberger) ...!mcnc!rti!sas!bts . . . now at 2400 baud, so maybe I'll stop bothering to flame long includes.
mikew@wyse.wyse.com (05/04/88)
It seems that volatile is ill-defined when combined with operators that operate in-place. A way of alleviating this problem would be to state that conforming programs can't have a volatile variable that is on boths sides of an equal sign, volatile variables can't be used on the left hand side of the following operators: +=, -=, *=, /=, |=, &=, and volatiles can't be used at all with the following operators: ++, --. Example: ... volatile int a,b; int c; a=a; /* illegal */ a=b; /* legal */ a=c; /* legal */ c=a; /* legal */ a=a+c; /* illegal */ a+=c; /* illegal */ ++a; /* illegal */ ...
chris@mimsy.UUCP (Chris Torek) (05/05/88)
In article <178@wyse.wyse.com> mikew@wyse.wyse.com writes: >It seems that volatile is ill-defined when combined with operators >that operate in-place. More than that, volatile is ill-defined *always*: on a machine with only halfword load and stores, a fullword `a = 0' is not atomic. Remember, though, that `ill-defined' does not mean `useless'. The following is ill-defined: struct rkdevice *rkaddr = (struct rkdevice *)0777440; -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
nevin1@ihlpf.ATT.COM (00704a-Liber) (05/06/88)
In article <178@wyse.wyse.com> mikew@wyse.wyse.com () writes: >It seems that volatile is ill-defined when combined with operators >that operate in-place. A way of alleviating this problem would >be to state that conforming programs can't have a volatile variable >that is on boths sides of an equal sign, volatile variables can't >be used on the left hand side of the following >operators: +=, -=, *=, /=, |=, &=, and volatiles can't be used at all >with the following operators: ++, --. I was going to propose this myself!! I have a problem with one of your examples, though. >volatile int a,b; >a=b; /* legal */ This type of assignment should be illegal, since >a=a; /* illegal */ is illegal. Suppose the declaration was: int foo(bar1, bar2) volatile int *bar1, *bar2; *bar1 = *bar2; There may be no way, with static checking, to determine if bar1 and bar2 are the same or different. At most one variable of type volatile is allowed to appear in any statement (except for function arguments). Also, volatile variables should probably be limited to types which only require one machine instruction to load them, such as char, int, and *void (ie, structs should not be allowed to be declared volatile), and arrays made up of these types. -- _ __ NEVIN J. LIBER ..!ihnp4!ihlpf!nevin1 (312) 510-6194 ' ) ) "The secret compartment of my ring I fill / / _ , __o ____ with an Underdog super-energy pill." / (_</_\/ <__/ / <_ These are solely MY opinions, not AT&T's, blah blah blah
be@dde.uucp (Bjorn Engsig) (05/06/88)
In article <13120@brl-adm.ARPA>, christiansen@chewi.che.wisc.EDU (REED CHRISTIANSEN) writes: ... > VOLATILE (that is, whose value(s) can change asynchronously due to > action by another process or by hardware action) ... --------------- --------------- That's not the only cases, consider: ----------------- get_value.c ------------------ static int *pointer; set_address(ptr); int *ptr; { pointer = ptr }; get_value() { /* stuff */ if (value_found) { *pointer = value; return 1; } else return 0; } ------------------ main.c ------------------- main() { /* volatile */ int result; set_address(&result); while (get_value()) process_it(); } Such kind of code is for example found in database host language interfaces, where you first define where to store the data from the database, and later you fetch the data until no more data are found. In real life, the set_address() is called a various number of times (probably many times) and then get_value() knows the addresses and doesn't need a large and varying number of parameters. This is not to oject against volatile, I know where I should put it. -- Bjorn Engsig @ Dansk Data Elektronik A/S, Herlev, Denmark Email: be@dde.dk Phone: + 45 2 84 50 11 Email: ..!uunet!mcvax!dkuug!dde!be Fax: + 45 2 84 52 20
henry@utzoo.uucp (Henry Spencer) (05/07/88)
> More than that, volatile is ill-defined *always*: on a machine > with only halfword load and stores, a fullword `a = 0' is not atomic. > > Remember, though, that `ill-defined' does not mean `useless'... Remember also that "volatile" does not mean "atomic"! -- NASA is to spaceflight as | Henry Spencer @ U of Toronto Zoology the Post Office is to mail. | {ihnp4,decvax,uunet!mnetor}!utzoo!henry
tanner@ki4pv.uucp (Dr. T. Andrews) (05/07/88)
In article <4651@ihlpf.ATT.COM>, nevin1@ihlpf.ATT.COM (he of the longish and often-seen .signature filled with bad cursive) writes:
) ... (ie, structs should not be allowed to be declared volatile), ...
Gee, thanks. That de-values my new whiz-bang disk controller (the
one with the nice structure filled with device registers and ending
with a memory-mapped i/o buffer).
--
{allegra clyde!codas decvax!ucf-cs ihnp4!codas killer}!ki4pv!tanner
chris@mimsy.UUCP (Chris Torek) (05/08/88)
In article <432@Aragorn.dde.uucp> be@dde.uucp (Bjorn Engsig) provides an example of ordinary aliasing: >static int *pointer; > >set_address(ptr) int *ptr; > { pointer = ptr }; ... >main() > { > /* volatile */ int result; > set_address(&result); This sort of aliasing is considered `normal' in C, and not cause for declaring variables as volatile. After that call to set_address, even though `result' is a local variable, a straightforward compiler (by which I mean one that does not analyse other functions during compilation of main()) would have to consider any function call (not just one to which &result is passed) as a change to `result': main() { int result; addr(&result); result = 1; f(); if (result==1) ... the compiler may not optimise away the test in the `if'. If you remove the call to addr, or allow the compiler to see the code for addr() and/or f() (some can do this at `link' time), and either addr() does not save its parameter in a global or static variable and/or f() does not use the same value (which would then be &result), the compiler may optimise away the `if' test. In (standard) Pascal, pointers may not point at local variables, and any purely local optimisation is therefore safe. The same is true in (standard) FORTRAN. This sort of problem is why `noalias' came into being (and, fortunately, went out again: compilers that do cross-module optimisation will soon be the norm, rather than the exception; here the problem becomes tractable). -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
ray@micomvax.UUCP (Ray Dunn) (05/12/88)
In article <11369@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >In article <178@wyse.wyse.com> mikew@wyse.wyse.com writes: >>It seems that volatile is ill-defined when combined with operators >>that operate in-place. > >More than that, volatile is ill-defined *always*: on a machine >with only halfword load and stores, a fullword `a = 0' is not atomic. > No....volatile is not ill defined, it is machine dependant, which is what you would expect it to be. There is no connection between "volatile" and "atomic", "volatile" does not imply "semaphore". -- Ray Dunn. | UUCP: ..!{philabs, mnetor}!micomvax!ray Philips Electronics Ltd. | TEL : (514) 744-8200 Ext: 2347 600 Dr Frederik Philips Blvd | FAX : (514) 744-6455 St Laurent. Quebec. H4M 2S9 | TLX : 05-824090
gwyn@brl-smoke.ARPA (Doug Gwyn ) (05/14/88)
In article <1054@micomvax.UUCP> ray@micomvax.UUCP (Ray Dunn) writes: >No....volatile is not ill defined, it is machine dependant, which is what >you would expect it to be. The semantics of "volatile" are LESS machine dependent than its omission! >There is no connection between "volatile" and "atomic", "volatile" does not >imply "semaphore". This much is true.
mouse@mcgill-vision.UUCP (der Mouse) (05/16/88)
In article <178@wyse.wyse.com>, mikew@wyse.wyse.com writes: > It seems that volatile is ill-defined when combined with operators > that operate in-place. [suggestion:] [S]tate that conforming > programs can't have a volatile variable that is on boths sides of an > equal sign, volatile variables can't be used on the left hand side of > [the (op)= operators] and volatiles can't be used at all with [++ and > --]. > Example: [edited] > volatile int a; int c; > a=a; /* illegal */ > a=a+c; /* illegal */ > a+=c; /* illegal */ I would argue that none of the above are at all hazy. In each case, I would claim that the result is exactly equivalent to the result obtained by using a temporary. For example, we can consider a+=c to be identical to a=a+c (a is a simple variable, remember). So the second and third are really the same. Then, I would say that a = <expression>; is identical to { /* not volatile */ int temp; temp = <expression>; a = temp; } On some machines, this may mean that a+=c must compile to different code depending on whether a is volatile. (For example, the VAX addl2 instruction might not be usable, depending on exactly how it works.) der Mouse uucp: mouse@mcgill-vision.uucp arpa: mouse@larry.mcrcim.mcgill.edu
daveb@geac.UUCP (David Collier-Brown) (05/16/88)
> >In article <178@wyse.wyse.com> mikew@wyse.wyse.com appears to write: > >More than that, volatile is ill-defined *always*: on a machine > >with only halfword load and stores, a fullword `a = 0' is not atomic. In article <1054@micomvax.UUCP> ray@micomvax.UUCP (Ray Dunn) writes: >No....volatile is not ill defined, it is machine dependant, which is what >you would expect it to be. >There is no connection between "volatile" and "atomic", "volatile" does not >imply "semaphore". In the case of a fullword device register on a halfword-fetch machine, the **machine** is ill-defined. Volatile hardly helps. It does help on a halfword device register by ensuring that two reads are done for status = device_register_foo; It remains up to the device designer to make it possible to read the register meaningfully in halves. --dave (please let me NOT work on one of those) c-b