GCC内联汇编:让编译器决定用于临时值的寄存器

时间:2011-08-03 23:00:44

标签: gcc inline-assembly avr avr-gcc

我需要加载18h并将其输出到端口60h,以下工作(在asm(“”)内)。

ldi r1, 0x18 ; 0x18 -> r1
sts 0x60, r1 ; output r1 -> 0x60

我不在乎寄存器r1或其他任何用途。是否有一种简单的方法让编译器决定使用哪个寄存器?

我可以使用外部r / w变量,但它会产生一些不必要的开销:

register uint8_t tmp;
asm volatile (
    "ldi %[tmp], 0x18 \n\t"
    "sts 0x60, %[tmp]"
    : [tmp] "=r"(tmp) :);

这适用于AVR atmega(8位)处理器。使用GCC 4.3.2

2 个答案:

答案 0 :(得分:1)

我不知道为什么我之前看到了开销,但使用外部register临时变量即使使用-O0(无优化)也无需开销。所以我正在使用:

register uint8_t tmp;
asm volatile (
    "ldi %[tmp], 0x18 \n\t"
    "sts 0x60, %[tmp]"
    : [tmp] "=r"(tmp) :);

它仍然需要声明一个变量,而不是在asm语句的输出块中描述,但它确实创建了我想要的汇编代码(没有开销)。

答案 1 :(得分:0)

我认为你应该使用" =&"输出操作数的约束。

机制是这样的:

  • 对于任何输入操作数,编译器将提供一些寄存器,并在内联汇编开始之前使用其操作数的值加载它们。输入操作数是只读,这意味着编译器将进一步期望您在程序集中保持寄存器不变。也就是说,编译器在组装后期望寄存器具有相同的值,因为它可能决定随后使用这些值。

  • 对于输出操作数,编译器也会为您分配一系列寄存器。在内联汇编结束时,您应该已经将这些寄存器与结果一起加载,因为编译器希望在那里找到它们。 警告:您为输出操作数提供的寄存器可能与您为输入操作数提供的寄存器重合!然而,这完全有道理:编译器在组装之前为某些寄存器提供输入,并在组装之后在同一个寄存器中收集结果。 / p>

  • 您可以使用仅输出修饰符('&')来防止这种情况发生。编译器保证仅输出寄存器永远不会兼作输入寄存器。

  • 您可以使用输入输出修饰符(' +')明确要求编译器使用相同的寄存器来输入和输出操作数。这实际上为您提供了对操作数的透明访问。重述相反的情况:使用此修饰符阻止编译器使用同一寄存器加倍 - 使用仅输出修饰符。

所以基本上,你的选择很简单:

  • 由于其只读性质和编译器的假设,不要为临时变量请求输入操作数。例如,如果您创建一个临时变量并使用某个值(可能为零)对其进行初始化,则编译器可能会重新使用它在以后再次需要其值(零)时为其提供的寄存器。
  • 请求输出操作数,因为它可能与某个输入操作数重合。当您使用所谓的临时操作时,您可能会意外地冲突输入操作数。
  • 将输出与仅输出修饰符一起使用。这样,编译器会为你分配一些你可以安全地抓取的寄存器(因为它需要你的一些输出)。

使用输入输出还可以为您提供安全寄存器。但是,操作数需要初始化(因为编译器假定您将其作为输入读取),并且如果在内联汇编的多个片段中使用相同的操作数,则需要还原(出于同样的原因)。相反,当使用仅输出修饰符时,编译器可以很容易地发现该变量永远不会被读取。