为什么这个代码是由avr-gcc生成的,它是如何工作的?

Mar*_*ouf 5 compiler-construction optimization assembly avr avr-gcc

这是我正在研究的C项目的反汇编AVR代码片段.我注意到这个好奇的代码正在生成,我无法理解它是如何工作的.我假设这是一种荒谬的优化......

解释是什么?

92:         ticks++;         // unsigned char ticks;
+0000009F:   91900104    LDS       R25,0x0104     Load direct from data space
+000000A1:   5F9F        SUBI      R25,0xFF       Subtract immediate
+000000A2:   93900104    STS       0x0104,R25     Store direct to data space
95:         if (ticks == 0) {
+000000A4:   2399        TST       R25            Test for Zero or Minus
+000000A5:   F009        BREQ      PC+0x02        Branch if equal
+000000A6:   C067        RJMP      PC+0x0068      Relative jump
Run Code Online (Sandbox Code Playgroud)

具体来说,为什么第二条指令从R25中减去0xFF而不仅仅是INC R25

Gre*_*ill 5

tl;dr编译器被设计为在这里使用更便携、更高效和更通用的解决方案。

SUBI指令集C(进)和H(半进位),CPU标志与后续指令的使用(没有ADDI在8位AVR BTW,所以要加立即值的x我们减去-x从它),而INC不会。由于SUBI&INC都有 2 个字节的长度并在 1 个时钟周期内执行,因此使用SUBI- OTOH不会有任何损失,如果使用 8 位大小的计数器,则可以轻松检测它是否已翻转(通过BRCC/ BRCS),并且如果你有一个 16 位或 32 位大小的计数器,它允许你以一种非常简单的方式增加它 - 只是INC,0x00FF会增加到0x0000,所以你必须检查最低字节是否0xFFINCing之前. OTOH,与SUBI你只是SUBI -1最低字节,然后ADC 0是接下来的字节,确保所有潜在的进位位都被考虑在内。

进一步阅读:

https://lists.gnu.org/archive/html/avr-gcc-list/2008-11/msg00029.html

http://avr-gcc-list.nongnu.narkive.com/SMMzdBkW/foo-subi-vs-inc

  • 看起来 INC 设置了 V、N、S、Z 标志,但 SUBI 设置了 H、V、N、S、Z、C。由于编译器接下来生成了一条 TST 指令,它似乎没有使用来自 SUBI 的标志。此外,使用 SUBI 似乎很奇怪,因为带有 0x01 的 ADDI 将再次等效。这真的有点神秘。 (2认同)
  • 我只是注意到没有 ADDI 指令。也许这可以解释部分原因。 (2认同)
  • @GregHewgill 我冒昧地(当然,如果您愿意,可以随意回滚)扩展您的答案;由于您基本上在这里使用了标志,因此我添加了一些链接、理由等来证实它。我不想发布另一个答案,因为你基本上已经回答了,但我觉得你的答案会更好一些幕后编译器琐事。 (2认同)

sta*_*lue 5

SUBI指令可用于向/从8位值添加/减去任何8位常数.它具有与INC相同的成本,即指令大小和执行时间.所以SUBI是编译器的首选,因为它更通用.没有相应的ADDI指令,可能是因为它是多余的.