It is interesting that inline assembler from GCC has no way to deal with call frames. My answer attempts to address this by adding a function wrapper and giving the syntax to get an address. The same complaint by 'NotLikeThat' is comments to Craig Estey's answer. Indeed, if you need to save/restore floating point registers, etc. That answer will not work. You also need to clue the compiler that a stack frame needs to be emitted. I tried to do this through the wrapping function.
This answer is not generic to all cases, do not copy paste it. It tries to address the concepts of getting a function address to a register and transferring control. 'C' is a procedural programming language so the compiler performs many optimizations based on the context of the function.
No information is generic, but might get you to a specific solution. GCC's label variables and other concepts might be suitable (and more portable) depending on the use case. The code is meant to be informative. All of my answers are written for people who can think about a problem and never canned solutions.
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)
: // probably unsafe without "lr" clobber in most use-cases
);
// and also __builtin_unreachable() to tell the compiler execution doesn't leave
The OP would do well to read this tutorial; even though it is for traditional ARM as opposed to an 'm0'.
This method works for simple functions but is not a generic solution. However, it demonstrates getting an address to a register. The OP's original query. The use case here is in a start
function where you are doing a tail call that will eventually call main().
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);
// prevent inlining and optimization across functions,
// so we can take advantage of the calling convention instead of using proper clobbers
void f(void) __attribute__((noinline,noipa))
{
__asm volatile (
" cmp r3,#0 \n"
// Below is a tail call, not setting LR, it will return to the caller of f()
// It relies on the caller of f() having populated the 'lr'.
// See further comments below.
" b %[my_function] \n"
" bx r14 \n" // does nothing...
: // no output
: [my_function] "r" (my_function) // input
);
}
This example relies on the fact it is embedded in a function. The function will do the stack manipulation needed to prepare a call for the particular function you call from. It may not be possible to inline this generically as you can specify clobbers such as r0-r3, memory and 'cc', but it would not tell the compiler that a stack frame must be well formed at this point. Some answers will depend on what the called function does as you need to match the EABI (calling convention expected). However, this is much beyond the capabilities of the original poster and they were struggling with much more basic concepts.
__attribute__((noinline,noipa))
was added to prevent inlining of an external function. Since the answer was written, it is much more likely that GCC will perform global optimizations that may try to inline the routine.
With the output (using particular build options with a specific compiler version),
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. This example (as on godbolt) needs to be embedded in a function wrapper to take care of stack frame. It is just meant as an example of how to get a function address to a register (the OPs original question).