no-op function pointer

Discussion in 'C Programming' started by bluejack, Dec 14, 2005.

  1. bluejack

    bluejack Guest

    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?
    bluejack, Dec 14, 2005
    #1
    1. Advertising

  2. bluejack

    Guest

    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, 2005
    #2
    1. Advertising

  3. bluejack

    bluejack Guest

    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.
    bluejack, Dec 14, 2005
    #3
  4. bluejack

    Jack Klein Guest

    On 13 Dec 2005 22:31:07 -0800, "bluejack" <> 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/~ajo/docs/FAQ-acllc.html
    Jack Klein, Dec 15, 2005
    #4
  5. 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" <> 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.
    Richard Crowley, Dec 15, 2005
    #5
  6. In article <>,
    Jack Klein <> wrote:
    >On 13 Dec 2005 22:31:07 -0800, "bluejack" <> 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
    Walter Roberson, Dec 15, 2005
    #6
  7. In article <>,
    Richard Crowley <> 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
    Walter Roberson, Dec 15, 2005
    #7
  8. bluejack

    Jordan Abel Guest

    On 2005-12-15, Walter Roberson <-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
    Jordan Abel, Dec 15, 2005
    #8
  9. Richard Crowley <> 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) <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.
    Keith Thompson, Dec 15, 2005
    #9
  10. bluejack

    Chuck F. Guest

    Keith Thompson wrote:
    > Richard Crowley <> 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/archives/2005/11/sonys_drm_rootk.html
    Chuck F., Dec 15, 2005
    #10
  11. bluejack

    bluejack Guest

    > 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
    bluejack, Dec 15, 2005
    #11
  12. 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:>
    Kenneth Brody, Dec 15, 2005
    #12
  13. "bluejack" <> 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) <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.
    Keith Thompson, Dec 15, 2005
    #13
  14. bluejack

    bluejack Guest


    > 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
    bluejack, Dec 15, 2005
    #14
  15. "bluejack" <> 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) <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.
    Keith Thompson, Dec 15, 2005
    #15
  16. bluejack

    bluejack Guest

    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
    bluejack, Dec 15, 2005
    #16
  17. bluejack

    Guest

    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, 2005
    #17
  18. bluejack

    tedu Guest

    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.
    tedu, Dec 16, 2005
    #18
  19. In article <>,
    <> 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
    Walter Roberson, Dec 16, 2005
    #19
  20. bluejack

    Chris Torek Guest

    In article <dnt6rh$mkc$>
    Walter Roberson <-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.
    Chris Torek, Dec 16, 2005
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. glen stark
    Replies:
    2
    Views:
    703
    Ron Natalie
    Oct 10, 2003
  2. Fraser Ross
    Replies:
    4
    Views:
    1,043
    Fraser Ross
    Aug 14, 2004
  3. murgan
    Replies:
    6
    Views:
    4,861
    Thad Smith
    Dec 21, 2005
  4. Vijai Kalyan
    Replies:
    4
    Views:
    704
    Vijai Kalyan
    Nov 8, 2005
  5. Replies:
    3
    Views:
    306
    Philip Potter
    Apr 11, 2008
Loading...

Share This Page