S
slebetman
Walter said:I think you missed bluejack's idea.
The usual implementation semantics for a function pointer is:
- set up any register preservation needed
- set up the parameters
- unconditionally perform a subroutine call to the address indicated
by the function pointer -- historically if not to the actual instruction
location whose value is stored in the pointer, then to an instruction
location determined by taking the pointer value to be an offset from
some register or some segment pointer
- save any expected return parameters
- undo any necessary register preservation
with this all handled inline.
A potential replacement semantics for a function pointer would be something
like this:
- construct a pointer to a block of code that would set up the
parameters, and another pointer to a block of code that would
retreive return parameters to the proper locatons, and put the address
of the structure containing both into a register reserved for this
purpose... or push the structure contents onto the stack
- unconditionally perform a lightweight call to the address indicated
by the function pointer, either as a direct value or as an offset
as per the above. A lightweight call would just record the return
address and then do the branch
- deallocate the above structure / pop the values from the stack
The process of building a function pointer would then involve writing
into memory one of two things:
- if the pointer is NULL, just write a lightweight jump back to the
return location, without having called the setup or teardown routine
- otherwise, write a sequence to retrieve the address of the
setup routine and to perform a lightweight call to it, followed by
a sequence to perform the subroutine call the the actual code
location, followed by a sequence to retrieve the address of the
teardown routine and to perform a lightweight call to it.
I can see how register preservation might be able to be handled later
after the call. But setting up of parameters must still be done before
the call. This is because the compiler cannot know in advance where the
parameters will be coming from so it cannot handle it later, it must be
handled by the caller. So the parameter passing overhead is still
there.
There is one possibility where this optimisation may be possible. That
is if the function is only called at only one place in the code. In
this case, the compiler definitely know in advance where all the
function parameters will be coming from and can put all parameter
passing code in the subroutine itself rather than the caller.
Note: I cannot at the moment think of any implementation of the
idea that does not involve at least -one- call/return: to go
below that, one would need self-modifying code... or code that
just tests for NULL and branches around the call, which is of course
what the CALL_MAYBE_NOOP macro does...
(Yes, we've looped back to where we started, but now we know that
the idea wasn't impossible, but that the CALL_MAYBE_NOOP implementation
is probably much more efficient anyhow...)
Ahh.. in this case we are indeed back where we started and in fact
doing a NULL test. In which case why not simply write if(fp!=NULL)
which makes it clearer what you are actually doing?