This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How to prevent OT 8 from generating an LJMP

If Optimize 8 (Reuse Common Entry Code) detects void MyFunc() as the last statement within void OuterFunc() (for example), it will produce an LJMP to MyFunc() rather than an LCALL. (see examples)

Nice optimization, but MyFunc() is part of a library written in assembly and by design reuqires a call rather than a jump. I tried surrounding its prototype with a #pragma OT (7), but that doesn't work. Surrounding OuterFunc() with the OT (7) pragma works, but that's a bit cumbersome and runs the risk of someone forgetting to do it.

void OuterFunc( void )
{
   MyFunc();   // Compiler generates LJMP.  Not good :o(
}


#pragma OT (7)
void OuterFunc( void )
{
   MyFunc();   // Compiler now generates the necessary
               // LCALL, but programmers WILL forget to use
               // this construct and will blow the whistle
               // when their software bombs.
}
#pragma OT (8)

Parents
  • Why does the library routine require a call rather than a jump?
    ...
    So are you doing some special stack hacking or something that makes the library routine dependent on the call sequence?


    Yes, but I chose not to mention that as I was fairly certain possible solutions would get lost in debate over technique.

    Suffice it to say the function "must" be called.

Reply
  • Why does the library routine require a call rather than a jump?
    ...
    So are you doing some special stack hacking or something that makes the library routine dependent on the call sequence?


    Yes, but I chose not to mention that as I was fairly certain possible solutions would get lost in debate over technique.

    Suffice it to say the function "must" be called.

Children
  • As long as you're sure of what you want, it's fine with me.

    I don't see a way to control particular optimizations for particular symbols (or even globally, without shutting off entire levels above).

    Unfortunately, #pragma is a preprocessor directive, and #define macros can't generate more preprocessor stuff. So you can't help client routines out by wrapping the pragma yourself:

    #define MyCall() #pragma save     #pragma ot(7)    MyCall();        #pragma restore
    

    because you can't generate the #pragma's.

    A cheesy workaround would be
    MyCall.i:
    #pragma save
    #pragma ot(7)
    MyCall();
    #pragma restore

    OuterFunc.c:
    void OuterFunc(void)
        {
        // instead of MyCall()
    #include "MyCall.i"
        }
    

    Hideous, eh?

    Hopefully someone can top these suggestions.

  • For anyone interested, I found a rather simple solution to this problem. One only needs to make the compiler believe there is more code to execute after the function in question, and this can be done by wrapping it in a macro:

    // API Prototypes
    
    #define  JumpIndirect( bparm, handler )  { JmpInd( bparm, handler ); global_var = 0; }
    
    void JmpInd( byte b, code* handler );
    
    
    
    // When in use
    
    void IntHandler( byte source_vector )
    {
        if( IntNotForMe() )
            JumpIndirect( source_vector, old_handler );  // Chain to the next handler.
    }
    

    Within the macro, "global_var" is just a dummy variable (or any other that can be safely borrowed for the purpose) and the assignment to 0 creates very little overhead.

    Now, obviously the macro isn't absolutely necessary...you could just write both instructions inline or use a function pointer and an indirect call, but function pointers are too ugly and difficult to use. To a user, the macro is more of a WYSIWIG solution.

  • make the compiler believe there is more code

    But that's not what your hack does --- there's no make-believe involved: there is more code after the sub-function call. The macro you wrap that in is just window-dressing.

    But even so, I rest less than convinced that a function like your JmpInd

    a) should fail if it's jumped to instead of called --- the only difference is in stack contents it has no business meddling with, even given the job it wants to do

    b) is any more elegant or easier to use than a function pointer

  • "But that's not what your hack does --- there's no make-believe involved: there is more code after the sub-function call. The macro you wrap that in is just window-dressing."

    The above sort of nit-picking reminds me why I don't visit this board very often. You can ALWAYS count on this sort of reply here...ALWAYS.


    "...the only difference is in stack contents it has no business meddling with, even given the job it wants to do"

    Who says? If the stack contents and the associate SP sfr are so off-limits and taboo, then why have both been accessible since the inception of the 8051 and, for that matter, still accessible to C51 itself?


    b) is any more elegant or easier to use than a function pointer

    Quote from Keil C51 User's Guide 09.2001, pg 379:

    "Function pointers are one of the most difficult aspects of C to understand and to properly utilize. Most problems involving function pointers are caused by improper declaration of the function pointer, improper assignment, and improper dereferencing."