473,386 Members | 1,621 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

no-op function pointer


Ahoy... before I go off scouring particular platforms for specialized
answers, I thought I would see if there is a portable C answer to this
question:

I want a function pointer that, when called, can be a genuine no-op.

Consider:

typedef int(*polymorphic_func)(int param);

struct various_funcs {
polymorphic_func func_a;
polymorphic_func func_b;
};

int really_does_something(int param) { return ++param; }
int really_a_noop(int param) { (void)param; return 0; }

struct various_funcs does_stuff = { really_does_something,
really_does_something };
struct various_funcs noop_funcs = { reall_a_noop, really_a_noop };

int main (int argc, char** argv) {
int x = 10;
int y = does_stuff.func_a(x);
int z = noop_funcs.func_b(x);
return z;
}

----------------------------------

A lot of typing for the very obvious; but does the reference to
noop_funcs.func_b(x) actually make a function call? Or would the
compiler simply assign 0 to z? Is there any C way to ensure a noop? Or
is it going to be all about which compiler / which platform?

Or does it seem unlikely that a compiler could optimize that down to a
noop?

Naturally, I'm really thinking about scenarios in which a variable
contains a function pointer that can only be known as a noop at
runtime, which actually makes this MORE of a language problem in my
mind than a compiler problem. If the NULL function pointer were a noop,
this would be very convenient. But of course, the NULL function pointer
is not a noop, it's a null pointer, and dereferencing it is a segfault.
So is there language support for a function pointer that does nothing?

Ideas? Redirection?

Dec 14 '05 #1
23 7736


I assume your function pointer will be null when it is noop. Before
you call the function verify whether thats NULL or not. If it is NULL
then make noop( ; ) or else call the function.

Dec 14 '05 #2

Checking for NULL and calling if not NULL is, erm, less of a noop than
I was hoping for, but conversations with some guys over at the gcc
newsgroup suggest that's about the best I can hope for.

Dec 14 '05 #3
On 13 Dec 2005 22:31:07 -0800, "bluejack" <bl******@gmail.com> wrote
in comp.lang.c:

Checking for NULL and calling if not NULL is, erm, less of a noop than
I was hoping for, but conversations with some guys over at the gcc
newsgroup suggest that's about the best I can hope for.


Put your no-op function in a different source file and compile it
separately.

In the function where you assign its address to a pointer, only have
an external definition.

That should make sure the function is called.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Dec 15 '05 #4
void function_that_does_nothing( void )
{
asm( " nop" );
return;
}

On Wed, 14 Dec 2005 22:47:14 -0600, Jack Klein wrote:
On 13 Dec 2005 22:31:07 -0800, "bluejack" <bl******@gmail.com> wrote
in comp.lang.c:

Checking for NULL and calling if not NULL is, erm, less of a noop than
I was hoping for, but conversations with some guys over at the gcc
newsgroup suggest that's about the best I can hope for.


Put your no-op function in a different source file and compile it
separately.

In the function where you assign its address to a pointer, only have
an external definition.

That should make sure the function is called.


Dec 15 '05 #5
In article <j7********************************@4ax.com>,
Jack Klein <ja*******@spamcop.net> wrote:
On 13 Dec 2005 22:31:07 -0800, "bluejack" <bl******@gmail.com> wrote
in comp.lang.c:
Checking for NULL and calling if not NULL is, erm, less of a noop than
I was hoping for,

Put your no-op function in a different source file and compile it
separately. That should make sure the function is called.


The OP was hoping for the opposite: some way of assigning a
value to a function pointer such that when that value was present,
no call would be made. This is, I would gather, a micro-optimization
to avoid the overhead of subroutine linkage for a routine that is
just going to return.

I would suggest to the OP that in a compiler capable of
automatic inlining, that a routine
const void no_op(const TYPE param) { }
would be a good candidate for inlining. The OP had something like
void no_op(TYPE param) { (void)param; return 0; }
but that implies some work on param: it's value has to be accessed
before it is (void) so if param is a trap representation for its
type, then (void) param is potentially going to trap, whereas
simply not using param would not. Also, returning 0 to a void
function is a type mismatch.

Automatic inlining is, to be sure, not promised by C89.
(Does C90 have inline functions?)
A question for the OP: since you show a parameter being
passed in, if the calculation of that parameter would involve
"work" or potential side effects, then what do you want done?
A "real" no_op in such a case would not even evaluate the
parameter(s), at least in the case of what -I- would think of
as "no op". These two are not equivilent:

void no_op(void *param) { }
/* ... */ fptr = no_op;
/* ... */ (*fptr)(param);
vs
#define CALL_MAYBE_NOOP(fptr,param) (fptr? (*fptr)(param) : (void)0)
/* ... */ fptr = NULL;
/* ... */ CALL_MAYBE_NOOP(fptr,param);

A closer match would be:

#define CALL_MAYBE_NOOP(fptr,param) (fptr? (*fptr)(param) : (void)(param))
/* ... */ fptr = NULL;
/* ... */ CALL_MAYBE_NOOP(fptr,param);

(My first iterations used 'if' and {}, but that wasn't quite equivilent
against the possibility that (*fptr)(param) was in a location that
expected an expression rather than a statement.)
--
If you lie to the compiler, it will get its revenge. -- Henry Spencer
Dec 15 '05 #6
In article <pa****************************@wustl.edu>,
Richard Crowley <rc******@wustl.edu> wrote:
void function_that_does_nothing( void )
{
asm( " nop" );
return;
}


Well, that would certainly do nothing on the compilers I use --
it would fail to link, as none of the libraries define
an asm() function.
--
"It is important to remember that when it comes to law, computers
never make copies, only human beings make copies. Computers are given
commands, not permission. Only people can be given permission."
-- Brad Templeton
Dec 15 '05 #7
On 2005-12-15, Walter Roberson <ro******@ibd.nrc-cnrc.gc.ca> wrote:
but that implies some work on param: it's value has to be accessed
before it is (void) so if param is a trap representation for its
type, then (void) param is potentially going to trap, whereas
simply not using param would not.


Except if it can inline it can optimize the statement out too. if not,
passing the parameter traps anyway
Dec 15 '05 #8
Richard Crowley <rc******@wustl.edu> writes:
void function_that_does_nothing( void )
{
asm( " nop" );
return;
}


The asm() statement, apart from being non-portable, is superfluous.
So is the return, for matter.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 15 '05 #9
Keith Thompson wrote:
Richard Crowley <rc******@wustl.edu> writes:
void function_that_does_nothing( void )
{
asm( " nop" );
return;
}


The asm() statement, apart from being non-portable, is superfluous.
So is the return, for matter.


Likewise the thread.

--
Read about the Sony stealthware that is a security leak, phones
home, and is generally illegal in most parts of the world. Also
the apparent connivance of the various security software firms.
http://www.schneier.com/blog/archive...drm_rootk.html
Dec 15 '05 #10
> The OP was hoping for the opposite: some way of assigning a
value to a function pointer such that when that value was present,
no call would be made. This is, I would gather, a micro-optimization
to avoid the overhead of subroutine linkage for a routine that is
just going to return.
Exactly so! I'm glad you understood! The other points on this thread
were interesting, but not to my case.
A question for the OP: since you show a parameter being
passed in, if the calculation of that parameter would involve
"work" or potential side effects, then what do you want done?
A "real" no_op in such a case would not even evaluate the
parameter(s), at least in the case of what -I- would think of
as "no op".
The goal is a polymorphic call such that, in instances of objects that
require the call,
the call will be made and will do real work. However, for instances
that do not require the call, the call will not be made. To achieve
that polymorphism, however, the function must have the same parameters
and return signature.

Even this:
#define CALL_MAYBE_NOOP(fptr,param) (fptr? (*fptr)(param) : (void)(param))
/* ... */ fptr = NULL;
/* ... */ CALL_MAYBE_NOOP(fptr,param);


.... carries the overhead of "fptr?"

I did some benchmarking to confirm that yes, testing and not calling is
far more lightweight than calling something that does nothing. It's
still not as lightweight as a genuine no-op, and has the drawback of
making the non-noop case more expensive, although in most applications
that still amortizes to better performance overall than calling the
noop. Since the noop is the common scenario, and there is real work
associated with the non-noop call, the relative cost of the test is
low.

As for inlining, as I understand inlining, it is not possible to inline
a run-time-determined function pointer.

Against accusations of premature optimization, I will say this: I am
designing a multi-purpose, low-level library. The capability of the
language, in this matter, determines library design for me. If there
*were* a way to make acting on a function pointer a genuine noop, I
would use the facility within the library calls, and it would be a good
thing. Since C does not appear to offer this facility in any portable
way (discussions over at the gcc newsgroup devolved quickly into
assembly language and on-the-fly code-rewriting. I am not going to
prematurely optimize to that degree!) ... instead I will design the
library around this limitation in the language. There are other ways to
achieve my objectives, although not quite as elegant.

Thanks for the discussion! I love this stuff.

-bluejack

Dec 15 '05 #11
bluejack wrote:
[...]
Even this:
#define CALL_MAYBE_NOOP(fptr,param) (fptr? (*fptr)(param) : (void)(param))
/* ... */ fptr = NULL;
/* ... */ CALL_MAYBE_NOOP(fptr,param);


... carries the overhead of "fptr?"

I did some benchmarking to confirm that yes, testing and not calling is
far more lightweight than calling something that does nothing. It's
still not as lightweight as a genuine no-op, and has the drawback of
making the non-noop case more expensive, although in most applications
that still amortizes to better performance overall than calling the
noop. Since the noop is the common scenario, and there is real work
associated with the non-noop call, the relative cost of the test is
low.

As for inlining, as I understand inlining, it is not possible to inline
a run-time-determined function pointer.

Against accusations of premature optimization, I will say this: I am
designing a multi-purpose, low-level library. The capability of the
language, in this matter, determines library design for me. If there
*were* a way to make acting on a function pointer a genuine noop, I
would use the facility within the library calls, and it would be a good
thing. Since C does not appear to offer this facility in any portable
way (discussions over at the gcc newsgroup devolved quickly into
assembly language and on-the-fly code-rewriting. I am not going to
prematurely optimize to that degree!) ... instead I will design the
library around this limitation in the language. There are other ways to
achieve my objectives, although not quite as elegant.

[...]

It's not a limitation of C, but a limitation of the computers themselves.
Unless the CPU itself were to have some "magic address" which, when used
as the target of a CALL (or equivalent to that CPU), would be treated by
the CPU as a no-op instead, it is not possible to design a true no-op in
an indirect call.

Even if the C language were to have some "treat this function pointer
dereference as a no-op" feature, how would it implement it? It would be
implemented, most likely, by comparing the pointer to a "magic value",
and then the call ignored if it matches.

How is this different than the above CALL_MAYBE_NOOP() macro, aside from
the macro being explicit? The generated machine code would most likely
be identical.

And, of course, the function must have a void return type.

--
+-------------------------+--------------------+-----------------------------+
| Kenneth J. Brody | www.hvcomputer.com | |
| kenbrody/at\spamcop.net | www.fptech.com | #include <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------------+
Don't e-mail me at: <mailto:Th*************@gmail.com>

Dec 15 '05 #12
"bluejack" <bl******@gmail.com> writes:
[...]
Against accusations of premature optimization, I will say this: I am
designing a multi-purpose, low-level library. The capability of the
language, in this matter, determines library design for me. If there
*were* a way to make acting on a function pointer a genuine noop, I
would use the facility within the library calls, and it would be a good
thing. Since C does not appear to offer this facility in any portable
way (discussions over at the gcc newsgroup devolved quickly into
assembly language and on-the-fly code-rewriting. I am not going to
prematurely optimize to that degree!) ... instead I will design the
library around this limitation in the language. There are other ways to
achieve my objectives, although not quite as elegant.


I think what you've been looking for is some kind of shortcut for the
if statement in:

typedef void (void_func)(void);

void call(void_func *p)
{
if (p) p();
}

As you've discovered, there's no such thing (at least in standard C).
And if you think about it, there's not much reason that there should
be. It's just a particular form of an if statement (or a conditional
branch). If the CPU provided an instruction that did the same thing
in hardware or microcode, it would probably have to do the same thing
that the explicit if statement does. I doubt that this kind of thing
is common enough to justify dedicating hardware to it, any more than

int *ptr;
...
if (ptr) ptr ++;

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 15 '05 #13
It's not a limitation of C, but a limitation of the computers themselves.
Unless the CPU itself were to have some "magic address" which, when used
as the target of a CALL (or equivalent to that CPU), would be treated by
the CPU as a no-op instead, it is not possible to design a true no-op in
an indirect call.


Well, yes and no.

If C required the functionality, it could be translated in different
ways. Eg, instead
of simply putting in a "call addy" assembly for every function pointer,
the function
pointer could "translate" to a snippet of assembly code. For a genuine
function
call that code might be "call addy", whilst for a noop (with a return
value) it might
be a "mov 0x00 (addy)" code. What the C programmer calls a function
pointer,
would actually be a couple of assembly commands, executed at runtime
rather
than inserted at compile time.

Of course, I don't actually program any assembly language (as is
probably obvious),
so perhaps this isn't as possible as it seems.

Anyhoo.... it's clear that my "elegant little solution" isn't going to
happen, and plenty of thousands of programmers have gotten by without
it in the past, so I'll just take a different design approach.

-b

Dec 15 '05 #14
"bluejack" <bl******@gmail.com> writes:
It's not a limitation of C, but a limitation of the computers themselves.
Unless the CPU itself were to have some "magic address" which, when used
as the target of a CALL (or equivalent to that CPU), would be treated by
the CPU as a no-op instead, it is not possible to design a true no-op in
an indirect call.


Well, yes and no.


[snip]

Please provide attributions for quoted material. The paragraph
starting with "It's not a limitation" was written by Kenneth Brody.

Read <http://cfaj.freeshell.org/google/>.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 15 '05 #15

Keith Thompson wrote
Please provide attributions for quoted material. The paragraph
starting with "It's not a limitation" was written by Kenneth Brody.


yeah, sorry 'bout that. It's this lousy google groups interface,
totally hopeless as a usenet client. Haven't reinstalled a proper
usenet client yet...

-b

Dec 15 '05 #16
bluejack wrote:
If C required the functionality, it could be translated in different
ways. Eg, instead of simply putting in a "call addy" assembly for every
function pointer, the function pointer could "translate" to a snippet of
assembly code. For a genuine function call that code might be "call addy",
whilst for a noop (with a return value) it might be a "mov 0x00 (addy)" code.
But in YOUR case, this won't work because the information is run-time
(you stated in earlier posts that macros won't work for you). So the
compiler MUST ALWAYS make a call and do a return. So, for the noop
function you wanted you'll be facint call and return overhead. Like you
said earlier, checking for NULL is much more lightweight and much
faster (compiles to a single instruction on a lot of platforms).
Anyhoo.... it's clear that my "elegant little solution" isn't going to
happen, and plenty of thousands of programmers have gotten by without
it in the past, so I'll just take a different design approach.


Due to the fundamentals of CPU design. Though I have seen CPUs where
returning from a subroutine is free by using a return bit in the
instruction instead of a dedicated return instruction, so all
instructions can return (think of the assembly that looks something
like: noop,return). But even so, checking for NULL is still faster
since it avoids the call overhead (calling itself is usually a single
instruction but parameter passing, especially if you have more than one
parameter, can take up lots of instructions).

Dec 15 '05 #17

bluejack wrote:
If C required the functionality, it could be translated in different
ways. Eg, instead
of simply putting in a "call addy" assembly for every function pointer,
the function
pointer could "translate" to a snippet of assembly code. For a genuine
function
call that code might be "call addy", whilst for a noop (with a return
value) it might
be a "mov 0x00 (addy)" code. What the C programmer calls a function
pointer,
would actually be a couple of assembly commands, executed at runtime
rather
than inserted at compile time.


if the compiler is capable of deciding to insert "call" or "mov" at
compile time, that means you should be capable as well. which means
you can just delete the noop call entirely.

Dec 16 '05 #18
In article <11*********************@z14g2000cwz.googlegroups. com>,
sl*******@yahoo.com <sl*******@gmail.com> wrote:
bluejack wrote:
If C required the functionality, it could be translated in different
ways. Eg, instead of simply putting in a "call addy" assembly for every
function pointer, the function pointer could "translate" to a snippet of
assembly code. For a genuine function call that code might be "call addy",
whilst for a noop (with a return value) it might be a "mov 0x00 (addy)" code.
But in YOUR case, this won't work because the information is run-time
(you stated in earlier posts that macros won't work for you). So the
compiler MUST ALWAYS make a call and do a return. So, for the noop
function you wanted you'll be facint call and return overhead. Like you
said earlier, checking for NULL is much more lightweight and much
faster (compiles to a single instruction on a lot of platforms).


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.

The compiler would, in this semantics, pre-compile the setup and
teardown routines according the current context of what registers were
used and where the arguments were to be retrieved from, so the
'construct a pointer' bit would simply be a matter of saving two
values known in advance to the compiler. The address saved in the
function pointer variable would still be dynamic, but only a light
dynamic call would be used, rather than a full "preserve all the registers,
save all the masks, change contexts. The code that was branched
-to- would do nothing, gracefully, for the no-op (NULL) case,
but for the non-NULL case, at the time the pointer was created the
compiler stored the code to deal with the parameters and perform the
"real" subroutine call.
There are three major differences between the above and the current
semantics.

The first is that I have created a new definition, that deliberately
taking the function pointer of NULL results in a something that is a no-op
rather than a crash-the-program-op.

The second is that I have -defined- this NULL case as not
even evaluating the arguments at all -- but that part of it is easily
redefined if you want to evaluate the arguments anyhow (in case of
side-effects.)

The third difference is that with the current traditional semantics,
*all* code is always in fixed memory and precompiled, so all aspects
of code itself can be stored in read-only (execute-only) memory, whereas
in the revised semantics, I have framed it in terms of the
function-pointer operation writing RAM that contains code that will
later be executed, thus needing a section that is writable and
executable, and (in a naive implementation) quite extendable
since one could potentially loop taking pointers to functions.

As C semantics do not allow for user creation of routines on the
fly, then an optimization of what I have described would be possible:
it would be possible for the program to take note of all routines
whose pointer is ever taken, and pre-write the code for calling
those, and write just those handlers to read-only (execute only)
memory, at locations that it knows about.

Indeed, this could be taken further as follows:

Instead of having the current semantics of inline parameter
setup and so on, followed by unconditional branch, pre-compile
the code that would be needed to do the parameter handling,
and do a lightweight branch to the address designated by the
stored function pointer. At compile time, the compiler is
able to detect a function pointer to NULL, and can return as
the pointer a block of code that does nothing gracefully and returns
with a light return; for all other calls, the address would be to the
be function code block, which would call the setup block, call
the real code just below, then call the teardown block, then do a
light return. All of this involves only precompiled entities and so
can go into read-only (execute-only) memory.

This should, I believe, work no matter what -legal- function pointer
manipulations were undertaken. It isn't possible to construct a
new function pointer such as by doing pointer arithmetic on an
existing one, and it isn't possible to construct a new NULL pointer
by subtracting a function pointer from itself. Therefore, no matter
how many levels one goes through, every operation on a
function pointer amounts to either passing a compile-(well, link-)
time constant around, storing such a constant, or taking the
address of a memory location that contains such a constant and working
with that. One should thus be able to implement this with just the minor
change to C semantics of permitting a no-op function pointer constructor,
which one might or might not choose to be accompanied by the semantics
of not evaluating the parameters.
Now, I don't imagine for a moment that anyone would bother to go
through the trouble to actually implement this, since clearly it
is only an optimization and the C standards stay well away from
matters of efficency. All I'm saying is that bluejack's proposal
is not "impossible" and could be made to work. But it'd only
-matter- for architectures and APIs on which there is a lightweight
call/return mechanism that is much less costly than the API's
full-call requirements.

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...)
--
I was very young in those days, but I was also rather dim.
-- Christopher Priest
Dec 16 '05 #19
In article <dn**********@canopus.cc.umanitoba.ca>
Walter Roberson <ro******@ibd.nrc-cnrc.gc.ca> wrote:

[enormous snippage]
Now, I don't imagine for a moment that anyone would bother to go
through the trouble to actually implement this, since clearly it
is only an optimization and the C standards stay well away from
matters of efficency. All I'm saying is that bluejack's proposal
is not "impossible" and could be made to work. But it'd only
-matter- for architectures and APIs on which there is a lightweight
call/return mechanism that is much less costly than the API's
full-call requirements.
Quite so. In fact, on many modern systems, calling a no-op
subroutine is just as fast as any other branch. Hence:

if (fp) (*fp)(arg);

might compile to:

brz fp_reg, 1f # if "fp" is zero, skip the call
nop # nothing to do in delay slot

jal fp_reg # otherwise execute subroutine call
mov arg, a0 # (and put argument in argument-zero register)
1:

which would never be any faster, and maybe *slower* (by about one
CPU cycle, whenever the register is nonzero), than:

jal fp_reg # jump and link (i.e., call subroutine)
mov arg, a0 # with argument in "a0" register

with the no-op subroutine being compiled to:

jmp ret_reg # return from subroutine
nop # nothing to do in delay slot

(the above is not intended to be either SPARC or MIPS assembly,
but rather something more or less the same as both).
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...
Some architectures can achieve this without self-modifying code
using an "execute" instruction that executes an instruction stored
in a register. In this case, we would do something like:

ld t0, instruction # which is either "call t1" or "nop"
ld t1, target # which is either the function to call, or irrelevant
exc t0 # execute instruction in register t0
(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...)


Or at least, completely portable C, and likely not significantly
slower. (The one-cycle slowdown above means that on a 1 GHz CPU,
it costs about one extra nanosecond when calling "real work"
functions. I say "about" because the 8 bytes for the brz+nop, or
4 bytes if we find an instruction for the delay slot, takes cache
space and could bump something out. On the other hand, perhaps
the instruction we move into the delay slot would have been in its
own CPU cycle anyway, and with a two-instructions-per-cycle CPU,
the branch-on-register-zero instruction could turn out to be entirely
"free" after all.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Dec 16 '05 #20
Walter Roberson wrote:
In article <11*********************@z14g2000cwz.googlegroups. com>,
sl*******@yahoo.com <sl*******@gmail.com> wrote:
bluejack wrote:
If C required the functionality, it could be translated in different
ways. Eg, instead of simply putting in a "call addy" assembly for every
function pointer, the function pointer could "translate" to a snippet of
assembly code. For a genuine function call that code might be "call addy",
whilst for a noop (with a return value) it might be a "mov 0x00 (addy)" code.
But in YOUR case, this won't work because the information is run-time
(you stated in earlier posts that macros won't work for you). So the
compiler MUST ALWAYS make a call and do a return. So, for the noop
function you wanted you'll be facint call and return overhead. Like you
said earlier, checking for NULL is much more lightweight and much
faster (compiles to a single instruction on a lot of platforms).


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?

Dec 17 '05 #21
In article <11*********************@g47g2000cwa.googlegroups. com>,
sl*******@yahoo.com <sl*******@gmail.com> wrote:
Walter Roberson wrote:
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

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.


At each call made to a function or function pointer, the compiler
knows where the parameters are coming from for that call. It
writes a section of code inline to wrangle the parameters into their
proper place (calling routines along the way as necessary), and then
does a full-call to the address obtained either from the pointer
or from the compile (link) time constant routine address. Like this:

Bn; /* all the code before the call */
Sn; /* the code to set up for this particular call
Cn; /* the actual call to the address from pointer or static value */
Tn; /* the code to teardown after the return, restoring registers etc. */
Mn; /* more code */

This can be replaced by,

Bn; /* all the code before the call */
push @Sn;
push @Tn;
LCn; /* lightweight call to address from pointer or static value */
Mn; /* more code */
....
S1: /* the setup code that would have been written for the first call */
return; /* lightweight return after S1 */
T1; /* the teardown code that would have been written for the first call */
return; /* lightweight return after T1 */
S2: /* setup code for second call */
return; /* lightweight return after S2 */
T2: /* teardown code after second call */
return; /* lightweight return after T2 */

You can see that as long as you can distinguish a boundary between
the previous code and the setup for the next call, then you could
take the block of setup code, move it elsewhere, and in its place insert
an absolute branch to that location and an asbolute return back after that
code; the only effect of such a transformation would be to slow the
execution by two branches. Clearly then you can take the moved
section of code, change the terminal absolute branch back after it to
be a return, and change the absolute branch into that section to be
a call. From there it follows that instead of putting the call inline,
that one could push the address that would be called, and defer the
calling into the setup code for the function itself. This would be
only a minor change to the call/return API. It shouldn't even change
optimization very much, I would think.
The success of this possibly depends upon the calling convention
remaining consistant. If, for example, calling a routine might
suffle the registers being used to track variables, then you could
run into restrictions on optimization. Suppose, for example, that
the optimizer uses Intra Procedural Analysis and determines that
the called routine is going to compute something (oh, say, take
the address of an array element), and that "soon" after the return
that the calling routine would compute the same thing -- and that
there is no specific return of the value of this computation from
the called routine to the calling routine. The traditional subroutine
ABI would be that anything computed inside a subroutine is opaque,
and that only the explicit result may be returned, and that those
are to come back through well-defined return registers. A human
optimizer looking over the same code might say, "Ah, but register
R6 of the calling code can be destroyed at this point, so if I
have the subroutine compute that item into R6 and I disable the
call linkage from preserving R6 and restoring the original value, then
I can skip computing that value in the calling routine, knowing that
it is already available. And if a human can make that kind of
optimization, then there are machine optimizers which could make
that kind of information lifetime analysis and number of times
called, number of times the internal results would be useful outside
and so on, and make the same kind of decision. If you have API-breaking
magic like that happening via the optimizer, then in theory
the code-rearrangement and semantics I set out code weaken the
opportunities for optimization. [In practice... probably not.]
--
Is there any thing whereof it may be said, See, this is new? It hath
been already of old time, which was before us. -- Ecclesiastes
Dec 17 '05 #22
Richard Crowley a écrit :
void function_that_does_nothing( void )
{
asm( " nop" );
return;
}


Huh ? What about

void function_that_does_nothing (void)
{
}

At least, it is portable !

--
A+

Emmanuel Delahaye
Dec 17 '05 #23
Walter Roberson wrote:
In article <11*********************@g47g2000cwa.googlegroups. com>,
sl*******@yahoo.com <sl*******@gmail.com> wrote:
Walter Roberson wrote:

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

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.


At each call made to a function or function pointer, the compiler
knows where the parameters are coming from for that call.
<snip>
Bn; /* all the code before the call */
Sn; /* the code to set up for this particular call
Cn; /* the actual call to the address from pointer or static value */
Tn; /* the code to teardown after the return, restoring registers etc. */
Mn; /* more code */
This can be replaced by,
Bn; /* all the code before the call */
push @Sn;
push @Tn;
LCn; /* lightweight call to address from pointer or static value */
Mn; /* more code */
....
S1: /* the setup code that would have been written for the first call */
return; /* lightweight return after S1 */
T1; /* the teardown code that would have been written for the first call */
return; /* lightweight return after T1 */
S2: /* setup code for second call */
return; /* lightweight return after S2 */
T2: /* teardown code after second call */
return; /* lightweight return after T2 */


If I understand it correctly, that would be result in worse performance
since it doesn't gain much over a NULL check of the function pointer
but requires two branches when an object actually needs to be handled
(fp is not NULL). That means we've speeded up "doing nothing" by
slowing down "doing something".

Furthermore, since the function pointer is not known at compile time
(the OP was emulating polymorphism) each function requires separate
set-up and tear-down codes for each location they are called from. So,
10 functions called in 3 places would result in 30 set-up and tear-down
codes.

Dec 18 '05 #24

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: banaticus | last post by:
What does this error message mean? What can I do to fix it? Here'e the command that I just tried running, and the messages that I received. I just barely unpacked python. linux:/Python-2.4 #...
2
by: asdf | last post by:
Hello, I was enjoying working in VS for half a year without any problems and now I cannot debug anymore. Without any really reason my Studio tells me that the page that I want to debug has - No...
2
by: Zack | last post by:
Hi, I am an access newbie and wanted to get a design check to see if I have done the right thing or if there is a better way. I don't like the Yes/No field option in a table. I want to have the...
4
by: James | last post by:
I have a VB windows forms application that accesses a Microsoft Access database that has been secured using user-level security. The application is being deployed using No-Touch deployment. The...
0
by: James | last post by:
I have a VB windows forms application that accesses a Microsoft Access database that has been secured using user-level security. The application is being deployed using No-Touch deployment. The...
59
by: Lennart Björk | last post by:
Hi All, I have a tiny program: <!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>MyTitle</title> <meta...
3
by: Rahul B | last post by:
Hi, I have a user UCLDEV1 which is a part of staff and a group(db2schemagrp1) to which i have not given any permissions. The authorizations of that user are shown as db2 =get authorizations...
0
natalie99
by: natalie99 | last post by:
Hey people! Being so lacking in access knowhow I have turned to good old 'asking the smart people' again! I need to create a query or lookup table (although I have had zero success with lookups...
2
Parul Bagadia
by: Parul Bagadia | last post by:
I have written a code for deleting certain value from linklist; it's not working; where as i have written one for deleting a no., after given no. which works fine! I even debugged it; but invain;...
1
by: sora | last post by:
Hi, I've developed a MFC program under VS 6.0. My debugger *was* working fine and I've used it often for my project. Then, one day, the errors below appear and they prevent me from using the...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.