简体   繁体   中英

ASM Inline call C external function

I'm trying to call an external function in c by an asm inline with a branch. I'm compiling to an arm m0 instruction set but it returns bad expression.

The code is:

__asm volatile (
                "   cmp     r3,#0                   \n"                     
                "   b %[my_function]                \n" //Call function
                "   bx r14                          \n"
                : // no output
                : [my_function] "i" (my_function) // input
                : "r0" // clobber
            );

The return is:

/tmp/ccICkDIE.s: Assembler messages:
/tmp/ccICkDIE.s:152: Error: bad expression -- `b #my_function'

What we need to do?

You want the BL instruction. This is "branch and link". It does a jump and stores the return address in r14 .

But, you've still got a problem ... When you do BL , it destroys the r14 that you need. You've still got more work to do, even after the following:

stmfd   sp!,{v1-v6,lr}              // preserve caller registers
bl      %[my_function]              // call function
ldmfd   sp!,{v1-v6,pc} @std         // restore caller registers and return

You'll need more investigation. You might want to disassemble the compiled function and see what wrapper goes around your inline asm and adjust accordingly. It may do the stmfd/ldmfd for you. Try marking r14 as a clobber.

You may be better off with just the BL . The BX without restore might produce an infinite loop or unpredictable results. I'd leave it off

After composing the below, I remembered the ethernut tutorial . He has virtually the same answer,

asm volatile(
    "mov lr, %1\n\t"
    "bx %0\n\t"
    : : "r" (main), "r" (JMPADDR));

The OP would do well to read this tutorial; even though it is for traditional ARM as opposed to an 'm0'.


You may use the 'r' constraint to place the address in a register and branch to it.

An example can be found on the online compiler godbolt.

extern int my_function(void);

void f(void)
{
__asm volatile (
                "   cmp     r3,#0                   \n"                     
                "   b %[my_function]                \n" //Call function
                "   bx r14                          \n"
                : // no output
                : [my_function] "r" (my_function) // input
                : "r0" // clobber
            );

 }

With the output,

f():
    ldr r3, .L2
       cmp     r3,#0                   
   b r3                
   bx r14                          

    bx  lr
.L2:
    .word   my_function()

We can see several issues with the output. r14 is lr and the b r3 will transfer control directly and return to the caller of f . The cmp r3, #0 seems completely un-needed (given limited context of question).

The sample above answers the question and it could be used for a tail-call macro or other use, but it obviously needs some work. A function pointer like,

int (*g)(void) = my_function;

will also work as the parameter 'my_function' to the GCC extended assembler.

Another method is just to use 'C' macro string concatenation. Here is a starting sample,

#define xstr(s) str(s)
#define str(s) #s
#define TAIL_CALL(func) __asm volatile(" b  " str(func) "\n")

For most code sizes (jump distance), the branch will be able to resolve (4MB?). If you use the function pointer method, then there is no issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM