structure function returns -- how?

Robert Firth firth at sei.cmu.edu
Tue Dec 23 01:48:32 AEST 1986


In article <7434 at utzoo.UUCP> henry at utzoo.UUCP (Henry Spencer) writes:
>> (c) Function allocates static space and returns pointer.  Caller copies.
>> 
>>     This is not reentrant, as has been pointed out.  Nor does it work if
>>     the function is recursive...
>
>Not true, given proper code generation.  The static area need be in use
>only during the return sequence, hence recursion is no problem -- execution
>of multiple instances of a recursive function overlaps, but the return
>sequences need not.  Static-area return values may even speed up recursive
>returns, since less copying may be needed.  Reentrancy is another story,
>although it should be noted that reentrancy is another story for a *whole*
>*lot* of things in a C/Unix environment:  a signal handler has to be very
>careful, since locking out signal handlers during critical sections in (say)
>stdio is expensive and hence seldom done.
>
Agreed, but the codegeneration had better be very careful.  The trouble
comes if you are returning a composite value, and the computation of one
or more components involves a function call.  You can't use the return
area to build up the value component by component.

>>(e) Function leaves result on stack.
>>
>>    This is usually easiest for the function.  The problem is that on many
>>    systems an interrupt or signal will destroy the result.  (flame)(This is a
>>    symptom of a major and persistent system design error: the use of a
>>    hardware register pointing into user space as a place to dump junk.  It
>>    is compounded by language implementations that use the hardware stack
>>    to allocate the LIFO address space of local variables)...
>
>No, it's a symptom of a major and persistent software error:  using a stack
>pointer as if it were an index register.  If you get it firmly into your
>head that you are dealing with a *stack*, not just an index register that
>happens to increment and decrement automatically in some circumstances,
>then the problem goes away.  When you say "leaves result on stack", what
>you really mean is "leaves result beyond the end of the stack, assuming that
>the stack will not be used as a stack until the result is picked up".  It
>is possible, although often awkward, to leave the result *on the stack*,
>i.e. as if it had been pushed onto the stack, in which case there isn't a
>problem with interrupts or signals.  (This is approximately what your e3
>alternative does, but in an unnecessarily roundabout way using a magic
>run-time routine.)

Alternatively, it's bad hardware design adding special semantics to one
of the index registers... Let's agree that hardware stacks are a mess
and agree to differ on the cause of the mess.

Pushing the result on the hardware stack and then taking the return can
be very hard indeed, since the Return instruction usually pops the link
off the stack.  So you have to copy maybe the entire call frame below your
pushed result.  It most cases that's a lot of code, and you probably
want it out of line.

>> (d) Function allocates heap space, rest as above.
>> 
>>     ...always works but can be rather expensive.
>
>It always works in the absence of reentrancy, or if your memory allocator
>is reentrant, which many aren't.

Yes, Henry, thank you for the caveat.  I confess I had simply forgotten
that people design languages in which preemptive parallelism is possible
and then don't make the libraries properly reentrant.  

>Additional alternatives that you have missed are some variations on (b)
>(caller preallocates):  (1) it isn't necessary for the address to be passed
>as a parameter if the caller and callee can establish mutual agreement
>on where the result goes, although this is generally practical only if
>the stack is contiguous; (2) it may be possible to use the argument area
>as the return area, if the caller ensures it is big enough and avoids
>popping it off the stack or re-using it before picking up the result,
>and the callee makes sure that result copying comes after all uses of
>arguments (especially in the case where the returned value is the value
>of an argument).

Agreed.  I was giving only general methods that should work on most
machines, but failed to make that clear.  These are all possible tricks
it's useful to know.  Your (1) saves a parameter (and may simplify the
stack handling on the caller's side); I'm dubious whether the space
saved by (2) is worth the burden it places on the called function.

Just to repeat a point, I still think the key factor in the
design is whether the caller ALWAYS knows the size of the returned
value.  If it does, you have a several easy solutions; if it doesn't,
you have a harder task.  And this does affect implementors of C, if
they want to use the same codegenerator for C and for languages with
dynamically-sized return values.

>[Incidentally, it reflects badly on you to chide us for re-inventing the
>wheel when you seem not to be aware of some of the prior art yourself.
>You might find Bell Labs Computing Science Tech Report No. 102, "The C
>Language Calling Sequence", by Johnson and Ritchie, Sept. 1981, to be
>interesting reading.]
>-- 
>				Henry Spencer @ U of Toronto Zoology
>				{allegra,ihnp4,decvax,pyramid}!utzoo!henry

Well, what I said was "It's a shame to see people reinvent solutions".
If that gave the impression of chiding someone, I apologise; the comment
was meant purely as an introduction to what was supposed to be a
technically helpful post.  Incidentally, why do you think I haven't
read a work just because I don't quote it?  Now if only C had full
type checking of parameter lists!



More information about the Comp.lang.c mailing list