为什么编译器不直接使用LSR


10

嗨,我一直在使用Arduino Uno(所以是ATmega328p)进行项目开发,其中时间非常重要,因此我想看看编译器将我的代码转换成哪些指令。在其中,我有一个uint8_t每次使用迭代都会向右移一位的方法data >>= 1,看来编译器将此翻译为5条指令(data在中r24):

mov     r18, r24
ldi     r19, 0x00
asr     r19
ror     r18
mov     r24, r18

但是,如果我查看指令集文档,我会看到一条指令正是这样做的: lsr r24

我是否忽略了某些内容,或者为什么编译器也没有使用它?寄存器r18r19不在其他任何地方使用。

我使用的是Ardunio,但如果我正确的话,请使用普通的avr-gcc编译器。这是生成序列的代码(已修剪):

ISR(PCINT0_vect) {
    uint8_t data = 0;
    for (uint8_t i = 8; i > 0; --i) {
//        asm volatile ("lsr %0": "+w" (data));
        data >>= 1;
        if (PINB & (1 << PB0))
            data |= 0x80;
    }
    host_data = data;
}

据我所知,Ardunino IDE正在使用系统提供的AVR gcc编译器,版本为6.2.0-1.fc24。两者都通过软件包管理器安装,因此应该是最新的。


1
程序集似乎不对应于C代码。
尤金(Eugene Sh)。

好吧,我使用Ardunio IDE对其进行了编译,然后avr-objdump在elf文件中使用了……似乎与之不符的是什么?
xZise

1
@Eugene Sh .:它确实对应于C代码。它仅对应于直线data >>= 1;
Curd

1
这是“用班次代替分工”是错误建议的情况之一。如果执行/ = 2,则编译器将生成lsr r24; (提示:请尝试使用gcc资源管理器来尝试生成asm代码)
PlasmaHH

什么编译器?什么处理器?显然,这是使问题有意义的必要信息。
Olin Lathrop'2

Answers:


18

根据C语言规范,任何小于操作大小的值int(取决于特定的编译器;在您的情况下int为16位宽)(在您的情况下为>>)都将上载到int操作之前的值。
编译器的这种行为称为整数提升

这正是编译器所做的:

  • r19 = 0是的整数提升值的MSByte data
  • (r19,r18)表示该整数的总提升值data,然后由asr r19和右移一位ror 18
  • 然后将结果隐式地返回给您的uint8_t变量data
    mov r24, r18,即丢弃r19中的MSByte。

编辑:
当然,编译器可以优化代码。
尝试重现该问题,我发现至少使用avr-gcc版本4.9.2不会发生此问题。它创建了非常有效的代码,即C行仅 data >>= 1;被编译为一条lsr r24指令。因此,也许您使用的是非常旧的编译器版本。


2
这不是总的浪费,因为有时您需要未优化的代码来在汇编程序级别进行调试。然后,如果您有未优化的代码,您将感到非常高兴。
凝结

3
如果我没记错的话,-mint8是使整数变为8位的标志。但是,这有很多不良的副作用。抱歉,我不太记得它们现在是什么,但是由于它们,我从未使用过该标志。多年前,我花了很多时间将avr-gcc与商用编译器进行比较。
乔恩(Jon)

1
哦,是的,C标准要求整数必须至少为16位,因此使用-mint8会破坏所有库。
乔恩(Jon)

9
奈杰尔·琼斯(Nigel Jones)在“用于8位微控制器的高效C代码”中说:“ ... C的整数提升规则可能是对我们这些在8位世界中劳动的人们所犯下的最可恶的罪行” ...
Dirceu Rodrigues Jr

1
@Jonas Wielicki:解决该问题的最佳方法是使用更好的编译器。例如,使用avr-gcc版本4.9.2,我无法重现该问题:对于C代码行,d >>= 1;我仅获得一条lsr r24指令。也许xZise使用的是非常旧的编译器版本。
凝结
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.