wald-david@CS.YALE.EDU (david wald) (10/26/88)
In article <35569@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >In article <1988Oct24.172209.27031@ateng.ateng.com> chip@ateng.ateng.com (Chip Salzenberg) writes: >>According to g-rh@XAIT.Xerox.COM (Richard Harter): >>> And the truth of the matter is that there is no penalty for >>>writing portable code [...] > >>But there *is* a penalty in pandering to broken compilers. If, for example, >>a compiler breaks on: >> >> foo(s) short s; >> { >> short *sp = &s; >> int i = *sp; >> >> printf("%d\n", i); >> } > >>Then you have to invest time and effort into avoiding a language construct >>-- taking the address of a function parameter -- that should have worked. >>(I know that some compilers do, in fact, break this contruct.) I'm sure all >>will agree that spent time and effort are just as much a "penalty" as >>execution time. > > This is a good example. It contains two coding techniques that I >do not use and, indeed, would not think of using. The first is taking >the address of a function parameter. The second is initialization to a >dynamic quantity. Why do these things? The latter is just asking for >trouble. The only reason for taking the address of a function parameter >that I can think of is that you are calling a routine that requires a >pointer rather than a value. I don't know whether they are "supposed" >to work or not and I don't care; they are dubious constructs. I'm not sure why you consider the first of these a dubious construct. (The second I am willing to consider a non-portable abbreviation for an extra line of code.) I can't think of an architecture where this would be unusually difficult to implement, and on most architectures it would be as easy as taking the address of any automatic variable. Is it dubious only because there are compilers which break on it, or is there a more basic reason? ============================================================================ David Wald wald-david@yale.UUCP waldave@yalevm.bitnet ============================================================================
g-rh@XAIT.Xerox.COM (Richard Harter) (10/26/88)
In article <41337@yale-celray.yale.UUCP> wald-david@CS.YALE.EDU (david wald) writes: >In article <35569@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >> This is a good example. It contains two coding techniques that I >>do not use and, indeed, would not think of using. The first is taking >>the address of a function parameter. The second is initialization to a >>dynamic quantity.... >I'm not sure why you consider the first of these a dubious construct. >(The second I am willing to consider a non-portable abbreviation for an >extra line of code.) I can't think of an architecture where this would >be unusually difficult to implement, and on most architectures it would >be as easy as taking the address of any automatic variable. Is it >dubious only because there are compilers which break on it, or is there >a more basic reason? There are two reasons. One is technical, the other is a matter of general principles. The technical reason is that a calling sequence parameter need not have an address. Consider a hypothetical machine P which passes the first n arguments through registers. Taking the address of such an argument would then be equivalent to taking the address of a register. I will grant that it is easy enough to put a kludge in the compiler to get around this; any parameter whose address is referred to is copied onto the stack and that becomes the effective location. But it is a kludge and there is no guarantee that the compiler writers will handle this for you (particularly in a a one pass compiler!) The reason that this is bad as a matter of general principles is that the address of a calling sequence parameter is not a meaningful thing. Arguments in C are copies of the parameters passed. The address (if it exists) is the address of the copy; it is not, so to speak, an address of a real object. The general point is that calling sequence parameters are a different breed of cat from other variables. Taking the address of a calling sequence parameter implicitly ignores one of the respects in which they are different. -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
chris@mimsy.UUCP (Chris Torek) (10/27/88)
In article <35620@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >... The technical reason is that a calling sequence parameter need not >have an address. Consider a hypothetical machine P which passes the >first n arguments through registers. Taking the address of such an >argument would then be equivalent to taking the address of a register. (Actually, the latter is possible on some machines---e.g., Pyramids, some PDP-11s.) >I will grant that it is easy enough to put a kludge in the compiler >to get around this .... But it is a kludge and there is no guarantee >that the compiler writers will handle this for you (particularly in a >one pass compiler!) I would not call it a kludge---among other things, it has been legislated in by the dpANS for C---and I would claim that it is no harder in a one-pass compiler than in a multi-pass compiler. It is, however, true that some compilers have been known to get it wrong, so that, for instance, the following program writes NULs on certain Sun---ahem, I mean 68000-based machines :-) (I think the *original* non-paging Sun 1 V7ish compiler had the bug): print(c) char c; { (void) write(1, &c, 1); } >The reason that this is bad as a matter of general principles is >that the address of a calling sequence parameter is not a meaningful >thing. In some languages, perhaps. In C, no: the address of a parameter is the address of a variable. Parameter variables should act just like any other local variable. If the natural calling sequence of the machine does not provide this, the compiler should take whatever steps are necessary to make it appear so. Fortunately, the dpANS agrees with me this time. . . . :-) -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
karl@haddock.ima.isc.com (Karl Heuer) (10/27/88)
In article <35620@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >In article <41337@yale-celray.yale.UUCP> wald-david@CS.YALE.EDU (david wald) writes: >>In article <35569@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >>>This is a good example. It contains two coding techniques that I do not >>>use and, indeed, would not think of using. The first is taking the address >>>of a function parameter. The second is initialization to a dynamic >>>quantity.... > >>I'm not sure why you consider the first of these a dubious construct. >>(The second I am willing to consider a non-portable abbreviation for an >>extra line of code.) Interesting. I recognize that some compilers get the first one wrong (when the argument has a type which is affected by the default argument promotions), but I've never considered it unportable to initialize auto variables to an arbitrary expression. >[A] parameter need not have an address. Consider a hypothetical machine P >which passes the first n arguments through registers. I will grant that it >is easy enough to put a kludge in the compiler to get around this ... but it >is a kludge and there is no guarantee that the compiler writers will handle >this for you (particularly in a a one pass compiler!) The guarantee appears in the language specification. Similarly, I feel justified in using expression of type "char *" even though there exist architectures which have no natural representation of pointers to objects smaller than an int; I assume that if they're going to call it a C compiler, the implementors will add whatever kludges are necessary to fit the language to their machine. >The reason that this is bad as a matter of general principles is that the >address of a calling sequence parameter is not a meaningful thing. Arguments >in C are copies of the parameters passed. The address (if it exists) is the >address of the copy; it is not, so to speak, an address of a real object. I've declared it as a variable; the declaration appears as part of the function header. It should be just as addressible as any other variable I've declared. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
tim@crackle.amd.com (Tim Olson) (10/27/88)
In article <35620@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: | There are two reasons. One is technical, the other is a matter | of general principles. The technical reason is that a calling sequence | parameter need not have an address. Consider a hypothetical machine P | which passes the first n arguments through registers. Taking the address | of such an argument would then be equivalent to taking the address of a | register. I will grant that it is easy enough to put a kludge in the | compiler to get around this; any parameter whose address is referred to | is copied onto the stack and that becomes the effective location. But it | is a kludge and there is no guarantee that the compiler writers will handle | this for you (particularly in a a one pass compiler!) You are correct: a calling sequence may stipulate that the first few parameters are passed in registers instead of on the stack. It is not, however, a "kludge" to force this parameter out to memory if its address is taken -- that is how it must work. This is analogous to a compiler which tries to keep local variables in registers, unless their address is taken (i.e. ignore the "register" keyword). | The reason that this is bad as a matter of general principles is | that the address of a calling sequence parameter is not a meaningful | thing. Arguments in C are copies of the parameters passed. The address | (if it exists) is the address of the copy; it is not, so to speak, an | address of a real object. The general point is that calling sequence | parameters are a different breed of cat from other variables. Taking the | address of a calling sequence parameter implicitly ignores one of the | respects in which they are different. How do you suppose varargs functions are implemented? They must take the address of the first parameter to "step through" the parameter list. Are you suggesting that varargs functions are not portable and should be avoided? -- Tim Olson Advanced Micro Devices (tim@crackle.amd.com)
g-rh@XAIT.Xerox.COM (Richard Harter) (10/27/88)
In article <10124@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes: >In article <35620@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >>>>This is a good example. It contains two coding techniques that I do not >>>>use and, indeed, would not think of using. The first is taking the address >>>>of a function parameter. The second is initialization to a dynamic >>>>quantity.... >>>I'm not sure why you consider the first of these a dubious construct. >>>(The second I am willing to consider a non-portable abbreviation for an >>>extra line of code.) >Interesting. I recognize that some compilers get the first one wrong (when >the argument has a type which is affected by the default argument promotions), >but I've never considered it unportable to initialize auto variables to an >arbitrary expression. Well, I didn't say it was not portable -- I said that I wouldn't think of using it. I am not, therefore in a position to know whether it is portable. I (personally) don't think it is good form, portable or not. It does two things. It makes the declarations order dependent, which is an unnecessary source of complications. It also mixes the notions of data space allocation and assignment statements. I prefer to keep the notions distinct. Call it a prejudice. >>[A] parameter need not have an address. Consider a hypothetical machine P >>which passes the first n arguments through registers... >The guarantee appears in the language specification. *the* language specification. Surely you jest. >Similarly, I feel >justified in using expression of type "char *" even though there exist >architectures which have no natural representation of pointers to objects >smaller than an int; I assume that if they're going to call it a C compiler, >the implementors will add whatever kludges are necessary to fit the language >to their machine. The cases are not similar. Expressions of type "char *" are fundamental to the language. Taking the address of a calling sequence parameter is an oddity, much as 5["foobar"] is an oddity. One avoids using oddities because they are intellectual clutter; the fact that you avoid thereby one of the major ways in which compilers break is simply a bonus. Are you sure, by the way, that such a guarantee is in the major language specifications? Taking an address is not invariably guaranteed. Indeed, taking the address of a register variable is specifically forbidden. You may not take the address of a variable if it does not have one. Is this explictly stated K&R that parameters have addresses? In H&S? In ANSI? >>The reason that this is bad as a matter of general principles is that the >>address of a calling sequence parameter is not a meaningful thing. Arguments >>in C are copies of the parameters passed. The address (if it exists) is the >>address of the copy; it is not, so to speak, an address of a real object. >I've declared it as a variable; the declaration appears as part of the >function header. It should be just as addressible as any other variable I've >declared. Granted. As in: foo(thing) int *thing; {register int i; thing = &i;} Parameters are a different kind of thing from automatic and static variables. There are slightly different rules for them. Be that as it may, you didn't address (you should excuse the expression) the point, which is that taking the address of a parameter is not a useful kind of thing to do. > >Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
henry@utzoo.uucp (Henry Spencer) (10/27/88)
In article <35620@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >... The technical reason is that a calling sequence >parameter need not have an address... >... Arguments in C are copies of the parameters passed. The address >(if it exists) is the address of the copy; it is not, so to speak, an >address of a real object. The general point is that calling sequence >parameters are a different breed of cat from other variables... Sorry, not so. C parameters are defined, and have been defined for a long time, to be local variables that are initialized to the values supplied by the caller. They are specifically *not* a different breed of cat from other local variables. They are real objects; if the & operator is used on one (legally, i.e. in the absence of "register"), the parameter must have an address. This admittedly makes extra work for implementations that normally pass parameters in registers, but that's part of the job. A compiler that botches this is broken. -- The dream *IS* alive... | Henry Spencer at U of Toronto Zoology but not at NASA. |uunet!attcan!utzoo!henry henry@zoo.toronto.edu
barmar@think.COM (Barry Margolin) (10/28/88)
In article <35664@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >taking the address of a parameter is not a useful kind of thing to do. Why not? You are assuming that taking the address of a variable is only used when you plan to use it as an output parameter for a function. There are, however, functions that take pointers to input data. A good example is write(), which takes a pointer to the data to be written. Suppose you write a function that takes a structure as a parameter, and it wants to write that structure to a file, using write(). It will either take the address of the structure parameter, or it will have to make an automatic copy of the structure and take the address of that. I think taking the address of the parameter is preferable to making ANOTHER copy of the structure (the first copy has to be made during the calling sequence of the function). Barry Margolin Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar
guy@auspex.UUCP (Guy Harris) (10/28/88)
>How do you suppose varargs functions are implemented? They must take >the address of the first parameter to "step through" the parameter list. Umm, how "varargs" functions are implemented is a function of how they're implemented; they are *generally* implemented in the fashion you describe, but there is no *requirement* that they be so implemented. <varargs.h> (or <stdarg.h> as in the draft ANSI C standard) can be implemented however the vendor chooses, as long as they Do The Right Thing.
karl@haddock.ima.isc.com (Karl Heuer) (10/28/88)
In article <35664@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >In article <10124@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes: >>I've never considered it unportable to initialize auto variables to an >>arbitrary expression. > >Well, I didn't say it was not portable -- I said that I wouldn't think of >using it. I was responding to the previous poster, who considered it a "non-portable abbreviation for an extra line of code". I agree, btw, that there are things to be avoided in this area as a matter of style (I won't list them, lest I start a new flame war), but I don't take it to the extreme that you do. >>Similarly, I feel justified in using expression of type "char *" even though >>there exist [word-based] architectures ... > >The cases are not similar. Expressions of type "char *" are fundamental to >the language. Taking the address of a calling sequence parameter is an >oddity ... I think it's merely a difference in degree. If someone tried to market an alleged C compiler that couldn't handle "char *", they'd be laughed out of existence. If they get sloppy on parameter addressing, they can probably shrug it off as a "minor bug" for a while. >Are you sure, by the way, that such a guarantee is in the major language >specifications? Taking an address is not invariably guaranteed. Yes, I'm sure. It's implicit in K&R (which is not, and was not intended to be, a precise language specification). According to the dpANS (the formal spec), the only lvalues that cannot be &'d are registers and bitfields. (There also exist lvalues that are not modifiable, namely arrays and consts; this is an orthogonal concept.) >>I've declared it as a variable; the declaration appears as part of the >>function header. It should be just as addressible as any other variable >>I've declared. > >{register int i; thing = &i;} My statement stands. Non-parameter variables are addressible if and only if they are not declared register; the same holds for parameters. This makes it "just as addressible". >[The point is] that taking the address of a parameter is not a useful kind of >thing to do. I'll grant that it's not all that common, but I think it can be as useful as taking the address of an auto variable, and for the same reasons. In effect, a parameter *is* an auto variable which is initialized to the value of the actual argument. Your reasoning raises a couple of other questions. Do you ever change the value of a parameter variable, or do you treat it as a constant? Do you ever declare a formal parameter with the "register" keyword? Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
wald-david@CS.YALE.EDU (david wald) (10/30/88)
In article <10164@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes: >In article <35664@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >>In article <10124@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes: >>>I've never considered it unportable to initialize auto variables to an >>>arbitrary expression. >> >>Well, I didn't say it was not portable -- I said that I wouldn't think of >>using it. > >I was responding to the previous poster, who considered it a "non-portable >abbreviation for an extra line of code". I'm afraid I was just adding confusion here. By non-portable I meant that I wouldn't be surprised to find compilers that couldn't deal with it. >Do you ever change the value of a parameter variable, or do you treat it >as a constant? If "do you" is a question of style, my answer is "never." I can't think of any real excuse for doing it, and, as a matter of style, I like to know that the parameters passed into a function I'm writing are always available, and right where I think they are. ============================================================================ David Wald wald-david@yale.UUCP waldave@yalevm.bitnet ============================================================================
pausv@teorix.liu.se (Paul Svensson) (10/30/88)
In article <29781@think.UUCP> barmar@kulla.think.com.UUCP (Barry Margolin) writes: >In article <35664@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >>taking the address of a parameter is not a useful kind of thing to do. > [edited] >the address of that. I think taking the address of the parameter is >preferable to making ANOTHER copy of the structure (the first copy has >to be made during the calling sequence of the function). > The OBVIOUS solution is of course to pass the structure by address in the first place, if you're so afraid of copying it. Yes, I agree with Barry that taking tha address of a parameter is not very useful, but that is NOT a good reason for not allowing it. There are other good reasons, such as making it easier to pass arguments in registers, for example. On some machines, like the pdp-10, taking the address of a register (or a bitfield, but i digress) is easy, on others its just plain IMPOSSIBLE. To allow "&" to work on parameters is essentially the same as to allow it to work on register variables, it requires the same kind if "fixes" to the compiler. (Unless parameters are always passed on the stack, an arcane and resource-wasting policy.) /Paul
g-rh@XAIT.Xerox.COM (Richard Harter) (10/31/88)
In article <10164@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes: >In article <35664@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >Your reasoning raises a couple of other questions. Do you ever change the >value of a parameter variable, or do you treat it as a constant? Sometimes I do change the value. I never [:-)] write code without basing it in a paradigm or model of construction. In the most common model parameters represent fixed inputs, outputs to be returned, and values to be updated. The latter two categories are conveniently handled with pointers, again unchanged. [I never pass or return structures.] However there is a situation in which it is natural to alter a parameter value, that being when the parameter is the principle value in the main control loop. Thus process_node (s) node *s; { while (s) { ... code operating on node s = s->link; } } >Do you ever declare a formal parameter with the "register" keyword? Surely you jest! -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
jpdres10@usl-pc.usl.edu (Green Eric Lee) (11/01/88)
In article <10164@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes: >In article <35664@XAIT.Xerox.COM> g-rh@XAIT.Xerox.COM (Richard Harter) writes: >>Are you sure, by the way, that such a guarantee is in the major language >>specifications? Taking an address is not invariably guaranteed. > >Yes, I'm sure. It's implicit in K&R (which is not, and was not intended to >be, a precise language specification). According to the dpANS (the formal >spec), the only lvalues that cannot be &'d are registers and bitfields. >(There also exist lvalues that are not modifiable, namely arrays and consts; >this is an orthogonal concept.) Note that many machines with large register sets or register windows machines have optimizing compilers that keep ALL local automatic simple variables in registers, not only variables declared specifically as "register". This wrecks havoc with Unix. That's why the Pyramid 90x has the bogosity of a memory-mapped register window stack (so that you CAN take the address of a variable and have it mean something). That won't help the AMD29000 or the SPARC much, though. I suspect that what will happen then is that when the optimizer is deciding what register to allocate for what, it'll notice that you're taking the address of that particular variable -- thus forcing it into RAM, which is a performance hit if you use that variable often (e.g. a "char *" auto-incremented in a loop will be 6 times slower in RAM on the AMD than it would be in a register). -- Eric Green {mit-eddie,osu-cis,...}!killer!elg, killer!usl!elg
ok@quintus.uucp (Richard A. O'Keefe) (11/02/88)
In article <1013@teorix.liu.se> pausv@teorix.liu.se (Paul Svensson) writes: >Yes, I agree with Barry that taking tha address of a parameter is not >very useful, but that is NOT a good reason for not allowing it. >There are other good reasons, such as making it easier to pass arguments >in registers, for example. ... To allow "&" to work on parameters >is essentially the same as to allow it to work on register variables, All the compiler has to do if it sees & applied to an argument is to spill that argument to memory and use the in-memory version. Most of the RISC machines I've looked at adopt a convention where non-scalar and "excess" arguments are passed in a block on the stack, and gaps are left for the arguments which are assigned to registers, so each argument- in-a-register has a slot reserved for it in memory anyway.
tim@crackle.amd.com (Tim Olson) (11/04/88)
In article <84@usl-pc.usl.edu> elg@killer.UUCP (Eric Lee Green) writes: | Note that many machines with large register sets or register windows | machines have optimizing compilers that keep ALL local automatic | simple variables in registers, not only variables declared | specifically as "register". This wrecks havoc with Unix. How so? | That's why | the Pyramid 90x has the bogosity of a memory-mapped register window | stack (so that you CAN take the address of a variable and have it mean | something). That won't help the AMD29000 or the SPARC much, though. I | suspect that what will happen then is that when the optimizer is | deciding what register to allocate for what, it'll notice that you're | taking the address of that particular variable -- thus forcing it into | RAM, That is precisely what happens. | which is a performance hit if you use that variable often (e.g. a | "char *" auto-incremented in a loop will be 6 times slower in RAM on | the AMD than it would be in a register). Not so. Just because a variable has been aliased to memory does not mean that a local copy cannot also be kept in the register file and used from there. The compiler just has to ensure that the memory copy is updated correctly. To use your example: inner loop where p inner loop where p is in a register is aliased to memory ; sum += *p++; ; sum += *p++; load 0,17,gr121,lr6 load 0,17,gr121,lr6 add gr119,gr119,1 add lr6,lr6,1 exbyte gr121,gr121,0 exbyte gr121,gr121,0 add gr120,gr121,gr120 add gr120,gr121,gr120 cplt gr121,gr119,10 add gr119,gr119,1 jmpt gr121,L00012 cplt gr121,gr119,10 add lr6,lr6,1 jmpt gr121,L00012 store 0,0,lr6,lr2 It is only one instruction longer when the variable is aliased to memory. -- Tim Olson Advanced Micro Devices (tim@crackle.amd.com)
chip@ateng.ateng.com (Chip Salzenberg) (11/13/88)
According to g-rh@XAIT.Xerox.COM (Richard Harter): > Well, I didn't say it was not portable -- I said that I wouldn't >think of using it. I am not, therefore in a position to know whether it is >portable. Finally, something we can all agree on. -- 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.