2011-08-03 40 views
1

我需要加载18h并将其输出到端口60h,以下工作(内部asm(“”))。GCC内联汇编:让编译器决定使用什么寄存器来临时值

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

+0

它创建了多少开销? – osgx

+1

'mov '不得不创建一个tmp变量,然后在输入块中描述它也是开销。 –

+0

你可以尝试更新的gcc和更高的-O3级别吗? – osgx

回答

1

我不知道为什么我以前看过开销,但是使用外部register临时变量即使使用-O0(没有优化)也没有开销。所以我使用:

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

它仍然需要将声明的变量,比汇编语句块的输出描述,但它确实创造汇编代码我想(无开销)。

0

我认为你应该对输出操作数使用“= &”约束。

的机制是这样的:

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

  • 对于输出操作数,编译器也会为您分配一系列寄存器。在内联汇编结束时,您应该将这些寄存器加载到结果中,因为编译器希望在那里找到它们。 注意事项:您为输出操作数提供的寄存器可能与您为输入操作数提供的寄存器一致!然而,这是非常有意义的:编译器为您提供在之前的某个寄存器中的输入,并在之后将您的结果收集到与您的程序集相同的寄存器中。

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

  • 您可以使用输入输出修饰符('+')明确要求编译器对操作数的输入和输出使用完全相同的寄存器。这实际上使您可以透明地访问操作数。重述相反不成立:不是使用此修饰符确实不是防止编译器使用相同的寄存器两倍 - 使用输出修饰符。

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

  • 不要请求一个临时变量输入操作数由于其只读性质和编译器的假设。例如,如果您创建一个临时变量并使用某个值(可能为零)对其进行初始化,则编译器可能会重复使用它提供给您的寄存器,以便稍后再次需要它的值(零)。
  • 不要求输出操作数,因为它可能与某些输入操作数重合。当您使用所谓的临时性时,您可能会意外地碰撞您的输入操作数。
  • 使用带有输出的输出修饰符。通过这种方式,编译器将为您分配一些可以安全地进行抓取的寄存器(因为它期望从那里得到一些输出)。

使用输入输出也为您提供安全的注册。但是,操作数需要初始化(因为编译器假定您将其作为输入读取),并且如果您在内联汇编的多个片段中使用相同的操作数,则需要恢复(出于同样的原因)。相反,当使用输出专用修改器时,编译器可以很容易地发现该变量从不读取。

相关问题