Optimization 为什么这个代码是由avr gcc生成的,它是如何工作的?
这是我正在处理的一个C项目中反汇编的AVR代码片段。我注意到这个奇怪的代码正在生成,我无法理解它是如何工作的。我假设这是某种荒谬的优化 原因是什么Optimization 为什么这个代码是由avr gcc生成的,它是如何工作的?,optimization,compiler-construction,assembly,avr,avr-gcc,Optimization,Compiler Construction,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
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
具体来说,为什么第二条指令从R25中减去0xFF,而不是仅仅从R25中减去0xFF;dr编译器的设计目的是使用更具可移植性、高效性和通用性的解决方案 指令设置用于后续指令的
C
(进位)和H
(半进位)CPU标志(8位AVR BTW中没有ADDI
,因此要添加x
的立即值,我们从中减去-x
),而不是。由于SUBI
和INC
都有2个字节的长度,并且在1个时钟周期内执行,因此使用SUBI
-oth不会丢失任何内容,如果使用8位大小的计数器,则可以很容易地检测它是否已翻转(按/),以及是否使用16位或32位大小的计数器,它允许您以一种非常简单的方式递增它-只需使用INC
,0x00FF
将增加到0x0000
,因此您必须在INC
初始化之前检查最低字节是否为0xFF
。OTOH,使用SUBI
您只需SUBI-1
最低字节,然后ADC 0
用于以下字节,确保所有潜在进位都已计算在内
进一步阅读:
SUBI指令可用于将任何8位常量与8位值相加/相减。它的成本与INC相同,即指令大小和执行时间。因此,编译器更喜欢SUBI,因为它更通用。没有相应的ADDI指令,可能是因为它是冗余的。如果不明显,我指的是第二行:从R25中减去0xFF。。。为什么不只是“INC R25”?正如@PeterCordes所指出的那样,这样的代码实际上只会通过
-O0
发出,这使得这个问题本身有点没有意义。。。使用-O3
/-Os
,这些指令将被更改为更紧凑的版本(分支上的标志等)。我包括了接下来的几个指令。。。这有助于缩小范围吗?我仍然不明白“SUBI R25,0xFF”和“INC R25”的区别,两者都是1个时钟。如果R25从255转为0,TST R25指令将正常工作。看起来INC设置了V、N、S、Z标志,但SUBI设置了H、V、N、S、Z、C。由于编译器接下来生成了一条TST指令,它似乎无论如何都没有使用SUBI的标志。此外,使用SUBI似乎有些奇怪,因为带有0x01的ADDI将再次等效。这真的有点神秘。我只是注意到没有ADDI指令。也许这可以部分解释。@GregHewgill我已经冒昧地(当然,如果你愿意,可以随意回滚)扩展了你的答案;因为你基本上是用这里的旗子打钉子,所以我添加了一些链接、基本原理等来证实它。我不想发布另一个答案,因为你基本上已经回答过了,但我觉得你的答案会更好,需要更多的幕后编译器琐事。@vaxquis:是的,我一直在看gcc asm输出(包括一个),我只是没有使用AVR的经验。你是对的,两者都有相同的大小/CPU时钟-但是说“SUBI
是首选(…),因为它更通用”只是手工操作,没有描述任何细节SUBI
并没有“更通用”,因为它需要寄存器r16-31,而不是INC
的r0-31范围。avr gcc编译器被手动调整为执行SUBI
而不是INC
,原因正是下面格雷格给出的-SUBI
正确设置进位标志,而INC
只是忽略它。使用SUBI
,它使16/32位计数器更加简单,因为您根本不必检查较低字节的溢出!