为什么在AVR上用a*bc而不是a*(bc)会使程序大小增加3倍?

问题描述 投票:2回答:1

最近我试着把我的代码打包到小ATTiny13上,用1kB的闪存。在优化过程中,我发现了一些奇怪的东西。让我们举个例子。

#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 在第一个例子中增加了这么多代码?

另一个问题是,如果在编译时两个数字都是已知的,为什么编译器不内联公式的正确部分。

c assembly gcc avr atmega
1个回答
8
投票

我们有这个。

x = x * 1200000 / 58000

请注意... 1200000/58000 = 20.69... 不是一个整数,所以必须以先乘后除的方式计算。你的架构没有这个数据类型的原生整数除法,所以必须模拟它,导致代码很多。

然而这个。

x = x * (1200000 / 58000)

我们发现 1200000 / 58000 = 20, 因为C使用了地板分割,所以这段代码简化为只。

x = x * 20
© www.soinside.com 2019 - 2024. All rights reserved.