c - 为什么 a*b/c 而不是 a*(b/c) 在 AVR 上提供 3 倍大的程序大小?

标签 c assembly gcc avr atmega

最近我试图将我的代码打包到带有 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/

相关文章:

c++ - 编译失败,出现 "relocation R_X86_64_32 against ` .rodata.str1。 8' can not be used when making a shared object"

c - 实现数学递归公式

python - 使用 ctypes 加载 .dll

c - 包含非标准 header 时,YouCompleteMe 不会显示错误

gcc - 如何在 Mac OS X 上使用 AVX/pclmulqdq

c++ - gcc 与 clang : expanding a captured parameter pack twice

c - Pthread 未打印正确的值

Linux NASM 检测 EOF

delphi - 如何在地址计算中使用标签

c - 生成c的函数调用