This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Using CALL/RET instructions in large model (MSP430X)

Hi,

Is there any way to force CCS compiler to generate "near" functions with RET instead of RETA at the epilogue inspite of large data/code model is used? I am writing a small and fast task scheduler which uses 16-bit type to keep user defined callback function (uint16_t). I tell the linker to place these functions within first 64-kB of the address space. Before calling such function I use type casting to void (*)()

static __inline void _call_addr16(uint16_t addr)
{
((void (*)()) addr)();
}

The compiler generates the CALL instruction instead of CALLA. Unfortunately the callee uses RETA so the stack is corrupted after return. Is it possible to fix it without writing raw assembly?

Thanks in advance,

Matthew

  • The caller must also use large code model (both, caller and calle mus tuse the same code and data model, as well as any library used). And the compiler should generate a CALLA then. If not, I’d call this a compiler bug. In large code model, function pointers are 20/32 bit, and so should be the result of your cast and therefore the call instruction.

    However, if you really want a RET instead of a RETA, then you can declare a function as naked. This will omit the function entry and exit code. But then you are responsible not only for the final RET, but also for saving and restoring all used registers on stack. And I don’t know how local variables have to be handled then.

    You could as well write the function in assembly then J

  • Hi,

    A bit late, I am sorry for that. I have updated the compiler from 4.1.5 to 4.1.9 and now it generates CALLA when large code model is selected. I have used  before a workaround which unfortunately adds an extra branch.

    #ifndef __LARGE_CODE_MODEL__
    static __inline void _call_addr16(sptr_t addr)
    {
        ((void(*)()) addr)(); // Generated instruction: CALL Rn
    }
    #else
    extern void _call_addr16(sptr_t addr);
    #endif

    And in the separate module:

    #ifdef __LARGE_CODE_MODEL__
    void _call_addr16 (unsigned short ptr)
    {
        __asm (" MOVX.A R12, PC"); 
        /* Compiler generates two instructions:
        MOVX.A R12, PC (or BRA R12)
        and RETA (never reached during execution) */
    } #endif

    Thanks!

    Matthew

  • Mateusz Maciag said:

    And in the separate module:

    #ifdef __LARGE_CODE_MODEL__
    void _call_addr16 (unsigned short ptr)
    {
        __asm (" MOVX.A R12, PC"); 
        /* Compiler generates two instructions:
        MOVX.A R12, PC (or BRA R12)
        and RETA (never reached during execution) */
    } #endif

    BTW, mova R12, PC will take one word less and execute one cycle faster than movx.a R12, PC

  • You are right. Maybe mov R12, PC would be even better. Although execution cycles are the same it would match the function parameter type. Thanks! 

  • Indeed, for some operations, MOVX.A can be replaced by MOVA. However, it won’t work for indexed mode (including symbolic mode) addressing, as MOVA can only take a 16 bit signed offset for them (and usually, the compiler abuses the constant offset part for the base address).
    Also, memory->memory transfers won’t work at all with MOVA.

     Mateusz: you can give your function the naked attribute. Then the compiler won’t generate a stack frame (won’t save and restore required registers, won’t allocate space for local vars and also add no RETA)

  • Does TI compiler support naked attribute? The manual does not mention about it and warnings about ignored attribute are generated. Anyway I have moved this function to the .asm file, so now of course there is no RETA in the output. I check __TI_COMPILER_VERSION__ macro as well and then decide if inline version should be used or not:

    ; ASM file
    	.global _call_addr16
    
    	.if __TI_COMPILER_VERSION__ < 4001009
    	.if __LARGE_CODE_MODEL__
    _call_addr16
    	MOV R12, PC
    
    	.endif
    	.endif
    
    /* C  header */
    #if defined(__LARGE_CODE_MODEL__) && (__TI_COMPILER_VERSION__ < 4001009)
    extern void _call_addr16 (sptr_t addr);
    #else
    static __inline void _call_addr16 (sptr_t addr)
    {
    	((void (*)()) addr)();
    }
    #endif

  • GCC (at least MSPGCC) does support the naked attribute. So perhaps the GCC version in CCS does support it too. For the standard CCS compiler, I don’t know. Apparently not.
    There’s the ‘noreturn’ attribute, but it might cause the compiler to be careless in the calling function (if you call a function that never returns, you don’t need to preserve anything).

    Well, handling these things in assembler is of course much easier. One doesn’t have to battle the compiler. J

**Attention** This is a public forum