直接跳转到另一个C ++函数

时间:2010-03-29 16:25:20

标签: c++ assembly systems-programming

我正在将一个小型学术操作系统从TriCore移植到ARM Cortex(Thumb-2指令集)。为了使调度程序工作,我有时需要将JUMP直接转换为另一个函数,而无需修改堆栈或链接寄存器。

在TriCore上(或者更确切地说,在tricore-g ++上),这个包装器模板(对于任何三个参数函数)都有效:

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 );

这将生成汇编程序指令J(又名JUMP)而不是CALL,在跳转到(否则正常)C ++函数superDispatch(SchedulerImplementation* obj, Task::Id from, Task::Id to)时保持堆栈和CSA不变。

现在我需要ARM Cortex上的等效行为(或者更确切地说,对于arm-none-linux-gnueabi-g ++),即生成B(又称BRANCH)指令而不是BLX(又名BRANCH与链接和交换)。但是arm-g ++没有interrupt_handler属性,我找不到任何等效属性。

所以我试图诉诸asm volatile并直接编写asm代码:

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"
                  );
}

到目前为止,至少在我的理论中这么好。 Thumb-2要求函数参数在寄存器中传递,即在这种情况下为r0..r2,因此它应该可以工作。

然后链接器以

消亡
undefined reference to `r6'

在asm声明的结束时......我不知道该怎么做。好吧,我不是C ++的专家,asm语法也不是很简单......所以有人给我一个暗示吗?对arm-g ++的正确__attribute__的提示将是一种方式,修复asm代码的提示将是另一种方式。另一种方法可能是告诉编译器当输入asm语句时a1..a3应该已经在寄存器r0..r2中(我查了一下,但没有找到任何提示)。

2 个答案:

答案 0 :(得分:1)

链接错误是由尝试使用分支指令跳转到指针引起的。这会生成b r6之类的代码,但无法链接,因为r6不是符号。将分支指令更改为mov pc,%0,您应该获得正确的跳转。

正如我在评论中提到的,ARM中断处理程序声明为interrupt属性,但正如您所发现的那样,这不会影响它们的调用方式。我想这是一个特定于平台的技巧,碰巧在TriCore上做了正确的事情。

您可以尝试使用GCC的扩展语法register int reg0 asm("r0") = a1;而非易失性mov指令在特定寄存器中声明变量。这可能允许编译器生成更好的代码。

答案 1 :(得分:0)

好吧,我现在知道出了什么问题。

直接跳到另一个函数的整个概念在ARM Cortex上没有实际意义,因为每次调用另一个函数时,TriCore都会使用上下文保存区(CSA)来保存整个CPU上下文。将其视为第二个独立堆栈,随每个CALL增长并随每个RET缩小。并且每个CSA块具有恒定的大小。

另一方面,ARM Cortex使用一个简单的标准堆栈(好吧,它知道系统堆栈和线程堆栈,但这在这里并不重要) - 而GCC只是保存了每个函数所需的内容,因此每个框架有不同的大小。因此,简单地跳转到另一个函数是不可能的,因为一旦跳转函数开始保存它使用的非易失性寄存器,堆栈就会被破坏。

关于链接器错误以及对r6的未定义引用......好吧,我应该更仔细地阅读指令集文档。 B立即地址的无条件分支,BX是期​​望寄存器中的分支地址的指令。我被手册中的指令列表所迷惑,其中BX很快被描述为“分支与交换”。我不想交换任何东西,我想要一个简单的跳转,所以我没有进一步阅读。

因此,在B代码中与BX交换asm volatile后,代码已编译完毕。但是,如上所述,整个概念不能按预期工作。也许其他人可以找到该代码的用例,我现在必须诉诸经典函数调用...