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