[comp.lang.c] volatile

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.