henry@utzoo.uucp (Henry Spencer) (03/29/88)
> >... such things generally are confined to the bowels of the operating > >system, where they belong. > > And would you mandate that C never be used for writing operating > systems (one of it's original purposes)? ... Nobody is saying that the concept is not necessary in certain specialized situations. The point is that these are a sufficiently small subset of C applications that the need should addressed with #pragma, rather than by cluttering up the type system with it. The principal problem with #pragma is that it is not portable, since its semantics are not standardized, but the sort of code we're talking about isn't portable anyway. -- "Noalias must go. This is | Henry Spencer @ U of Toronto Zoology non-negotiable." --DMR | {allegra,ihnp4,decvax,utai}!utzoo!henry
tneff@atpal.UUCP (Tom Neff) (03/29/88)
In article <1988Mar29.004454.2867@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes: > ... The point is that [OS-type programs where /volatile/ is important] >are a sufficiently small subset of C applications that the need should be >addressed with #pragma, rather than by cluttering up the type system with it. Well, in all fairness I could easily live with #pragma volatile(foo) instead, *IF* compiler writers actually bothered to code the pragmas when XJ311 "let them off the hook" by degrading /volatile/ from a required language feature to a totally elective pragma. I'm not sure they would, especially if they were busy trying to figure out /noalias/. :-) TMN -- Tom Neff
henry@utzoo.uucp (Henry Spencer) (03/30/88)
With shared memory, again, volatile is the least of your problems. -- "Noalias must go. This is | Henry Spencer @ U of Toronto Zoology non-negotiable." --DMR | {allegra,ihnp4,decvax,utai}!utzoo!henry
mikew@wyse.wyse.com (Mike Wexler) (03/30/88)
In article <1988Mar29.004454.2867@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes: >... The principal problem with #pragma >is that it is not portable, since its semantics are not standardized, but >the sort of code we're talking about isn't portable anyway. I would disagree that is isn't possible to write portable code that requires volatiles. An example(possibly the only one), is the use of System V or Berkeley shared memory. With either of these you can have volatile values in portable(At least to similar Unix systems) programs. Given this it might be useful for the Posix standard to require the C compiler to support volatile or a volatile pragma.
walter@garth.UUCP (Walter Bays) (03/30/88)
In article <1988Mar29.004454.2867@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes: >Nobody is saying that the concept is not necessary in certain specialized >situations. The point is that these are a sufficiently small subset of C >applications that the need should addressed with #pragma, rather than by >cluttering up the type system with it. The principal problem with #pragma >is that it is not portable, since its semantics are not standardized, but >the sort of code we're talking about isn't portable anyway. Programs using signal handlers and/or shared memory could be portable. -- ------------------------------------------------------------------------------ Any similarities between my opinions and those of the person who signs my paychecks is purely coincidental. E-Mail route: ...!pyramid!garth!walter USPS: Intergraph APD, 2400 Geng Road, Palo Alto, California 94303 Phone: (415) 852-2384 ------------------------------------------------------------------------------
nevin1@ihlpf.ATT.COM (00704a-Liber) (03/30/88)
In article <12578@brl-adm.ARPA> PEPRBV%CFAAMP.BITNET@husc6.harvard.EDU (Bob Babcock) writes: >>`Volatile,' in particular, is a frill for >>esoteric applications, and much better expressed by other >>means. Its chief virtue is that nearly everyone can forget >>about it. >What about an interrupt routine which receives control on a keyboard >interrupt and sets a globally known flag. This is not really legal within C, since you are not allowed to dereference an absolute address (such as doing x = *NULL, where NULL == 0). However, because of the relationship between C and actual memory address, I can see how you would want a 'consistent' way of declaring 'volatile' objects (even though it is non-portable). The arguments for and against this are very close to the arguments for and against a portable asm (actually, though, what was wanted was a CONSISTENT way of declaring inline assembly code). If ANSI is going to discuss consistency of non-portable constructs, then volatile should still be considered for ANSI C. To be fair, though, all the other consistent non-portable constructs should also be considered (a real BIG can of worms). BTW, there already IS a semi-portable way of doing inline machine code. Just look Sjoerd Mullender & Robert van Renesse's winning entry in the 1984 Obfuscated C Contest!! :-) :-) -- _ __ 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
barmar@think.COM (Barry Margolin) (03/30/88)
In article <134@wyse.wyse.com> mikew@wyse.UUCP (Mike Wexler) writes: >I would disagree that is isn't possible to write portable code that requires >volatiles. An example(possibly the only one), is the use of System V >or Berkeley shared memory. With either of these you can have volatile >values in portable(At least to similar Unix systems) programs. Given >this it might be useful for the Posix standard to require the C compiler >to support volatile or a volatile pragma. But such programs are NOT portable. A program using System V shared memory is not portable to a BSD system and vice-versa. And none of them are portable to non-Unix systems. As you yourself implied in your last sentence, the right place for such a requirement is in the Posix standard, not the C standard. And the mechanism that can be used for this is #pragma. The C standard doesn't specify the format of particular pragmas (whether this is wise is debatable), but Posix already requires various extra C features (for example, I believe it adds things to include files that are specified in the C standard), so it could also require that compilers implement a particular set of pragmas with a given syntax. Barry Margolin Thinking Machines Corp. barmar@think.com uunet!think!barmar
lvc@tut.cis.ohio-state.edu (Lawrence V. Cipriani) (03/30/88)
In article <134@wyse.wyse.com> mikew@wyse.UUCP (Mike Wexler) writes: >I would disagree that is isn't possible to write portable code that requires >volatiles. An example(possibly the only one), is the use of System V >or Berkeley shared memory. ... I might not understand volatile correctly but this might also be an example: volatile int customer_changeable_var = 0; main( ) ... if (customer_chageable_var != 0) { ... } If the volatile were dropped off, the compiler would be free to optimize out the "impossible" code. A customer changeable global variable is a handy way for implementing optional features that customers may or may not want; especially in programs that should avoid disk i/o. -- Larry Cipriani, AT&T Networks Systems (day) Ohio State University (night) Domain: lvc@tut.cis.ohio-state.edu Path: ...!cbosgd!osu-cis!tut.cis.ohio-state.edu!lvc (weird but right)
karl@haddock.ISC.COM (Karl Heuer) (03/31/88)
In article <134@wyse.wyse.com> mikew@wyse.UUCP (Mike Wexler) writes: >In article <1988Mar29.004454.2867@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes: >>the sort of code we're talking about isn't portable anyway. >I would disagree that is isn't possible to write portable code that requires >volatiles. An example(possibly the only one), is the use of System V >or Berkeley shared memory. Why drag shared memory into it? Isn't the following an example of a strictly conforming program that needs volatile? #include <signal.h> static sig_atomic_t volatile gotcha; static void catch(int signo) { gotcha = 1; } int main(void) { (void)signal(SIGINT, catch); gotcha = 0; do; while (!gotcha); return (0); } Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
bvs@light.uucp (Bakul Shah) (03/31/88)
You must be able to tell a compiler that certain variables have side effects *IF* you are using a highly optimizing compiler and you do one or more of the following: o access a global variable from a signal handler o access a shared variable o patch a variable in the executable file o implement co-routines o access a memory mapped io address o access a variable from procedures written in two different languages An application program, written in ``pure'' C (that is, it does not use shared memory, uses either routines written in C or standard routines, does not use signal handling, does not need didling with executables, does not have co-routines/lighweight processes, does not use setjmp/longjmp) does not need volatile and can safely ignore it. But if you write a program that does do any of the above strange things, `volatile' is a *portable* way of telling the compiler about side effects. If a compiler wishes to ``ignore'' volatile, it must treat all non-register variables as implicitly volatile. Since you don't know what kind of compiler is available on another machine, I claim use of volatile will actually promote portability for your IO drivers, co-routines, customizable applications, shared memory using programs, non-trivial signal handlers, operating systems, etc. Granted, you don't get complete portability but code needing change will be lot less and need less work. -- Bakul Shah ..!{ucbvax,sun}!amdcad!light!bvs
richard@aiva.ed.ac.uk (Richard Tobin) (03/31/88)
In article <1988Mar29.004454.2867@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes: >situations. The point is that these are a sufficiently small subset of C >applications that the need should addressed with #pragma, rather than by >cluttering up the type system with it. The principal problem with #pragma >is that it is not portable, since its semantics are not standardized, but >the sort of code we're talking about isn't portable anyway. It's true that any particular instance of the use of such constructs isn't portable, but the concept behind it is. That is, of course code that manipulates a memory-mapped device register (or similar) won't run on different machines, but that doesn't mean that it's unreasonable to have a standard way of expressing that a location is such a register. Furthermore, #pragma isn't even portable between different compilers on the same machine, whereas something like "volatile" is. And it's increasingly common to have more than one C compiler available. -- Richard -- Richard Tobin, JANET: R.Tobin@uk.ac.ed AI Applications Institute, ARPA: R.Tobin%uk.ac.ed@nss.cs.ucl.ac.uk Edinburgh University. UUCP: ...!ukc!ed.ac.uk!R.Tobin
edw@IUS1.CS.CMU.EDU (Eddie Wyatt) (03/31/88)
An aside: > #include <signal.h> > static sig_atomic_t volatile gotcha; > static void catch(int signo) { gotcha = 1; } > int main(void) { > (void)signal(SIGINT, catch); > gotcha = 0; > do; while (!gotcha); > return (0); > } PLEASE, declare the signal handler correctly Mr. Lint :-). static void catch(int signo, code; struct sigcontext *scp) { gotcha = 1; } In the pass I've had my signal handle return to random pieces of code by not having the correct definition. I attributed the problem of local's of the signal handle being over layed onto the stack context pointer. The problem was fixed by using the suggested defination for the signal handle which seems to confirm the suspicion. -- Eddie Wyatt e-mail: edw@ius1.cs.cmu.edu
nevin1@ihlpf.ATT.COM (00704a-Liber) (03/31/88)
In article <9176@tut.cis.ohio-state.edu> lvc@tut.cis.ohio-state.edu (Lawrence V. Cipriani) writes: |I might not understand volatile correctly but this might also be an |example: | | volatile int customer_changeable_var = 0; | | main( ) | ... | | if (customer_chageable_var != 0) | { | ... | } | |If the volatile were dropped off, the compiler would be free to |optimize out the "impossible" code. The compiler would *not* be free to optimize out this code!! Because customer_changeable_var is EXTERNAL to main(), this optimization cannot occur. The fact that it is declared 'volatile' is irrelevant (unless you are saying that the customer should be able to change the variable directly by toggling a hardwired switch which is directly mapped to the memory location at which customer_changeable_var is stored. But this case is non-portable, and the previously posted arguments apply). -- _ __ 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
barmar@think.COM (Barry Margolin) (03/31/88)
In article <9176@tut.cis.ohio-state.edu> lvc@tut.cis.ohio-state.edu (Lawrence V. Cipriani) writes: > volatile int customer_changeable_var = 0; > > main( ) > ... > > if (customer_chageable_var != 0) > { > ... > } >If the volatile were dropped off, the compiler would be free to >optimize out the "impossible" code. A customer changeable global >variable is a handy way for implementing optional features that >customers may or may not want; especially in programs that should >avoid disk i/o. I don't think volatile is necessary for the above example. If another module were linked with this, and it included an "extern int customer_changeable_var" declaration, it could assign a different value to this variable. This is possible because you didn't specify "static" in your declaration, and it is a global variable. The compiler, therefore, cannot assume that this variable has a constant value just because it doesn't see any assignments in this module. If you were add the "static" modifier to the declaration, the compiler WOULD be free to optimize away the unreachable code. But that's OK, because in order for the customer to change the variable's value he would have to edit the source and recompile the module. The only other case, and maybe this is what you are talking about, is if the module you are describing is part of the kernel, and the variable is intended to be updated by patching the core image. In that case, if you declare it static then you do, indeed, need the volatile modifier, but if you don't declare it static you don't have to declare it volatile, either, because of the reason in my first paragraph. Barry Margolin Thinking Machines Corp. barmar@think.com uunet!think!barmar
gwyn@brl-smoke.ARPA (Doug Gwyn ) (03/31/88)
In article <1264@PT.CS.CMU.EDU> edw@IUS1.CS.CMU.EDU (Eddie Wyatt) writes: >static void catch(int signo, code; struct sigcontext *scp) { gotcha = 1; } This Berkeley extension does not conform to the standard, which states that the signal handler has precisely one argument (of type int). The extension is benign on some architectures but definitely not on all. P.S. Yes, X3J11 discussed this issue at some length, and decided to uphold established practice instead of switching to the Berkeley invention.
rcvie@tuvie (ELIN Forsch.z.) (03/31/88)
>With shared memory, again, volatile is the least of your problems.
With signal handling, it may be one of your main problems. (The same may
be with `errno', if you can't declare it volatile.)
Dietmar Weickert,
ALCATEL-ELIN Research Center, Vienna, Austria.
henry@utzoo.uucp (Henry Spencer) (04/01/88)
> Well, in all fairness I could easily live with #pragma volatile(foo) instead, > *IF* compiler writers actually bothered to code the pragmas... If they don't, don't buy their compilers! -- "Noalias must go. This is | Henry Spencer @ U of Toronto Zoology non-negotiable." --DMR | {allegra,ihnp4,decvax,utai}!utzoo!henry
henry@utzoo.uucp (Henry Spencer) (04/01/88)
> > static void catch(int signo) { gotcha = 1; } > > PLEASE, declare the signal handler correctly Mr. Lint :-). He did. Page 120 of the X3J11 draft: void (*func)(int). Any provision for additional parameters is a non-standard extension. Any *requirement* for additional parameters is a major mistake. -- "Noalias must go. This is | Henry Spencer @ U of Toronto Zoology non-negotiable." --DMR | {allegra,ihnp4,decvax,utai}!utzoo!henry
chris@mimsy.UUCP (Chris Torek) (04/01/88)
-In article <9176@tut.cis.ohio-state.edu> lvc@tut.cis.ohio-state.edu -(Lawrence V. Cipriani) writes: -> volatile int customer_changeable_var = 0; -> if (customer_chageable_var != 0) ->[without something like volatile] the compiler would be free to ->optimize out the "impossible" code. In article <18686@think.UUCP> barmar@think.COM (Barry Margolin) writes: -I don't think volatile is necessary for the above example. and in article <4217@ihlpf.ATT.COM> nevin1@ihlpf.ATT.COM (00704a-Liber) writes: -The compiler would *not* be free to optimize out this code!! Because -customer_changeable_var is EXTERNAL to main(), this optimization cannot -occur. lvc is right; barmar and nevin1 are wrong. Remember, compilation need not take place until after everything is linked together. Admittedly there are few (possibly no) compilers which would make this `optimisation' today. I expect this to change in the near future. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
karl@haddock.ISC.COM (Karl Heuer) (04/01/88)
In article <1264@PT.CS.CMU.EDU> edw@IUS1.CS.CMU.EDU (Eddie Wyatt) writes: >> static void catch(int signo) { gotcha = 1; } >PLEASE, declare the signal handler correctly Mr. Lint :-). I did. Note that I was writing a "strictly conforming program". >static void catch(int signo, code; struct sigcontext *scp) { gotcha = 1; } > >In the pass I've had my signal handle return to random pieces of >code by not having the correct definition. Yeah, I've also been bitten occasionally by bugs in Berzerkeley code. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
chip@ateng.UUCP (Chip Salzenberg) (04/01/88)
In article <1988Mar29.004454.2867@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes: >Nobody is saying that the concept is not necessary in certain specialized >situations. >[...] the sort of code we're talking about isn't portable anyway. What about signal handlers? /* begin example */ volatile int hit = 0; int foo(); main() { signal(SIGINT, foo); while (!hit) ; exit(0); } foo() { ++hit; } /* end example */ Without volatile, you can't protect this program from being over-optimized. >"Noalias must go. This is non-negotiable." --DMR Definitely. -- Chip Salzenberg "chip@ateng.UU.NET" or "codas!ateng!chip" A T Engineering My employer's opinions are a trade secret. "Anything that works is better than anything that doesn't."
edw@IUS1.CS.CMU.EDU (Eddie Wyatt) (04/01/88)
>>> static void catch(int signo) { gotcha = 1; } >>PLEASE, declare the signal handler correctly Mr. Lint :-). > >I did. Note that I was writing a "strictly conforming program". > >>static void catch(int signo, code; struct sigcontext *scp) { gotcha = 1; } >> >>In the pass I've had my signal handle return to random pieces of >>code by not having the correct definition. > >Yeah, I've also been bitten occasionally by bugs in Berzerkeley code. It's questionable whether its a bug or not. The Sun man page reads: The handler routine can be declared: handler(sig, code, scp) int sig, code; struct sigcontext *scp; What bothers me about this entry is it doesn't say you have to declare the function that way. There's no entry in the BUGS section eluding to what happen if you don't conform either. -- Eddie Wyatt e-mail: edw@ius1.cs.cmu.edu
lvc@tut.cis.ohio-state.edu (Lawrence V. Cipriani) (04/01/88)
In article <18686@think.UUCP> barmar@fafnir.think.com.UUCP (Barry Margolin) writes: >In article <9176@tut.cis.ohio-state.edu> lvc@tut.cis.ohio-state.edu (Lawrence V. Cipriani) writes: >> ... my small example of using volatile ... >> >>If the volatile were dropped off, the compiler would be free to >>optimize out the "impossible" code. ... > ... Notes on use of static ... >Barry Margolin You are right. Try this instead: volatile static customer_changeable_var = 0; main() ...blah blah blah... if (customer_changeable_var != 0) { ...code for optional feature... } This would be in a compiled program a customer could edit with adb or sdb. If the compiler were free to optimize out the "impossible" code this would no longer work. Thanks for the note. -- Larry Cipriani, AT&T Network Systems and Ohio State University Domain: lvc@tut.cis.ohio-state.edu Path: ...!cbosgd!osu-cis!tut.cis.ohio-state.edu!lvc (weird but right)
chris@mimsy.UUCP (Chris Torek) (04/04/88)
[This article was reported as truncated; if you have seen it before, skip it] -In article <9176@tut.cis.ohio-state.edu> lvc@tut.cis.ohio-state.edu -(Lawrence V. Cipriani) writes: -> volatile int customer_changeable_var = 0; -> if (customer_chageable_var != 0) ->[without something like volatile] the compiler would be free to ->optimize out the "impossible" code. In article <18686@think.UUCP> barmar@think.COM (Barry Margolin) writes: -I don't think volatile is necessary for the above example. and in article <4217@ihlpf.ATT.COM> nevin1@ihlpf.ATT.COM (00704a-Liber) writes: -The compiler would *not* be free to optimize out this code!! Because -customer_changeable_var is EXTERNAL to main(), this optimization cannot -occur. lvc is right; barmar and nevin1 are wrong. Remember, compilation need not take place until after everything is linked together. Admittedly there are few (possibly no) compilers which would make this `optimisation' today. I expect this to change in the near future. -- 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) (04/05/88)
I've got a question on 'volatile'. In the following code fragment: /*...*/ extern volatile int foo; int bar; int int_function(); /*...*/ bar = foo++ + int_function(); /*...*/ how is foo incremented? Is the value saved when it is read for the addition, the value 1 added to it, and stored back in foo? Is foo just incremented whenever the compiler would normally increment a post-increment non-volatile variable? Is this just an error that a compiler should flag. I've looked in the standard for the answer and I can't find one. -- _ __ 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
henry@utzoo.uucp (Henry Spencer) (04/06/88)
> What about signal handlers?
I'm only gonna say this one more time: just about the only fully portable
thing a signal handler can do is set a flag. Not just any flag, but a
flag of type sig_atomic_t, defined in <signal.h> in an ANSI C implementation.
Since sig_atomic_t can be as magic as necessary, volatile isn't *really*
needed here. (I concede that it is useful in some classes of semi-portable
code.)
--
"Noalias must go. This is | Henry Spencer @ U of Toronto Zoology
non-negotiable." --DMR | {allegra,ihnp4,decvax,utai}!utzoo!henry
ned@ghostwheel.UUCP (Ned Nowotny) (04/08/88)
Many people have claimed that "volatile" is necessary for a variety of applications. Every one of which has been successfully written at one time or another. Does this mean that all these signal handlers, interrupt handlers, etc. are written in assembly or some other language? I suppose it also means that those which are written in K&R C are incorrect... Uh... completely unoptimizable. (To -O or not to -O...) In fact, why is it assumed that silent optimaztion of poorly (or, perhaps, correctly) written code is desirable? Just because a compiler can be made smart enough to move a loop invariant out of a loop does not mean that it is a good idea. It makes more sense to just provide the programmer with a warning. If the code is incorrect, the programmer learns something valuable. If it is correct, the programmer may save himself (or herself) a frustrating bout of debugging. Optimizers should not do optimazations which can be expressed in the source language itself. However, if they recognize a possibly poor construct, they should warn the programmer. (At least until automatic code generators can be made better programmers than your typical Unix wizard.) -- Ned Nowotny (ned@ghostwheel.aca.mcc.com.UUCP)
nw@amdahl.uts.amdahl.com (Neal Weidenhofer) (04/09/88)
In article <4269@ihlpf.ATT.COM>, nevin1@ihlpf.ATT.COM (00704a-Liber) writes: > I've got a question on 'volatile'. In the following code fragment: > > /*...*/ > extern volatile int foo; > int bar; > int int_function(); > /*...*/ > bar = foo++ + int_function(); > /*...*/ > > how is foo incremented? Is the value saved when it is read for the > addition, the value 1 added to it, and stored back in foo? Is foo just > incremented whenever the compiler would normally increment a post-increment > non-volatile variable? Is this just an error that a compiler should flag. > I've looked in the standard for the answer and I can't find one. > -- > _ __ NEVIN J. LIBER ..!ihnp4!ihlpf!nevin1 (312) 510-6194 It's not an error but any of the other possibilities you list are valid. All that dpANS mandates of a conforming implementation in a case like this is that: At some time after the last sequence point before the beginning of the statement and before the sequence point at the end of the statement, the varaible foo is "read" once and "written" once (whatever this may mean for a specific implementation.) Or that the results of running the program are the same as they would be if this were true--the "as if" rule. (N.B. There are also lots of mandates about the arithmetic, etc. that I don't believe are relevant to your question.) Extra disclaimer: Even though I'm a member of the committee, I'm not a spokesperson for us. The above represents my understanding of our intent. The opinions expressed above are mine (but I'm willing to share.) Regards, Neal Weidenhofer It's not the earth ...{hplabs|ihnp4|ames|decwrl}!amdahl!nw The meek inherit, Amdahl Corporation It's the dirt. 1250 E. Arques Ave. (M/S 316) P. O. Box 3470 Sunnyvale, CA 94088-3470 (408)737-5007
nevin1@ihlpf.ATT.COM (00704a-Liber) (04/09/88)
In article <150@ghostwheel.UUCP> ned@ghostwheel.aca.mcc.com.UUCP (Ned Nowotny) writes: |Many people have claimed that "volatile" is necessary for a variety of |applications. Every one of which has been successfully written at one |time or another. Does this mean that all these signal handlers, |interrupt handlers, etc. are written in assembly or some other language? |I suppose it also means that those which are written in K&R C are |incorrect... Uh... completely unoptimizable. (To -O or not to -O...) No, what this means is that in the future, when we have highly optimizing compilers and highly efficient hardware (such as caches,etc.), that this code may break. In order to do certain types of optimizations, certain assumptions have to be made. For example, if I know that a certain variable has no aliases, then in the implementation I can keep it's value in a cache (which is faster) without having to update the actual memory location it is suppose to be stored at. As things stand now, these assumptions are made on a compiler-by-compiler basis. |In fact, why is it assumed that silent optimaztion of poorly (or, |perhaps, correctly) written code is desirable? Just because a compiler |can be made smart enough to move a loop invariant out of a loop does not |mean that it is a good idea. It makes more sense to just provide the |programmer with a warning. If the code is incorrect, the programmer |learns something valuable. If it is correct, the programmer may save |himself (or herself) a frustrating bout of debugging. |Optimizers should not do optimazations which can be expressed in the |source language itself. Not all optimizations can be 'hand-coded'. For those which can be hand-coded, however, I would still rather have the compiler do all the work for me. Hand-optimizing takes a lot of time and usually turns well-wriiten readable code into something that can be entered in the Obfuscated C Contest :-). |However, if they recognize a possibly |poor construct, they should warn the programmer. (At least until |automatic code generators can be made better programmers than your |typical Unix wizard.) Lint, not C, is suppose to warn the programmer of possibly poor constructs. Code generators are not better designers (which is what you should be using your typical Unix wizard for :-)), but they can be better implementers. :-) -- _ __ 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
friedl@vsi.UUCP (Stephen J. Friedl) (04/09/88)
In article <150@ghostwheel.UUCP>, ned@ghostwheel.UUCP (Ned Nowotny) writes: > Optimizers should not do optimazations which can be expressed in the > source language itself. However, if they recognize a possibly > poor construct, they should warn the programmer. Huh? I think this is terribly naive. First, a construct "source-optimized" for one machine may turn out to be counterproductive on another machine. The best way to do pre/post ++ and -- probably varies from one architecture to another, and surely there are a host of other examples (array referencing, function calls, etc.) Second, how far should this be carried? Require traversing a chunk of data with pointers rather than with an array index? Require inline |asm| code? Mental data-flow analysis? Finally, and most importantly, isn't one major goal of a high-level language to insulate us from those details? Defining "poor construct" solely from the view of the compiler or CPU rather than from the view of a human is contrary to this notion; surely I am not the only one who has sometimes sacrificed "efficiency" in favor of other issues (readability, portability) when it was appropriate. Having a smart translation system helps me get the best of both worlds. Steve -- Steve Friedl V-Systems, Inc. "Yes, I'm jeff@unh's brother" friedl@vsi.com {backbones}!vsi.com!friedl attmail!vsi!friedl
walter@garth.UUCP (Walter Bays) (04/10/88)
In article <4346@ihlpf.ATT.COM> nevin1@ihlpf.UUCP (00704a-Liber,N.J.) writes: >No, what this means is that in the future, when we have highly optimizing >compilers and highly efficient hardware (such as caches,etc.), that this code >may break. In order to do certain types of optimizations, certain assumptions >have to be made. For example, if I know that a certain variable has no >aliases, then in the implementation I can keep it's value in a cache (which >is faster) without having to update the actual memory location it is >suppose to be stored at. As things stand now, these assumptions are made >on a compiler-by-compiler basis. I agree. Worse, in many cases the future is now. Usually the compiler must forgo optimizations that would work for nearly all programs for fear of breaking a few. And remember, optimization is more important for RISC than for CISC. C wizards will use 'volatile' and 'noalias'. Ordinary users may totally ignore them, and will still get the benefit of faster applications running on faster kernels. -- ------------------------------------------------------------------------------ Any similarities between my opinions and those of the person who signs my paychecks is purely coincidental. E-Mail route: ...!pyramid!garth!walter USPS: Intergraph APD, 2400 Geng Road, Palo Alto, California 94303 Phone: (415) 852-2384 ------------------------------------------------------------------------------
mouse@mcgill-vision.UUCP (der Mouse) (04/12/88)
In article <7594@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes: > In article <1264@PT.CS.CMU.EDU> edw@IUS1.CS.CMU.EDU (Eddie Wyatt) writes: >> static void catch(int signo, code; struct sigcontext *scp) { gotcha = 1; } > This Berkeley extension does not conform to the standard, Which standard? The dpANS? That's question-begging. > P.S. Yes, X3J11 discussed this issue at some length, and decided to > uphold established practice instead of switching to the Berkeley > invention. Berkeley practice seems pretty firmly established to me. What sort of OS-centricism is going on here? der Mouse uucp: mouse@mcgill-vision.uucp arpa: mouse@larry.mcrcim.mcgill.edu
gwyn@brl-smoke.ARPA (Doug Gwyn ) (04/12/88)
In article <1045@mcgill-vision.UUCP> mouse@mcgill-vision.UUCP (der Mouse) writes: >In article <7594@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes: >> In article <1264@PT.CS.CMU.EDU> edw@IUS1.CS.CMU.EDU (Eddie Wyatt) writes: >>> static void catch(int signo, code; struct sigcontext *scp) { gotcha = 1; } >> This Berkeley extension does not conform to the standard, >Berkeley practice seems pretty firmly established to me. Far from it; the additional arguments were a fairly recent change in the Berkeley variant of UNIX; they run counter to the established definition and implementation of signal() AT THE TIME BERKELEY DECIDED TO CHANGE THEIR IMPLEMENTATION, and to top it off, the problem of variadic argument lists wasn't dealt with (because all the world's a VAX, presumably). Thus Berkeley's approach is simply not suitable for an OS-independent standard. I wish they would invent new interfaces when they decide to change things like this.
henry@utzoo.uucp (Henry Spencer) (04/13/88)
> Berkeley practice seems pretty firmly established to me. What sort of > OS-centricism is going on here? Berklocentrism, and it's you that's doing it. Recent releases of x.yBSD are the *only* variants of Unix that interface to signal handlers in that way. Not only that, but it is actively incompatible with the standard interface ("standard" meaning the /usr/group standard and the SVID -- real standards -- in addition to widespread existing practice and drafts of X3J11 and POSIX) on any machine that has trouble with varargs functions; the Berkloids have this wretchedly stupid habit of not giving new interfaces new names. All the world's a VAX, right? Grr. -- "Noalias must go. This is | Henry Spencer @ U of Toronto Zoology non-negotiable." --DMR | {allegra,ihnp4,decvax,utai}!utzoo!henry
chris@mimsy.UUCP (Chris Torek) (04/13/88)
>> extern volatile int user_interrupt; >> while (!user_interrupt) >> sleep(1); In article <49255@sun.uucp> limes@sun.uucp (Greg Limes) writes: >Please do not misunderstand me, I would like to see "volatile" >added -- but why would it be required above? If "user_interrupt" >is an external variable, then the compiler is not free to assume >that it will not change across a function call. The compiler *is* free to make that assumption if it examines the function and finds that it does not write into user_interrupt. For instance, if `sleep' is implemented as (say) typedef unsigned long u_long; sleep(unsigned n) { static volatile u_long *const timerp = (u_long *)0xfc000040; register u_long expect = *timerp + n; while (*timerp < expect) /*void*/; } and the code for sleep is stored in some format that the compiler can read, then the compiler can see that sleep cannot change user_interrupt. The main problem with volatile is that it does not have well- defined semantics. It *cannot* have well-defined semantics. Consider a load/store architecture versus a Vax-like architecture. If I write volatile char c; ... c++; the load/store machine must generate something like movbl _c,r212 addl #1,r212 movlb r212,_c which is one read and one write; the Vax-like machine just runs incb _c which is one read/modify/write. The reason volatile exists is because it is a machine-independent means of specifying something that is inherently machine-dependent but which affects every implementation that does what might be called `nonobvious optimisation'. Like the size of `pointer_t' (void *) or the length of `size_t', volatile is a machine- and compiler-dependent construct that can be used to give machine- and compiler-independent results: #include <atomic.h> semaphore_t sem; ... SEM_LOCK(&sem); ... SEM_UNLOCK(&sem); where <atomic.h> might say typedef volatile int semaphore_t; #define SEM_LOCK(semp) while ((*semp)++) --*(semp) #define SEM_UNLOCK(semp) --*(semp) or it might say instead typedef volatile short semaphore_t; #define SEM_LOCK(semp) \ while (_builtin_test_and_set(semp)) \ _builtin_atomic_clear(semp) #define SEM_UNLOCK(semp) _builtin_atomic_clear(semp) or any of a number of other possibilities. What `volatile' DOES is machine-dependent. What `volatile' EXPRESSES (`hands off! this is a weird object!') is not, or not entirely. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
terry@wsccs.UUCP (Every system needs one) (04/17/88)
In article <4346@ihlpf.ATT.COM>, nevin1@ihlpf.ATT.COM (00704a-Liber) writes: > In article <150@ghostwheel.UUCP> Ned Nowotny writes: > |I suppose it also means that those which are written in K&R C are > |incorrect... Uh... completely unoptimizable. (To -O or not to -O...) > > No, what this means is that in the future, when we have highly optimizing > compilers and highly efficient hardware (such as caches,etc.), that this code > may break. It will only break if the same assumptions are not made for the new hardware as were made for the old. It is stupid to require volitile to tell the compiler not to optimize things outside your address space (such as the hardware a device driver talks to); the compiler should recognized that what you are getting at is not in your address space and assume that something else will change it. While it isn't guaranteed that something out of your address space is volitile, and it may not be true that everything volitile is outside your address space, these are pretty safe assumtions, and make better sense than the programmer having to tell the compiler "hey stupid, don't make that assumption here". The only thing this tends to do is make it easier to write compilers, not easier to write programs which those compilers are to be used on. Face it, you're talking programmer interface to the system vs. ease of compiler developement. For my money, it makes a lot more sense to pay the developement penalty writing the compiler than in every subsequent program that is compiled with it. > In order to do certain types of optimizations, certain assumptions > have to be made. For example, if I know that a certain variable has no > aliases, then in the implementation I can keep it's value in a cache (which > is faster) without having to update the actual memory location it is > suppose to be stored at. Checking for alias, again, seems to be a problem in lexical analysis. Can you present a case where a compiler could NEVER know if it looked? Certainly, it's possible to speed the compiler by hitting it over the head with the two-by-four 'noalias', but at what a cost? You are paying for faster compiles vs. faster execution speed by breaking working programs. You could not use the caching feature, thus speeding up the compiler, or you could use the caching feature, at a cost of a slower compile time. It's just a penalty you must pay to use an architectural feature. > As things stand now, these assumptions are made on a compiler-by-compiler > basis. What is the difference between using the word 'noalias' on a machine that can't cache (and therefore can not optimize via cache-storage) vs. compiling the code on a machine that can? The decision to implement cache-storage optimization MUST, by virtue of not all machines having caching, be made on a compiler-by-compiler basis. I see no real advantage in providing something to a stupid compiler that loses any backward compatability I may have had so that it doesn't have to be smart enough to know that caching is available on the machine it is running on. Machine dependancy is the responsibility of the compiler writer. Optimization is the responsibility of the compiler writer. If architectural differences prevent effective use of the compiler, this is the responsibility of the compiler writer. > |In fact, why is it assumed that silent optimaztion of poorly (or, > |perhaps, correctly) written code is desirable? To provide source-code compatability. Optimization should be done with the understanding that the assumptions the optimizer makes have to fall within possible parctice. If different vendor's compilers make several different sets of assumtions, which they will, obviously, it is the vendor's responsibility that the assumptions be valid for the code being compiled. Basically, if it works without -O, it sould work with -O, regardless of what the compiler writer's optimization does to achieve it's goal. If this makes writing compilers harder, so what? Not only will new code be portable, old code will be portable, as well. > |Just because a compiler > |can be made smart enough to move a loop invariant out of a loop does not > |mean that it is a good idea. I disagree. Please give an example where 'invariant' is 'variant'. If an optimization occurrs incorrectly, then the optimizer is wrong. > |It makes more sense to just provide the programmer with a warning. This makes the assumption that the compiler is smart enought to know it may be wrong. If it's likely enough wrong to require a warning, it shouldn't do it. > |If the code is incorrect, the programmer > |learns something valuable. If it is correct, the programmer may save > |himself (or herself) a frustrating bout of debugging. Why is it incorrect to have a loop invariant in a loop? Agreed that it is less efficient, but using that argument, I could easily make a case for the use of an automatic weapon on traffic violators, as shooting a speeder is more likely to prevent him speeding in the future than is giving him a ticket, and thus more efficient. Issuing errors for optimization assumptions is simply overkill, unless the method of arriving at the assumption is invalid. > |Optimizers should not do optimazations which can be expressed in the > |source language itself. > Not all optimizations can be 'hand-coded'. For those which can be > hand-coded, however, I would still rather have the compiler do all the work > for me. Hand-optimizing takes a lot of time and usually turns well-wriiten > readable code into something that can be entered in the Obfuscated C Contest > :-). Well stated. I think the primary concern is who is going to have to give: Will the hardware people build machines that think more like a programmer so that compiler writers don't have to bend over backwards to make something work? (Side effects: verbose compiler output or slower compiles or machine dependant programmer interfaces, such as ANSI-C) Will compiler writes produce slower compilers or more verbose code so that programmers don't have to worry about machine dependency in supposedly machine-independant languages? (Side effects: incorrect optimization assumptions, applications are not backward compatible, old applications have to be rewritten) Will programmers rewrite all their code in ANSI-C and hope it takes all possible future hardware into account? (Side effects: Millions of programmer hours) Something has to give and ANSI seems to have decided on applications programmers. Can you guarantee in writing that ANSI-C will work on machines 15 years from now, machines with unguessable architectures? | Terry Lambert UUCP: ...{ decvax, ihnp4 } | | @ Century Software : ...utah-cs!uplherc!sp7040!obie!wsccs!terry | | SLC, Utah | | These opinions are not my companies, but if you find them | | useful, send a $20.00 donation to Brisbane Australia... | | 'There are monkey boys in the facility. Do not be alarmed; you are secure' |
eric@pyrps5 (Eric Bergan) (04/21/88)
In article <488@wsccs.UUCP>, terry@wsccs.UUCP (Every system needs one) writes: > > Machine dependancy is the responsibility of the compiler writer. > Optimization is the responsibility of the compiler writer. > If architectural differences prevent effective use of the compiler, this is > the responsibility of the compiler writer. I agree with these, but does this imply: If language deficiencies prevent effective optimization, scrap the optimizations? The key question seems to be, "Is C (particularly K&R) perfect?". Are we willing to add constructs to the language that both better document what is going on and provide the ability to do better optimization (and hence get better performance)? "volatile" not only helps the optimizer, I would suggest it also helps document what is going on. The flip side of the argument is "How far should we bend C for optimization purposes?" It would be great from an optimizer's viewpoint to be able to definitively know if a variable has no aliases. I suspect that this is too traumatic a change for the language, however, so we will have to rely on doing less optimization then might be available. (Have there been any studies of just how much performance improvements might be possible if an optimizer definitively knew what was and was not aliased?) Notice that noalias also does not really better document the program - although an "aliased" keyword might. But even then, there is simply too much pointer use going on to expect the average software house to rewrite all their code using either an "alias" or a "noalias". (Note that the use of "volatile" is much less frequent, so the pain of converting to that is much less. Also, aggresive optimizers are already causing developers using volatile variables problems.) One other side point. There have been a few suggestions that why worry about the optimizers, let's just keep building faster hardware. I'd like to point out that as the hardware continues to get faster (and rely more heavily on register access, or keeping pipelines running), the optimizer must get more sophisticated to take advantage of that hardware, otherwise no performance improvement will be seen.
daveb@geac.UUCP (David Collier-Brown) (04/24/88)
In article <20345@pyramid.pyramid.com> eric@pyrps5 (Eric Bergan) writes: [in response to <488@wsccs.UUCP>, from terry@wsccs.UUCP | The flip side of the argument is "How far should we bend C for | optimization purposes?" It would be great from an optimizer's viewpoint | to be able to definitively know if a variable has no aliases. I suspect | that this is too traumatic a change for the language, however, so | we will have to rely on doing less optimization then might be available. | (Have there been any studies of just how much performance improvements | might be possible if an optimizer definitively knew what was and was | not aliased?) As a sidebar to the question, consider this fragment from comp.arch, | Article 4111 of comp.arch: | From: fnf@mcdsun.UUCP (Fred Fish) | This is just the tip of the iceburg, there are lots of other optimizations | that become obvious. By examining the static and dynamic characteristics | of the program, the data section objects can be sorted to get the most | frequently used objects into low data memory. The linker might also | decide that certain sections of the program reference portions of | data memory more often than others, and insert the appropriate code to | change the data mapping on the fly, rather than using a static mapping. It is interesting to note that there have not been, to date, **any** other discussion of the necessity of "volatile" et all, only of their desirability in a given language, taking their necessity as **a given**. Are the two discussion groups disjoint? --dave (If you think that wasn't a nasty comment, sed <.signature 's/months/picoseconds/') c-b -- David Collier-Brown. {mnetor yunexus utgpu}!geac!daveb Geac Computers International Inc., | Computer Science loses its 350 Steelcase Road,Markham, Ontario, | memory (if not its mind) CANADA, L3R 1B3 (416) 475-0525 x3279 | every 6 months.
mash@mips.COM (John Mashey) (04/26/88)
In article <2642@geac.UUCP> daveb@geac.UUCP (David Collier-Brown) writes: .... > It is interesting to note that there have not been, to date, >**any** other discussion of the necessity of "volatile" et all, only >of their desirability in a given language, taking their necessity as >**a given**. This is an assertion of non-fact. There was such a discussion that ran for about a week, a month or so ago, at least in comp.lang.c. People gave examples of potential usage; some of us who have it and use it said so; I observed that both of our kernels had about 200 instances of volatile, and we'd miss it a lot if it weren't there. -- -john mashey DISCLAIMER: <generic disclaimer, I speak for me only, etc> UUCP: {ames,decwrl,prls,pyramid}!mips!mash OR mash@mips.com DDD: 408-991-0253 or 408-720-1700, x253 USPS: MIPS Computer Systems, 930 E. Arques, Sunnyvale, CA 94086
terry@wsccs.UUCP (Every system needs one) (04/28/88)
In article <20345@pyramid.pyramid.com>, eric@pyrps5 (Eric Bergan) writes: > In article <488@wsccs.UUCP>, I write: > > > > Machine dependancy is the responsibility of the compiler writer. > > Optimization is the responsibility of the compiler writer. > > If architectural differences prevent effective use of the compiler, this is > > the responsibility of the compiler writer. > > I agree with these, but does this imply: > > If language deficiencies prevent effective optimization, scrap the > optimizations? Yes, or rewrite them. > "volatile" not only helps the optimizer, I would suggest it also helps > document what is going on. Yes, but why is volitile int foo; better than int foo; /* VOLITILE*/ Except for the optimizer? > The flip side of the argument is "How far should we bend C for > optimization purposes?" [..."optimizing"...] But even then, there ^^^^^^^^^^^^^^^^^^^^^^ exactly! > is simply too much pointer use going on to expect the average software > house to rewrite all their code using either an "alias" or a > "noalias". Yes. > (Note that the use of "volatile" is much less frequent, so > the pain of converting to that is much less. Also, aggresive optimizers > are already causing developers using volatile variables problems.) I'll agree with this one because of the also, but it makes my teeth itch :-). > I'd like to point out that as the hardware continues to get faster (and > rely more heavily on register access, or keeping pipelines running), the > optimizer must get more sophisticated to take advantage of that hardware, > otherwise no performance improvement will be seen. I have some questions as to weather or not hardware that gives you the choice of fast or source code compatible (like SPARC) by requiring more instructions be generated to implement code "the way God meant it to be" is actually "faster" than hardware that uses less but "slower" instructions. But that belongs in comp.arch, not here. The problem is that the .arch seems to be going off and tromping on my .lang.c... there should be dichotomy. | Terry Lambert UUCP: ...{ decvax, ihnp4 } ...utah-cs!century!terry | | @ Century Software OR: ...utah-cs!uplherc!sp7040!obie!wsccs!terry | | SLC, Utah | | These opinions are not my companies, but if you find them | | useful, send a $20.00 donation to Brisbane Australia... | | 'There are monkey boys in the facility. Do not be alarmed; you are secure' |
daveb@geac.UUCP (David Collier-Brown) (04/29/88)
In article <2642@geac.UUCP> daveb@geac.UUCP (David Collier-Brown) writes: | It is interesting to note that there have not been, to date, | **any** other discussion of the necessity of "volatile" et all, only | of their desirability in a given language, taking their necessity as | **a given**. In article <2082@winchester.mips.COM> mash@winchester.UUCP (John Mashey) writes: | This is an assertion of non-fact. There was such a discussion that | ran for about a week, a month or so ago, at least in comp.lang.c. | People gave examples of potential usage; some of us who have it and use | it said so; I observed that both of our kernels had about 200 | instances of volatile, and we'd miss it a lot if it weren't there. Sorry John, that's not a discussion of the necessity for such a facility. (Yes, I read your article when it was published). That your compiler-wriers believed that such was necessary **is** germane, and I'll happily agree that, if found necessary by the compiler-writers, one would be a little foolish to not use it (:-)). I reiterate: the question of the necessity of certain information for optimization purposes is: 1) in part architectural, 2) in part a question of compiler technology, and 3) open. Specifically: 1) what architectures currently use asynchronously-changing memory locations for program notification of events? DEC Vax, various CDC boxes, MIPS(tm),... 2) what other programmer-visible alternatives are there? Interrupts, event queues, (Hoare) monitors... 3) what is the state of compiler/translator technology for the new architectures, especially for parallel processing? Is the "volatile" question valid, has it been dealt with, and if so, how? VLIW, (Brinch Hansen's) Edison, Concurrent <whatever>, etc... --dave c-b -- David Collier-Brown. {mnetor yunexus utgpu}!geac!daveb Geac Computers International Inc., | Computer Science loses its 350 Steelcase Road,Markham, Ontario, | memory (if not its mind) CANADA, L3R 1B3 (416) 475-0525 x3279 | every 6 months.
limes@sun.uucp (Greg Limes) (04/30/88)
In article <502@wsccs.UUCP> terry@wsccs.UUCP (Every system needs one) writes: [previous junk removed for brevity] > Yes, but why is > volitile int foo; > better than > int foo; /* VOLITILE*/ > Except for the optimizer? The first not only documents for the user that "foo" may be changed outside the expected flow of control of the program (i.e. by hardware or by a signal handler), but making it a part of the language forces optimiser designers to leave open this trapdoor. It also standardises where they will put the trapdoor, if/when they realize that programmers need to be able to break out of the dreaded while (!signal_caught); loops ... try running the previous line through the SunOS 4.0 compiler at any optimisation level higher than about, say, -O1 ... Compiler technology is getting really good, and we need a way to tell the optimisers that strange things can happen. Frankly, I need volatile so I can let the good optimisations rip on my code, without breaking the communcation lines between mainline and interrupt-level; without volatile, I am forced to miss the whole boat. Those of you who do not want volatile, fine; ignore it when you see it, do not use it, and compile with your optimisers lobotmized. I want to use all the nifty stuff they jammed into "-O4". -- Greg Limes [limes@sun.com] frames to /dev/fb
limes@sun.uucp (Greg Limes) (04/30/88)
In article <2674@geac.UUCP> daveb@geac.UUCP (David Collier-Brown) writes: > > I reiterate: the question of the necessity of certain information >for optimization purposes is: > 1) in part architectural, > 2) in part a question of compiler technology, and > 3) open. > > Specifically: > 1) what architectures currently use asynchronously-changing >memory locations for program notification of events? DEC Vax, >various CDC boxes, MIPS(tm),... ... and any program with a signal handler that modifies a global variable, which is pretty inclusive if you ask me, it all comes to the same effect from the POV of both the mainline thread and the compiler ... > 2) what other programmer-visible alternatives are there? >Interrupts, event queues, (Hoare) monitors... ... use only specific transfer variables, test these only around calls to synchronous functions that the compiler can never know about and must assume that they modify these locations ... -- Greg Limes [limes@sun.com] frames to /dev/fb
brooks@lll-crg.llnl.gov (Eugene D. Brooks III) (04/30/88)
>In article <2642@geac.UUCP> daveb@geac.UUCP (David Collier-Brown) writes: >| It is interesting to note that there have not been, to date, >| **any** other discussion of the necessity of "volatile" et all, only >| of their desirability in a given language, taking their necessity as >| **a given**. Unfortunately, the ANSI C standard proposal did not give much attention to multiprocessing issues, but the use of volatile in tightly coupled shared memory programs is very important. A shared memory variable is often "cacheable" for a short period of time, and by this I include "cache in the usual sense" and the use of registers as cache. Volitile allows the user to tell the compiler which references are not to be taken from cache, but obviously needs further development in the context of multiprocessing. Perhaps this will happen in the second "standards cycle" for C. With all the VLSI based multiprocessors hitting the market, good language support for them will become very important.
mash@mips.COM (John Mashey) (05/01/88)
In article <2674@geac.UUCP> daveb@geac.UUCP (David Collier-Brown) writes: >In article <2642@geac.UUCP> daveb@geac.UUCP (David Collier-Brown) writes: >| It is interesting to note that there have not been, to date, >| **any** other discussion of the necessity of "volatile" et all, only >| of their desirability in a given language, taking their necessity as >| **a given**. >In article <2082@winchester.mips.COM> mash@winchester.UUCP (John Mashey) writes: >| This is an assertion of non-fact. There was such a discussion that >| ran for about a week, a month or so ago, at least in comp.lang.c. >| People gave examples of potential usage; some of us who have it and use >| it said so; I observed that both of our kernels had about 200 >| instances of volatile, and we'd miss it a lot if it weren't there. > Sorry John, that's not a discussion of the necessity for such a >facility. (Yes, I read your article when it was published). > That your compiler-wriers believed that such was necessary **is** >germane, and I'll happily agree that, if found necessary by the >compiler-writers, one would be a little foolish to not use it (:-)). It wasn't our compiler-writers, it was us (the OS group) who thought we needed it. > I reiterate: the question of the necessity of certain information >for optimization purposes is: > 1) in part architectural, > 2) in part a question of compiler technology, and > 3) open. > Specifically: > 1) what architectures currently use asynchronously-changing >memory locations for program notification of events? DEC Vax, >various CDC boxes, MIPS(tm),... > 2) what other programmer-visible alternatives are there? >Interrupts, event queues, (Hoare) monitors... As I thought I'd mentioned before, the most immediate cause of wanting volatile has nothing to do with computer architecture in the sense of 1), or OS theory/practice in the sense of 2), but the very simple, practical reason, that in an open systems design, using a standard bus, and buying off-the-shelf controllers, you will discover that many such controllers, otherwise desirable, use memory as a communication mechanism. You get a choice: a) Add volatile to C, and be able to optimize your drivers with minimum performance loss, thus maintaining the ability to use C rather than assembler without irritating performance lossage. You also get to port existing drivers fairly easily. OR b) Don't optimize your drivers very much. OR c) Eliminate many potential peripheral controllers, or at best, have to completely redo drivers you get from elsewhere. OR d) Build all your own peripheral controllers. Of these options, most people do b), because c) and d) aren't practical for many organizations; a) is clearly more pleasant, an much more in tune with the philosophy in UNIX that using C shouldn't cause you a big performance hit, and that C should be able to describe devices and otehr interfaces not under the software architect's control. Does that address the question: volatile matches the reality of what's out there, not what might be possible otherwise. -- -john mashey DISCLAIMER: <generic disclaimer, I speak for me only, etc> UUCP: {ames,decwrl,prls,pyramid}!mips!mash OR mash@mips.com DDD: 408-991-0253 or 408-720-1700, x253 USPS: MIPS Computer Systems, 930 E. Arques, Sunnyvale, CA 94086
wcs@skep2.ATT.COM (Bill.Stewart.<ho95c>) (05/02/88)
In article <51437@sun.uucp> limes@sun.UUCP (Greg Limes) writes: :In article <2674@geac.UUCP> daveb@geac.UUCP (David Collier-Brown) writes: :> 1) what architectures currently use asynchronously-changing :>memory locations for program notification of events? DEC Vax, :>various CDC boxes, MIPS(tm),... :... and any program with a signal handler that modifies a global :variable, which is pretty inclusive if you ask me, ..... Any program with a signal handler that *reads* global variables needs the moral equivalent of volatile, if you're going to allow optimizing compilers to avoid "redundant" writes to memory. -- # Thanks; # Bill Stewart, AT&T Bell Labs 2G218, Holmdel NJ 1-201-949-0705 ihnp4!ho95c!wcs # skep2 is a local machine I'm trying to turn into a server. Please send # mail to ho95c or ho95e instead. Thanks.
daveb@geac.UUCP (David Collier-Brown) (05/02/88)
In article <6612@lll-winken.llnl.gov> brooks@lll-crg.llnl.gov.UUCP (Eugene D. Brooks III) writes: >obviously needs further development in the context of multiprocessing. >Perhaps this will happen in the second "standards cycle" for C. With all >the VLSI based multiprocessors hitting the market, good language support >for them will become very important. Yup. The Sequent is probably a good example: their cache algorithms are subtle and (seemingly) elegant --dave c-b -- David Collier-Brown. {mnetor yunexus utgpu}!geac!daveb Geac Computers International Inc., | Computer Science loses its 350 Steelcase Road,Markham, Ontario, | memory (if not its mind) CANADA, L3R 1B3 (416) 475-0525 x3279 | every 6 months.
daveb@geac.UUCP (David Collier-Brown) (05/02/88)
In article <2114@winchester.mips.COM> mash@winchester.UUCP (John Mashey) writes: | As I thought I'd mentioned before, the most immediate cause of wanting | volatile has nothing to do with computer architecture in the sense of | 1), or OS theory/practice in the sense of 2), but the very simple, | practical reason, that in an open systems design, using a standard | bus, and buying off-the-shelf controllers, you will discover that | many such controllers, otherwise desirable, use memory as | a communication mechanism. Agreed. I was asking an architecture/theory question, in hopes of invalidating part of the problem. And it is a real problem: there is nothing improper or undesirable in using a portion of the address space for special purposes. Adding volatile or equivalent directives to languages is a perfectly reasonable tradeoff, expecially if the alternative is to break off a part of the address space for "i/o ports" as some microcomputer manufacturers do. Thats why I addressed it to the architects. I know why I'd need it as a programmer: (a). Or I can use (b) and write a very-machine-specific driver using lots of source-to-source transformations. I've written drivers in GMAP. Never again! --dave c-b -- David Collier-Brown. {mnetor yunexus utgpu}!geac!daveb Geac Computers International Inc., | Computer Science loses its 350 Steelcase Road,Markham, Ontario, | memory (if not its mind) CANADA, L3R 1B3 (416) 475-0525 x3279 | every 6 months.
johnl@ima.ISC.COM (John R. Levine) (05/04/88)
>In article <2114@winchester.mips.COM> mash@winchester.UUCP (John Mashey) writes: > [ "volatile" makes it possible to program memory-mapped devices in C, > without having to resort to subterfuges like turning off the optimizer > and hoping the compiler is dumb enough. (I hope this doesn't misrepresent > his argument.) ] Nobody I know has argued that something like "volatile" isn't useful. There's no doubt that it is. The question is whether it belongs in ANSI C, which purports to be a standard for a language for portable programs. The problem is that volatile means many different things to different people. For some people it means a memory-like device register. For others, it means shared memory that might be modified by another process, or memory that might be modified by an interrupt routine or by the operating system. Some optimists even hope that something like this: extern volatile foo; ... foo++; could be used for synchronization of shared memory areas. All of these are useful, and all of them are different. That's why a single keyword in the language doesn't suffice to describe them. You'd have many different implementations giving different semantics to the same syntax, hardly a recipe for portability. Use a compiler-specific extension, or use a #pragma. (I note in passing that for many architectures, if you're addressing device registers you also need some way to tell the compiler to use aligned 16-bit references or some such, which clearly needs to be a #pragma.) I suppose that if the C committee were prepared to decree a standard for the semantics of shared variables, it would then be reasonable to define a keyword to access them. Considering the plethora of shared memory implementations, and the equally large plethora of synchronizing schemes, I doubt any such standard would at this point be acceptable to more than a small minority of users. Until then, though, let's restrict the standard language to things that we know how to make portable. To open another can of worms, this same argument applies to noalias. -- John R. Levine, IECC, PO Box 349, Cambridge MA 02238-0349, +1 617 492 3869 { ihnp4 | decvax | cbosgd | harvard | yale }!ima!johnl, Levine@YALE.something Rome fell, Babylon fell, Scarsdale will have its turn. -G. B. Shaw
john@frog.UUCP (John Woods) (05/05/88)
In article <502@wsccs.UUCP>, terry@wsccs.UUCP (Every system needs one) writes: > Yes, but why is < > volitile int foo; < > better than < > int foo; /* VOLITILE*/ < > Except for the optimizer? Because /* VOLITILE */ is in all uppercase, and makes me want to smash in my terminal screen with a sledgehammer. :-) -- John Woods, Charles River Data Systems, Framingham MA, (617) 626-1101 ...!decvax!frog!john, ...!mit-eddie!jfw, jfw@eddie.mit.edu "Take Greg Nowak. He's divisible by 59." - Not Matt Crawford
ray@micomvax.UUCP (Ray Dunn) (05/05/88)
In article <2674@geac.UUCP> daveb@geac.UUCP (David Collier-Brown) asks: [On the necessity of "volatile"] > Specifically: > 1) what architectures currently use asynchronously-changing >memory locations for program notification of events? DEC Vax, >various CDC boxes, MIPS(tm),... The asking of this question is a clear example of the distorted view many people have, whose environment is restricted to DEC, SUN, CDC, IBM etc *standard* machines. This is not meant as a flame, only as an observation. The answer to the question is "as many architectures as there are proprietary designs of micro-processor based equipment which use asynchronously changing memory locations, including *all*, for example, which use memory mapped I/O. As I said in an earlier posting (on reflection not very succinctly), a compiler for a particular [micro-processor] CPU can make *no* assumptions about the architecture that the compiled code is running on, other than [the architecture of the microprocessor itself]. Writing a compiler for a 68000 is a different exercise than writing one for a VAX, which is a fully defined environment. So it's not reasonable to make these controls [e.g. "volatile"] a compiler specific thing. They *have* to be part of the language. -- 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
mcdonald@uxe.cso.uiuc.edu (05/05/88)
>I know has argued that something like "volatile" isn't useful. >There's no doubt that it is. The question is whether it belongs in ANSI C, >which purports to be a standard for a language for portable programs. >The problem is that volatile means many different things to different people. >Until then, though, let's restrict the standard language to things that we >know how to make portable. I strongly disagree that the definition of a language should be restricted to "portable" things. I think that portability, per se, is a desirable goal with a very low priority. Speed, more speed, lots more speed, a friendly user interface, and general usefulness are much more important. A large fraction of programs today are likely being written for environments like the IBM-PC and the Mac which are connected to specific hardware and operating environment. Most portable programs run on those machines are decidedly inferior to ones written specifically for them. I believe that a language, per se, should be divided into a "portable" part and a language-specified non-portable syntax. I feel that what has been done to C by the ANSI committee is quite exemplary. "Volatile" is a generally useful concept, and seems to me to indicate something useful in enough cases, and also fits nicely into the declaration syntax of the language, that it should stay. I would personally like to see compiler vendors allow inline assembler. For that, a "pragma" would do. The big plus for leaving volatile as-is is that vendors would be FORCED to allow some sort of machine-specific code. It isn't far enough for me, as some vendors are going to make it impossible to ADDRESS the part of memory where the interesting locations lie. The only thing I can do about that is to complain, and not buy their products. Doug McDonald "I want a nifty program, not a portable one"
ray@micomvax.UUCP (Ray Dunn) (05/09/88)
In article <1001@ima.ISC.COM> johnl@ima.UUCP (John R. Levine) writes: >..... The question is whether it belongs in ANSI C, >which purports to be a standard for a language for portable programs. >..... ANSI C surely does not purport to be any such thing. ANSI C standardizes the language to enable portability to be easier to achieve *IF THAT IS WHAT YOU ARE TRYING TO ACHIEVE*. Am I to be forbidden from writing my UNIX device drivers for my Brand X machine in 'C' because they are not portable, and were never intended to be portable? Jeesh! I've posted one or two comments about the inappropriateness of portability as the main reason d'etre in much programming activity, but I never thought I would have to refute an attempt to say that 'C' was only a language for portable programs!! -- 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
woerz@iaoobelix.UUCP (Dieter Woerz) (05/10/88)
In article <1001@ima.ISC.COM> johnl@ima.UUCP (John R. Levine) writes: > ... >The problem is that volatile means many different things to different people. >For some people it means a memory-like device register. For others, it means >shared memory that might be modified by another process, or memory that might >be modified by an interrupt routine or by the operating system. Some >optimists even hope that something like this: > ... >could be used for synchronization of shared memory areas. > >All of these are useful, and all of them are different. I don't think, that these are different. They have all one thing in com- mon, in all cases, the compiler should not cache the value he has read some time ago from the memory, or in the case of write accesses, the value should be not be stored in a register. So, in all of the cases men- tioned above, the value to be read could be modified by some external "process" (device, other process writing into shared memory, interrupt routine, ...) or it should immediately modifify a memory location, so that another "process" is notified of the change. ------------------------------------------------------------------------------ Dieter Woerz Fraunhofer Institut fuer Arbeitswirtschaft und Organisation Abt. 453 Holzgartenstrasse 17 D-7000 Stuttgart 1 W-Germany BITNET: iaoobel.uucp!woerz@unido.bitnet UUCP: ...{uunet!unido, pyramid}!iaoobel!woerz
johnl@ima.ISC.COM (John R. Levine) (05/11/88)
In article <1030@micomvax.UUCP> ray@micomvax.UUCP (Ray Dunn) writes: >As I said in an earlier posting (on reflection not very succinctly), a >compiler for a particular [micro-processor] CPU can make *no* assumptions >about the architecture that the compiled code is running on, other than [the >architecture of the microprocessor itself]. > ... >So it's not reasonable to make these controls [e.g. "volatile"] a compiler >specific thing. > >They *have* to be part of the language. It seems to me that we have a confusion here between making constructs part of the language and making them part of the standard. Nobody has ever said that ANSI C compilers shouldn't have extensions -- they all will, of some sort or another. But whether "volatile" belongs in the standard is entirely another thing. The standard defines a language for writing portable programs, i.e. any standard-conforming program should do the same thing on all ANSI C processors. I have never seen any suggestion that a program containing "volatile" would be portable except perhaps to other processors which happen to have similar memory, I/O, and hardware architectures. So, sure, go ahead and put it in your compiler where it will be useful for all sorts of stuff. But keep it out of the standard until you can define it in a way that will produce the same results on all C processors. -- John R. Levine, IECC, PO Box 349, Cambridge MA 02238-0349, +1 617 492 3869 { ihnp4 | decvax | cbosgd | harvard | yale }!ima!johnl, Levine@YALE.something Rome fell, Babylon fell, Scarsdale will have its turn. -G. B. Shaw
nevin1@ihlpf.ATT.COM (00704a-Liber) (05/12/88)
In article <225800029@uxe.cso.uiuc.edu> mcdonald@uxe.cso.uiuc.edu writes: >I believe that a language, per se, should be divided into a "portable" >part and a language-specified non-portable syntax. But what does 'language-specified non-portable' really mean? The problem is, 'volatile' means different things on different machines and even in different implementations on the same machine. If it can't be well-defined, it shouldn't be in the language. Personally, I feel that there is a need for 'volatile' in C, but the way it is specified in dpANS C is not good enough for it to be useful. -- _ __ 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
chris@mimsy.UUCP (Chris Torek) (05/12/88)
In article <4727@ihlpf.ATT.COM> nevin1@ihlpf.ATT.COM (00704a-Liber) writes: >... If it can't be well-defined, it shouldn't be in the language. This line of reasoning also rules out integer-to-pointer casts, as I noted earlier: struct rkdevice *rkaddr = (struct rkdevice *)0777440; I am perfectly happy with ill-defined constructs. (I consider other some arguments for and against volatile valid.) -- 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@brl-smoke.ARPA (Doug Gwyn ) (05/12/88)
In article <1011@ima.ISC.COM> johnl@ima.UUCP (John R. Levine) writes: >The standard defines a language for writing portable programs, i.e. >any standard-conforming program should do the same thing on all ANSI C >processors. This simply isn't true. Since this is explained on p.4 of the draft proposed Standard, and the remarks about "volatile" are completely wrong, one has to conclude that this fellow has decided to explain the proposed Standard to you all without having understood it himself. There are two forms of Standard-conforming implementations (hosted and freestanding) and two forms of compliance for programs (conforming and strictly conforming). Conforming programs need not be at all portable; they merely need to be acceptable to a conforming implementation. In particular, conforming programs may be utterly dependent on implementation-specific features. >I have never seen any suggestion that a program containing >"volatile" would be portable except perhaps to other processors which happen >to have similar memory, I/O, and hardware architectures. The following program is strictly conforming (i.e. portable): #include <stdio.h> main() { volatile int i; for ( i = 0; i < 10; ++i ) printf( "%d\n", i ); } It is not the use of the "volatile" type qualifier that makes a program non-portable, but rather dependence on characteristics not guaranteed by the language specification. >So, sure, go ahead and put it in your compiler where it will be useful for >all sorts of stuff. But keep it out of the standard until you can define it >in a way that will produce the same results on all C processors. The whole purpose of "volatile" is to provide a well-defined method to obtain strict virtual machine semantics, as opposed to highly- optimized "equivalent" semantics. This is obviously more nearly universal than almost anything else in the proposed Standard. How you as a programmer choose to exploit this is up to you.
mcdonald@uxe.cso.uiuc.edu (05/12/88)
>In article <2674@geac.UUCP> daveb@geac.UUCP (David Collier-Brown) asks: > [On the necessity of "volatile"] >> Specifically: >> 1) what architectures currently use asynchronously-changing >>memory locations for program notification of events? DEC Vax, >>various CDC boxes, MIPS(tm),... >The asking of this question is a clear example of the distorted view many >people have, whose environment is restricted to DEC, SUN, CDC, IBM etc >*standard* machines. This is not meant as a flame, only as an observation. >The answer to the question is "as many architectures as there are >proprietary designs of micro-processor based equipment which use >asynchronously changing memory locations, including *all*, for example, >which use memory mapped I/O. >As I said in an earlier posting (on reflection not very succinctly), a >compiler for a particular [micro-processor] CPU can make *no* assumptions >about the architecture that the compiled code is running on, other than [the >architecture of the microprocessor itself]. >Writing a compiler for a 68000 is a different exercise than writing one for >a VAX, which is a fully defined environment. Not really. It is perfectly possible to plug any device one wants to into a Q-bus or Unibus VAX. And ALL VAX devices use memory-mapped IO! It's true that you can't plug anything you want into a VAXBI VAX ( unless you use a converter): therefore we aren't buying any VAXBI VAXes. And Vms allows one to actually use those devices, albeit with a bit of trouble. >So it's not reasonable to make these controls [e.g. "volatile"] a compiler >specific thing. >They *have* to be part of the language. Agreed. Sorry to sound like a broken record, but this is IMPORTANT for us who do real-time work. It is important to realize that there is no good reason to REQUIRE that all a given ANSI-C program run the same on all machines. In fact it is vitally important that there be NO SUCH REQUIREMENT. The requirement for portability should (indeed MUST) stop somewhere, and "volatile" looks like the perfect place to stop. If it didn't stop at volatile, it most certainly would stop after a call to an assembler routine, which would be slow. Doug McDonald | VOLATILE must stay! This is non-negotiable.
terry@wsccs.UUCP (Every system needs one) (05/14/88)
In article <51431@sun.uucp>, limes@sun.uucp (Greg Limes) writes: > In article <502@wsccs.UUCP> terry@wsccs.UUCP (Every system needs one) writes: > [previous junk removed for brevity] > > Yes, but why is > > volitile int foo; > > better than > > int foo; /* VOLITILE*/ > > Except for the optimizer? > > The first not only documents for the user that "foo" may be changed > outside the expected flow of control of the program (i.e. by hardware > or by a signal handler), but making it a part of the language forces > optimiser designers to leave open this trapdoor. If you are implying that the compiler writer would have to write code to make valid assumptions before optimizing, my heart bleeds. > It also standardises > where they will put the trapdoor, if/when they realize that programmers > need to be able to break out of the dreaded > while (!signal_caught); > loops ... try running the previous line through the SunOS 4.0 compiler > at any optimisation level higher than about, say, -O1 ... I have grave reservations about the compiler you mentioned anyway. All I'm saying, again and again and again, is that the compiler writer, not the compiler user should bear the burden of matching the language to the architecture. I have agreed before that volitile is OK for DMA where the user prefers to use a buzz-loop instead of an interrupt. Say a message passing operating system. The whole direction of the argument has been changed, now, I hope. The question, bluntly, is: Why can't the compiler figure out what is volitile and THEN optimize without being hit over the head? Is this a matter of it being impossible, or are the compiler writers pushing the responsibilty onto the user without justification? If it can be done by the compiler with more work on the compiler-writer's part, is the increased time worth it, in light of the coding time it would save [hypothetically] for the compiler user? Who's time is more important? I vote pro user. The "better except for the optimzer" referred to the optimizer having to work harder, NOT to optimizations being impossible without "volitile". I don't believe in impossible; just very very hard and requiring intelligent thought. > Compiler technology is getting really good, and we need a way to tell > the optimisers that strange things can happen. If it's sooooo advanced, it could determine volatility (or aliasing) without me having to tell it. This is my entire point. If it isn't that advanced, it should either quit pretending or be fixed. | Terry Lambert UUCP: ...{ decvax, ihnp4 } ...utah-cs!century!terry | | @ Century Software OR: ...utah-cs!uplherc!sp7040!obie!wsccs!terry | | SLC, Utah | | These opinions are not my companies, but if you find them | | useful, send a $20.00 donation to Brisbane Australia... | | 'Admit it! You're just harrasing me because of the quote in my signature!' |
woerz@iaoobelix.UUCP (Dieter Woerz) (05/15/88)
In article <1011@ima.ISC.COM> johnl@ima.UUCP (John R. Levine) writes: > ... >or another. But whether "volatile" belongs in the standard is entirely another >thing. The standard defines a language for writing portable programs, i.e. >any standard-conforming program should do the same thing on all ANSI C >processors. I have never seen any suggestion that a program containing >"volatile" would be portable except perhaps to other processors which happen >to have similar memory, I/O, and hardware architectures. > ... I don't see, what "volatile" has todo with the hardware architecture. In my understanding, "volatile" simply says that the compiler has to access the variable in the memory every time the variable is access- ed. There seems to be nothing dependent on the hardware, exept that you can't get access to I/O ports on architectures, which don't have memory mapped I/O. So "volatile" has nothing todo with portability, because every compiler can be made to access a variable in memory every time it is read or writtten. I agree, that if you depend on "volatile" for doing memory mapped I/O, that wouldn't be too portable on an architecture with port I/O. But that low level I/O-Software normally doesn't have to be portable. ------------------------------------------------------------------------------ Dieter Woerz Fraunhofer Institut fuer Arbeitswirtschaft und Organisation Abt. 453 Holzgartenstrasse 17 D-7000 Stuttgart 1 W-Germany BITNET: iaoobel.uucp!woerz@unido.bitnet UUCP: ...{uunet!unido, pyramid}!iaoobel!woerz
peter@athena.mit.edu (Peter J Desnoyers) (05/16/88)
The debate over |volatile| seems to focus on one point: if this keyword is only necessary for non-portable code, why does it have to be part of the language? I think this point has been made before, but I will make it again. Non-portable language constructs (e.g. pragmas) are specific to a compiler. Memory-mapped IO and other such things which require volatile are specific to a target machine. Most software in this world is written on a system different from that on which it is run. (You know, the stuff that runs microwave ovens, telephone systems, and just about everything else.) Now, if I developed the code for my network box (for example) on the department VAX, using vendor A's compiler and their pragmas to massage the IO chips, and I want to switch to using workstations that may not be supported by vendor A, you would say tough. Since my target architecture is not portable, anything associated with it can't be portable, either. Only Unix applications should be portable, anyway. Whereas, if |volatile| is added to the language, then I can compile my code on any cross-compiler for my target machine, download it to my ICE, and go merrily on my way. Since the semantics of |volatile| will have to be implemented in any reasonable compiler, doesn't |volatile| seem to make much more sense? Peter Desnoyers peter@athena.mit.edu
karl@haddock.ISC.COM (Karl Heuer) (05/17/88)
In article <5367@bloom-beacon.MIT.EDU> peter@athena.mit.edu (Peter J Desnoyers) writes: >The debate over |volatile| seems to focus on one point: if this keyword is >only necessary for non-portable code, why does it have to be part of the >language? [Goes on to discuss cross-compilers] There's a simpler refutation: the premise is false. The signal() function is part of the standard library, and |volatile| is necessary for handlers of asynchronous signals. Earlier I posted a strictly conforming program which contained some lines like: #include <signal.h> static volatile sig_atomic_t gotcha = 0; static void catch(int signo) { gotcha = 1; } which argument has not been refuted to my knowledge. If |volatile| were removed, we'd either have to go back to the old rules (everything's volatile) or absorb its meaning into |sig_atomic_t|. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint Followups to comp.std.c.
chris@mimsy.UUCP (Chris Torek) (05/17/88)
In article <526@wsccs.UUCP> terry@wsccs.UUCP (Every system needs one) writes: >Why can't the compiler figure out what is volitile and >THEN optimize without being hit over the head? In many cases it can. This is one of several reasonably good arguments against volatile. One pro-volatile (or #pragma) counterargument is that it helps the reader to realise that there is something unusual going on here too---not that this cannot be done with a comment. >>Compiler technology is getting really good, and we need a way to tell >>the optimisers that strange things can happen. >If it's sooooo advanced, it could determine volatility (or aliasing) >without me having to tell it. Could and should: I agree. But (as dmr put it) the nice thing about `volatile' is that the average programmer can ignore it; this was not true of `noalias'. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
mahar@weitek.UUCP (Mike Mahar) (05/18/88)
Consider the following C program. Remember this is All the information you get. The actual addresses of variables are unknown. Any other modules are compiled separately. What you see is all you get. char fifo; buf_fill(buffer,cnt) char *buffer; int cnt; { int i; for( i = 0; i < cnt; i++) { buffer++ = fifo; } } Question: Is it safe to compile the preceding routine like this: _buf_fill: movl (a7),d1 #get count movl 4(a7),a1 #getbuffer address clrl d0 #clear i movb _fifo,d2 #pre-load fifo L1: movb d2,(a1)+ #store into buffer addq #1,d0 cmpl d1,d0 jlt L1 rts If the variable "fifo" was physicaly mapped to a hardware FIFO, this optimization will give unexpected results. Given all the information above, the compiler must assume that the variable "fifo" is volatile and the code given is incorrect. Without some form of volatile declaration, the compiler must assume that all global variables are volatile. This disables a whole class of optimizations. The information is just not available. If there is a class of optimiziations that are impossible, it makes the compiler writters job a lot easier. The question, "Can I do this"? is always answered "Nope, Gee that was easy". -- Mike Mahar UUCP: {turtlevax, cae780}!weitek!mahar
levy@ttrdc.UUCP (Daniel R. Levy) (05/18/88)
In article <11531@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes: # In article <526@wsccs.UUCP> terry@wsccs.UUCP (Every system needs one) writes: # >Why can't the compiler figure out what is volitile and # >THEN optimize without being hit over the head? # >If it's sooooo advanced, it could determine volatility (or aliasing) # >without me having to tell it. # # Could and should: I agree. But (as dmr put it) the nice thing about ^^^^^ Hey, I thought this was chewed over some time back. This isn't always possible. Incremental compilation is one problem (can that global change over this operation or can't it? I dunno, I can't see the other modules, so I'll play it safe). (Hmmm, what about smart linkers which could make the final judgment call on an optimization?) How about variables which map into references to special hardware -- the compiler doesn't know if optimizing access to them is safe or not. How about shared memory? -- |------------Dan Levy------------| Path: ihnp4,<most AT&T machines>!ttrdc!levy | AT&T | Weinberg's Principle: An expert is a | Data Systems Group | person who avoids the small errors while |--------Skokie, Illinois--------| sweeping on to the grand fallacy.
chris@mimsy.UUCP (Chris Torek) (05/18/88)
In article <390@attila.weitek.UUCP> mahar@weitek.UUCP (Mike Mahar) writes: >Consider the following C program. Remember this is All the information you >get. The actual addresses of variables are unknown. Any other modules >are compiled separately. What you see is all you get. You have made what can be called an unreasonable restriction. Why is the compiler not able to see the other modules? The only possible answers are `they do not exist' or `the compiler is not smart enough'. If they do not exist, there is no problem; if they are later created, the compiler is expected to be smart enough to see them then. (All this requires is that you defer code generation until after the link step.) [compressed, and with the bug fixed:] >char fifo; >buf_fill(char *buffer, int cnt) { > int i; > for (i = 0; i < cnt; i++) *buffer++ = fifo; >} If I as a reader saw this, I might be tempted to rewrite it thus: register int i = cnt, fillbyte = fifo; while (--i >= 0) *buffer++ = fillbyte; The compiler may be able to tell whether `fifo' is volatile, but given the lack of such a declaration or a comment or `#pragma' or any other clue, *I* may not, so please use something like volatile char fifo; or char fifo; /* DANGER : i/o space : volatile : DANGER */ or char fifo; #pragma volatile fifo; -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
mouse@mcgill-vision.UUCP (der Mouse) (05/18/88)
In article <526@wsccs.UUCP>, terry@wsccs.UUCP (Every system needs one) writes: > All I'm saying, again and again and again, is that the compiler > writer, not the compiler user should bear the burden of matching the > language to the architecture. I have agreed before that volitile is > OK for DMA where the user prefers to use a buzz-loop instead of an > interrupt. Say a message passing operating system. Or for emptying a serial line card silo into the user's buffer....what happens to for (count=sludevice->wcr;count>0;count--) { *bufptr++ = sludevice->recvbuf; } when the optimizer rewrites it to temp = sludevice->recvbuf; for (count=sludevice->wcr;count>0;count--) { *bufptr++ = temp; } Or are you intending to prohibit such an optimization altogether? Then sorry, you'll be left in the dust. The compiler simply doesn't have enough information to guess whether sludevice points to a memory-mapped device or not! > I don't believe in impossible; just very very hard and requiring > intelligent thought. Ah, you expect the compiler to notice that the file it's compiling happens to look something like a device driver, this struct pointer variable name ends in "device", therefore it should be treated as volatile? Good luck trying to convince your compiler vendor. >> Compiler technology is getting really good, and we need a way to >> tell the optimisers that strange things can happen. > If it's sooooo advanced, it could determine volatility (or aliasing) > without me having to tell it. This is my entire point. If it isn't > that advanced, it should either quit pretending or be fixed. There's an intermediate stage. Compiler technology is good enough to do some serious optimizations if it's told what's volatile. It is not good enough to figure out what's volatile and what isn't (I don't expect it to be, either, not before we have serious (Turing-capable) artifical intelligence). Since joe programmer writing matrix inversion routines (say) can completely ignore volatile and get all the optimizations, we put up with making the few who write code that uses signals or memory-mapped devices or shared memory or other things that more-or-less require volatile use volatile. der Mouse uucp: mouse@mcgill-vision.uucp arpa: mouse@larry.mcrcim.mcgill.edu
smryan@garth.UUCP (Steven Ryan) (05/18/88)
A compiler cannot crossreference all modules to detect "volatile": - this is enormously expensive. - it requires access to internals of other modules including possible binary-only copyrighted libraries, et cetera. - it still won't work. Besides the problem the memorymapped i/o, which can vary from configuration to configuration even on the same processor, many operating systems set up communication areas in user space which can be asynchronously modified. For example, in CDC NOS, the CIO PP can modify fet fields and the buffer while the user program is running in the same address space. IAF can set an interrupt flag. RPV can be used to call an arbritrary routine during a fault or interrupt. A programmer has the options: - write a good machine independent algorithm and not attempt linear speedup with an optimiser. - handoptimise it for a specific machine or specific configuration. - give the compiler as much information as possible, in a machine independent fashion, so that it can optimise safely and effectively for a specific processor. Optimisers face many internal predicates and usually they just cannot decide. As an example of a different optimisation problem, INTEGER A(N),B(N),X(N) DO 1 I=1,N 1 A(X(I)) = B(N) is vectorisable on the CYBER FORTRAN 200 as is DO 2 I=1,N 2 B(I) = A(X(I)) but DO 2 I=1,N 2 A(X(I)) = A(X(I)) is not vectoriable. If X is not injection (Xi=Xj, i/=j), the last loop has feedback. The optimiser does not generally know if X is injective, so that it assumes feedback in all cases. In summary, do not expect an optimiser to magically guess all relations of a program. It has worse time understanding a program than the authour, and we all know how hard it is to completely understand one of our own middling programs. Hafa an godne daege. sm ryan
gwyn@brl-smoke.ARPA (Doug Gwyn ) (05/18/88)
In article <390@attila.weitek.UUCP> mahar@attila.UUCP (Mike Mahar) writes: >char fifo; >buf_fill(buffer,cnt) > char *buffer; > int cnt; > { > int i; > for( i = 0; i < cnt; i++) > *buffer++ = fifo; ^ MISSING * ADDED > } >Question: Is it safe to compile the preceding routine like this: > movb _fifo,d2 #pre-load fifo >L1: In this particular case it is safe to pre-load `fifo' before the loop. (It would be disallowed if `fifo' were declared "volatile".) However, if the code were changed to char fifo[3]; buf_fill(buffer,cnt) char *buffer; int cnt; { int i; for( i = 0; i < cnt; i++) *buffer++ = fifo[1]; } Then it would NOT be permitted to pre-load fifo[0], because the function could be invoked as buf_fill(fifo,3); and the generated code must handle the aliasing correctly.
bill@proxftl.UUCP (T. William Wells) (05/18/88)
In article <5367@bloom-beacon.MIT.EDU>, peter@athena.mit.edu (Peter J Desnoyers) writes: > The debate over |volatile| seems to focus on one point: if this > keyword is only necessary for non-portable code, why does it have to > be part of the language? Volatile is useful in portable code. Here is the skeleton of one way to use it. #include <signal.h> volatile sig_atomic_t Sighappened; /* Must be THIS type. */ void sighandler( int signum ) { /* Ignore SIGINT until the previous one has been processed. Unfortunately, between the time of the signal and this call a SIGINT may cause the default action to be taken (this is implementation dependent). Grrrrr. */ signal(SIGINT, SIG_IGN); Sighappened = 1; } main() { signal(SIGINT, sighandler); while (1) { if (Sighappened) { ... handle the signal /* Permit SIGINT to be recognized again. */ Sighappened = 0; signal(SIGINT, sighandler); continue; } ... do something else } } (Pardon if this code has trivial flaws; I do not have easy access to an ANSI-ish compiler.)
chris@mimsy.UUCP (Chris Torek) (05/19/88)
In article <659@garth.UUCP> smryan@garth.UUCP (Steven Ryan) writes: >A compiler cannot crossreference all modules to detect "volatile": > - this is enormously expensive. > - it requires access to internals of other modules including > possible binary-only copyrighted libraries, et cetera. > - it still won't work. I grant your second point, but claim that `binary-only' is a red herring. There is no reason the `binary' library cannot be a binary that includes volatility and aliasing information. As to your first point, interactive displays---particularly windows on a graphical bitmapped screen---are also `enormously expensive'. What is slow now is fast tomorrow, and though it be slow, we have decided that it is worth it. The third point is the key issue. As with automatic alias detection, there are some situations in which volatility may be undecidable. Consider, for instance, char *ptr; /* volatile? */ ... if (cond) remap_memory(ptr, sharedspace) If `cond' reduces to the halting problem, the compiler cannot tell whether a region described by `ptr' is remapped into a shared space. As with `noalias', the solution is that the compiler be conservative. Undecidable cases are (I claim) not only extremely rare, but also bad programming practise. `volatile' and `noalias' are very similar in this respect. Both can be determined automatically in most cases in which they would in fact make any difference. Both are relatively expensive to compute. I claim that both can and should be computed whenever it is feasible. Given their existence in some language, it is not critical that the result of the computation always be correct, but it is helpful for the compiler[*] to diagnose missing or incorrect `volatile' and `noalias' declarations. Given their absence in the same language, it is then essential that the result of the computation be correct or (if `too hard') conservative. [*or a diagnostic tool a la `lint'] The critical difference between `volatile' and `noalias' in C is that an incorrect `volatile' appelation merely inhibits some optimisation, while an inappropriate `noalias' causes improper operation. This is why I do not object to a keyword for `volatile', but do object to one for `noalias'. Neither is necessary, but only the latter can cause grievous harm. >For example, in CDC NOS, the CIO PP can modify fet fields and the buffer >while the user program is running in the same address space. IAF can set >an interrupt flag. RPV can be used to call an arbritrary routine during a >fault or interrupt. So? For any of these to occur, the CIO PP must be told where the objects reside. The telling points out the volatility. It takes a great deal of cleverness, but a truly smart flow analysis routine will note that dmac->dma_addr = buf; dmac->dma_wc = count; dmac->dma_csr = DMA_RD | DMA_GO; causes `count' words at the location given by `buf' to become uncertain until the interrupt occurs. It would be nice if, even though someone forgot to say so, the compiler would complain about the following line: if (buf[2 * count - 1] == '\n') since it may be (and probably is) an error to check the location after the DMA channel has been started. Perhaps the programmer knows that the DMA device cannot have overwritten this location by the time the test is performed. More likely it is an error. >In summary, do not expect an optimiser to magically guess all relations of >a program. It has worse time understanding a program than the authour ... I will grant this point if and only if you will grant that sometimes the compiler is better at finding the relationships in a program[*], and that with more work, the compiler will improve. [*If you disbelieve, take a look at the output from some good compilers. Even a weak optimiser like 4BSD /lib/c2 has noticed things I have not. I once spent a number of minutes puzzling over a `bug' where c2 had replaced a constant with a register name, only to realise that all code paths leading to that instruction happened to leave that value in that register.] There are pragmatic reasons not to find all cases of volatility (it may take too long on a given machine), but it can be done. When it can be done, it should be done. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
bill@proxftl.UUCP (T. William Wells) (05/20/88)
In article <526@wsccs.UUCP>, terry@wsccs.UUCP (Every system needs one) writes: > The whole direction of the argument has been changed, now, I hope. The > question, bluntly, is: > > Why can't the compiler figure out what is volitile and > THEN optimize without being hit over the head? Is this > a matter of it being impossible, ... Yes. Let me tell you why volatile is desirable. After that, please can we spend net bandwidth on something more interesting? There are two things that a user of a C compiler might like to have happen in a given piece of code. 1) If the compiler detects that a memory access is not necessary, get rid of it. This makes the program smaller and faster. 2) Every time that a memory access is implied by the code, a memory access must be made. This is necessary for several kinds of things: memory mapped I/O, shared memory, and signals. If we decided that C was not going to support hardware specific programs (and wouldn't that cause screams!) two of the three reasons for alternative two go away, BUT THE THIRD DOES NOT. Now, since you asked, why can't the compiler determine that a variable is going to be used for a signal? There are two reasons, one practical, and one theoretical: The practical reason is separately compiled modules. What do you do if the variable is used in one file and the signal handler in another? The second is theoretical: even if you had all the source code in one module, you would still have problems determining which variables are actually modifiable by a signal handler. This is because any question of the form: "does my program access some variable while executing a particular part of the program" is equivalent to a halting problem; the only general way to solve such problems is to run the program and find out. Now, given the problem, what should the solution be? The solution is one of: 1) Allow memory access optimizations. 2) Forbid memory access optimizations. 3) Tell the compiler when in can or can't do memory access optimizations. The first makes signal handlers (and the other hardware things) unreasonably difficult. The second makes the code VERY slow. That leaves the third. AND THAT IS WHY VOLATILE EXISTS. Got it?
scc@cl.cam.ac.uk (Stephen Crawley) (05/20/88)
In article <166@iaoobelix.UUCP> woerz@iaoobelix.UUCP (Dieter Woerz) writes: >In article <1011@ima.ISC.COM> johnl@ima.UUCP (John R. Levine) writes: >> .... I have never seen any suggestion that a program containing >>"volatile" would be portable except perhaps to other processors which happen >>to have similar memory, I/O, and hardware architectures. >> ... > >I don't see, what "volatile" has todo with the hardware architecture. >In my understanding, "volatile" simply says that the compiler has to >access the variable in the memory every time the variable is access- >ed. There seems to be nothing dependent on the hardware, exept that >you can't get access to I/O ports on architectures, which don't have >memory mapped I/O. So "volatile" has nothing todo with portability, >because every compiler can be made to access a variable in memory >every time it is read or writtten. > The "volatile" construct is also necessary to write portable multi-process applications that use shared memory. Consider two processes A and B with a shared variable v, and the following code int x = v /* A1 */ | v = 2 /* B1 */ int y = v /* A2 */ | Assume that v starts with the value 1, and that the statements are actually executed in the order A1,B1,A2. If the C compiler generates a memory fetch for each access to v, A's variables x and y will contain 1 and 2 respectively. If the compiler does not generate a memory fetch for each access, x and y will both contain 1. To get behaviour that is the same for all compilers, the variable v must be declared volatile. [It might be argued that if processes A and B ought were using some synchronisation mechanism (e.g. a Unix semaphore call after A1) and that an optimizing compiler would notice this and realise that statement A2 needed to have a fetch. I claim that such an argument is invalid. In some tightly coupled multiprocessor systems, shared memory cells are the basis for ALL synchronisation. If the compiler won't let me declare volatile variables, I can't implement synchronisation primitives. Even in a uniprocessor UNIX system, there are situations where it is better to use a shared variable for synchronising 2 processes rather than frittering away a couple of milli-seconds on semaphore system calls] -- Steve
mmengel@cuuxb.ATT.COM (~XT4103000~Marc Mengel~C25~G25~6184~) (05/20/88)
In article <11566@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
$So? For any of these to occur, the CIO PP must be told where the
$objects reside. The telling points out the volatility. It takes a
$great deal of cleverness, but a truly smart flow analysis routine will
$note that
$ [code initializing DMA deleted]
$causes `count' words at the location given by `buf' to become uncertain
$until the interrupt occurs. It would be nice if, even though someone
$forgot to say so, the compiler would complain about the following line:
$ if (buf[2 * count - 1] == '\n')
$...
Hmm.. I would agree with you here only if C had a COBOL style statement
declaring what machine a program is being compiled for. Your suggestion
would fail miserably if I compiled a program for Brand X 68020 Box on
a Brand Y 68020 Box. It would also have to know if the program was
being compiled to run on the bare machine, or under operating system Z,
etc.
Certainly you can postulate a machine description file detailed enough
to allow this sort of analysis, but that would *not* be C, you will
instead have built a new language C-plus-machine-description.
$There are pragmatic reasons not to find all cases of volatility (it may
$take too long on a given machine), but it can be done. When it can be
$done, it should be done.
Certainly agreed; however if a compiler claims to know that memory
location 0x1234567 is a hardware register that *must* be volatile,
it will be wrong in cross-system-same-cpu development environments,
when creating new operating systems, etc. The compiler cannot know
enough about the environment it is compiling into to detect volatitlity
because the environment may have been created after the compiler, and
may even be under development.
$--
$In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
$Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
--
Marc Mengel
attmail!mmengel
...!{moss|lll-crg|mtune|ihnp4}!cuuxb!mmengel
smryan@garth.UUCP (05/21/88)
Enormously expensive refers quadratic, cubic, quadric, or higher order time. Best possible optimisation uses exponential time. (There are few transistive closures involved, graph colourring, et cetera.) What is involved is spending a few days to compile a large system. Some of us have customers who notice if compilation times increases by a millsecond. Also, please do not assume the Church-Turing Hypothesis. The point being, do not expect magic from a compiler. It can provide at best linear improvement. And like everything else in life, the more you put into the more you get out. The less you put in, the less you get out. It's up to you decide if the effort is worthwhile.
henry@utzoo.uucp (Henry Spencer) (05/23/88)
> int x = v /* A1 */ | v = 2 /* B1 */ > int y = v /* A2 */ | > > If the C compiler generates a memory fetch for each access to v, A's > variables x and y will contain 1 and 2 respectively. If the compiler > does not generate a memory fetch for each access, x and y will both > contain 1. To get behaviour that is the same for all compilers, the > variable v must be declared volatile. Unfortunately, this is not sufficient. "Volatile" does not guarantee that operations are atomic. It is entirely possible for x and/or y to contain trash because they caught the variable midway through the assignment. (Not likely to be a problem with the values 1 and 2, but 123534234 and 878787970 are a different matter.) "Volatile" makes *no* promises about how things will look in parallel processes; C is defined as a sequential language. (Please don't bring up signal handlers as an exception unless you have read the very careful weasel-wording about them in recent X3J11 drafts.) With very careful cooperation from compiler and hardware, "volatile" may permit such things to be done safely. But that is an implementation- dependent property, not a requirement of the language that all compilers must satisfy. -- NASA is to spaceflight as | Henry Spencer @ U of Toronto Zoology the Post Office is to mail. | {ihnp4,decvax,uunet!mnetor}!utzoo!henry
rpw3@amdcad.AMD.COM (Rob Warnock) (05/27/88)
+--------------- | > int x = v /* A1 */ | v = 2 /* B1 */ | > int y = v /* A2 */ | | > If the C compiler generates a memory fetch for each access to v, A's | > variables x and y will contain 1 and 2 respectively... | > ...variable v must be declared volatile. | Unfortunately, this is not sufficient. "Volatile" does not guarantee | that operations are atomic. It is entirely possible for x and/or y to | contain trash because they caught the variable midway through the | assignment. (Not likely to be a problem with the values 1 and 2, but | 123534234 and 878787970 are a different matter.) +--------------- Note that 12345678 is likely to be a problem only on machines for which the memory bus is not wide enough to represent 12345678 in a single "write" cycle. There is a perfectly respectable mutual exclusion technique which can be used on multi-processor machines, which requires no special hardware, and requires only that writes of a small integer are atomic. (The "small integer" has to be able to hold a processor number.) In the two-processor case, this is called "Dekker's Algorithm". (For large numbers of processors, it is called "expensive"! ;-} ;-} ) The "volatile" type is necessary and sufficient for correctly implementing Dekker's Algorithm in "optimizing C". Rob Warnock Systems Architecture Consultant UUCP: {amdcad,fortune,sun,attmail}!redwood!rpw3 ATTmail: !rpw3 DDD: (415)572-2607 USPS: 627 26th Ave, San Mateo, CA 94403
bill@proxftl.UUCP (T. William Wells) (05/28/88)
In article <11566@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes: > There are pragmatic reasons not to find all cases of volatility (it may > take too long on a given machine), but it can be done. When it can be > done, it should be done. > -- > In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) > Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris The reasons are NOT pragmatic, they are theoretical. It is NOT possible to find all cases of volatility in a program. It is not even reasonable to try to find most cases of volatility in a program. Let me show you why. Consider a device register (Dr). Consider a pointer variable (dp) which is of type `pointer to type of' the device register. We need to know whether the pointer variable ever points to volatile memory. Take your program. Replace every exit() call with while (1);. Wherever dp is assigned to, add the statement: if (dp == &Dr) exit(0); just after the assignment. Make any kludges necessary to cope with embedded assignments. Your fudged program will halt if and only if dp can be a pointer to Dr, thus requiring that dp is a pointer to something volatile. Thus the problem of determining whether dp is a pointer to volatile is the same problem as determining whether your fudged program halts. Now, in this contrived example, it is easy to see how one might, by exmaining references to variables in the source, determine that dp is a pointer to something volatile. (Assuming that the program does no memory allocation, anyway.) That is an example of how the halting problem has easy solutions in certain cases. Let us consider an unsolvable problem: a program which maps memory from a file. Is the mapped data volatile? It is only if the file can be modified. This information is not available to the compiler. You, the program writer may know: "the data in this file will be computed by a cooperating process, it is volatile" or "this is a file of data which has been pre-computed and will not be changed". Your program might be designed to handle both cases. You, the program writer may know: "this pointer variable is only used when the mapped file is volatile" and "this pointer variable is only used when the mapped file does not change". The compiler CAN'T determine which is which, because the compiler must assume that the mapped file is either one or the other. Let's consider a related problem. Suppose that your volatile object (or a pointer to it) is going to be in dynamically allocated memory. NO algorithm is going to be able to determine whether the object in question will even exist, much less that it is volatile. If you answer that that is bad programming practice, well perhaps you have forgotten something: C is used to program systems. Most of these systems manage resources in some way or another. Parts of these managed resources will often be volatile, but, since it is the management system itself which determines what is volatile, there is no way that one can avoid writing code that requires determination of volatility. Since the compiler can't do it, you, the programmer, must tell it. As an example of this, consider one possible way to implement a scheduler. First, you have a low level scheduler which uses fixed priorities to determine the next task to run. Second, you have a high level scheduler which periodically analyzes the processor activity and adjusts the priorities accordingly. (Hmmm, this sounds familiar. UNIX perhaps? :-) Now, there may be nothing in the low level scheduler which requires that the priority be considered volatile. Or you could say that the existence of (/dev/kmem on UNIX) global accessing methods to the data for the low level scheduler requires that every variable in it be volatile. You can't insist that the compiler have access to the high level scheduler code, because that is a separately compiled unit; there might even be more than one of them. Or, you can assume that all variables in the low level scheduler are not volatile unless specifically declared so. Understamd: volatile is intended for programmers who write programs that deal with multiprogramming or multiprocessing in some way. Said programmer, in the normal course of events, writes programs where the volatility of an object is an important aspect of the object. And the programs which he writes can't be analyzed by any algorithm to determine the volatility of the objects. The volatile keyword is there as an aid to the programmer who is stuck with this problem. Demanding that the compiler writers `do something' will not accomplish anything. Creating the volatile keyword will.
chris@mimsy.UUCP (Chris Torek) (05/29/88)
>In article <11566@mimsy.UUCP> I wrote: >>There are pragmatic reasons not to find all cases of [*] volatility (it >>may take too long on a given machine), but it can be done. When it can >>be done, it should be done. [* I left a word out here: `potential'. I did not leave it out of earlier discussion.] In article <227@proxftl.UUCP> bill@proxftl.UUCP (T. William Wells) writes: >The reasons are NOT pragmatic, they are theoretical. It is NOT >possible to find all cases of volatility in a program. I know that; and you deleted the part of my article that mentioned it. There are indeed cases in which volatility cannot be determined (as there are cases where aliasing cannot be determined). In such cases, the compiler can simply be conservative and assume that the variable may be volatile or aliased. In the limit, the compiler may simply assume that all variables are volatile and aliased; it will then act much like the C compilers we have been using on Unix systems. It may produce suboptimal code, but it will produce correct code, and it does not need a `volatile' keyword. This does not mean that `volatile' is bad. It merely means that I feel that a compiler that, given something like the following, prints a warning that `rkaddr' should be declared `volatile' (since the proposed C has the keyword) is superiour to one that does not. struct rkdevice *rkaddr = (struct rkdevice *)0777440; (Clearly this requires knowledge of the machine internals, and may have to be enabled by switches, e.g. It remains useful.) -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
johnl@ima.ISC.COM (John R. Levine) (05/29/88)
In article <21821@amdcad.AMD.COM> rpw3@amdcad.UUCP (Rob Warnock) writes: >There is a perfectly respectable mutual exclusion technique which can be >used on multi-processor machines, which requires no special hardware, and >requires only that writes of a small integer are atomic. (The "small integer" >has to be able to hold a processor number.) In the two-processor case, this >is called "Dekker's Algorithm". (For large numbers of processors, it is >called "expensive"! ;-} ;-} ) > >The "volatile" type is necessary and sufficient for correctly implementing >Dekker's Algorithm in "optimizing C". Not so fast, there. In large multi-CPU systems, there is often a write-behind cache and writes by one processor are not always visible to other processors until you do some sort of cache-flush operation. (For example, on 370 architecture machines the cache is only flushed on process switch, start I/O, and a very slow flush no-op.) I can imagine that you might follow each write to a volatile variable with a flush, but that would cause a huge and unnecessary performance penalty in the common case that volatile is used to share data among asynchronous events on a single processor, such as routines called via signal(), so compiler writers would justifiably be reluctant to put flushes all over their object code. Then you get compiler switches to explain to the compiler what you meant by "volatile" or #pragma lines to say this is a single-cpu volatile and that is a multi-cpu volatile. Perhaps this suggests that it's premature to standardize the semantics of volatile, or even to include it in the standard language, but that is beating a dead and increasingly smelly horse. -- John R. Levine, IECC, PO Box 349, Cambridge MA 02238-0349, +1 617 492 3869 { ihnp4 | decvax | cbosgd | harvard | yale }!ima!johnl, Levine@YALE.something Rome fell, Babylon fell, Scarsdale will have its turn. -G. B. Shaw
ray@micomvax.UUCP (Ray Dunn) (05/30/88)
In article <1988May23.003847.1114@utzoo.uucp> (Henry Spencer) writes: [quoting an article without attributing it] >> .... To get behaviour that is the same for all compilers, the >> variable v must be declared volatile. > >Unfortunately, this is not sufficient. "Volatile" does not guarantee >that operations are atomic. It is entirely possible for x and/or y to >contain trash because they caught the variable midway through the >assignment. Fortunately this is sufficient when the programmer understands what he is programming, and chooses data types etc which will ensure atomicity, if that is what he is trying to achieve. Even in assembler, problems will result if a non-atomic instruction is used in a semaphore mechanism. All "volatile" gives in this shared memory context is a basic low level tool that a careful programmer can use to sufficiently handle this class of problems. All the compiler need be aware of is that a reference to a volatile variable must always generate a memory reference. -- 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
scc@cl.cam.ac.uk (Stephen Crawley) (05/30/88)
In article <21821@amdcad.AMD.COM> rpw3@amdcad.UUCP (Rob Warnock) writes: >There is a perfectly respectable mutual exclusion technique which can be >used on multi-processor machines, which requires no special hardware, and >requires only that writes of a small integer are atomic. (The "small integer" >has to be able to hold a processor number.) In the two-processor case, this >is called "Dekker's Algorithm". (For large numbers of processors, it is >called "expensive"! ;-} ;-} ) > >Rob Warnock >Systems Architecture Consultant A more recent solution to the mutual exclusion is NOT expensive for multiple processors: "A Fast Mutual Exclusion Algortithm" Leslie Lamport, Nov 14 1985. Report #5, DEC Systems Research Centre The review at the beginning of the report (by Butler Lampson) says it better than I can: " To build a useful computer system from a collection of processors that communicate by sharing memory, but lack any atomic operation more complex than a memory read or write, it is necessary to implement mutual exclusion by using only these operations. Solutions to this problem have been known for twenty years, but they are linear in the number of processors. Lamport presents a new algorithm which takes constant time (five writes and two reads) in the absence of contention, which is the normal case. To achieve this performance it sacrifices fairness [*], which is probably unimportant in practical applications. The paper gives an informal argument that the algorithm's performance in the absence of contention is optimal [!!], and a fairly formal proof of safety and freedom from deadlock, using a slightly modified Owicki-Gries method. The proofs are extremely clear, and use very little notation." [All typo's in the above are mine!] [* The body of the report notes that there is a variation of the algorithm that is starvation free, at the cost of one extra memory reference in the absence of contention.] In case you want to rush out and buy a copy, the address for DEC SRC is given as Digital Systems Research Center 130 Lytton Avenue Palo Alto, California 94301 -- Steve
dsill@nswc-oas.arpa (Dave Sill) (05/31/88)
From: Steven Ryan <smryan@garth.uucp> >Enormously expensive refers quadratic, cubic, quadric, or higher order time. >Best possible optimisation uses exponential time. (There are few transitive >closures involved, graph colourring, et cetera.) > >What is involved is spending a few days to compile a large system. > >Some of us have customers who notice if compilation times increases by a >millisecond. What takes hours today will probably only take minutes tomorrow. Anyway, compilation time is secondary to run-time once development is complete. >Also, please do not assume the Church-Turing Hypothesis. Huh? I think it's quite safe to assume the Church-Turing *Thesis*. And what does it have to do with optimization, anyway? >The point being, do not expect magic from a compiler. It can provide at best >linear improvement. Since when? (And what's wrong with linear improvement?) What about a compiler that recognizes certain polynomial algorithms that can be done linearly? Or a compiler that recognizes the Sieve of Eratosthenes in its standard benchmark form and simply outputs the correct count? Or a compiler that checks to see if a program has no input (the output is the same every time it's run), determines what it does by running it once, and produces code to simply generate the correct output? Sure, compile times could be humongous when optimization is enabled, but run times would be next to nil. ========= The opinions expressed above are mine. "We must remove the TV-induced stupor that lies like a fog across the land." -- Ted Nelson
brooks@lll-crg.llnl.gov (Eugene D. Brooks III) (06/01/88)
In article <1036@ima.ISC.COM> johnl@ima.UUCP (John R. Levine) writes: >Not so fast, there. In large multi-CPU systems, there is often a write-behind >cache and writes by one processor are not always visible to other processors >until you do some sort of cache-flush operation. (For example, on 370 >architecture machines the cache is only flushed on process switch, start I/O, >and a very slow flush no-op.) Bus based multiprocessors which do not have automatic cache coherency taken care of in the hardware are SICK as far as tightly coupled shared memory multiprocessing is concerned. If there is no automatic maintenance of cache coherence one has to have to kinds of loads and two kinds of stores to handle options of treatment of cached data. Multiprocessors without these needed features don't get used for tightly coupled multiprocessing, they are used in throughput mode. I stand by the need for volatile, I need volatile for several programs I run on shared memory multiprocessors if an "optimizing" compiler is to be used. This is not "educated guesswork" or "gut feelings" on my part. I have been programming shared memory multiprocessors for a half decade now and have been hosed over by optimizing compilers many times. Volatile is not a dead horse, expect several manufactures who deliver shared memory multiprocessors to have volatile with teeth in the future. It is fundamentally important!
brooks@lll-crg.llnl.gov (Eugene D. Brooks III) (06/02/88)
In article <206@gannet.cl.cam.ac.uk> scc@cl.cam.ac.uk (Stephen Crawley) writes: >A more recent solution to the mutual exclusion is NOT expensive for multiple >processors: > > "A Fast Mutual Exclusion Algortithm" > Leslie Lamport, Nov 14 1985. > Report #5, DEC Systems Research Centre > I have coded Lamports algorithm on the Sequent Balance and it actually beat the performance of their ALM hardware that lives on the Multibus. Lamports algorithm is a good practical algorithm for machines that done have test_and_set support which runs at the same speed as main memory. If a collision for the lock occurs the algorithm is order Nprocessors in time, which is why you do want test and set hardware support. Lamport's paper is very recommended reading for those interested in shared memory multiprocessing.
peter@athena.mit.edu (Peter J Desnoyers) (06/02/88)
In article <11709@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: > >In the limit, the compiler may simply assume that all variables are >volatile and aliased; it will then act much like the C compilers we >have been using on Unix systems. It may produce suboptimal code, >but it will produce correct code, and it does not need a `volatile' >keyword. C compilers on most Unix systems do not assume that all variables are volatile, and they certainly do not assume that all memory is volatile. In the first case, the program results would be at best completely undefined, and in the second case code generation would be impossible, as there would be no guarantee that the processor would ever get to see it before it was modified. > >This does not mean that `volatile' is bad. It merely means that >I feel that a compiler that, given something like the following, >prints a warning that `rkaddr' should be declared `volatile' (since >the proposed C has the keyword) is superiour to one that does not. > > struct rkdevice *rkaddr = (struct rkdevice *)0777440; > >(Clearly this requires knowledge of the machine internals, and >may have to be enabled by switches, e.g. It remains useful.) >-- >In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) >Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris Most compilers know nothing of a machine's internals. They run on a vax or some other machine, and generate code for a microprocessor. Not a system, a microprocessor. Which physical memory locations are going to be volatile may not be known when the program is written, and certainly not when the compiler is written. Besides, the proprietary design of a board is no business of the compiler company, anyway. The situation is similar for MSDOS machines. The compiler does not know whether a piece of code will be executed with EMS, EEMS, company X's I/O board in slot Y, or all of the above. The programmer does. These and a thousand other factors determine what areas of the memory map are volatile. Others may be used to communicate with TSR's, and will never be known to the compiler writers no matter how hard they look. /* flame on */ Don't be a Unix chauvinist. If everyone wrote Unix applications on Unix machines, nothing would get done. Remember that somewhere down the line, someone is using those machines to develope code that gets burned into a ROM, put in a box, and sold to a customer. /* flame off */ Peter Desnoyers peter@athena.mit.edu
chris@mimsy.UUCP (Chris Torek) (06/02/88)
>In article <11709@mimsy.UUCP> I claimed that >>In the limit, the compiler may simply assume that all variables are >>volatile and aliased; it will then act much like the C compilers we >>have been using on Unix systems. It may produce suboptimal code, >>but it will produce correct code, and it does not need a `volatile' >>keyword. This last point---that C without `volatile' has been used for systems work---seems to have been ignored by those who claim that volatile is necessary. (Note that I claim only that it is unnecessary, not that it is bad.) In article <5590@bloom-beacon.MIT.EDU> peter@athena.mit.edu (Peter J Desnoyers) writes: >C compilers on most Unix systems do not assume that all variables are >volatile, True enough: register variables are not considered volatile. >and they certainly do not assume that all memory is volatile. Let us try an experiment: f() { register int i, *ip; i = *ip; i = *ip; } produces movl (r10),r11 # i = *ip movl (r10),r11 # i = *ip That sure looks like the code I would expect from volatile int *ip; and not like the code I might expect without it (since `i' is unused, I would be unsurprised to find the body of f() elided altogether). (In fact, unless the `-i' flag is used, the 4BSD optimiser /lib/c2 can make some changes to memory references that can cause trouble, so this is somewhat weak.) >In the first case, the program results would be at best completely >undefined, and in the second case code generation would be impossible, >as there would be no guarantee that the processor would ever get to >see it before it was modified. This does not follow. The virtual machine semantics provided in the absence of volatile declarations are still there in the presence of such declarations; volatile simply adds a constraint, or a set of constraints. The precise constraints added are machine-dependent, but the virtual machine semantics do not magically vanish. >>[fancy automatic volatility detection] requires knowledge of the >>machine internals .... >Most compilers know nothing of a machine's internals. And this is considered a virtue. I say not. >... Which physical memory locations are going to be volatile may not >be known when the program is written, Were that the case, not even the programmer would know where to insert `volatile' declarations. >and certainly not when the compiler is written. Quite probably not. Hence it should be runtime configurable. (This is a `quality of implementation' issue, given the existing draft standard.) >The situation is similar for MSDOS machines. The compiler does not know >whether a piece of code will be executed with EMS, EEMS, company X's >I/O board in slot Y, or all of the above. The programmer does. Then the programmer can select which map file the compiler should see, so that the compiler can error-check the programmer's declarations, or in a more ambitions system, correct or create them. >Don't be a Unix chauvinist. All I did was to hold up one example of a number of systems that have managed without `volatile': an existence proof, as it were, that volatile is *not necessary*. I can use a similar proof to show that C is not necessary---e.g., MVS. Does that make me an IBM chauvinist? (That I detest MVS is irrelevant. I also happen to like `volatile'.) -- 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) (06/02/88)
In article <1078@micomvax.UUCP> ray@micomvax.UUCP (Ray Dunn) writes: |In article <1988May23.003847.1114@utzoo.uucp> (Henry Spencer) writes: |>Unfortunately, this is not sufficient. "Volatile" does not guarantee |>that operations are atomic. It is entirely possible for x and/or y to |>contain trash because they caught the variable midway through the |>assignment. |Fortunately this is sufficient when the programmer understands what he is |programming, and chooses data types etc which will ensure atomicity, if that |is what he is trying to achieve. C itself does not guarantee that access to any particular data type, including char, is atomic. My question is: is there *any* use for 'volatile' which does not require 'atomicity' at some level? If not, then 'volatile' doesn't really fix any of the problems we have without it. -- _ __ NEVIN J. LIBER ..!ihnp4!ihlpf!nevin1 (312) 510-6194 ' ) ) You are in a little twisting maze of / / _ , __o ____ email paths, all different. / (_</_\/ <__/ / <_ These are solely MY opinions, not AT&T's, blah blah blah
faustus@ic.Berkeley.EDU (Wayne A. Christopher) (06/02/88)
In article <11783@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:
] This last point---that C without `volatile' has been used for systems
] work---seems to have been ignored by those who claim that volatile is
] necessary. (Note that I claim only that it is unnecessary, not that it
] is bad.)
It's unnecessary only if you don't want to optimize the code. The reason
we've gotten by without it is that we haven't had good compilers.
] Let us try an experiment:
]
] f() {
] register int i, *ip;
] i = *ip;
] i = *ip;
] }
]
] produces
]
] movl (r10),r11 # i = *ip
] movl (r10),r11 # i = *ip
]
] That sure looks like the code I would expect from
]
] volatile int *ip;
Only because you're using a bad compiler. I'll bet gcc would give you the
results you expect. I don't think it makes any sense to declare an
automatic variable volatile anyway.
Wayne
brooks@maddog.llnl.gov (Eugene D. Brooks III) (06/02/88)
>> "A Fast Mutual Exclusion Algortithm" >> Leslie Lamport, Nov 14 1985. >> Report #5, DEC Systems Research Centre >> Having been pestered by several people to post the code for Lamport's algorithm to the net so people don't have wait for his paper, which you really ought to read if you want to discuss this item, here is an expression of the algorithm in C fragments. The "shared" is meant to indicate the status of the storage of the memory cells. I hopefully have not blown it, please don't start up your flame machines if I did. READ LESLIE'S PAPER! #define TRUE 1 #define FALSE 0 /* Zero so b[] starts out FALSE. */ #define PROCUNDEF -1 #define MAXPROC 8 /* How many processors you have. */ shared int x = PROCUNDEF; shared int y = PROCUNDEF; shared int b[MAXPROC]; /* Code fragment to get locked access to critical region. PROCNUM is a unique identifier for the cpu executing the code. */ tryagain : b[PROCNUM] = TRUE; x = PROCNUM; if(y != PROCUNDEF) { b[PROCNUM] = FALSE; while(y != PROCUNDEF); goto tryagain; } y = PROCNUM; if(x != PROCNUM) { b[PROCNUM] = FALSE; for(j = 0; j < MAXPROC; j += 1) { while(b[j] != FALSE); } if(y != PROCNUM) { while(y != PROCUNDEF); goto tryagain; } } /* Code fragment to unlock critical region. */ y = PROCUNDEF; b[PROCNUM] = FALSE; brooks@maddog.llnl.gov, brooks@maddog.UUCP
peter@athena.mit.edu (Peter J Desnoyers) (06/03/88)
In article <4922@ihlpf.ATT.COM> nevin1@ihlpf.UUCP (00704a-Liber,N.J.) writes: > >C itself does not guarantee that access to any particular data type, >including char, is atomic. My question is: is there *any* use for 'volatile' >which does not require 'atomicity' at some level? If not, then 'volatile' >doesn't really fix any of the problems we have without it. > I had always thought that the typical application of |volatile| would be something like: volatile char * z80sio_rr0 = io1 + 0; /* is this declared */ volatile char * z80sio_rr1 = io1 + 1; /* correctly? */ ... or the non-portable, but no doubt common union z80sio { struct sio_r { char * rr0, * rr1, * rr2; } read; struct sio_w { char * ww0, * ww1, ...; } write; } (substitute your favorite - i.e. least hated - comm chip, peripheral, or what have you.) When developing code for a development platform such as a Unix box, code like this only has to be written once, when the operating system is written. When developing code for an embedded system (what people then use those Unix boxes to develop :-) code like this is written once, but that's all the code there is. Peter Desnoyers peter@athena.mit.edu
karl@haddock.ISC.COM (Karl Heuer) (06/03/88)
In article <4922@ihlpf.ATT.COM> nevin1@ihlpf.UUCP (00704a-Liber,N.J.) writes: >C itself does not guarantee that access to any particular data type, >including char, is atomic. Yes it does. |sig_atomic_t| carries such a guarantee, though it's not specified which primitive type this is a synonym for. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
rpw3@amdcad.UUCP (06/05/88)
In article <1036@ima.ISC.COM> johnl@ima.UUCP (John R. Levine) writes: +--------------- | In article <21821@amdcad.AMD.COM> rpw3@amdcad.UUCP (Rob Warnock) writes: | >The "volatile" type is necessary and sufficient for correctly implementing | >Dekker's Algorithm in "optimizing C". | Not so fast, there. In large multi-CPU systems, there is often a write-behind | cache and writes by one processor are not always visible to other processors | until you do some sort of cache-flush operation... +--------------- I'm sorry, I was talking about the programming language considerations. Of course you are correct about the larger systems issue. I was assuming that any data pages used for inter-processor sychronization would be flagged as "don't cache" to the MMU/cache. (Most MMU's have some way to indicate to the cache that given pages are not to be cached, but that reads/writes are to be made directly to memory.) Even if you have special semaphore hardware ("magic" memory locations), you still must flag them as "don't cache"... Rob Warnock Systems Architecture Consultant UUCP: {amdcad,fortune,sun,attmail}!redwood!rpw3 ATTmail: !rpw3 DDD: (415)572-2607 USPS: 627 26th Ave, San Mateo, CA 94403
chris@mimsy.UUCP (Chris Torek) (06/07/88)
In article <13249@shemp.CS.UCLA.EDU> casey@admin.cognet.ucla.edu (Casey Leedom) writes: (Hi Casey. What are you doing at UCLA?) >> volatile char * z80sio_rr0; ... >Does it mean that the char pointers z80sio_rr0 ... [is] volatile, or does >it mean that the char's pointed to ... are volatile? Obviously for this >example the second interpretation is what is desired, .... Yes; and that is what it means. >... then how would I declare a pointer to some object type, say char, >with the intent that the pointer itself was volatile? char *volatile p; The form `volatile char *p' is the same as `char volatile *p'. I once suggested (mostly unseriously) that `?' be used for `volatile', and `=' for const/readonly, so that one would write instead char ?*pointer_to_volatile_characters; char *?volatile_pointer_to_characters; char ?*?volatile_ptr_to_volatile_chars; and things like int ?* =clockaddr = (int ?*)CLOCK_ADDR; /* readonly pointer to volatile int */ and char =rom_string[] = "foo baroo"; And you thought int (*(**(*foo(int a, int b))(long))[3][4])(char *) { was bad...! -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
Paul_L_Schauble@cup.portal.com (06/08/88)
Here's another request. I just had a go around with 'far' in Microsoft C. Could one of the wizards please post a description of how const, volatile, &c work in declarations?
ray@micomvax.UUCP (Ray Dunn) (06/09/88)
In article <4922@ihlpf.ATT.COM> nevin1@ihlpf.UUCP (00704a-Liber,N.J.) writes: >... My question is: is there *any* use for 'volatile' >which does not require 'atomicity' at some level? ... Yes, any use created by the simple, straight forward requirements of memory mapped I/O. -- 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
daveb@geac.UUCP (David Collier-Brown) (06/09/88)
In article <1988May23.003847.1114@utzoo.uucp> (Henry Spencer) writes: | Unfortunately, this is not sufficient. "Volatile" does not guarantee | that operations are atomic. It is entirely possible for x and/or y to | contain trash because they caught the variable midway through the | assignment. In article <4922@ihlpf.ATT.COM> nevin1@ihlpf.UUCP (00704a-Liber,N.J.) writes: | C itself does not guarantee that access to any particular data type, | including char, is atomic. My question is: is there *any* use for 'volatile' | which does not require 'atomicity' at some level? If not, then 'volatile' | doesn't really fix any of the problems we have without it. I suspect that, historically, C merely assumed that accesses to atomic (pardon the pun) data types were atomic, and accesses to composite ones were non-atomic. This tends to make a volatile declaration sufficient in the two "old" cases: 1) A device register, because the register was integer-width[1], because the device-register-changer part of the device handshook with the memory-access hardware, because the memory-access hardware[2] mediated no simultaneous accesses from simultaneously-running processors[3] and two processes could not be running simultaneously on the same processor. 2) A signal handler, because a user process and a signal handler could not be switched between in the "middle" of an access to a memory location, and two processes could not be running at the same time on the same processor. If one makes a minimum set of restrictions on device registers and signal-handlers part of the language/runtime specification, volatile is sufficient. If one does not, it isn't. This of course does not deal with the harder problem of multiprocessors. In fact, it was not even sufficient for some of the first micros, who had to have their device registers polled by an interrupt handler/driver to see when the "valid to read" bit was on (:-{). --dave (volatile is an engineering tradeoff, not an elegant invention) c-b [1] Character-width on micros. [2] One of the XXX-bus controllers on DEC equipment, the system controller on Honeybuns. [3] Then a rara avis. Disclaimer: the authors of the language may disagree about details, corrections are invited. -- David Collier-Brown. {mnetor yunexus utgpu}!geac!daveb Geac Computers Ltd., | "His Majesty made you a major 350 Steelcase Road, | because he believed you would Markham, Ontario. | know when not to obey his orders"
mouse@mcgill-vision.UUCP (der Mouse) (06/13/88)
In article <3732@pasteur.Berkeley.Edu>, faustus@ic.Berkeley.EDU (Wayne A. Christopher) writes: [responding to a post of Chris Torek's] > I don't think it makes any sense to declare an automatic variable > volatile anyway. Sure it does. If a pointer to the variable in question gets stored in some sort of global area and used by a signal handler or another process (presumably one running in the same address space, so the pointer makes sense to it), it can be necessary. ("Necessary" in the sense in which it's "necessary" to make, say, a device register volatile. As Chris has pointed out, volatile is never strictly necesary - just ban enough optimizations....) (isn't it fun to ^ design rivers into prose? :-) der Mouse uucp: mouse@mcgill-vision.uucp arpa: mouse@larry.mcrcim.mcgill.edu
bill@proxftl.UUCP (T. William Wells) (06/17/88)
In article <4922@ihlpf.ATT.COM>, nevin1@ihlpf.ATT.COM (00704a-Liber) writes: > |Fortunately this is sufficient when the programmer understands what he is > |programming, and chooses data types etc which will ensure atomicity, if that > |is what he is trying to achieve. > > C itself does not guarantee that access to any particular data type, > including char, is atomic. My question is: is there *any* use for 'volatile' > which does not require 'atomicity' at some level? If not, then 'volatile' > doesn't really fix any of the problems we have without it. The standard solves this problem, almost: 4.7: "The type defined [in header <signal.h>] is sig_atomic_t which is the integral type of an object that can be accessed as an atomic entity, even in the presence of asynchronous interrupts." Of course, this does not really deal with multiprocessor systems unless one stretches the definition of asynchronous interrupts to include asynchronous memory use, a long stretch indeed.
gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/04/88)
In article <319@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes: >My extreme hostility to "volatile" (and, as I had forgot to mention, >making all standard procs names "reserved words") In a freestanding implementation of ANSI C, the standard library functions are NOT reserved. In a hosted implementation they are reserved for very good reasons, including several having nothing to do with optimization. >volatile (and "reserved word" procs) instead try to make respectable the idea >that a complex expensive optimizer will give you substantially better >performance than a little ingenuity. They don't HAVE to "try to make the idea respectable", since it happens to be true. >First of all, in a sense, volatile and register exclude each other; in >traditional C all variables are volatile save for those declared to be >register (and this explains why there is a restriction on &...). Traditional C simply had nothing to say about whether ordinary variables, or register ones for that matter, were effectively volatile. This is one of the things X3J11 had to decide. In making the decision, they realized that the other choice also needed support, thus addition of "volatile". >People who really care about speed know that register is perfectly >adequate, IF USED COMPETENTLY, given a compiler that does a >straightforward but COMPETENT generation job (most tuned PCCs), to make >non benchmark programs run fast. Most highly-optimizing compilers pretty much ignore programmer-supplied "register" specifiers. Because C has always had the rule that the address-of operator cannot be applied to a register variable, "register" CANNOT suffice in place of volatile/nonvolatile. >Arguments for volatile, as opposed to register, are essentially: > [1] you cannot trust programmers to be competent and understand > what they are writing, ... Since proper use of "volatile" definitely DOES require programmer understanding, you're imagining things if you think that anyone on X3J11 supported that argument. In fact, "trust the programmer" was one of our mottos. > [2] you want people to pay a lot of money for your latest > optimizing compiler technology for C, and you want it to be blessed > by dpANS, so you can say of other compilers "they do not > have/implement volatile" without people questioning whether it is > useful at all (it's in the standard!). It happens that I have no financial interest in the production of C compilers, yet I support "volatile", which (contrary to your claims) is a way to AVOID trouble with optimization. Optimization is happening anyway; it is by no means caused by the existence of "volatile". Griff would have been sunk in his signal handler example without "volatile". >As to point [1], ... >As to point [2], ... What is the point of setting up straw men then knocking them down? Since your points [1] and [2] are the product of your own fevered imagination, you're wasting our time discussing them. >instead of the obviously proper better structured and efficient C way Way to go -- you took a vectorizable loop and turned it into an unvectorizable one. What an efficiency improvement that is. >you never make a program wrong by declaring a variable register, Wrong! >but it may be wrong if you fail to place volatile where it is needed. If it needs the "volatile" to be correct, then it is ALREADY crucially dependent on features that ARE NOT GUARANTEED and never have been. > (usually good peephole optimization is enough) The marketplace apparently does not agree with you. > volatile is a loss; it is a ... new and difficult concept Not at all. The concept has long been familiar to C programmers who need to worry about it, but there previously hadn't been a good solution to the problem. Simply stated, it is "if you need to be sure that a variable is accessed exactly at the times that it would appear to be, judging by the C source code, then declare it using volatile". >when I first studied C many years ago, I was >really struck by the cleverness of having a register storage class, its >careful definition, the obvious advantage of having the programmer >select the few crucial variables to cache, obviating the need for a >complex and large optimizer to do the same job, only worse. Optimizer technology has come a long way since the invention of C. "register" was an aid to optimization technology of the 1970s, "noalias" was an aid to optimization technology of the 1980s (but alas we don't have it). "volatile" does not aid optimization, but it does make it more tolerable. I should point out that X3J11 has several active members who are much better able to evaluate optimization technology than you or I. The committee was therefore able to make informed decisions on these matters, and I suggest that you defer to their judgement.