最近我试图将我的代码打包到带有 1kB 闪存的小型 ATTiny13 中。在优化过程中,我发现了一些奇怪的东西。让我们以示例代码为例:
#include <avr/interrupt.h>
int main() {
TCNT0 = TCNT0 * F_CPU / 58000;
}
它当然没有意义,但有趣的是输出大小 - 它产生 248 字节 .
代码快速说明:
F_CPU
是由 -DF_CPU=...
定义的常量切换为 avr-gcc
, TCNT0
是 8 位寄存器(在 ATTiny13 上)。在实际程序中,我将方程结果分配给 uint16_t,但仍观察到相同的行为。如果表达式的一部分被括在括号中:
TCNT0 = TCNT0 * (F_CPU / 58000);
输出文件大小为 70 字节 .巨大的差异,但这些操作的结果是相同的(对吧?)。
我查看了生成的汇编代码,尽管我不太了解 ASM,但我看到无括号版本添加了一些标签,例如:
00000078 <__divmodsi4>:
78: 05 2e mov r0, r21
7a: 97 fb bst r25, 7
7c: 16 f4 brtc .+4 ; 0x82 <__divmodsi4+0xa>
7e: 00 94 com r0
80: 0f d0 rcall .+30 ; 0xa0 <__negsi2>
82: 57 fd sbrc r21, 7
84: 05 d0 rcall .+10 ; 0x90 <__divmodsi4_neg2>
86: 14 d0 rcall .+40 ; 0xb0 <__udivmodsi4>
88: 07 fc sbrc r0, 7
8a: 02 d0 rcall .+4 ; 0x90 <__divmodsi4_neg2>
8c: 46 f4 brtc .+16 ; 0x9e <__divmodsi4_exit>
8e: 08 c0 rjmp .+16 ; 0xa0 <__negsi2>
以及更多。我只学习了一段时间的 x86 汇编程序,但据我所知,除法有简单的助记符。为什么
avr-gcc
在第一个示例中添加了这么多代码?另一个问题是,如果两个数字在编译时已知,为什么编译器不内联方程的右边部分。
最佳答案
我们有这个:
x = x * 1200000 / 58000
请注意
1200000/58000 = 20.69...
不是整数,所以这必须计算为首先乘以然后地板除法。您的架构没有针对此数据类型的原生整数除法,因此必须对其进行模拟,从而产生大量代码。然而这个:
x = x * (1200000 / 58000)
我们发现
1200000 / 58000 = 20
, 由于C使用地板师 ,所以这段代码被简化为:x = x * 20
关于c - 为什么 a*b/c 而不是 a*(b/c) 在 AVR 上提供 3 倍大的程序大小?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62302796/