Go straight to another C ++ function

I am moving a small academic OS from TriCore to ARM Cortex (Thumb-2 instruction set). In order for the scheduler to work, sometimes I need to directly connect JUMP to another function without changing the stack or register of links.

In TriCore (or rather tricore-g ++) this shell template (for any function with three arguments) works:

template< class A1, class A2, class A3 > inline void __attribute__((always_inline)) JUMP3( void (*func)( A1, A2, A3), A1 a1, A2 a2, A3 a3 ) { typedef void (* __attribute__((interrupt_handler)) Jump3)( A1, A2, A3); ( (Jump3)func )( a1, a2, a3 ); } //example for using the template: JUMP3( superDispatch, this, me, next ); 

This will lead to the generation of the assembler command J (aka JUMP) instead of CALL , leaving the stack and CSA unchanged when switching to the (otherwise normal) C ++ function superDispatch(SchedulerImplementation* obj, Task::Id from, Task::Id to)

Now I need equivalent behavior on ARM Cortex (or rather for arm-none-linux-gnueabi-g ++), i.e. generate command B (aka BRANCH) instead of BLX (aka BRANCH with reference and exchange). But the interrupt_handler attribute for arm-g ++ is missing, and I could not find the equivalent attribute.

So I tried resorting to asm volatile and writing asm code directly:

 template< class A1, class A2, class A3 > inline void __attribute__((always_inline)) JUMP3( void (*func)( A1, A2, A3), A1 a1, A2 a2, A3 a3 ) { asm volatile ( "mov.w r0, %1;" "mov.w r1, %2;" "mov.w r2, %3;" "b %0;" : : "r"(func), "r"(a1), "r"(a2), "r"(a3) : "r0", "r1", "r2" ); } 

So far, so good, in my theory, at least. Thumb-2 requires that function arguments be passed in this register, i.e. R0..r2, so it should work.

But then the linker dies with

 undefined reference to `r6' 

on the closing bracket of the asm operator ... and I don't know what to do with it. Ok, I'm not an expert in C ++, and the asm syntax is not very simple ... so did anyone get a hint for me? A hint for the correct __attribute__ for arm-g ++ would be in one case, a hint for fixing the asm code would be different. Another way would probably be to tell the compiler that a1..a3 should already be in the r0..r2 registers when the asm operator was introduced (I studied this a bit, but did not find any hint).

+6
c ++ assembly systems-programming
source share
2 answers

Well, now I realized what went wrong.

In general, the concept of Jumping directly to another function is controversial on ARM Cortex, because TriCore uses the Save Area (CSA) context to save the entire processor context every time you call another function. Think of it as a second independent stack that grows with each CALL and contracts with each RET . And each CSA block has a constant size.

ARM Cortex, on the other hand, uses a simple standard stack (well, it knows about the system stack and the thread stack, but that doesn't matter here) - and GCC just saves what it needs for each function, so each frame has a different size. A simple switch to another function, therefore, is out of the question, because the stack will be damaged as soon as the function with the transition starts saving the non-volatile registers that it uses.

And about the linker error with the undefined link to r6 ... well, I had to carefully read the instructions for the set of instructions. B is the unconditional branch to the immediate address, BX is the command that expects the branch address in the register. I was deceived by the list of instructions in the manual, where the BX was soon described as "Branch with exchange." I did not want to change anything, I need a simple jump, so I did not read further.

So, after exchanging B with BX in the asm volatile code, the code is compiled. But, as stated above, the whole concept cannot work as expected. Maybe someone else can find a precedent for this code, I need to resort to the classic function calling now ...

0
source share

A communication error was caused by an attempt to use the jump instruction to jump to a pointer. This generates code of type b r6 , which is not bound, because r6 not a character. Change the jump instruction to mov pc,%0 , and you should get the correct jump.

As I mentioned in the comments, ARM interrupt handlers are declared with the interrupt attribute, but as you discovered, this does not affect the way they are called. I guess this was a platform specific trick and this happened to TriCore.

You can try declaring variables in specific registers using the advanced GCC syntax, register int reg0 asm("r0") = a1; , not volatile mov . This may allow the compiler to generate better code.

+1
source share

All Articles