[net.lang.c] Addresses of static functions

peterc@ecr.UUCP (Peter Curran) (01/29/85)

This is a discussion of a C semantics issue which I have not seen discussed
on the net, or elsewhere.

The original question was, "Is it OK to take the address of a >>static<<
function, and pass it to a separately-compiled function?"

Saying the answer is "no" appears to allow the compiler to take advantage of
the fact to optimize the calling sequence when data flow analyses, or whatever,
indicate that the full calling sequence is not required.

Saying the answer is "yes" means that I don't have to declare the comparison
function used deep inside an implementation of an abstract data type to be
external, even though its only use is to pass its address to "qsort", and it
shouldn't be visible to any of the users of the data type.

These arguments can be presented in various other ways, which I will omit.

This problem arose practically in Version 2.0 of the Venix system for the
IBM PC/XT, in their kludge to support large programs.  (The kludge is not
Venturecom's fault - they have actually done a goood job of supporting large
programs on a brain-damaged machine.)  However, they require a special form
of call for inter-segment calls, which they do not use for static calls, since
they are guaranteed to remain within a single segment.  This is of course only a
special case of the optimizations which may be possible.

In reality, the answer might have to be "yes", making the Venix code in error.
The reason is that a function may invoke another function by address. That
function may be a static function in the same module or an external function in
a separate module - unless the compiler gets very tricky, it will have to use
the full calling sequence.  Therefore, optimization may only be permitted for
functions which do not have their addresses taken.

Regardless of your views on all this, if you are really trying to write
portable C code, don't pass the addresses of static functions around.

ndiamond@watdaisy.UUCP (Norman Diamond) (01/30/85)

> The original question was, "Is it OK to take the address of a >>static<<
> function, and pass it to a separately-compiled function?"
> 
> This problem arose practically in Version 2.0 of the Venix system for the
> IBM PC/XT ...  they require a special form of call for inter-segment calls,
> which they do not use for static calls, since they are guaranteed to remain
> within a single segment.
> 
> In reality, the answer might have to be "yes", making the Venix code in error.
> The reason is that a function may invoke another function by address.

I think that Venix is in error, for exactly this reason.  They (and their
customers) have not been the only Intel users to be burnt by this quirk, and
C is not the only language in which this problem exists.

If the address of the function is taken, anywhere in the source module, then
the compiler should generate long calling sequences for the function.

-- Norman Diamond

UUCP:  {decvax|utzoo|ihnp4|allegra|clyde}!watmath!watdaisy!ndiamond
CSNET: ndiamond%watdaisy@waterloo.csnet
ARPA:  ndiamond%watdaisy%waterloo.csnet@csnet-relay.arpa

"Opinions are those of the keyboard, and do not reflect on me or higher-ups."

mike@hcradm.UUCP (Mike Tilson) (01/31/85)

The legality of exporting the address of a static function (e.g. by passing
a pointer to another routine in another compiled file) ought to be
identical to the legality of exporting the address of a static data
item -- if one is legal then the other should also be legal.  I think
this argues that the operation should be legal.

Quite some time ago, HCR implemented a memory mapping scheme to expand the
code address range of the smaller PDP-11 processors, similar to the later
Venix version.  In earlier versions, this kind of operation caused trouble.
In our implementation we fixed the problem.  We discovered that real programs
used this feature.  A typical use involved exporting an array of function
pointers with each function being "static".  This hid the function names, which
weren't supposed to ever be called except via the pointer table.  This seemed
like a reasonable thing to do.  I don't think it should be considered
non-portable.

/  Mike Tilson
/  Human Computing Resources Corp.

kpmartin@watmath.UUCP (Kevin Martin) (02/08/85)

In article <360@ecr.UUCP> peterc@ecr.UUCP (Peter Curran) writes:
>The original question was, "Is it OK to take the address of a >>static<<
>function, and pass it to a separately-compiled function?"
>
>Saying the answer is "no" appears to allow the compiler to take advantage of
>the fact to optimize the calling sequence...
>
>Saying the answer is "yes" means that I don't have to declare the comparison
>function used deep inside an implementation of an abstract data type to be
>external, even though its only use is to pass its address to "qsort", and it
>shouldn't be visible to any of the users of the data type.

Seems to me the correct answer is 'yes'. Any time you take the address of
*any* function, you need to ensure that the function accepts the 'long'
calling sequence, and that you generate a pointer that is usable anywhere.

This doesn't stop you from generating a function with *both* a fast and slow
entry (if your architecture and stack design allow it), and using the slow entry
for generating the pointers for (possibly) external calls, but calling the
quick entry entry when you call the function directly.
                  Kevin Martin, UofW Software Development Group

jsdy@SEISMO.ARPA (02/25/85)

Suggestion to make this usable (people who don't understand or WANT to
understand the 80*86 have my full understanding & envy, and may turn to
the next message.):
The problem seems to be that static functions, under VENIX, are
compiled as local to the segment and therefore have a short return
sequence rather than a long return sequence.  Well, the '&' unop, when
applied to a function, produces a pointer whose only use is as an
object from which to make an indirect call.  It need not be the exact
address in memory of the routine (although debuggers prefer this GREAT-
LY!).  Further, a static can only be accessed within its own module
(obviously).  Well, let's say that all function-addresses must be long
pointers, and that calls indirected from such pointers must ne long
calls.  Well, we can still have local static functions, if we think
it's worth it.  Just have, for each static whose address is taken in
the module, a long-return (unnamed) function whose sole purpose is
to copy the args back onto the stack and do a short-call to the static
routine.  'Course, this will cause problems with varargs functions ...

Maybe it would just be better to make all functions long-return
functions.

;-Z
.r .sig

ptw@encore.UUCP (P. Tucker Withington) (02/25/85)

>it's worth it.  Just have, for each static whose address is taken in
>the module, a long-return (unnamed) function whose sole purpose is
>to copy the args back onto the stack and do a short-call to the static

Since the static function by def'n can't be seen outside the module, why not
have *it* be the "(unnamed)" function?  Seems even debuggers could live with
that.  If they can live with inverted and non-inverted loops (did you use -O?)
they should be able to deal with direct and "indirect" functions.

Presumably a function declared static should be optimized for "local" use.
Taking the & of it and passing it out of the module, while legal, is perhaps
a bit perverse (although C++ might not agree with that).

Or you could choose a different CPU.

                               o.o      --tucker
                                ~

jsdy@hadron.UUCP (Joseph S. D. Yao) (03/13/85)

> >it's worth it.  Just have, for each static whose address is taken in
> >the module, a long-return (unnamed) function whose sole purpose is
> >to copy the args back onto the stack and do a short-call to the static
> ... [suggests, essentially, making statics long-return just like externs,
>	then doesn't like it] ...
> Or you could choose a different CPU.

I like that last choice best.  The 80*86 architecture never made me
happy.  Unfortunately, right about now the best selling u-chip is the
80*86 family, judging from the number of implementations I've seen.
Least expensive, first in the market (~), IBM-approved, etc.  Don't
flame me for those observations -- they're what is.	;-S

Joe Yao		hadron!jsdy@seismo.{ARPA,UUCP}