[comp.sys.mac.programmer] serious code generation bug in Lightspeed C

tim@hoptoad.uucp (Tim Maroney) (01/12/89)

There is a serious bug in how Lightspeed C handles the assignment
operation.  The lvalue is evaluated before the value is computed.  This
means that assigments to relocatable structure members do not work
correctly.

For example, suppose you have a relocatable data structure and a handle
to that structure in "h".  Further suppose that one member of the
structure referred to by "h" is of type Ptr, called "p".  Then this
straightforward statement gives an incorrect result:

	(*h)->p = NewPtr(size);

The address of "(*h)->p", the lvalue, is computed before the right-hand
side is evaluated.  The address of "(*h)->p" may change as a result of
the NewPtr trap, therefore it may be wrong on return from the trap and
the result of NewPtr may be assigned into invalid memory.

This is pretty amazing, even for a compiler like Think's that
deliberately produces low-budget, non-optimized code.  There is a
work-around, assigning the result of NewPtr into an intermediate
variable and then assigning it to the lvalue, but it is unwieldy, and
it is difficult to spot all places where this compiler bug could bite.
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"Gangsters would kidnap my math teacher, Miss Albertine, and I'd track them
 down and kill them one by one until she was free, and then she'd break off
 her engagement with my sarcastic English teacher, Mr. Richardson, because
 she'd fallen hopelessly in love with her grim-faced and silent
 fourteen-year-old savior." -- Nite Owl, in WATCHMEN by Alan Moore

kaufman@polya.Stanford.EDU (Marc T. Kaufman) (01/13/89)

In article <6275@hoptoad.uucp> tim@hoptoad.uucp (Tim Maroney) writes:
>There is a serious bug in how Lightspeed C handles the assignment
>operation.  The lvalue is evaluated before the value is computed.  This
>means that assigments to relocatable structure members do not work
>correctly.

I disagree that this is a bug.  This is how I want the compiler to work.  The
flip side of the coin is statements like:

	*p = ++*p;

I guess I would argue that a programmer wanting to write bulletproof code that
will survive compiler "improvements" and optimizer changes would
go out of his way to avoid writing order-of-evaluation dependent code.

How about:	HLock(h);
		(*h)->p = NewPtr(size);
		HUnlock(h);

since NewPtr is specifically named as a procedure that can have a side
effect of moving unlocked blocks (yes, I know, you don't want to do that here
because you couldn't move h out of the way to make room).

>For example, suppose you have a relocatable data structure and a handle
>to that structure in "h".  Further suppose that one member of the
>structure referred to by "h" is of type Ptr, called "p".  Then this
>straightforward statement gives an incorrect result:
                                    ^^^^^^^^^ (you mean "unanticipated")
	(*h)->p = NewPtr(size);

>The address of "(*h)->p", the lvalue, is computed before the right-hand
>side is evaluated.  The address of "(*h)->p" may change as a result of
>the NewPtr trap, therefore it may be wrong on return from the trap and
>the result of NewPtr may be assigned into invalid memory.

>This is pretty amazing,...

No, it isn't.  Many languages actually require the LHS to be evaluated before
the RHS, so that side effects with things like index values don't occur.
(FORTRAN comes to mind).  I don't think that C requires this, but it doesn't
prohibit it.  It is, in fact, the obvious order of evaluation if you walk the
expression tree in the order it was generated.  It is, in fact, the way Algol
is specified, and the way Algol machines (such as the Burroughs B5500 et. al.)
work internally.

>                        even for a compiler like Think's that
>deliberately produces low-budget, non-optimized code.  There is a
>work-around, assigning the result of NewPtr into an intermediate
>variable and then assigning it to the lvalue, but it is unwieldy, and
>it is difficult to spot all places where this compiler bug could bite.

Again, this is NOT a compiler bug.  It is a programmer bug.  You shouldn't
depend on order-of-evaluation defaults.

Marc Kaufman (kaufman@polya.stanford.edu)

rang@cpsin3.cps.msu.edu (Anton Rang) (01/13/89)

>There is a serious bug in how Lightspeed C handles the assignment
>operation.  The lvalue is evaluated before the value is computed.  This
>means that assigments to relocatable structure members do not work
>correctly.
>
>For example, suppose you have a relocatable data structure and a handle
>to that structure in "h".  Further suppose that one member of the
>structure referred to by "h" is of type Ptr, called "p".  Then this
>straightforward statement gives an incorrect result:
>
>	(*h)->p = NewPtr(size);

  This isn't a bug, though you could argue that it is undesirable.  As
Apple says in IM, don't write code like this--lock the handle before
you assign into it, then unlock it; alternatively, use a temporary.
  I'm not sure if C specifies whether an lvalue or rvalue should be
evaluated first in an assignment statement.  There are many
ambiguities, for instance:

  *p = f();  /* where f() modifies p */

in which the programmer probably expects *p to be evaluated first.

		Anton

+---------------------------+------------------------+----------------------+
| Anton Rang (grad student) | "VMS Forever!"         | "Do worry...be SAD!" |
| Michigan State University | rang@cpswh.cps.msu.edu |                      |
+---------------------------+------------------------+----------------------+

james@utastro.UUCP (James McCartney) (01/13/89)

In article <6275@hoptoad.uucp>, tim@hoptoad.uucp (Tim Maroney) writes:
> There is a serious bug in how Lightspeed C handles the assignment
> operation.  The lvalue is evaluated before the value is computed.  This
> means that assigments to relocatable structure members do not work
> correctly.

   Whether or not it is a bug, it used to be documented in the Xeroxed
supplement to the Lightspeed C manual. It doesn't seem to be in the new 
manual. I got bit by this one too long ago. Another workaround is to
lock and unlock the handle if you have several of these type of assignments.
   --- James McCartney

beard@ux1.lbl.gov (Patrick C Beard) (01/13/89)

In article <6275@hoptoad.uucp> tim@hoptoad.uucp (Tim Maroney) writes:
>There is a serious bug in how Lightspeed C handles the assignment
>operation.  The lvalue is evaluated before the value is computed.  This
>means that assigments to relocatable structure members do not work
>correctly.
>

Not only is this problem not a bug in the compiler, and a bug in the 
programmer, but it would lead to inefficient implementation of the
compiler and inefficient code generation!  No way do I want the 
compiler to know whether or not a lhs has a relocatable object in it.
That would force the compiler to generate a temporary location to
store the result of the NewPtr call, EVERY time I have a relocatable
object in the lhs.  Either the compiler generates the temporary, or 
the programmer does.  And with judicious Locking and unlocking of the
handle in question, a temporary need not be generated.  Therefore
your assertion that the lhs should be evaluated after the rhs is very
misguided.  Depending on the order of evaluation beyond operator precedence
rules leads to very non-portable code.

In fact, the problem is much more general than you think.  You can be
bitten by this bug in Pascals as well.  If you use the WITH statement,
e.g.
	WITH h^ BEGIN
		p := NewPtr(size);	{ put a pointer into a relocatable object. }
	END;

You can have the same problem.  h^ is evaluated at the beginning of the
WITH in most Pascal implementations.  Same problem.

Patrick Beard
Berkeley Systems, Berkeley CA

dplatt@coherent.com (Dave Platt) (01/13/89)

In article <6275@hoptoad.uucp> tim@hoptoad.uucp (Tim Maroney) writes:
> There is a serious bug in how Lightspeed C handles the assignment
> operation.  The lvalue is evaluated before the value is computed.  This
> means that assigments to relocatable structure members do not work
> correctly.
> 
> For example, suppose you have a relocatable data structure and a handle
> to that structure in "h".  Further suppose that one member of the
> structure referred to by "h" is of type Ptr, called "p".  Then this
> straightforward statement gives an incorrect result:
> 
> 	(*h)->p = NewPtr(size);
> 
> The address of "(*h)->p", the lvalue, is computed before the right-hand
> side is evaluated.  The address of "(*h)->p" may change as a result of
> the NewPtr trap, therefore it may be wrong on return from the trap and
> the result of NewPtr may be assigned into invalid memory.
> 
> This is pretty amazing, even for a compiler like Think's that
> deliberately produces low-budget, non-optimized code.  There is a
> work-around, assigning the result of NewPtr into an intermediate
> variable and then assigning it to the lvalue, but it is unwieldy, and
> it is difficult to spot all places where this compiler bug could bite.

I sympathize with you... I've been bitten by this characteristic of LsC
at least five times during the past year.  However, I must disagree with
your assessment of this as a "bug".  It isn't... at least, not according
to the Wholly White Book as written by Kernighan and Ritchie.

To quote from page 50 of the 1978 edition of K&R:

   Function calls, nested assignment statements, and increment and
   decrement operators cause "side effects" -- some variable is changed
   as a byproduct of the evaluation of an expression.  In any expression
   involving side effects, there can be subtle dependencies on the order
   in which variables taking part in the expression are stored.  One
   unhappy situation is typified by the statement

	a[i] = i++;

   The question is whether the subscript is the old value of i or the
   new.  The compiler can do this in different ways, and generate
   different answers depending on its interpretation.  When side effects
   (assignment to actual variables) takes place is left to the
   discretion of the compiler, since the best order depends strongly on
   machine architecture.

   The moral of this discussion is that writing code which depends on
   order of evaluation is a bad programming practice in any language.
   Naturally, it is necessary to know what things to avoid, but it you
   don't know _how_ they are done on various machines, that innocence
   may halp protect you.  (The C verifier _lint_ will detect most
   dependencies on order of evaluation).

The statemement that you've specified above can be rewritten (in the
abstract) as:

	OFFSET_TO_P[0[h]] = NewPtr(size);

Because the NewPtr() function can scramble or compact the heap, the
subexpression 0[h] (or *h, if you prefer) can be changed as a side
effect.  Hence, if the compiler has chosen to evaluate 0[h] prior to
calling NewPtr (as it is _permitted_ to do by the K&R definition!), then
the value it evaluated and cached away will have been invalidated, and
the result of NewPtr() will be stored into the wrong place.

As I said... I sympathize with your dismay and annoyance, but it's not
due to a bug in LsC;  it's due to a well-documented instance in which
different compilers may choose different orders of evaluation.

Running your program with TMON's heap-scramble option turned on will
catch most of these problems quite quickly.  Failing to perform such a
stress-test will tend to leave such bugs undiscovered until two days
after your product goes into general commercial release, at which point
machines will begin crashing with utmost predictability ( 1/2 ;-).

-- 
Dave Platt    FIDONET:  Dave Platt on 1:204/444        VOICE: (415) 493-8805
  UUCP: ...!{ames,sun,uunet}!coherent!dplatt     DOMAIN: dplatt@coherent.com
  INTERNET:   coherent!dplatt@ames.arpa,    ...@sun.com,    ...@uunet.uu.net 
  USNAIL: Coherent Thought Inc.  3350 West Bayshore #205  Palo Alto CA 94303

drc@claris.com (Dennis Cohen) (01/13/89)

In article <6275@hoptoad.uucp> tim@hoptoad.uucp (Tim Maroney) writes:
>
>This is pretty amazing, even for a compiler like Think's that
>deliberately produces low-budget, non-optimized code.  There is a
>work-around, assigning the result of NewPtr into an intermediate
>variable and then assigning it to the lvalue, but it is unwieldy, and
>it is difficult to spot all places where this compiler bug could bite.

I couldn't let this pass.  While I agree that it is undesirable behavior,
it is unfair to single out THINK/Symantec on this one.  This same "bug"
exists in MPW C, MPW Pascal, Turbo Pascal, TML Pascal, and LightSpeed
Pascal -- in fact, in every Mac compiler I've used.  This is another
example of why you are told to HLock your handles before doing these
sorts of things (that move memory).  The earliest programming examples
on the Mac listed the lvalue problem as a place where you HLock or use
a temporary.  It isn't really a bug since the code is supposed to be
compatible with MPW and you don't really want different behavior for such
a thing.

To sum up -- this isn't a BUG, it is just a very unfortunate and well-
documented FEATURE.


Dennis Cohen
Claris Corp.
------------
Disclaimer:  Any opinions expressed above are _MINE_!

) (01/14/89)

Tim Maroney (sun!hoptoad!tim) point out that Think C evaluates the left-hand
side of
	(*h)->p = NewPtr(size)
before the right-hand side (and consequently, the dereferenced *h points
to stale data).

This is explicitly permitted by C, both under existing practice and
in the Draft Ansi Standard.  The only exception to this would be
if h is defined as "volatile" (which, it in fact is).  Think C
doesn't appear to support volatile yet (and doesn't claim to be
Ansi compatible).

It would certainly be reasonable for Think to put a note in their
manuals warning programmers of this problem.  Until then, the cautious
programmer would be well advised to write

	{
	    Ptr		temp = NewPtr(size);

	    (*h)->p = temp;
	}

Which should operate correctly.

Thanks, by the way, for pointing this out -- I'm going to go through
a huge pile of code to see if I've been caught by the same problem.

Martin Minow
minow%thundr.dec@decwrl.dec.com

grg@berlin.acss.umn.edu (George Gonzalez) (01/14/89)

This reminds me of the medieval discussion of how many teeth does a
horse have.  Philosophers would allegedly discuss for hours how many
teeth they thought a horse ought to have.  Apparently nobody considered
actually looking at a horse and counting the teeth.

Wouldnt it make more sense for somebody to look at the C standard(s)
and see what they say about lhs vs rhs first evaluation?

tim@hoptoad.uucp (Tim Maroney) (01/15/89)

OK, I stand corrected.  The evaluation order is undefined.  Thanks to
those who pointed this out, and particularly to those who pointed out
what a weird and frustrating problem it is regardless.

On a couple of specific points:

It is silly to say that this is a well documented problem.  I've read
three drafts of Inside Mac, starting with the loose-leaf binder edition
unfathomable aeons ago, and that an assignment could require locking
has never been explicitly mentioned.  Dennis Cohen claims that it was
mentioned in a comment in one of the earliest programming examples on
the Mac.  This is not documentation, this is source code, and I hope
Apple doesn't become as bad as Sun at understanding the difference.
This is not a documented problem at all, much less well documented.
Both Apple and compiler developers should document it; neither does.

In article <1676@helios.ee.lbl.gov> beard@ux1.lbl.gov (Patrick C Beard)
writes:
>No way do I want the 
>compiler to know whether or not a lhs has a relocatable object in it.
>That would force the compiler to generate a temporary location to
>store the result of the NewPtr call, EVERY time I have a relocatable
>object in the lhs.  Either the compiler generates the temporary, or 
>the programmer does.  And with judicious Locking and unlocking of the
>handle in question, a temporary need not be generated.

True, the result from NewPtr (or whatever the value of the RHS may be)
has to be stored if the RHS is evaluated first.  However, if the LHS is
evaluated first, then the address for the lvalue has to be stored
instead.  The cost of storing one is precisely the same as the cost of
storing the other.  Efficiency is a bogus rationale.

Defensive?  Not me, buddy!  Maybe YOU'RE defensive, but I'M not! :-)
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"There are no Famous People on the net.  Only some of us with bigger mouths
 than others."  -- Dan'l Danehy-Oakes, The Roach

rang@cpsin3.cps.msu.edu (Anton Rang) (01/16/89)

In article <6315@hoptoad.uucp> tim@hoptoad.uucp (Tim Maroney) writes:
>   It is silly to say that this is a well documented problem.  I've read
>   three drafts of Inside Mac, starting with the loose-leaf binder edition
>   unfathomable aeons ago, and that an assignment could require locking
>   has never been explicitly mentioned.

  Well, maybe not *well* documented, but documented at least.  From
Inside Mac, volume II, page 16:

    The Lisa Pascal compiler frequently dereferences handles during
    its normal operation.  You should take care to write code that
    will protect you when the compiler dereferences handles in the
    following cases:

	[ ... ]

    Assigning the result of a function that can move or purge blocks
    (or of any function in a package or another segment) to a field in
    a record referred to by a handle, such as

	aHandle^^.field := NewHandle(...)

    A problem may arise because the compiler generates code that
    dereferences the handle before calling NewHandle--and NewHandle
    may move the block containing the field.

  Maybe this should have been emphasized more, but it was at least
mentioned (otherwise I probably would have run into it before).

+---------------------------+------------------------+----------------------+
| Anton Rang (grad student) | "UNIX: Just Say No!"   | "Do worry...be SAD!" |
| Michigan State University | rang@cpswh.cps.msu.edu |                      |
+---------------------------+------------------------+----------------------+

pratt@boulder.Colorado.EDU (Jonathan Pratt) (01/16/89)

In article <6315@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>OK, I stand corrected.  The evaluation order is undefined.  Thanks to
>those who pointed this out, and particularly to those who pointed out
>what a weird and frustrating problem it is regardless.
...
>It is silly to say that this is a well documented problem.  I've read
>three drafts of Inside Mac, starting with the loose-leaf binder edition
>unfathomable aeons ago, and that an assignment could require locking
>has never been explicitly mentioned.  Dennis Cohen claims that it was
>mentioned in a comment in one of the earliest programming examples on
>the Mac.  This is not documentation, this is source code, and I hope
>Apple doesn't become as bad as Sun at understanding the difference.
>This is not a documented problem at all, much less well documented.

As I struggle to avoid turning on the flame thrower...

From the ancient (10/9/84) promotional edition of IM (the phone book)
memory management chapter, page 11:

The Pascal compiler frequently dereferences handles during its normal
operation.  You should take care to write code that will not require
the compiler to deference [sic] handles in the following cases:

- Use of the WITH statement with a handle, such as

    WITH aHandle^^ DO ...

- Assigning the result of a function that can move or purge blocks
  to a field in a record referred to by a handle, such as

    aHandle^^.field := NewHandle(..)

And so on.  The last example is the most relevant - *well documented*.

Jonathan
pratt@boulder.colorado.edu

dowdy@Apple.COM (Tom Dowdy) (01/16/89)

In article <6315@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>It is silly to say that this is a well documented problem.  I've read
>three drafts of Inside Mac, starting with the loose-leaf binder edition
>unfathomable aeons ago, and that an assignment could require locking
>has never been explicitly mentioned.

Yes it has.  Volume II page 16 of Inside Mac says:

<And so the flocks were warned...> 

* Assigning the result of a function that can move or purge blocks
(or of any function in a package or other segment) to a field in
a record referred to by a handle such as:

aHandle^^.field := NewHandle(...)

A problem may arise because the compiler generates code that dereferences
the handle before calling NewHandle - and NewHandle may move the block
containing the field.

< Here endeth the lesson >

It goes on to suggest to either lock the handle down, or use a temporary
variable for the assignment.  (Which is the approach that I usually take,
as it allows for some additional sanity checking and error control in the
FailNIL model)

So it *is* documented.  I suppose that the warning could be a bit
more stern, and perhaps the compiler guides could warn you as well,
but it is in the Memory Manager section, which most Mac programmers
read before proceeding.  But this is subtle enough that perhaps
more warnings may be in order.  The reason most people don't think
about this is that they assume that compilers generate code from
left to right as you and I read.  

A subtle bug that is hinted at here effects users of Object Pascal
more often, but can bite others as well:
  
If you call a function with a field from a handle based record, or
Object Pascal object, as one of the parameters and the function
or procedure you are calling is in another, unloaded, segment
you will be in for a crash.  The MPW 3.0 compilers check for
this condition and warn you about it.  

  Tom Dowdy                 Internet: dowdy@apple.COM
  Apple Computer MS:27Y     AppleLink:DOWDY1
  20525 Mariani Ave         UUCP:     {sun,voder,amdahl,decwrl}!apple!dowdy
  Cupertino, CA 95014    
  "The 'Oo-Ah' Bird is so called because it lays square eggs."   

tim@hoptoad.uucp (Tim Maroney) (01/17/89)

In article <1532@cps3xx.UUCP> rang@cpswh.cps.msu.edu (Anton Rang) writes:
>  Well, maybe not *well* documented, but documented at least.  From
>Inside Mac, volume II, page 16:
>
>    The Lisa Pascal compiler frequently dereferences handles during
>    its normal operation.  You should take care to write code that
>    will protect you when the compiler dereferences handles in the
>    following cases:

Yes, but that starts with "The Lisa Pascal compiler".  There is nothing
documented about this being a problem with C.  In fact, since this section
started with a mention of the WITH statement, I assumed that all it was
talking about was Pascal.  There's no Apple documentation that this is
a problem in C -- and that includes the MPW C documentation.  Nor does
THINK document it with reference to LSC, though someone says they did
in previous editions of the manual.

>	aHandle^^.field := NewHandle(...)
>
>    A problem may arise because the compiler generates code that
>    dereferences the handle before calling NewHandle--and NewHandle
>    may move the block containing the field.

Notice again that this is phrased in compiler-specific language.  As it
happens, it also applies to C compilers and other Pascal compilers, but
that's not how things read.
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"I've got troubles of my own, and you can't help me out.
 So take your meditations and your preparations and ram it up yer snout!"
    - Frank Zappa, "Kozmik Debris"

florman@randvax.UUCP (Bruce Florman) (01/18/89)

In article <24012@apple.Apple.COM> dowdy@Apple.COM (Tom Dowdy) writes:

>A subtle bug that is hinted at here effects users of Object Pascal
>more often, but can bite others as well:
>  
>If you call a function with a field from a handle based record, or
>Object Pascal object, as one of the parameters and the function
>or procedure you are calling is in another, unloaded, segment
>you will be in for a crash.  The MPW 3.0 compilers check for
>this condition and warn you about it.  

    I'm not sure that I understand this statement.  Are you saying that 
the following code will crash if Foo and Bar are in different segments, 
and the segment containing Foo is unloaded when Bar is executed?

------------------------------------------------------------------------
TYPE
    SomeRecord = RECORD
	field1, field2 : INTEGER;
      END;

    SomeRecPtr = ^SomeRecord;
    SomeRecHdl = ^SomeRecPtr;

FUNCTION Foo(a : INTEGER) : BOOLEAN;
BEGIN
  Foo := a > 37;
END;

PROCEDURE Bar(srH : SomeRecHdl);
BEGIN
  IF Foo(srH^^.field2) THEN
    SysBeep(10);
END;
------------------------------------------------------------------------

    I can see where there would be a problem if Foo's parameter were 
being passed by reference, or if Foo's result were being assigned to 
a field of the record, but I don't understand how the code above can 
cause a problem.  Am I just being dense, or what?

>  Tom Dowdy                 Internet: dowdy@apple.COM
>  Apple Computer MS:27Y     AppleLink:DOWDY1
>  20525 Mariani Ave         UUCP:     {sun,voder,amdahl,decwrl}!apple!dowdy
>  Cupertino, CA 95014    
>  "The 'Oo-Ah' Bird is so called because it lays square eggs."   

Bruce Florman			florman@rand.org

yahnke@vms.macc.wisc.edu (Ross Yahnke, MACC) (01/18/89)

In article <6327@hoptoad.uucp>, tim@hoptoad.uucp (Tim Maroney) writes...

>In article <1532@cps3xx.UUCP> rang@cpswh.cps.msu.edu (Anton Rang) writes:
>>  Well, maybe not *well* documented, but documented at least.  From
>>Inside Mac, volume II, page 16:
>>
>>    The Lisa Pascal compiler frequently dereferences handles during
>>    its normal operation.....
> 
>Yes, but that starts with "The Lisa Pascal compiler".  There is nothing
>documented about this being a problem with C...
>Nor does THINK document it with reference to LSC...
>-- 
>Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim

Tim, you are simply wrong, and obstinate to belabour the issue. As other 
people in this group have pointed out, the problem you've mentioned is
documented in Inside Mac. Yes, of course the examples are in Pascal. 
That was the only development language readily available when the Mac 
was brought out. I don't think it takes a PhD. to extrapolate the hows 
and whys of the problem from Pascal to C, the two languages are not that 
incredibly different. For you to say that "There is nothing documented 
about this being a problem with C" is a specious palaver, and also 
completely untrue.

In the latest set of documentation for LSC, the small set of double 
manuals with the Symantec label that was issued for the 2.0 release,
on Page 121 of the User's Manual:

  If you are dereferencing Handles, make sure that the memory the Handle 
  points won't move while using it. Otherwise, HLock it (and HUnlock it 
  later). Example:

    HLock(aTEH);
    /* Without the previous HLock,  */
    /* the next two calls may not always work */

    /* (1) printf calls Memory Mgr: */
    printf("first char in text handle is %c\n", **(**aTEH).hText);

    /* (2) Handle dereference on the left is done BEFORE call: */
    (**aTEH).hText = (Handle)NewHandle(somesize);

    HUnlock(aTEH);

So the problem *IS* well documented if only you bother to read the 
manual. You do *own* a copy of the manual, don't you?

////////////////////////////////////////////////////////////
 Internet: yahnke@vms.macc.wisc.edu
   Bitnet: yahnke@wiscmacc(.bitnet)
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

tim@hoptoad.uucp (Tim Maroney) (01/19/89)

In article <1112@dogie.edu> yahnke@vms.macc.wisc.edu (Ross Yahnke, MACC) writes:
>Tim, you are simply wrong, and obstinate to belabour the issue. As other 
>people in this group have pointed out, the problem you've mentioned is
>documented in Inside Mac. Yes, of course the examples are in Pascal. 
>That was the only development language readily available when the Mac 
>was brought out. I don't think it takes a PhD. to extrapolate the hows 
>and whys of the problem from Pascal to C, the two languages are not that 
>incredibly different. For you to say that "There is nothing documented 
>about this being a problem with C" is a specious palaver, and also 
>completely untrue.

(Gosh, *both* specious and untrue?)

Ross, open your IMv2 to page 16.  Got it?  OK.  Now, look at the three
notes under "The Lisa Pascal compiler frequently dereferences handles
during its normal operation."  What is the first one?  That's right, it
is about the WITH statement, and has no applicability to any C
compiler.  What is the third one?  Yes, it concerns implicit passing by
reference of structures, something which no C compiler does, and again
has absolutely nothing to do with C.  Now, what is the second one?  It
concerns assigment to relocatable handles, and may or may not appear to
have any relevance to C.  Given that it is surrounded by
Pascal-specific references, and that the description in the text
frequently and explicitly limits its scope to the Lisa Pascal compiler,
it is frankly ridiculous to claim that this is documented by Apple as a
problem with C, or with any language other than Pascal.  It may be
obdurate in your eyes to point this out, but it is also true.  Or perhaps
you would prefer this phrasing: "It is a verisimilar assertion, and also
completely true."

>In the latest set of documentation for LSC, the small set of double 
>manuals with the Symantec label that was issued for the 2.0 release,
>on Page 121 of the User's Manual:
>
>So the problem *IS* well documented if only you bother to read the 
>manual. You do *own* a copy of the manual, don't you?

I stand corrected, with the disclaimer that I was only repeating what
another poster had said.  I can only wonder why you didn't bother to
send one of your flames to him.

Of course I have a copy of the manual, since I use the software.
Accusing someone of a crime in public is extremely rude.
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"There are no Famous People on the net.  Only some of us with bigger mouths
 than others."  -- Dan'l Danehy-Oakes, The Roach

mbkennel@phoenix.Princeton.EDU (Matthew B. Kennel) (01/20/89)

In article <6337@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>In article <1112@dogie.edu> yahnke@vms.macc.wisc.edu (Ross Yahnke, MACC) writes:
>>Tim, you are simply wrong, and obstinate to belabour the issue.
>>[other hosations follow...]
>
>(Gosh, *both* specious and untrue?)
>
>Ross, open your IMv2 to page 16.  Got it?  OK.  Now, look at the three
>notes under "The Lisa Pascal compiler frequently dereferences handles
>during its normal operation."  What is the first one?  That's right, it
>is about the WITH statement, and has no applicability to any C
>compiler.

An intelligent C compiler should perform this optimization automatically,
i.e. reduction of common subexpressions.

  What is the third one?  Yes, it concerns implicit passing by
>reference of structures, something which no C compiler does, and again
>has absolutely nothing to do with C.

There is no reason why a C compiler couldn't do it if it could figure
out that the results would conform to the standard.  Suppose it realized
that the called function never changed any of the values of the passed
structure, but only read its contents.  I would think it a reasonable
optimization to only pass the address of this structure and tweak off
the values from that instead of copying the whole thing onto the stack
or something like that.

>[more gripes follow]

I agree that this problem is not explicitly mentioned in IM, but as it
is also a function of specific compilers used, it is not Apple's respon-
sibility to alert programmers to this fact.  Indeed, a good programmer
ought to be able to guess that since Lisa Pascal did in fact have this
problem, that perhaps other compilers might as well.

Matt Kennel
mbkennel@phoenix.princeton.edu

>-- 
>Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
>"There are no Famous People on the net.  Only some of us with bigger mouths
> than others."  -- Dan'l Danehy-Oakes, The Roach

PS: this is my personal gripe....
Why does LSC generate such LAME code?  Does it optimize ANYTHING?

To be honest, I like everything about their C compiler (I bought it
after all), except the compiler!  On the upgrade from V2 to V3, all
sorts of bells & whistles were added (very useful ones, no doubt) but
the code generator did not seem to improve substantially.

Sorry to be so disrespectful of an otherwise fine product, but the code
generator is definitely NOT up to contemporary standards, even on 
microcomputers.

(what do you mean?  Port gcc onto a Mac 128?  No problem!) :)

lsr@Apple.COM (Larry Rosenstein) (01/21/89)

In article <1858@randvax.UUCP> florman@rand-unix.UUCP (Bruce Florman) writes:
>In article <24012@apple.Apple.COM> dowdy@Apple.COM (Tom Dowdy) writes:
>
>>  
>>If you call a function with a field from a handle based record, or
>>Object Pascal object, as one of the parameters and the function
>>or procedure you are calling is in another, unloaded, segment
>>you will be in for a crash.  The MPW 3.0 compilers check for
>>this condition and warn you about it.  

This is true only if the value you are passing is larger than 4 bytes.
Values smaller than 4 bytes are passed by value, but the Pascal compiler
passed a pointer for larger values, and the called routine copies the
parameter.  If the parameter is a field of a handle or object (objects are
implemented as handles), you have the same dereferencing problem.

The key thing to remember is that a call to one of your procedures could
result in the Memory Manager being called if the called routine is in an
unloaded segment.  

Also, you have to beware of ROM calls that claim to take a value paramter
but really don't.  The example people always run into is DrawString.  Since
a string is larger than 4 bytes, the compile passes as a pointer.
DrawString is in ROM so not segment is loaded, but the ROM code doesn't
copy the string (for efficiency).  DrawString does cause memory to move (it
may load a font).  The usual symptom is that the first call to DrawString
results in garbage but subsequent ones do work.

The MPW Pascal 3.0 compiler now tells you of all the cases where a use of
an object field could result in problems because of a heap compaction.  You
can turn off these errors with a compile flag if you know the problem can't
occur in a particular case.

Also, the MPW Pascal compiler doesn't have some of the problems mentioned
in this discussion (although the Lisa compiler did) when dealing with
objects.  

For example, if x is an object type with field h, and you write:
	x.h := NewHandle(...)
the compiler evaluates the left side of the assignment after the right side,
to prevent problems.  It doesn't do this in the case of explicit handles.

If you write
	with x do
	 h := NewHandle(...);
the compiler caches the object in a register and not a pointer to its
fields, so the statement is totally safe.  Again, this is not true of
explicit handles.

-- 
		 Larry Rosenstein,  Object Specialist
 Apple Computer, Inc.  20525 Mariani Ave, MS 46-B  Cupertino, CA 95014
	    AppleLink:Rosenstein1    domain:lsr@Apple.COM
		UUCP:{sun,voder,nsc,decwrl}!apple!lsr

tim@hoptoad.uucp (Tim Maroney) (01/21/89)

In article <6337@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>Now, look at the three
>notes under "The Lisa Pascal compiler frequently dereferences handles
>during its normal operation."  What is the first one?  That's right, it
>is about the WITH statement, and has no applicability to any C compiler.

In article <5623@phoenix.Princeton.EDU> mbkennel@phoenix.Princeton.EDU
(Matthew B. Kennel) writes:
>An intelligent C compiler should perform this optimization automatically,
>i.e. reduction of common subexpressions.

I really can't agree.  That changes the language definition.  Unless you
mean something different from an implicit WITH statement; if you mean that
common subexpressions are reduced at the code generation level, so that
two references to (*h)->m are only computed once, there are also problems
with that if any routines with side effects are called between the two
references.

>>  What is the third one?  Yes, it concerns implicit passing by
>>reference of structures, something which no C compiler does, and again
>>has absolutely nothing to do with C.
>
>There is no reason why a C compiler couldn't do it if it could figure
>out that the results would conform to the standard.  Suppose it realized
>that the called function never changed any of the values of the passed
>structure, but only read its contents.  I would think it a reasonable
>optimization to only pass the address of this structure and tweak off
>the values from that instead of copying the whole thing onto the stack
>or something like that.

Given global optimization, that is, optimization that computes the
attributes of various routines and calls and optimizes the entire
linked body of software based on that, that's true.  However, such an
optimizer would also be smart enough to avoid dereferencing problems in
that case.  Closer to reality, I've never seen any globally optimizing
C compiler, only peephole optimized ones.

>Why does LSC generate such LAME code?  Does it optimize ANYTHING?
>Sorry to be so disrespectful of an otherwise fine product, but the code
>generator is definitely NOT up to contemporary standards, even on 
>microcomputers.

True, but in its defense, it is trying for speed and an optimizing pass
would slow things down a lot.  Still, for production software, they
should have an optional optimizing pass.  I mean, forget contemporary
standards, register optimization was pretty thoroughly understood at
the end of the 1970's.  There's no excuse for doing everything on the
stack any more.
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"But don't you see, the color of wine in a crystal glass can be spiritual.
 The look in a face, the music of a violin.  A Paris theater can be infused
 with the spiritual for all its solidity."
    -- Lestat, THE VAPIRE LESTAT, Anne Rice