代码之家  ›  专栏  ›  技术社区  ›  orithena

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

  •  6
  • orithena  · 技术社区  · 15 年前

    我正在将一个小型的学术操作系统从三叉戟移植到手臂皮质(拇指-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 (A.K.A.跳跃)而不是 CALL 当跳转到(否则正常)C++函数时,堆栈和CSA保持不变。 superDispatch(SchedulerImplementation* obj, Task::Id from, Task::Id to) .

    现在,我需要在arm cortex(或者,更确切地说,对于arm-none-linux-gnueabi-g++)上有一个等价的行为,即生成一个 B (A.K.A.分支)指令,而不是 BLX (A.K.A.Link and Exchange分支机构)。但是没有 interrupt_handler arm-g++的属性,我找不到任何等效的属性。

    所以我想求助于 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代码的提示是另一种方法。另一种方法可能是告诉编译器 a1..a3 应该已经在寄存器中了 r0..r2 当输入asm语句时(我仔细研究了一下,但没有发现任何提示)。

    2 回复  |  直到 15 年前
        1
  •  1
  •   Mike Seymour    15 年前

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

    正如我在注释中提到的,ARM中断处理程序声明为 interrupt 属性,但正如您发现的,这不会影响它们的调用方式。我想这是一个平台特有的技巧,它恰巧在Tricore上做了正确的事情。

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

        2
  •  0
  •   orithena    15 年前

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

    直接跳到另一个函数的整个概念在arm cortex上是没有意义的,因为tricore每次调用另一个函数时都使用上下文保存区域(context save area,CSA)来保存整个CPU上下文。把它看作是第二个独立的堆栈,每个堆栈都随着 CALL 和每个收缩 RET . 每个CSA块的大小都是固定的。

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

    关于链接器错误和未定义的引用R6…嗯,我应该更仔细地阅读说明集文档。 B 是一个无条件的分支 立即的 地址, BX 在寄存器中需要分支地址的指令。我被手册中的说明列表愚弄了 BX 很快被称为“交易所的分支机构”。我不想交换任何东西,我想要一个简单的跳跃,所以我没有进一步阅读。

    所以,在交换之后 具有 BX asm volatile 代码,编译的代码。但是,正如上面所指出的,整个概念不能如预期的那样工作。也许其他人可以找到这个代码的用例,我现在不得不求助于经典的函数调用…