调用const函数地址传递给gcc内联汇编程序(avr-gcc)

时间:2014-11-14 12:13:56

标签: c++ gcc avr inline-assembly avr-gcc

我正在为AVR编写RPC库,需要将函数地址传递给某些内联汇编程序代码,并从汇编程序代码中调用该函数。但是当我尝试直接调用函数时,汇编程序会抱怨。

这个最小的例子test.cpp说明了这个问题(在实际情况下我传递的是args,而函数是模板化类的静态成员的实例化):

void bar () {
    return;
}

void foo() {
    asm volatile (
        "call %0" "\n"
        :
        : "p" (bar)
    );
}

使用avr-gcc -S test.cpp -o test.S -mmcu=atmega328p进行编译工作正常,但当我尝试使用avr-gcc -c test.S -o test.o -mmcu=atmega328p avr-as抱怨进行汇总时:

test.c: Assembler messages:
test.c:38: Error: garbage at end of line

我不知道它为什么写“test.c”,它所指的文件是test.S,它在第38行包含这个:

call gs(_Z3barv)

我已经尝试了对内联汇编程序的参数的所有甚至远程敏感的约束,我可以找到here,但没有一个我尝试过的。

我想如果删除了gs()部分,一切都应该有效,但所有约束似乎都会添加它。我不知道它的作用。

奇怪的是,像这样的间接调用组合就好了:

void bar () {
    return;
}

void foo() {
    asm volatile (
        "ldi r30, lo8(%0)" "\n"
        "ldi r31, hi8(%0)" "\n"
        "icall" "\n"
        :
        : "p" (bar)
    );
}

生成的汇编程序如下所示:

ldi r30, lo8(gs(_Z3barv))
ldi r31, hi8(gs(_Z3barv))
icall

而且avr-并没有抱怨任何垃圾。

2 个答案:

答案 0 :(得分:1)

请注意call需要一个常量,已知的汇编时值。 "p"约束不包括该语义;它还允许来自char* x无法处理的变量(例如call)的指针。 (我似乎记得有时候gcc足够聪明,能够以这样的方式进行优化,以便在这里工作 - 但这基本上是无证件的行为和非确定性的,所以最好不要指望它。)

如果您实际调用的函数是编译时常量,则可以使用"i" (bar)。如果不是,那么除了使用icall之外别无其他选择。

顺便说一下,https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html#Machine-Constraints的AVR部分记录了一些特定于AVR的约束。

答案 1 :(得分:0)

我尝试了各种方法将C函数名称传递给内联ASM代码但没有成功。但是我确实找到了一种解决方法,似乎可以提供所需的结果。

回答问题:

https://www.nongnu.org/avr-libc/user-manual/inline_asm.html所述,您可以在原型声明中为C函数指定ASM名称:

void bar (void) asm ("ASM_BAR");    // any name possible here
void bar (void)
{
    return;
}

然后您可以从ASM代码轻松调用该函数:

asm volatile("call ASM_BAR");

与库函数一起使用:

这种方法不适用于库函数,因为它们有自己的原型声明。要从ISR更有效地调用system_tick()库的time.h函数,您可以声明一个辅助函数。遗憾的是,GCC不会将内联设置应用于来自ASM代码的调用。

inline void asm_system_tick(void) asm ("ASM_SYSTEM_TICK") __attribute__((always_inline));
void asm_system_tick(void)
{
    system_tick();
}

在下面的例子中,GCC只生成周围代码的推/弹指令,而不是函数调用!请注意,system_tick()是专门为ISR_NAKED设计的,并且可以自行完成所有必需的堆栈操作。

volatile uint8_t tick = 0;
ISR(TIMER2_OVF_vect)
{
    tick++;
    if (tick > 127)
    {
        tick = 0;
        asm volatile ("call ASM_SYSTEM_TICK");
    }
}

因为inline属性不起作用,所以每个函数调用需要8个额外的cpu周期。与通过正常函数调用进行推/拉操作所需的5632个CPU周期(每次运行ISR需要44个CPU周期)相比,它仍然是一个非常令人印象深刻的改进。