7

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

在 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++ 函数时,堆栈和 CSA 保持不变superDispatch(SchedulerImplementation* obj, Task::Id from, Task::Id to)

现在我需要在 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 语法也不是很简单……所以有人给我提示吗?提示正确__attribute__arm-g++ 是一种方法,提示修复 asm 代码将是另一种方法。另一种方法可能是在输入 asm 语句时告诉编译器a1..a3应该已经在寄存器r0..r2中(我调查了一下,但没有找到任何提示)。

4

2 回答 2

1

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

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

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

于 2010-03-29T18:11:09.843 回答
0

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

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

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

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

因此,在代码中与B代码交换后,代码编译完成。但是,如上所述,整个概念无法按预期工作。也许其他人可以找到该代码的用例,我现在不得不求助于经典的函数调用......BXasm volatile

于 2010-03-31T16:35:02.840 回答