[comp.lang.c] #defines with parameters

gwyn@smoke.BRL.MIL (Doug Gwyn ) (11/26/88)

In article <264@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>In article <1988Nov22.170953.24489@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>    Definitions of parameterized macros ("function-like" macros in
>    X3J11speak) have always been required to have the "(" immediately
>    following the identifier.  The May draft standard requires that in
>    the invocation, the "(" must be "the next preprocessor token",
>    which basically means that white space there is okay.
>Now I have always had a low opinion of the X3J11 work (e.g. not to
>realize that unsigned and int are different types, as they obey
>different rules for arithmetic, and char short and long are just
>range/length modifiers, and not the other way round), but this last
>idea really leaves me gasping...

It would be nice if you checked what X3J11 had done before venturing an
opinion.  In the dpANS, unsigned and int are definitely different types,
as they always have been in C.

If you're referring to the adoption of "value preserving" rules for type
conversion, that is clearly a logical improvement over "sign preserving"
rules, leading to surprising behavior less often, and turned out upon
investigation to not break a significant amount of code already written
according to sign-preserving rules.  Both variations were found in
existing practice before X3J11 had to make a choice, and they chose the
patently better set of rules.

char never has been a "range/length" modifier in C, and X3J11 hasn't
tried to change that.  short and long have always produced different
types, not different versions of the same type, and X3J11 hasn't tried
to change that, either.

>Obviously there must me a way to distinguish between macro bodies that
>begin with a "(" and macro definitions with a parameter list, is there
>one ?

Yes, the ( introducing the parameters in the definition of a function-
like macro must not be preceded by white space.  Both the May draft and
the current dpANS denote this special ( by the syntactic category
"lparen", which it defines correctly.  Henry was mistaken about this.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (11/26/88)

In article <8982@smoke.BRL.MIL> I wrote:
>"sign preserving"

To forestall yet another round of comments, please read this as the
technically more accurate "unsigned preserving".  Thanks.

chris@mimsy.UUCP (Chris Torek) (11/27/88)

>>In article <1988Nov22.170953.24489@utzoo.uucp> henry@utzoo.uucp
>>(Henry Spencer) writes:
>>>Definitions of parameterized macros ... have always been required to
>>>have the "(" immediately following the identifier.  The May draft
>>>standard requires that in the invocation, the "(" must be "the next
>>>preprocessor token", which basically means that white space there is okay.

Translation:

	#define	IDENT(a)	(a)

		IDENT   ( foo   )

is perfectly legal and produces `foo';

	#define	ANAME	(bar)

		ANAME(a,b)

produces `(bar)(a, b)' (syntactically a function call).

>In article <264@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
[much not worth quoting]

In article <8982@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes:
>It would be nice if you checked what X3J11 had done before venturing an
>opinion.

Indeed.  However:

>If you're referring to the adoption of "value preserving" rules for type
>conversion, that is clearly a logical improvement over "sign preserving"
>rules, leading to surprising behavior less often, and turned out upon
>investigation to not break a significant amount of code already written
>according to sign-preserving rules.  Both variations were found in
>existing practice before X3J11 had to make a choice, and they chose the
>patently better set of rules.

All of this is true save the `clearly ... improvement', `surprising
...  less often', and `patently better' (which are all opinions).  The
problem with value-preserving rules is that predicting the type of of
an expression requires knowing something that cannot be known
portably:  it requires knowing whether the expanded (rvalue) type of an
lvalue actually contains more bits than the lvalue's type.  That is,
the type of the expansion of an `unsigned short' might be `unsigned
int' or `int', and there is no way to tell which.  (It is, however,
true that casting to whichever type is desired will patch things up and
eliminate any difference between the two methods.)

>[parentheses in macro def/ref:]  Henry was mistaken about this.

While he did not use dpANS phrasing, he did get the details right.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@smoke.BRL.MIL (Doug Gwyn ) (11/27/88)

In article <14724@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>>In article <264@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>[much not worth quoting]
>(It is, however, true that casting to whichever type is desired will
>patch things up and eliminate any difference between the two methods.)

Which is another reason the difference between "value preserving" and
"unsigned preserving" rules isn't very important: anyone who cares
will be using explicit casts to be sure the proper conversions occur.

>In article <8982@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn) writes:
>>[parentheses in macro def/ref:]  Henry was mistaken about this.
>While he did not use dpANS phrasing, he did get the details right.

He may have.  However, I think Grandi interpreted him as saying that
white space was allowed before the ( in the DEFINITION of a function-
like macro, and I was responding to that.

	#define foo (x) ((x)+1)
	foo(z)
produces after substitution:
	(x) ((x)+1)(z)
whereas
	#define foo(x) ((x)+1)
	foo (z)
produces
	((z)+1)

X3J11 isn't proposing to change the way this has always worked.

pcg@aber-cs.UUCP (Piercarlo Grandi) (11/27/88)

I am happy I managed to sidetrack the sterile discussion on macros in
cpp into something dearer to my heart (can you say "axe to grind" ? I
can ;->...).

I am relieved that the dpANS does not imply prescience and telepathy
are required in cpp or the compilers to understand which macros are
parameterless and which are not :-) :-)...

On the other hand the substance of my point on unsigned/int remains; I
apologize to Doug Gwin for having used the wrong terminology.

What I really was unhappy with in dpANS (among many other things...) is
that signed has been introduced as a new keyword, and I ought to have
said unsigned/signed, not unsigned/int.

As I understand it, this means that char,short,int,long are distinct
types, whereas unsigned and signed are type modifiers. In a sense then,
the view of dpANS is that unsigned [int] and [signed] int are the same
type, only that one has a sign and the other hasn't.

Clearly this mostly is a matter of emphasis, in that unsigned short and
signed short are distinct types, as would be short unsigned and short int,
but it is quite important to note that in dpANS there is an explicit
affinity between unsigned and signed short, not between short and long
unsigned, even if we concede that type modifiers create new types, instead
of merely subtypes.

I would not agree that char has never been a length specifier for int;
before dpANS, C was defined by Ritchie's and Johnson's compilers (in a
sense even K&R was just the manual for the compilers). In both
compilers, and reading carefully K&R, one has the strong impression
that, beyond a somewhat artificial distinction between integral and
integer types, char was always meant to be a kind of short short int,
and indeed one can apply to a char all the same operations one can
apply to an integer; also the rules about lengtheneing of char and short
in many contexts, and the common use of int where char could have been used,
tend to reinforce the impression.

I would also not agree that in this matter dpANS respected existing
practice; the introduction of the signed modifier is certainly an
innovation, and a confusing one as well (judging from all the fuss
about it and char).

In Johnson's compiler instead one can fully use char,short,long as type
modifiers, and int and unsigned as based types, and I regard this (in
my never humble opinion) as the most natural thing to do, and one that
would not have required the introduction of signed.

    I understand that using a compiler as definition of a language is
    not kosher, but before dpANS this was the way (fortunately :->).

Using this interpretation, there is little argument about adopting a
"[un]signed preserving" or "value preserving" rule; the only rules are
that conversion from a shorter to a long form of the same type results
in no changes, and tyhe same applies when a longer value is shortened
to a length that can represent it, and conversion across the two base
types is defined only if the source unsigned is not too large or the
source int is not too large or negative; anything else is machine
dependent, except that truncations to a length of unsigned must respect
the rules of modular arithmetic.

This interpretation may not be what Ritchie, Johnson and K&R meant, but
it is consistent with them (well, I admit that saying char int and char
unsigned is somewhat funny, but you can get used to it ;-}), especially
with the point that unsigned obeys a different type of laws of
arithmetic (modular) than int (algebraic), thus distinguishing them
more forcefully than differences of range, that after all are just
accidents of implementation. Note also that there is no need of a signed
keyword to disambiguate between variations of char in this interpretation;
char by itself may be either, char int and char unsigned are well defined.

As to the rest of dpANS, I will not rekindle spent discussions, except
for a cheap leer at trigraphs :-) :-) (I understand that the committee
were more or less forced to include them, so it is not wholly their
fault...).

As Ritchie said (more or less) in an interview to Computer Language:
"dpANS C is the best standard that could have come out of a
committee".
-- 
Piercarlo "Peter" Grandi			INET: pcg@cs.aber.ac.uk
Sw.Eng. Group, Dept. of Computer Science	UUCP: ...!mcvax!ukc!aber-cs!pcg
UCW, Penglais, Aberystwyth, WALES SY23 3BZ (UK)

guy@auspex.UUCP (Guy Harris) (11/29/88)

>What I really was unhappy with in dpANS (among many other things...) is
>that signed has been introduced as a new keyword, and I ought to have
>said unsigned/signed, not unsigned/int.

The reason why it was introduced has nothing to do with X3J11's opinion
on whether "int" and "unsigned int" are similar types or not.  It was
introduced solely to provide a way for a programmer to specify that a
"char"-sized variable is to be signed; presumably, it applies to
"short", "int", and "long" purely for orthogonality.

>I would not agree that char has never been a length specifier for int;

The only way you can validly disagree with that statement is if you can
produce a compiler wherein "char int" is a valid type specifier.  I have
yet to see any such compiler.

>In Johnson's compiler instead one can fully use char,short,long as type
>modifiers, and int and unsigned as based types,

Funny, the SunOS C compiler is based on Johnson's PCC, and when I try to
compile

	foo()
	{
		char int x;
	}

it complains about an "illegal type modification".  That compiler, at
least, sure doesn't let you use "char" as a type modifier....

>and I regard this (in my never humble opinion) as the most natural
>thing to do, and one that would not have required the introduction of
>signed.

I don't regard it as the most natural thing to do.  I would have
preferred it had C made a distinction between:

	1) a type that was the unit of storage allocation;

	2) a type that was a "character", and that might require a
	   transfer function to convert between it and integral types
	   (big deal, the main loop of a character-string-to-decimal
	   routine might have included

		n = n*10 + (int(c) - int('0'));

	   instead of

		n = n*10 + (c - '0');

	   even if the language didn't specify an automatic
	   character-to-int conversion here, people could have lived
	   with it);

	3) a very small integral type.

For various reasons, including the crudely practical one of the behavior
of debuggers when confronted with "char", I tend to think of characters
and integral types as different sorts of beasts.

jss@hector.UUCP (Jerry Schwarz) (11/29/88)

In article <277@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) 
makes some groundless attacks on the ANSI commitee apparently
due to misunderstanding the (proposed) standard.  I respond to
one case in which either he or I have clearly misunderstood something.

>
>Using this interpretation, there is little argument about adopting a
>"[un]signed preserving" or "value preserving" rule; the only rules are
>that conversion from a shorter to a long form of the same type results
>in no changes, and tyhe same applies when a longer value is shortened
>to a length that can represent it, and conversion across the two base
>types is defined only if the source unsigned is not too large or the
>source int is not too large or negative; anything else is machine
>dependent, except that truncations to a length of unsigned must respect
>the rules of modular arithmetic.

This is pretty much what the standard says about conversions, but it
in no way resolves the difference between the "value preserving" and
"unsigned preserving" rules.  These address a different question.
Namely the types of certain expressions.  For example what is the
type of i+us (where i is an int, and us is an unsigned short).


Jerry Schwarz
AT&T Bell Labs, Murray Hill

bill@twwells.uucp (T. William Wells) (11/30/88)

In article <277@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
: As I understand it, this means that char,short,int,long are distinct
: types, whereas unsigned and signed are type modifiers. In a sense then,
: the view of dpANS is that unsigned [int] and [signed] int are the same
: type, only that one has a sign and the other hasn't.

These are the C int-ish types

the types               other ways of saying the same type

char
signed char
unsigned char
signed short int        short, signed short, short int
unsigned short int      unsigned short
signed int              int, signed (or nothing, in some cases)
unsigned int            unsigned
signed long int         long, signed long, long int
unsigned long int       unsigned long

(The order of the words is irrelevant.)

---

If you want to, you can think of this as types and modifiers, though
the standard does not speak of them that way. If so, here is how you
do it:

The types:

	char
	int

That's right. Only two.

The modifiers:

	unsigned        signed
	short           long

The first pair modifies char or int; the second int only. You can
specify only one of each pair.

---

Here are the characteristics of the types:

		signedness values
char            *          *
signed char     signed     no smaller integer range exists
unsigned char   unsigned   no smaller unsigned range exists
short           signed     contains signed char values
unsigned short  unsigned   contains unsigned char values
int             signed     contains short values
unsigned        unsigned   contains unsigned short values
long            signed     contains int values
unsigned long   unsigned   contains unsigned int values

* Char may be either signed or unsigned. However, it is always treated
as a distinct type. The characters in the source character set are
positive values, but anything else may be positive or negative.
There is no relationship between char and signed or unsigned char,
other than that they occupy the same amount of storage.

Each unsigned type must be able to represent the positive values of
its corresponding signed type. Signed and unsigned types must occupy
the same amount of storage.

---
Bill
{uunet|novavax}!proxftl!twwells!bill

ggs@ulysses.homer.nj.att.com (Griff Smith) (12/01/88)

In article <9016@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
> "volatile" also serves a need.  In fact, it directly addresses one of
> the comments I saw earlier today in this newsgroup, about use of C for
> accessing device registers.  Again, it is transparent to virtually all
> existing code, it addresses a real need, and it can be ignored by anyone
> who does not specifically need it.  What is your objection to it?

I agree, and I don't object, but some brief encounters with an ANSI
compiler make me worry about it a bit.  The problem is that the
existence of the volatile qualifier makes it possible to do
optimizations that were previously forbidden (or at least difficult to
slip past the customers).  I blew several hours discovering that a flag
set by a signal handler had been optimized out of existence because it
wasn't declared volatile.  If I were writing new software I would be
aware of the problem and use the proper declaration, but what am I to
do about fixing all the old stuff that now has subtle errors caused by
optimizations that used to be illegal?

The response I got from a C++ guru around here wasn't encouraging: he
suggested declaring everything volatile and ignoring the issue.  Maybe
he's right, but that attitude could easily lead to a habit of forcing
all declarations to include the volatile qualifier just to avoid a `silly'
rule.  This would negate any efficiency improvements the compiler
designers were trying to achieve.

Do any of you have some practical experience, plus suggestions for
living in the brave new ANSI-C world?
-- 
Griff Smith	AT&T (Bell Laboratories), Murray Hill
Phone:		1-201-582-7736
UUCP:		{most AT&T sites}!ulysses!ggs
Internet:	ggs@ulysses.att.com

tps@chem.ucsd.edu (Tom Stockfisch) (12/02/88)

In article <10919@ulysses.homer.nj.att.com> ggs@ulysses.homer.nj.att.com (Griff Smith) writes:
>In article <9016@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
>> "volatile" also serves a need.  In fact, it directly addresses one of...

>....  I blew several hours discovering that a flag
>set by a signal handler had been optimized out of existence because it
>wasn't declared volatile.  If I were writing new software I would be
>aware of the problem and use the proper declaration, but what am I to
>do about fixing all the old stuff that now has subtle errors caused by
>optimizations that used to be illegal?


Presumably there are only a few
1.  Turn off optimizations (perhaps only some of them) when compiling signal
handler modules.
All relevant flags should be declared static in these files.

2.  Access the flags in other modules only via function calls to routines
in the signal handler modules.

Now the compiler can't optimize away references to the flags.

Of course, if your compiler does extensive global optimization (accross
modules), you may still be in trouble.
-- 

|| Tom Stockfisch, UCSD Chemistry	tps@chem.ucsd.edu

guy@auspex.UUCP (Guy Harris) (12/02/88)

>...but what am I to do about fixing all the old stuff that now has
>subtle errors caused by optimizations that used to be illegal?

Back off on the optimization level, if your compiler will let you?  I
think the SunOS optimizer is generally "safe" at level -O2 (that's the
default level on Sun-4s, and only a very few modules crank it lower -
mostly things like bitblt code, not e.g. all the kernel or all the
device drivers):

     -O[level] Optimize the object code.  Ignored when either -g,
               -go,  or  -a is used.  On Sun-2 and Sun-3 systems,
               -O with the level omitted is equivalent to -O1; on
               Sun-4  systems,  it  is  equivalent  to  -O2.   on
               Sun386i systems, all levels are  the  same  as  1.
               level is one of:

                    1    Do postpass assembly-level  optimization
                         only.

                    2    Do global  optimization  prior  to  code
                         generation,        including        loop
                         optimizations,   common    subexpression
                         elimination,   copy   propagation,   and
                         automatic register allocation. -O2  does
                         not  optimize  references  to or defini-
                         tions of external or indirect variables.

                    3    Same  as  -O2,  but  optimize  uses  and
                         definitions  of external variables.  -O3
                         does not trace the  effects  of  pointer
                         assignments.  Neither -O3 nor -O4 should
                         be used  when  compiling  either  device
                         drivers,  or programs that modify exter-
                         nal   variables   from   within   signal
                         handlers.

                    4    Same as -O3, but trace  the  effects  of
                         pointer assignments.

I suspect some compilers can be told to back off to a safer level as well.

henry@utzoo.uucp (Henry Spencer) (12/03/88)

In article <10919@ulysses.homer.nj.att.com> ggs@ulysses.homer.nj.att.com (Griff Smith) writes:
>> "volatile" also serves a need...
>
>I agree, and I don't object, but some brief encounters with an ANSI
>compiler make me worry about it a bit...

Well, if I was to quibble, I would observe that there is no such thing
as an ANSI C compiler yet, since there is no ANSI standard yet.  In fact,
any compiler that is actually in users' hands right now probably does not
fully match even the current draft.  End of quibble...

>The problem is that the
>existence of the volatile qualifier makes it possible to do
>optimizations that were previously forbidden (or at least difficult to
>slip past the customers).

No, actually, it makes it possible for the users to defend themselves
against optimizations that the compiler writers had in the works anyway.
Even in the Good Old Days it wasn't uncommon to find machines where it
was common knowledge that one should not compile tricky code (e.g. the
kernel) with -O.

>I blew several hours discovering that a flag
>set by a signal handler had been optimized out of existence because it
>wasn't declared volatile.  If I were writing new software I would be
>aware of the problem and use the proper declaration, but what am I to
>do about fixing all the old stuff that now has subtle errors caused by
>optimizations that used to be illegal?

The old stuff always had subtle errors; the standard did not create them,
it simply called them to your attention.  The behavior of signal handlers
has never been guaranteed in any way.  Don't confuse "what I can get away
with on a 4.2BSD VAX" with the definition of C.  Those optimizations were
never illegal, they just weren't common.  The standard has actually
improved the situation -- at least now you have behavior that you can
count on.

The only way to fix the old stuff is, well, to fix it.  You can use a
"#define volatile /* */" to make it compatible with old compilers.
-- 
SunOSish, adj:  requiring      |     Henry Spencer at U of Toronto Zoology
32-bit bug numbers.            | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

desnoyer@Apple.COM (Peter Desnoyers) (12/03/88)

>In article <10919@ulysses.homer.nj.att.com> ggs@ulysses.homer.nj.att.com (Griff Smith) writes:
>
>>....  I blew several hours discovering that a flag
>>set by a signal handler had been optimized out of existence because it
>>wasn't declared volatile.  

I've seen people blow days trying to get a pre-ANSI compiler to frob
device registers properly. Not using '-O' doesn't turn off all
optimizations in some (most?) compilers - just the ones in the
optimizing pass. 

>If I were writing new software I would be aware of the problem and use
>the proper declaration, but what am I to do about fixing all the old
>stuff that now has subtle errors caused by optimizations that used to
>be illegal? 
>
Do what you did before - turn off optimization. Same solution, same
results. 

pet peeve - It is a common thing to have to write a zero to a device
register. The obvious code, (* reg_addr) = 0, often results in:

  xor reg_addr, reg_addr

even with optimization off. This sucks - it reads the register twice,
XORs the two (possibly different) values, and then writes the possibly
non-zero value. If I wanted to read the register, I would have said
so. I probably just cleared my data_ready flag or lost other input.
The solution I have seen (pre-ANSI) was:

  extern int zero = 0;
  (* reg_addr) = zero;	/* KLUDGE */

Unfortunately I don't think specifying bus semantics is within the
purview of the ANSI committee (please correct me if I'm wrong - my
knowledge of the details of the standard is limited) and volatile is
not sufficient to force the desired activity.

				Peter Desnoyers

blarson@skat.usc.edu (Bob Larson) (12/03/88)

In article <21560@apple.Apple.COM> desnoyer@Apple.COM (Peter Desnoyers) writes:
>pet peeve - It is a common thing to have to write a zero to a device
>register. The obvious code, (* reg_addr) = 0, often results in:
>
>  xor reg_addr, reg_addr

What machine is this for?  3 memory references for a simple clear
doesn't seem very good to me.  Most 68000 compilers would use the
clr instruction, which does have the undesired side effect of doing
an (ignored) read though.

>Unfortunately I don't think specifying bus semantics is within the
>purview of the ANSI committee (please correct me if I'm wrong - my
>knowledge of the details of the standard is limited) and volatile is
>not sufficient to force the desired activity.

Fortunatly you are wrong, and volitile is sufficient.


-- 
Bob Larson	Arpa: Blarson@Ecla.Usc.Edu	blarson@skat.usc.edu
Uucp: {sdcrdcf,cit-vax}!oberon!skat!blarson
Prime mailing list:	info-prime-request%ais1@ecla.usc.edu
			oberon!ais1!info-prime-request

desnoyer@Apple.COM (Peter Desnoyers) (12/06/88)

In article <13784@oberon.USC.EDU> blarson@skat.usc.edu (Bob Larson) writes:
>In article <21560@apple.Apple.COM> desnoyer@Apple.COM (Peter Desnoyers) writes:
>>pet peeve - It is a common thing to have to write a zero to a device
>>register. The obvious code, (* reg_addr) = 0, often results in:
>>
>>  xor reg_addr, reg_addr
>
>What machine is this for? 
I'm pretty sure Turbo C on the PC does this. Even with optimization
off. I'll check. 

>Most 68000 compilers would use the
>clr instruction, which does have the undesired side effect of doing
>an (ignored) read though.
>
Almost as bad. The important point is that if these
micro-optimizations are performed in the compiler pass of a pre-ANSI
compiler, there is NO way to turn them off. Thus the naive view that
everything worked fine before ANSI started messing with the language
is demonstrably wrong.

>>...(please correct me if I'm wrong ... [whether volatile specifies
>>one write and no reads]
I stand corrected. Thank you.

				Peter Desnoyers

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/06/88)

First of all I wish to admit I have been inaccurate; as somebody remarked
"noalias" was only briefly in the dpANS C, and "far" and "near" never
actually made it in the official documents sent out for review. But all
were for a long time in various drafts, and as usually happens people
actually advertised "far" and "near" as examples of draft, unofficial
ANSI C conformance. Thank goodness X3J11 frustrated their efforts in the end.

In article <225@twwells.uucp> bill@twwells.UUCP (T. William Wells) writes:

    In article <277@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
    : As I understand it, this means that char,short,int,long are distinct
    : types, whereas unsigned and signed are type modifiers. In a sense then,
    : the view of dpANS is that unsigned [int] and [signed] int are the same
    : type, only that one has a sign and the other hasn't.
    
    If you want to, you can think of this as types and modifiers, though
    the standard does not speak of them that way. If so, here is how you
    do it:
    
    The types:
    
    	char
    	int
    
    That's right. Only two.
    
    The modifiers:
    
    	unsigned        signed
    	short           long
    
    The first pair modifies char or int; the second int only. You can
    specify only one of each pair.

OK, the standard does not speak of modifiers and types, but then the
result is the long lists you give and lots of confusion. I think there
are two issues here, one the introduction of signed as a keyword, and
the other neat ways of defining semantics. As to the latter, the whole
type system would be greatly simplified if one were to say that

[1] there are two distinct types, int and unsigned; they are distinct
types because different rules of arithmetic apply to them. This would
make it clear, and I have found very few people that actually
understand that unsigned is not just "positive int", that the same
operators applied to int and unsigned have very different semantics.

[2] Each of the two distinct types may come in three different extra
lengths, char, short and long, that are exactly equivalent among them
for the same type except for the different size.

As to the last point, char has been so far just a short short; a char
value can be operated upon exactly as an integer. Historically char
constants have been really the size of integer constants...

I would have liked, instead of the unnecessary and confusing signed
modifier, a nice range(abs_max) modifier for types integer and
unsigned, and char/short/long defined in terms of it. This would have
added tremendously to the portability of programs. The lack of some
sort of ranges and the use of short and long are one of the few things
I dislike in Algol68 and C.

I hope such an interesting extension makes it into C++, or at least into
the GNU C++ compilers (hint hint Michael Tiemann!).

    There is no relationship between char and signed or unsigned char,
    other than that they occupy the same amount of storage.

Now I reiterate the question: why was a new keyword introduced "signed"
when it just sufficed to sanction the existing practice of some
compilers (PCC had it, more recent BSD versions fixed this "bug") to
say "int char" or better "char int"?

Since storage classes, modifiers, and type names could be given in any
order in a declaration; most compilers therefore simply collected
keywords, and set a flag in the symbol table entry for the variable for
each keyword they collected.

This meant that "int static char int static" was accepted as a legal
declaration by many compilers :-). This has been later often
"corrected", notably in 4BSD pcc. Amusingly it persists even today in
other compilers, among them g++ 1.27, where interestingly "sizeof (char
int)" is 4 and "sizeof (int char)" is 1 on a 68020... Of course in my
opinion both ought to be 1, or an order storageclass/modifier/type
might be enforced, making "int char" illegal and "sizeof (char int)"
equal to 1.
-- 
Piercarlo "Peter" Grandi			INET: pcg@cs.aber.ac.uk
Sw.Eng. Group, Dept. of Computer Science	UUCP: ...!mcvax!ukc!aber-cs!pcg
UCW, Penglais, Aberystwyth, WALES SY23 3BZ (UK)

gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/07/88)

In article <330@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>First of all I wish to admit I have been inaccurate; as somebody remarked
>"noalias" was only briefly in the dpANS C, and "far" and "near" never
>actually made it in the official documents sent out for review. But all
>were for a long time in various drafts, and as usually happens people
>actually advertised "far" and "near" as examples of draft, unofficial
>ANSI C conformance. Thank goodness X3J11 frustrated their efforts in the end.

You're still inaccurate.  "far" and "near" were never at any time in
any draft of the proposed ANSI C standard.

"noalias" was not in any draft other than the one sent out for the
second public review.  How could it be, when it had just been invented
at the previous meeting and was retracted at the next?

Are you just making this stuff up, or do you have drug-using advisors,
or what?

>As to the last point, char has been so far just a short short; a char
>value can be operated upon exactly as an integer.

Except that whether it acts as signed or unsigned depends on the
implementation.

>Historically char constants have been really the size of integer
>constants...

You mean "character constants"; in C they ARE integer constants
specified in a certain character-oriented way.

>Now I reiterate the question: why was a new keyword introduced "signed"
>when it just sufficed to sanction the existing practice of some
>compilers (PCC had it, more recent BSD versions fixed this "bug") to
>say "int char" or better "char int"?

I have never seen a C compiler that accepted "int char"; certainly
Ritchie didn't intend for it to be valid.  Also, char has never been
guaranteed to be signed; read K&R 1st Edition.  It happened to be most
efficient on the PDP-11 to make it signed, but it was implemented as
unsigned on some other architectures.  The VAX port of the C compiler
could have done it either way; signed behavior was chosen, presumably
to aid in porting code developed on the PDP-11.  The IBM 370 and WE 3B
compilers had chars act as unsigned all along.  Ritchie specifically
blessed this implementation dependence in his BSTJ article on C in 1978.

>Amusingly it persists even today in other compilers, among them
>g++ 1.27, where interestingly "sizeof (char int)" is 4 and "sizeof
>(int char)" is 1 on a 68020...

I don't know what C++ rules for basic types really are, but if as I
suspect g++ is getting it wrong, you should report this bug to the GNU
project.

ray@micomvax.UUCP (Ray Dunn) (12/08/88)

In article <10919@ulysses.homer.nj.att.com> ggs@ulysses.homer.nj.att.com (Griff Smith) writes:
>....
>I agree, and I don't object, but some brief encounters with an ANSI
>compiler make me worry about it a bit.  The problem is that the
>existence of the volatile qualifier makes it possible to do
>optimizations that were previously forbidden (or at least difficult to
>slip past the customers).  I blew several hours discovering that a flag
>set by a signal handler had been optimized out of existence because it
>wasn't declared volatile.

There is the implication here that the introduction of the "volatile"
qualifier is the *cause* of the problem.

This is of course not true, the qualifier is an attempt to provide a
*solution* to the problem.

Many existing pre-ANSI compilers will optimize away memory references that
are in fact required because the variable is "volatile" but the compiler has
no way of knowing.

In pre-ANSI 'C', *all* variables are regarded as being *non-volatile*, the
compiler is free for example to keep the variable in a register, and there
is usually no way to selectively stop the compiler optimizing their
references away.

You usually can turn off optimizations globally while compiling a module.
One technique to use is to collect all fucntions containing volatile stuff
into one or more modules, and only compile *them* with optimisation off.

The "volatile" qualifier is a *much* better solution when you have an ANSI
Conforming compiler.

-- 
Ray Dunn.                      |   UUCP: ..!philabs!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

bill@twwells.uucp (T. William Wells) (12/08/88)

In article <330@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
:                                                          I think there
: are two issues here, one the introduction of signed as a keyword, and
: the other neat ways of defining semantics. As to the latter, the whole
: type system would be greatly simplified if one were to say that

`Signed' is there so that we can have `signed char'. As to why one
would want that, the original C specified that, while a char was an
integer, whether it could contain negative values is up to the
implementer. `Signed char' has the same size as a char, but it is
always signed. (And yes, there are good reasons not to change the
current definition of `char').

`Signed' was then allowed as a modifier for the other types, for
reasons of symmetry.

(This is all in the Rationale, in section 3.1.2.5.)

As for simplifying the type system; the current one is as simple as
is possible for it to be, given that it *must* be compatible with the
old one, and given the addition of a `signed char'.

: [1] there are two distinct types, int and unsigned; they are distinct
: types because different rules of arithmetic apply to them. This would
: make it clear, and I have found very few people that actually
: understand that unsigned is not just "positive int", that the same
: operators applied to int and unsigned have very different semantics.

Then you have simply been hanging around inexperienced C programmers.

Not only that, but you are wrong about there being two distinct
types; from the view you are adopting, there are three.  See below.

: [2] Each of the two distinct types may come in three different extra
: lengths, char, short and long, that are exactly equivalent among them
: for the same type except for the different size.

This would be nice if it were compatible with history; however...

: As to the last point, char has been so far just a short short; a char
: value can be operated upon exactly as an integer. Historically char
: constants have been really the size of integer constants...

This is false. Char has not been and ought not be made just a short
short.  Char is a funny type. It is neither signed nor unsigned though
it is always implemented as one or the other.

Let me repeat this: there are three signednesses in C:

    1) integers - these have positive and negative values.
    2) unsigned - these have positive values only.
    3) char - these have positive values. Sometimes they have
       negative values as well but it depends on the implementation.

: I would have liked, instead of the unnecessary and confusing signed
: modifier, a nice range(abs_max) modifier for types integer and
: unsigned, and char/short/long defined in terms of it. This would have
: added tremendously to the portability of programs.

And here again you are wrong. While it is sometimes nice to be able
to specify numeric ranges, it is hardly necessary to do so for
portability. Understanding the C paradigm, one can use the existing
types to create portable code. I do it all the time; my code is
regularly ported to everything from micros to mainframes.

:                       to sanction the existing practice of some
: compilers (PCC had it, more recent BSD versions fixed this "bug") to
: say "int char" or better "char int"?

`Char int' is, and always has been, illegal. I don't know of a single
compiler that accepts it. I just checked: the one on my system
(Microport, a system V) and the one on my work machine (SunOS,
more-or-less BSD) reject it. I believe that both are based on the
PCC.  I know that the C compiler (from ISC) we used on our PDP-11 and
our VAX, between four and six years ago, both rejected `char int' and
similar constructs. I'm almost certain that both compilers were PCC
based.

---

Let me suggest that you should carefully read the various books on C,
starting with K&R (the original), the Harbison & Steele book, and
some other good C textbooks. *Then* read the latest draft of the C
standard.  Do all of this without reference to what you would wish
the language to be. Understand what it *is*; develop familiarity with
the paradigms and facts of the language. Then, and only then, will be
you be equipped to complain about C's deficiencies.

In the mean time, I'm going to stop responding to your statements
about what C ought to be.  You have formed these opinions in the
absence of knowledge; I have come to believe that I am wasting my
time trying to correct them.

If you have questions about the language, go ahead and ask them, and
you'll even get a civil reply, but please stop telling us what the
language should be until you know what it *is*.

---
Bill
{uunet|novavax}!proxftl!twwells!bill

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/10/88)

In article <1450@micomvax.UUCP> ray@micomvax.UUCP (Ray Dunn) writes:

    In article <10919@ulysses.homer.nj.att.com> ggs@ulysses.homer.nj.att.com (Griff Smith) writes:

    >The problem is that the
    >existence of the volatile qualifier makes it possible to do
    >optimizations that were previously forbidden (or at least difficult to
    >slip past the customers).

They were not forbidden, you could do any optimization that did not impact
the semantics of programs. A Classic C program compiled with a dpANS C
compiler changes semantics, unless you tag with volatile all variables not
tagged register.

    There is the implication here that the introduction of the "volatile"
    qualifier is the *cause* of the problem.

    This is of course not true, the qualifier is an attempt to provide a
    *solution* to the problem.

It IS the cause! volatile is not necessary, register is sufficient...
I have tried to give arguments for this (at length :->).

    In pre-ANSI 'C', *all* variables are regarded as being *non-volatile*,
    the compiler is free for example to keep the variable in a register, and
    there is usually no way to selectively stop the compiler optimizing their
    references away.

Exactly the opposite!  In Classic C the concept was that the compiler would
take register as an hint of which variables could always be safely cached in
register (and the user be forbidden to take pointer to them), and all the
others could be cached only if it was guaranteed that doing so would not
"cause surprises".

    You usually can turn off optimizations globally while compiling a
    module.  One technique to use is to collect all fucntions containing
    volatile stuff into one or more modules, and only compile *them* with
    optimisation off.

Think again. You REALLY want the semantics of your code dependent on the
flags you give to a compiler?

    The "volatile" qualifier is a *much* better solution when you have an
    ANSI Conforming compiler.

Actually it is a backwards step wrt to register, as it encourages bloated
compilers and the delusion that they can select which variables to put in
registers better than you can do, while requiring careful analysis as to
which variables MUST be declared volatile (as failing to tag them makes
your program erroneous, where register is perfectly safe).
-- 
Piercarlo "Peter" Grandi			INET: pcg@cs.aber.ac.uk
Sw.Eng. Group, Dept. of Computer Science	UUCP: ...!mcvax!ukc!aber-cs!pcg
UCW, Penglais, Aberystwyth, WALES SY23 3BZ (UK)

gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/11/88)

In article <369@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>They were not forbidden, you could do any optimization that did not impact
>the semantics of programs. A Classic C program compiled with a dpANS C
>compiler changes semantics, unless you tag with volatile all variables not
>tagged register.

This, as with practically everything else Grandi has said about C,
is simply not true.  There are a few fairly subtle changes in the
semantics going from K&R 1st Ed. C to ANSI C, for example mixed
type promotion in expressions and linkage rules, but they affect
few programs.  There was never a guarantee that C data had what is
now known as the "volatile" property.  Existing compilers in fact
give the lie to such a claim.

>Exactly the opposite!  In Classic C the concept was that the compiler would
>take register as an hint of which variables could always be safely cached in
>register (and the user be forbidden to take pointer to them), and all the
>others could be cached only if it was guaranteed that doing so would not
>"cause surprises".

Please cite references for this unique notion of what pre-ANSI C's
rules were.  So far as I have been able to determine, it was never
specified one way or another, so by the usual rules for interpreting
C rules, this would have been an unwise assumption to make.
(Particularly since existing implementations did not follow that rule.)

>Actually it is a backwards step wrt to register, as it encourages bloated
>compilers and the delusion that they can select which variables to put in
>registers better than you can do, while requiring careful analysis as to
>which variables MUST be declared volatile (as failing to tag them makes
>your program erroneous, where register is perfectly safe).

"register" is still a supported specifier under ANSI C.  Use it if
you think your compiler does a poor job of register allocation.

The use of "volatile" does not require much analysis.  If you were
paying attention, I already explained in response to Griff Smith
the cases where you need to use it.  In pre-ANSI C the same problem
existed, but there was no standard solution for it.

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/12/88)

I realize that in my crudeness and brutality there is no hope for me to
achieve the extremely rarified levels of wisdom and learning of certain
people endowed with a quick grasp of issues and gentlemany manners of debate.

I therefore appeal (bowing my head, palms joined :->) to higher authority.

Let me quote and summarize from one such easily recognizable higher authority,
and repeat my own contentions (if it is boring for you, think how it is for me):

-----------------------------------------------------------------------------

#   4. What's in a name [ .... ]
#   Objects declared as characters ("char") are large enough to store any
#   member of the implementation's character set, and if a genuine character
#   from that character set is stored ina character variable, its value is
#   equivalent to the integer code of that character. Other quantities may be
#   stored in a character variable, but the implementation is machine
#   dependent.

character type == an integer type of sufficient length, whether "unsigned" or
"int" is up to the implementation.

#   Up to three sizes of integer, declared "short int" "int", and "long int"
#   are available.  [ .... ]

integer type == any one of the three lengths of "int", not just "int".

#   Unsigned integers, declared "unsigned", obey the laws of arithmetic
#   modulo "2^n", where "n" is the number of bits in the representation. (on the
#   PDP-11, unsigned long quantitied are not supported).

unsigned integer type == "unsigned" integer of all lengths, except of the
PDP-11. Semantics are different from thsoe of integer types, as they obey the
rules of modular, not algebraic, arithmetic.

#   [ .... ] Because objects of the foregoing types can be usefully interpreted 
#   as numbers, the will be referred to as "arithmetic" types. Types "char" and
#   "int" of all sizes will be collectively called "integral" types. [ .... ]

character type == "char", some large enough integer or unsigned integer type;
unsigned integer type == "unsigned" of all lengths;
integer type == "int" of all lengths (occasionally includes also "unsigned"s);
integral type == all three of them.
arithmatic type == integral types plus all lengths of "float".

#   6.1 Characters and integers
#   A character or a short integer may be used whenever an integer is used.
#   In all cases the value is converted to an integer.

There is no behavioural difference between char, short and other lengths of
"int", but for their range.

#   Conversion of a shorter integer to a longer always involves sign
#   extension; integers are signed quantities.

Integer types involve sign extension, by contrast with unsigned integer types.

#   Whether or not sign extension occurs for characters is machine dependent,
#   [ .... ].

Whether or not "char" is an integer or unsigned integer type is not prescribed.

#   [ .... ] When a longer integer is converted to a shorter or to a "char",
#   it is truncated on the left; excess bits are simply discarded.

There is no behavioural difference between "char" and "short", or other
lengths, except their size.

#   6.5 Unsigned
#   Whenever an unsigned integer and a plain integer are combined, the
#   plain integer is converted to unsigned and the result is unsigned.
#   The value is the least unsigned integer congruent to the signed
#   integer (module "2^wordsize"). [ .... ] When an unsigned integer is
#   converted to "long", the value of the result is the same numerically
#   as that of the unsigned integer. [ .... ]

The rules for conversions involving unsigned integers are different from
those for integers.

#   7. Expressions [ .... ]
#   The handling of overflow and divide check is expression evaluation is
#   machine dependent. [ .... ]

Note insofar overflow is concerned this only applies to integer types, as
unsigned integer types cannot overflow by definition. In other words,
exceeding the range of a length of "int" is not well defined, while exceeding
the range of a length of "unsigned" is.

Another case where there are behavioural differences between unsigned integer
and integer types.

#   7.2 Unary operators
#   [ .... ] The result of the unary "-" operator is the negative of its
#   operand. The usual arithmetic conversions are performed. The negative of
#   an "unsigned" quantity is computed by subtracting its value from "2^n",
#   where "n" is is the number of bits in an "int". [ .... ]

Another case where there are behavioural differences between unsigned integer
and integer types.

#   7.5 Shift operators
#   [ .... ] The right shift is guaranteed to be logical (0 fill) if "E1"
#   is "unsigned"; otherwise it may be (and is, on the PDP-11), arithmetic
#   (fill by a copy of the sign bit).

Another case where there are behavioural differences between unsigned integer
and integer types.

#   8.2 Type specifiers
#   [ .... ] The words "long", "short" and "unsigned" may be thought of as
#   adjectives; the following combinations are acceptable: [ .... ]

Here lies the crux of the matter. Throughout it is repeatedly and explicitly
stated that unsigned integer types behave differently from integer types, and
that the character type does not behave differently from a sufficiently
long/short unsigned integer or integer type.

Given this and the quoted phrase, it is apparent in hindsight that syntax and
semantics are incomplete, as there is no way to ensure the signedness of a
"char" (a similar problem exists with bit fields), and that syntax does not
properly reflect semantics.

dpANS C addresses the first point only, adding the "signed" keyword that can
thought of as another adjective and adding several cases to the table of
acceptable combinations.

My contentions (for the last time!) are that

    [1] this is not necessary, as it is more natural to drop the pretense
    that "char" is a type distinct from "int", and instead adopt the notion
    that "char" is like "short", an adjective that modifies the length of its
    base type;

    [2] it does not resolve the issue of making clear that "unsigned" is
    semantically different from "int", while the various lengths of either
    type are, but for the different ranges, semantically equivalent among
    themselves, and this distinction is important;

    [3] both points can be economically addressed by redefining as integral
    types the class of all integer and unsigned types, as integer types the
    various lengths of "int", as unsigned types the various lengths of
    "unsigned", and as length adjectives/modifiers the keywords "char",
    "short", "long"; when the adjective is omitted, the base type has the
    length of "short" or "long", depending on the implementation; when the
    base type type is omitted, "int" is presumed, except for length "char",
    where the choice is implementation dependent.

    [4] the proposed rationalization, provided that "unsigned int" is made as
    a special case equivalent to "unsigned", is backward compatible;

    [5] because of a easily made "mistake", some compilers, in the past or
    now, did not/do complain when the rationalized syntax was/is used, and
    this could be easily blessed instead of eradicated;

    [6] if it is felt desirable to substantially modify the declaration of
    "int" or "unsigned" types, a new keyword could be introduced for range
    definition, or the syntax for bit fields could be allowed outside

-------------------------------------------------------------------------

Kind reader, having had the patience to reach this point, make a last effort,
and please circle what you believe to be the correct answers:

[1] The material quoted above:

    [A] Is excerpted in an accurate, substantial and non misleading way
	from "The C programming Language - Reference Manual" (1978) the
	authoritative definition of Classic C.
    [B] I have made it up.

[2] The summaries I have made of the various passages quoted:

    [A] Accurately reflect the contents of said Reference Manual, or at
	least a consistent and historically defensible interpretation of
	those contents.
    [B] I have never read/understood the Reference Manual.

[3] The final contentions and suggestions are:

    [A] Supported by fair and reasonable technical arguments, based on the
	contents of said Reference Manual, as well as other more mundane points.
    [B] My advisor (if I had one) must be on drugs.
-- 
Piercarlo "Peter" Grandi			INET: pcg@cs.aber.ac.uk
Sw.Eng. Group, Dept. of Computer Science	UUCP: ...!mcvax!ukc!aber-cs!pcg
UCW, Penglais, Aberystwyth, WALES SY23 3BZ (UK)

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/12/88)

It seems that my problem with Doug Gwin is that he cannot conceive that his
rationales can be doubted: if you want signed characters, the only way is
to add a signed keyword, if you want efficient code the only way is a complex
optimizer that relies on volatile.

Since he cannot believe that there can be other approaches, he cannot help
assuming that I have not understood his premises, because my conclusions are
different.  The problem is that I am fully aware of his premises, and still
I have different ones.

In article <9143@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>)
writes:

    There was never a guarantee that C data had what is now known as the
    "volatile" property.

You mean that nobody explicitly stated the obvious rule that an optimizer
shall not turn a correct program into in incorrect one? Of course! As I said,
C was not designed for optimizing compilers; it was designed as a low level
language, and the very idea that an optimizer could do nasty things behind
the programmer's back was unthinkable.

C is indeed virtually unique in having the register keyword (other languages
allow you to assign variables to specific registers or memory locations,
notably Ada, LIS, Modula-2, Mesa?, but C's register is not like that, of
course), by which the programmer helps the compiler, precisely because the
compiler is not expected assumed or required to have or need an optimizer.

The point here is not to ASSUME that nasty optimization is necessary and
legitimate (and then indeed volatile become necessary to cover your back) the
point is to DEFEND the philosophy under which nasty optimization is necessary
in a language like C that was consciously designed with quite different
assumptions and goals (ever heard this about UNIX and C : simple is beutiful
AND reliable AND easier AND faster AND... or are you a post-V7 guy? :->).

Also, given your belief that Classic C did not explicitly prohibit wanton
caching of non register variables, well, this was what dpANS C ought to be
about, closing loopholes, clarifying that it was never explicitly forbidden
because it was never thought necessary, encouraging the use of register. NOT
introducing volatile. X3J11 has indeed done a fine job in most other areas on
clarifying, closing loopholes, etc...

    Existing compilers in fact give the lie to such a claim.

Then they are buggy! I would like to remind you that C is not what you think
it to be (a close cousin of Ada, a son of BSD), it is what has been defined
to be and used for years and years before a lot of commercial interests saw a
competitive advantage in claiming their compiler would improve sloppy C code;
the world does not begin with dpANS, or with 4.xBSD/S5.x.

    Please cite references for this unique notion of what pre-ANSI C's rules
    were.  So far as I have been able to determine, it was never specified
    one way or another, so by the usual rules for interpreting C rules, this
    would have been an unwise assumption to make.  (Particularly since
    existing implementations did not follow that rule.)

Again, it has never been a usual rule that an optimizer is allowed to turn a
correct program into an incorrect one. If an existing implementation does it,
it is buggy. This volatile argument is as old as programming; every PL/1
programmer knows that many a compiler will generate buggy code when the
optimizer is let loose on code with exception handlers.

They do not think that that is a fault of the language, they rightly think
that it is the compiler that is wrong, and they greatly resent having to use
a compiler option to influence the semantics of a piece of code (ahhhh, if
only PL/1 included volatile for variables, instead of only for procedures! :->).

    "register" is still a supported specifier under ANSI C.  Use it if you
    think your compiler does a poor job of register allocation.

But in any case I still must volatilize all other variables, otherwise the
compiler will do funny things behind my back. Volatile and register are
constrasting approaches to solve the same problem, one is safe and does not
suggest that a complex optimizer is needed, the other is potentially unsafe
and deludes people into buying complex, often unreliable compilers for a
language that was carefully evolved and designed not to require them (and
therein lies one of its beauties, maybe the most important -- otherwise we
would be using Ada or PL/1).

Volatile encourages people to think that a complex optimizer will clean up
their sloppy C code. Too bad that the cleanup cannot be as good as
competently written code, that omitting volatile where it is needed results
in very hard to find bugs, that complex optimizers are themselves, for
obvious reasons, often very buggy.

    The use of "volatile" does not require much analysis.  If you were paying
    attention, I already explained in response to Griff Smith the cases where
    you need to use it.

My dear Doug Gwin, rest assured that your paternalistic innuendo is not
necessary.  I know perfectly well the rationale for "volatile" (enable
optimizations where side effects may occur); I dispute that the only solution
to the problem is "volatile", as I dispute the rationale itself.

I would also like to emphasize that the real big problem will be multi
threaded C code, not signal handlers or device drivers. As any Ada programmer
knows, if you use tasking and fail to stick pragmas volatile/shared wherever
they are needed, maybe in dozens of modules, and that may be a lot of places,
you get into BIG trouble.

    In pre-ANSI C the same problem existed, but there was no standard
    solution for it.

The problem did not exist, because it was unthinkable that the compiler would
do funny things behind your back! If it did, the solutions were:

Debug the compiler! Or, if you could not do that, wrap the reference to the
variable around an asm procedure (which is usually necessary anyway in device
drivers to correctly access device registers, as compilers cannot give the
guarantee that they will generate the exactly right code), as procedures are
all considered "volatile" (in C and almost all other languages I know --
several dozen -- except PL/1 and some AI/Lispish ones).

I would like to add a note (hope it does not start another stream of
misunderstanding, misrepresentation and abuse towards me :->)

    I would like to be able (maybe in C++ ?) to declare a procedure as
    "register", to signify that the values it returns only depend on the
    arguments and not on any globals, and thus it will always return the same
    values given the same arguments.

    Apart from giving a very clever (that, as I said, I do not like) compiler
    a good opportunity to do some caching of procedure's results, it would be
    extremely useful to a mechanical (lint?) or human reader/verifier of the
    code, as it would clearly document which procedures are functions and
    which are generators/closures, an extremely important distinction.
-- 
Piercarlo "Peter" Grandi			INET: pcg@cs.aber.ac.uk
Sw.Eng. Group, Dept. of Computer Science	UUCP: ...!mcvax!ukc!aber-cs!pcg
UCW, Penglais, Aberystwyth, WALES SY23 3BZ (UK)

henry@utzoo.uucp (Henry Spencer) (12/14/88)

In article <369@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>... In Classic C the concept was that the compiler would
>take register as an hint of which variables could always be safely cached in
>register (and the user be forbidden to take pointer to them), and all the
>others could be cached only if it was guaranteed that doing so would not
>"cause surprises".

Please cite references for this.  It's never been true.
-- 
SunOSish, adj:  requiring      |     Henry Spencer at U of Toronto Zoology
32-bit bug numbers.            | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

ok@quintus.uucp (Richard A. O'Keefe) (12/14/88)

In article <375@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>My contentions (for the last time!) are that
>
>    [1] this is not necessary, as it is more natural to drop the pretense
>    that "char" is a type distinct from "int", and instead adopt the notion
>    that "char" is like "short", an adjective that modifies the length of its
>    base type;
>

That change would *FORCE* compiler writers to break working code.
There are programs which were written for machines with unsigned chars
(predating the introduction of 'unsigned char') where the programmers
relied on the char range being 0..255.  While this was not _portable_,
that implementation was _permitted_.  If you now rule that
	char = char int and is *signed*
then to conform to the standard, compiler writers for those machines
would _have_ to make plain chars signed.  The method X3J11 chose
_allows_ compiler writers for those machines to keep their old semantics
for plain 'char', and thus lets them offer backwards compatibility to their
customers.  It simply wasn't possible to make the new standard compatible
with all the old compilers, but X3J11 didn't introduce incompatibility
lightly.

Don't forget, the signed/unsigned ambiguity of 'char' was provided in the
first place for very practical reasons:  there are machines where signed
chars are more efficient, and there are machines where unsigned chars are
more efficient.  That is still true, so we _still_ want a way of saying
"whichever of signed byte/unsigned byte is cheaper, I promise not to care".

To be perfectly frank, I don't expect to have any use for 'signed char'.
I particularly don't want all my programs which use 'char' (because I
wanted the efficiency and didn't need a large range) to be forced to use
signed bytes, so the "char = char int = signed char" rule, while it
would not _break_ my programs, would definitely make them less efficient.
NO THANK YOU!

wald-david@CS.YALE.EDU (david wald) (12/15/88)

In article <377@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>You mean that nobody explicitly stated the obvious rule that an optimizer
>shall not turn a correct program into in incorrect one? Of course! As I said,
>C was not designed for optimizing compilers; it was designed as a low level
>language, and the very idea that an optimizer could do nasty things behind
>the programmer's back was unthinkable.

and:

>Again, it has never been a usual rule that an optimizer is allowed to turn a
>correct program into an incorrect one. If an existing implementation does it,
>it is buggy. This volatile argument is as old as programming; every PL/1
>programmer knows that many a compiler will generate buggy code when the
>optimizer is let loose on code with exception handlers.

You twice invoke this rule that "an optimizer shall not turn a correct
program into an incorrect one," and I have yet to see someone hint that
this statement is false.  But I have still not seen any supporting
references for your view of what constitutes "correctness." Your view
seems to be that all variables are volatile unless declared register,
and you assert that this was the intention of the "register" keyword.

K&R's statement of the meaning of register is

   "A register declaration advises the compiler that the variable in
   question will be heavily used.  When possible, register variables
   are placed in machine registers, which may result in smaller and
   faster programs." [4.7]

Clearly, the intent is to allow the optimization of placing the most
heavily used variables in processor registers.  Similarly,

    A register declaration is best thought of as an auto declaration,
    together with a hint to the compiler that the variables declared
    will be heavily used.  Only the first few such declarations are
    effective.  Moreover, only variables of certain types will be
    stored in registers....[Appendix A, 8.1]


As for your suggested use of "register," (i.e., saying "not volatile"),
I fail to see how this purpose is served if "only the first few such
declarations are effective."

Further, the semantic distinctions you refer to between "register" and
"auto" variables merely reflect the realities of registers on many
architectures:

   "In practice, there are some restrictions on register variables,
   reflecting the realities of underlying hardware.  Only a few
   variables in each function may be kept in registers, and only
   certain types are allowed.  The word register is ignored for
   excess or disallowed declarations.  And it is not possible to
   take the address of a register variable....  The specific
   restirictions vary from machine to machine....[4.7]

In this context, the rule about taking the address of a register
variable appears simply as an afterthought, due to the PDP-11
architecture, rather than an essential aspect of the semantics.

Finally, with regard to your comments about the place of optimizing
compilers in C:

   "Smaller, faster programs can be expected if register declarations
   are used appropriately, but future improvements in code
   generation may render them unnecessary" [Appendix A, 8.1]

Thus anticipating the future obsolesence of "register" as optimizers
improve.

If K&R is not a good sign of the original intent of a keyword, what are
you using as your authority?


============================================================================
David Wald                                              wald-david@yale.UUCP
						       waldave@yalevm.bitnet
============================================================================

henry@utzoo.uucp (Henry Spencer) (12/15/88)

In article <377@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>    There was never a guarantee that C data had what is now known as the
>    "volatile" property.
>
>You mean that nobody explicitly stated the obvious rule that an optimizer
>shall not turn a correct program into in incorrect one? ...

No, that's not what he meant:  he meant that your definitions of "correct"
and "incorrect" are historically wrong, and do not correspond to the way
C was defined, implemented, and used.  Very little has ever been guaranteed
about how C programs are evaluated, although some C programs (notably the
Unix kernel) have quietly relied on the limitations of old compilers.
ANSI C actually considerably strengthens the guarantees made to the
programmer.

Might one ask how long you have been using C, Mr. Grandi?
-- 
"God willing, we will return." |     Henry Spencer at U of Toronto Zoology
-Eugene Cernan, the Moon, 1972 | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

guy@auspex.UUCP (Guy Harris) (12/15/88)

>It seems that my problem with Doug Gwin is that he cannot conceive that his
>rationales can be doubted: if you want signed characters, the only way is
>to add a signed keyword, if you want efficient code the only way is a complex
>optimizer that relies on volatile.

Err, umm, first of all, that's "Gwyn", not "Gwin", as you can check the
attribution line in your posting.

Second of all, you appear also not to conceive that the notion that C is
a language whose compilers shouldn't optimize can be doubted, either. 

To put it bluntly, that's rubbish.  There are plenty of people out there
who can, I suspect, testify that yes, indeed, optimizing C compilers
*can* produce better code than non-optimizing ones, *even when the C
source is, in some sense, "good" code.*

As such, statements such as:

>You mean that nobody explicitly stated the obvious rule that an optimizer
>shall not turn a correct program into in incorrect one? Of course! As I said,
>C was not designed for optimizing compilers; it was designed as a low level
>language, and the very idea that an optimizer could do nasty things behind
>the programmer's back was unthinkable.

are unlikely to be very convincing to a large (and, I suspect
increasing) portion of the audience.  Sorry, but the notion that "the
very idea that an optimizer could do nasty things behind the
programmer's back was unthinkable" isn't as widely accepted as you
appear to wish it to be - and, I suspect, never was - and just because
some people *assumed* that the compiler generated "straightforward" code
doesn't mean 1) it was true in most implementations or 2) it was to be
taken in any way to be a guarantee. 

By and large, your arguments on "volatile", etc.  amount to "C was
always supposed to be a language whose compilers did little
optimization, and C compilers shouldn't do lots of optimization,
period."  Merely stating this premise repeatedly isn't going to convince
a lot of us, because we've seen quite good C code turned into better
code with a high-power optimizer on than with it off (and no, simply
declaring that that code must therefore not have been very good isn't
going to convince us, either).  Please try another approach, preferably
one involving hard data showing that good *and* readable C code
*doesn't* get turned into significantly better code when run through a
compiler with a more powerful optimizer than when run through a more
"straightforward" compiler - and showing this in a reasonable range of
modern architectures, including (but not limited to!) modern RISC
architectures.  "Proof by blatant assertion" isn't good enough.

ok@quintus.uucp (Richard A. O'Keefe) (12/16/88)

In article <377@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>This volatile argument is as old as programming; every PL/1
>programmer knows that many a compiler will generate buggy code when the
>optimizer is let loose on code with exception handlers.

>They do not think that that is a fault of the language, they rightly think
>that it is the compiler that is wrong, and they greatly resent having to use
>a compiler option to influence the semantics of a piece of code (ahhhh, if
>only PL/1 included volatile for variables, instead of only for procedures! :->).

(a) It's PL/I, not PL/1.
(b) PL/I *does* include "volatile" for variables.
    There are two pairs of attributes: IRREDUCIBLE/REDUCIBLE and
    ABNORMAL/NORMAL.  The default in PL/I is as in dpANS C: if you do not
    explicitly say otherwise the compiler will assume a variable is _not_
    "volatile".

ray@micomvax.UUCP (Ray Dunn) (12/17/88)

In article <369@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>In article <1450@micomvax.UUCP> I wrote:
>
>    This is of course not true, the qualifier is an attempt to provide a
>    *solution* to the problem.
>
>It IS the cause! volatile is not necessary, register is sufficient...
>I have tried to give arguments for this (at length :->).
>....etc. etc. etc....

Sigh...

It's not easy to come to a conclusion when each side is discussing totally
different subjects!

I'm talking about 'C', Mr. Grandi appears to be talking about some strange
set of ad hoc rules which are figments of his fertile imagination.

As several people have now said Piercarlo, your conceptions of what 'C'
defines on both this question of "volatile", and of associating signedness
to the word "int" amongst other things, are imaginary.  Please go back and
*absorb* the 'C' spec again, from the ground up.

I hope I have managed to refrained myself from escalating this into a
flamefest....
-- 
Ray Dunn.                      |   UUCP: ..!philabs!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

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/18/88)

In article <860@quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes:

	[ on my suggestion to make "char int" denote guaranteed signed chars ]

    That change would *FORCE* compiler writers to break working code.

Why ever? I have never advocated removing the rule that "char" by itself is
either "int" or "unsigned", merely added a way to guarantee that it would be
signed.

    There are programs which were written for machines with unsigned chars
    (predating the introduction of 'unsigned char') where the programmers
    relied on the char range being 0..255.  While this was not _portable_,
    that implementation was _permitted_.  If you now rule that

	char = char int and is *signed*

Never implied this. (I care about backwards compatibility, even if I don't
like it where is rewards, like in your example, non portable practices).

    but X3J11 didn't introduce incompatibility lightly.

Yet it introduced an unnecessary keyword, the perpetuates the unfortunate
syntax that favors the confusions that "char" is indeed a type and "unsigned"
is indeed just a variant of "int".

    [ .... ] we _still_ want a way of saying "whichever of signed
    byte/unsigned byte is cheaper, I promise not to care".

Indeed!. Since 'char' is  to be a length modifier, I assumed it was obvious
that in case the base type is missing, it would default to either 'int' or
'unsigned', while for all other length modifiers it would always default to
'int'.

In dpANS C the rule is conversely that the type modifier 'signed' is the
default for all the lengths of 'int', but not for type 'char' where the
default may be either 'signed' or unsigned'.

    To be perfectly frank, I don't expect to have any use for 'signed char'.

I have, I have! (I mean "char int" of course). In many many cases one wants
to really consider "char" a length specifier, and create arrays of byte sized
integers or unsigneds (or don't cares).
-- 
Piercarlo "Peter" Grandi			INET: pcg@cs.aber.ac.uk
Sw.Eng. Group, Dept. of Computer Science	UUCP: ...!mcvax!ukc!aber-cs!pcg
UCW, Penglais, Aberystwyth, WALES SY23 3BZ (UK)

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/18/88)

In article <1988Dec15.005828.1874@utzoo.uucp> henry@utzoo.uucp (Henry
Spencer) writes:

    >You mean that nobody explicitly stated the obvious rule that an
    optimizer >shall not turn a correct program into in incorrect one? ...

    No, that's not what he meant:  he meant that your definitions of
    "correct" and "incorrect" are historically wrong,

As somebody else has remarked to you, in the classic era of C, long since
dead, it was *unthinkable* that the compiler would be much more than a clever
translator. Also, AT&T C compilers *did* define for a long time what C was...
After all, the Ritchie PDP-11 compiler was known as the "Standard C Compiler"
for some reason, wasn't it?

Now, we all agree that language definitions *should not* depend on particular
implementations, and in clarifying many issues X3J11 has considerably
improved matters on this, but here we are discussing history and rationales.

Register, +=, ++, are all sure signs that C was meant to be compiled by a simple
compiler that relied on the programmer to use such constructs to help code
generation.

    and do not correspond to the way C was defined,

Just a moment. Ritchie never explicitly did allow aggressive optimization.
So, at the very least, your argument that it was allowed by default, and my
own that it was disallowed by default, are matters of opinion.

But, but I support my opinion by argument, as always. Ritchie introduced
"register" in Classic C, not "volatile", after all, and this *means*
something.  If he had meant aggressive optimization to be allowed, he might
not have introduced "register"; he would have surely deemed "volatile"
absolutely necessary, the more so as he used C to write, of all things,
Unix's device drivers.

    implemented, and used.

On PCs, on PCs...

    Might one ask how long you have been using C, Mr. Grandi?

Regularly, since 1980 (of course I had already bought K&R as soon as it was
out).  Probably my main impulse in extolling the virtues of Classic C is
therefore plain old nostalgia for the good ol' days of V7 Unix and C... (on a
Onyx with V7, or an 11/34 with 2.9BSD).

I still keep in my office a photograph of the 11/34 (248KB, 80MB disk, I used
to have five concurrent users with good performance).
-- 
Piercarlo "Peter" Grandi			INET: pcg@cs.aber.ac.uk
Sw.Eng. Group, Dept. of Computer Science	UUCP: ...!mcvax!ukc!aber-cs!pcg
UCW, Penglais, Aberystwyth, WALES SY23 3BZ (UK)

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/18/88)

In article <45697@yale-celray.yale.UUCP> wald-david@CS.YALE.EDU (david wald)
writes:

    K&R's statement of the meaning of register is

	[  .... quotes from K&R that form the basis of MY argument .... ]

    As for your suggested use of "register," (i.e., saying "not volatile"), I
    fail to see how this purpose is served if "only the first few such
    declarations are effective."

Hey, here we start to read different thing. I read that "effective" refers to
putting things into registers, not to the register keywords itself.  Even if
a register variable is not in fact held in a register (either because they
have all been allocated or the type is not suitable) this does not mean that
the register keyword is being ignored; on the contrary, you still cannot take
its address, for example. "register" contains a hint, and it is the hint that
may or not be effective, not the keyword.

    Further, the semantic distinctions you refer to between "register" and
    "auto" variables merely reflect the realities of registers on many
    architectures:

Realities that are not "mere"; they are quite important to portability.

    In this context, the rule about taking the address of a register variable
    appears simply as an afterthought,

I think it is the very core of the beauty of the idea of "register".

    due to the PDP-11 architecture, rather than an essential aspect of the
    semantics.

I disagree. First, *many* architectures do disallow pointers to registers, so
it was natural to introduce the restriction for the sake of portability.
Second, there is also the (at least equally important) consequence you may be
missing that the rule has on opportunities to alias a register variable...

    Finally, with regard to your comments about the place of optimizing
    compilers in C:

       "Smaller, faster programs can be expected if register declarations are
       used appropriately, but future improvements in code generation may
       render them unnecessary" [Appendix A, 8.1]

    Thus anticipating the future obsolesence of "register" as optimizers
    improve.

This is a good point. My answer to it is that Ritchie was too hopeful and
that when he foresaw the rise of optimizers he could not anticipate that
aggressive optimizers would turn out to be as a rule large, mostly slow,
quite often very buggy, and how effective "register" could be in achieving
efficient code at far lower expense.

Also, he might have missed the important point that an automated optimizer
can do reliably only a static analysis of frequency of use of variables,
without resorting to really amazing technology and/or information on the
expected distribution of input data.

    If K&R is not a good sign of the original intent of a keyword, what are
    you using as your authority?

A different, but still equally plausible, and maybe better, reading of K&R
than yours. And, maybe, some extra contextual information on how and why C
was designed and evolved in its early (pre BSD, pre SysV) history.
-- 
Piercarlo "Peter" Grandi			INET: pcg@cs.aber.ac.uk
Sw.Eng. Group, Dept. of Computer Science	UUCP: ...!mcvax!ukc!aber-cs!pcg
UCW, Penglais, Aberystwyth, WALES SY23 3BZ (UK)

ok@quintus.uucp (Richard A. O'Keefe) (12/19/88)

In article <420@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>In article <860@quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes:
>	[ on my suggestion to make "char int" denote guaranteed signed chars ]
>    That change would *FORCE* compiler writers to break working code.

>Why ever? I have never advocated removing the rule that "char" by itself is
>either "int" or "unsigned", merely added a way to guarantee that it would be
>signed.

So sorry!  I thought you wanted your proposal to be self-consistent.
Silly me.  The proposal, as I understood it, was that 'char' was to be a
size modifier _just_ _like_ 'short' and 'long'.  Now the rule is that
	short x;	==	short int x;
	long x;		==	long int x;
so naturally I assumed that you were proposing that
	char x;		==	char int x;
(Certainly "short short" would have worked like that.)

>    but X3J11 didn't introduce incompatibility lightly.
>
>Yet it introduced an unnecessary keyword.

It has been explained that X3J11 did not "introduce" the 'signed' keyword,
but accepted it as _existing_ _practice_.  If someone else has tried to
deal with a problem, you don't spit in their face even if you don't like
their solution much.  

>Indeed!. Since 'char' is  to be a length modifier, I assumed it was obvious
>that in case the base type is missing, it would default to either 'int' or
>'unsigned', while for all other length modifiers it would always default to
>'int'.

I'm sorry, but I don't see how such an exception is "obvious".

I personally regard the whole of C's integer type system as radically
misguided, and would argue with the utmost vehemence against its adoption
in any other language.  But C is the way it is, and there is no use
crying over spilt milk.  It's usable, it's understood.  Let's freeze it
and get on to better things.

guy@auspex.UUCP (Guy Harris) (12/20/88)

>    No, that's not what he meant:  he meant that your definitions of
>    "correct" and "incorrect" are historically wrong,
>
>As somebody else has remarked to you, in the classic era of C, long since
>dead, it was *unthinkable* that the compiler would be much more than a clever
>translator.

Err, umm, I think Henry was around in the classic era of C; so was I,
and so, I suspect, was Doug Gwyn.  It was never obvious to me that it
was "unthinkable", even if compilers at the time may not have done it
(although I know the VAX PCC's "c2" optimizer would do optimizations
like that - or, at least, the S3-vintage one in 4.xBSD, and I seem to
remember somebody, either Doug Gwyn or Henry Spencer, saying that the
Ritchie compiler would do so as well).

>Register, +=, ++, are all sure signs that C was meant to be compiled by
>a simple compiler that relied on the programmer to use such constructs
>to help code generation.

Err, umm, I seem to remember that "+=" was also present in Algol 68;
does that mean that it was also meant to be compiled by "a simple
compiler...."

>    implemented, and used.
>
>On PCs, on PCs...

And UNIX systems.  Did you not read the article posted by Doug Gwyn in
which he stated

	Wrong.  Even Ritchie's PDP-11 C compiler would occasionally do things
	when the peephole optimizer was enabled that caused trouble in device
	drivers, etc.  This was not generally considered a bug; one merely
	revised the code to outwit the optimizer or else turned off the
	optimizer.  Many of these problems could have been circumvented through
	the use of "volatile", if it had existed.

or did you simply choose to pretend he didn't say this?

henry@utzoo.uucp (Henry Spencer) (12/20/88)

In article <422@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes:
>As somebody else has remarked to you, in the classic era of C, long since
>dead, it was *unthinkable* that the compiler would be much more than a clever
>translator...

Curious, it was never unthinkable for me.  In fact, it's never been true,
given some of the peephole optimizations that even the original Ritchie
compiler was quite happy to do.

>After all, the Ritchie PDP-11 compiler was known as the "Standard C Compiler"
>for some reason, wasn't it?

I never heard it called that, actually, that I recall.  I've only been
using it since 1975, mind you.

>Register, +=, ++, are all sure signs that C was meant to be compiled by a simple
>compiler that relied on the programmer to use such constructs to help code
>generation.

Not really.  For one thing, operators like += (originally =+) are a
significant convenience even without any code-generation implications.
For another, you've missed a crucial distinction:  some of these operators
make it *possible* for a relatively simple compiler to generate good code,
given a clever programmer.  That does not imply that this was the only
mode of operation contemplated; some of DMR's own comments here and there
specifically indicate that he anticipated higher optimization later.

>Just a moment. Ritchie never explicitly did allow aggressive optimization.

Nor did he disallow it.

>... Ritchie introduced
>"register" in Classic C, not "volatile", after all, and this *means*
>something.  If he had meant aggressive optimization to be allowed, he might
>not have introduced "register"...

You are confusing *allowing* aggressive optimization with *requiring* it.
DMR put "register" in so that good code could be generated without major
optimization, but it was never meant to preclude more sophisticated means.
DMR himself observes in K&R1 that improved optimization might render the
"register" keyword unnecessary.

>he would have surely deemed "volatile"
>absolutely necessary, the more so as he used C to write, of all things,
>Unix's device drivers.

Dennis didn't set out to solve all the world's problems right from the
beginning.  If he thought about the issue, he probably assumed that it
would be dealt with, by him or someone else, when the problem began to
become too serious for simple workarounds.  It has been.

>    implemented, and used.
>
>On PCs, on PCs...

I don't understand this interjection.  If you intend to imply that my views
have been formed on PCs, this is laughable -- I've never compiled a C
program on a PC in my life.  (Well, I think I compiled a five-line one
on a PC running Coherent once, after I asked Randall Howard whether I
could try to crash it and he said "go ahead".  Famous last words... :-))
I learned C on a pdp11 with the Ritchie compiler under V5 (later V6 and
V7).

>    Might one ask how long you have been using C, Mr. Grandi?
>
>Regularly, since 1980 (of course I had already bought K&R as soon as it was
>out).  Probably my main impulse in extolling the virtues of Classic C is
>therefore plain old nostalgia for the good ol' days of V7 Unix and C...

What good ol' days?  "The thing history teaches us is that there never
were any good old days."  utzoo was a pdp11 running V7 until about 6
months ago.  The 11 is now our terminal server, so we still use the old
compiler on occasion for maintaining its software.  As I alluded to above,
C has been my major programming language since 1975.  I'm afraid that
I can't get nostalgic over a "Classic C" that never was.
-- 
"God willing, we will return." |     Henry Spencer at U of Toronto Zoology
-Eugene Cernan, the Moon, 1972 | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/20/88)

    >Register, +=, ++, are all sure signs that C was meant to be compiled by
    >a simple compiler that relied on the programmer to use such constructs
    >to help code generation.
    
    Err, umm, I seem to remember that "+=" was also present in Algol 68;

You remember well...

    does that mean that it was also meant to be compiled by "a simple
    compiler...."

You will also remember as well that they were introduced into the language
precisely to offer a cheap opportunity for the compiler to generate good code
without too much effort. While Algol68 is a compiler writer's nightmare from
a parsing viewpoint (and the support library is not easy to write as well),
it was carefully designed not to require complex code generation. The
and-becomes operators were one of the neatest ideas, readily borrowed in C.

    >    implemented, and used.  > >On PCs, on PCs...

    And UNIX systems.  Did you not read the article posted by Doug Gwyn in
    which he stated

	Wrong.  Even Ritchie's PDP-11 C compiler would occasionally do things
	when the peephole optimizer was enabled that caused trouble in device
	drivers, etc.  This was not generally considered a bug; one merely
	revised the code to outwit the optimizer or else turned off the
	optimizer.  Many of these problems could have been circumvented
	through the use of "volatile", if it had existed.

    or did you simply choose to pretend he didn't say this?
    
It so happens that his article has received this site only today, as far as
I could see (note that occasionally articles get lost as well).

I have already answered him privately, because I'd rather not debate publicly
an article so rich in morbid comments (it is me that is using "sleazy
ploys"?  hey man, watch your ton..  oops keyboard :-) !) and other exercises
of style. In any case, I don't see as my duty to address all the funny things
certain people say. It will be my pleasure however to address that message as
soon as I have time to digest it, because finally it contains some arguments,
and they are exactly those I had expected (...leave elegance to tailors :->).

As to the matter in the specific paragraph you cite, I thought it was obvious
that it supports my arguments. It merely reiterates what is common knowledge,
that even PCC peephole optimizers tend to be quite buggy, and in more than
one way.  What you "generally" consider to be a bug or a feature is your
business after all, but word twisting can only go so far.

Me, I see this as a further reason to believe that optimizers are often not
reliable, and relying on ever more complex ones should not be encouraged, by
way of volatile, on purely technical grounds. After all register will work
even if you don't use the optimizer, will it?
-- 
Piercarlo "Peter" Grandi			INET: pcg@cs.aber.ac.uk
Sw.Eng. Group, Dept. of Computer Science	UUCP: ...!mcvax!ukc!aber-cs!pcg
UCW, Penglais, Aberystwyth, WALES SY23 3BZ (UK)

guy@auspex.UUCP (Guy Harris) (12/20/88)

>This is a good point. My answer to it is that Ritchie was too hopeful and
>that when he foresaw the rise of optimizers he could not anticipate that
>aggressive optimizers would turn out to be as a rule large, mostly slow,
>quite often very buggy, and how effective "register" could be in achieving
>efficient code at far lower expense.

Please note that there are those who would - with good reason - claim
that there exist agressive optimizers that *are* cost-effective (cf.
MIPS).  Please note also that aggressive optimizers do more than just
choose whether to put variables into registers.  Please note, therefore,
that your sweeping claim may fail to convince (as is the case with a lot
of sweeping claims posted to USENET; why do those who present sweeping
claims merely repeat them more loudly when they are disputed?  Do they
think that their mere *insistence* of the validity of those claims will
convince people to dispute facts that oppose those claims?).

>A different, but still equally plausible,

...but neither more nor less plausible...

>and maybe better,

...and maybe worse...

>reading of K&R than yours.

In other words, it is at best a debate between two people reading the
same stuff different ways, although one side (the minority one,
apparently) is reading a lot more into the statements in K&R than the
other.

Frankly, unless and until DMR himself states that your interpretation is
correct, I'm going to assume that the less aggressive, if you will,
interpetation (i.e., the one that reads less into it) is correct.

>And, maybe, some extra contextual information on how and why C
>was designed

Oh, so you were there when it was designed, or perhaps personally
involved in its design?

>and evolved in its early (pre BSD, pre SysV) history.

*Sigh* as has been stated before, the Ritchie compiler (pre-3BSD,
pre-System V) performed what you might well consider "aggressive"
optimizations, as Doug Gwyn states.

pcg@aber-cs.UUCP (Piercarlo Grandi) (12/20/88)

In article <753@auspex.UUCP> guy@auspex.UUCP (Guy Harris) writes:

#   Oh wow, another *ex cathedra* pronouncement from Pontifex Grandi I.

The only one that takes tremendously seriously his own postings is you, Guy.
It is you that is getting in the popist mould, scorching the heretic with
excommunications.  I may be advocating unfashionable ideas, but with
reasonable arguments, and ones that people can understand, and can debate and
so gain more understanding of the issues, if they want.

I am merely trying to persuade you that there are good points to be made on
my side as well, not that I am infallible like a committee :->. All I get in
return is cheap jabs at my person.

#   Please note that there are those who would - with good reason - claim
#   that there exist agressive optimizers that *are* cost-effective (cf.
#   MIPS).

Oh yes. But the existence of people that claim, with fair and reasonable
arguments, that aggressive optimization works so well on their hardware (and
can be ported to other architectures as well), does not imply that it is a
generally desirable idea. C and UNIX were designed by and for the people that
could not afford the MIPSes of their day, weren't they? Cost effectiveness
depends a lot on what you happen to have in your wallett... Is by any chance
your example meant to show that dpANS C is custom designed for well heeled
customers of the MIPSes of this world :-( ?  :-> :->.

#   Please note also that aggressive optimizers do more than just choose
#   whether to put variables into registers.

Indeed, indeed. Please note that I have never said otherwise (I am even on
record as having admitted that they do also introduce many interesting
bugs).  While I think that register variable selection is one of the
optimizations that are best left to the programmer, if he is competent
enough, I have given some examples of optimizations that I think best not to
leave to the programmer.  Please note that the sad argument about complex
optimizer (un)reliability stands high in many people's everyday experience.
-- 
Piercarlo "Peter" Grandi			INET: pcg@cs.aber.ac.uk
Sw.Eng. Group, Dept. of Computer Science	UUCP: ...!mcvax!ukc!aber-cs!pcg
UCW, Penglais, Aberystwyth, WALES SY23 3BZ (UK)

chip@ateng.ateng.com (Chip Salzenberg) (12/22/88)

According to pcg@aber-cs.UUCP (Piercarlo Grandi), concerning the fact that
the signedness of char is implementation-defined:

>[...] it is apparent in hindsight that syntax and
>semantics are incomplete, as there is no way to ensure the signedness of a
>"char" (a similar problem exists with bit fields), and that syntax does not
>properly reflect semantics.

Sure.  But for hysterical -- oops, I meant historical -- reasons, X3J11
couldn't fix it.  They did provide a way to specify signed and unsigned
chars when we care to, which is all we really need anyway.

>My contentions (for the last time!) are that
>    [1] this is not necessary, as it is more natural to drop the pretense
>    that "char" is a type distinct from "int", and instead adopt the notion
>    that "char" is like "short", an adjective that modifies the length of its
>    base type;

Well, sure.  But you're too late.

X3J11 did a good job.  Let's leave well enough alone.
-- 
Chip Salzenberg             <chip@ateng.com> or <uunet!ateng!chip>
A T Engineering             Me?  Speak for my company?  Surely you jest!
	   Beware of programmers carrying screwdrivers.

guy@auspex.UUCP (Guy Harris) (12/22/88)

>As to the matter in the specific paragraph you cite, I thought it was obvious
>that it supports my arguments.

It's "obvious" only if you take "optimizing references to variables in C
is bad" as an axiom.  You seem to take it as an axiom.  Others do not.

>It merely reiterates what is common knowledge, that even PCC peephole
>optimizers tend to be quite buggy,

1) Ritchie's compiler wasn't PCC-based

2) it only reiterates that if you consider optimizing references to
   variables in that fashion to be a bug.  Again, others do not.

Note that in the cases where you consider X to be true, and others do
not, it should be quite apparent that repeating "X is true" 5000 times
is only going to annoy the others, not convince them.

>What you "generally" consider to be a bug or a feature is your
>business after all, but word twisting can only go so far.

Just because *you* consider it to be a bug, does that make it a bug even
if 99% of the users of the Ritchie compiler did not?  Yeesh.  Talk about
word-twisting; the pretzel factory seems to be working overtime....

guy@auspex.UUCP (Guy Harris) (12/22/88)

>The only one that takes tremendously seriously his own postings is you,
>Guy.

Rubbish.  Anybody who REFUSES to believe that optimizing repeated
references to variables, say, is anything other than a bug, and who
REFUSES to believe that aggressive optimization can be beneficial, and
REFUSES to give any evidence other than repeated postings of his own
pure opinions, is taking himself far more seriously than he deserves. 
That description fits you to a T, as *several* of the other posters on
this topic will agree.

>It is you that is getting in the popist mould, scorching the heretic with
>excommunications.  I may be advocating unfashionable ideas, but with
>reasonable arguments,

Excuse me, but who died and left you in charge, so that merely by
*stating* that your arguments are reasonable you can render them
reasonable?  Frankly, several people whose opinion I *highly* respect,
based on *demonstrated* technical competence, disagree with your
arguments and seem to disagree that they are reasonable.

>I am merely trying to persuade you that there are good points to be made on
>my side as well, not that I am infallible like a committee :->. All I get in
>return is cheap jabs at my person.

Right.  I'm sorry, but I can only conclude that somebody who REFUSES to
believe that those arguing with him can possibly be correct, and
REPEATEDLY claims that in some so-called "Classic" era of C it was
considered "unthinkable" to do the aforementioned types of optimization
and REFUSES to provide ANY objective evidence for this (and apparently
makes up reasons to dismiss evidence to the contrary) sure sounds like
he considers himself infallible.

>Oh yes. But the existence of people that claim, with fair and reasonable
>arguments, that aggressive optimization works so well on their hardware (and
>can be ported to other architectures as well), does not imply that it is a
>generally desirable idea.

*What*?  I very much doubt that the MIPS people don't think that
aggressive optimization is a generally-desirable idea.  In fact, I have
obtained the impression that they *did* think so - if they didn't, why
would they have bothered?

>C and UNIX were designed by and for the people that could not afford
>the MIPSes of their day, weren't they?

Define "MIPSes of their day".  MIPS boxes are hardly megabuck
mainframes.  They are actually quite cost-effective.

>Indeed, indeed. Please note that I have never said otherwise (I am even on
>record as having admitted that they do also introduce many interesting
>bugs).  While I think that register variable selection is one of the
>optimizations that are best left to the programmer, if he is competent
>enough, I have given some examples of optimizations that I think best not to
>leave to the programmer.  Please note that the sad argument about complex
>optimizer (un)reliability stands high in many people's everyday experience.

I hardly think there is a consensus for your view that aggressive
optimization of C is a Bad Thing.  Heck, a simple-minded assembler can
quite likely be more bug-free than most non-aggressively-optimizing C
compilers; shall we then discard C?

One can reasonably discuss the question of whether aggressive
optimization is a Good Thing.  One cannot do so, however, if one of
those involved in the discussion offers arguments that reduce to
"obviously, it's bad" rather than citing *objective* data to indicate
that it might be bad.  A lot of the hostility towards your postings is
really hostility towards your totally-unjustified unshakable faith in
your beliefs; when people *repeatedly* cite objective reasons *why* your
believes are *not* correct, you either ignore them or come up with even
more pronouncements (i.e., in response to people pointing out that
"Classic" C compilers did - *deliberately* - perform the optimizations
you decry, you simply dismiss that as being an indication that the
compilers in question are "buggy").