【发布时间】:2020-03-16 02:40:44
【问题描述】:
m68k-linux-gnu-gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
CFLAGS = -Wall -Werror -ffreestanding -nostdlib -O2 -m68000 -mshort
我很困惑为什么 gcc 会为 const 数组上的简单 for 循环生成这样(看似)非最佳代码。
const unsigned int pallet[16] = {
0x0000,
0x00E0,
0x000E,
...
0x0000
};
...
volatile unsigned long * const VDP_DATA = (unsigned long *) 0x00C00000;
...
for(int i = 0; i < 16; i++) {
*VDP_DATA = pallet[i];
}
结果:
296: 41f9 0000 037e lea 37e <pallet+0x2>,%a0
29c: 223c 0000 039c movel #924,%d1
2a2: 4240 clrw %d0
2a4: 0280 0000 ffff andil #65535,%d0
2aa: 23c0 00c0 0000 movel %d0,c00000 <_etext+0xbffc2c>
2b0: b288 cmpl %a0,%d1
2b2: 6712 beqs 2c6 <main+0x46>
2b4: 3018 movew %a0@+,%d0
2b6: 0280 0000 ffff andil #65535,%d0
2bc: 23c0 00c0 0000 movel %d0,c00000 <_etext+0xbffc2c>
2c2: b288 cmpl %a0,%d1
2c4: 66ee bnes 2b4 <main+0x34>
我最关心的问题:
为什么无用的第一个元素比较 2b0?这永远不会命中,也永远不会分支回来。它只是最终成为第一次迭代的重复代码。
- 有没有更好的方法来编写这个死简单循环,以使 gcc 不会产生这种奇怪的代码?
- 是否有任何我可以利用的编译器标志/优化?
O3只是展开循环,我也不想要它,因为在这部分代码中,空间比速度更重要。 - 也许我太谨慎了,但我只是认为这不会是最难生成的代码。我期待更多类似的东西(可能是错误的,但你明白了):
lea pallet,%a0
movel #7,%d0
1:
movel %a0@+,c00000
dbra %d0,1
我知道我必须在我的代码中更明确一点才能让它写成长块。我的主要观点是为什么 gcc 似乎无法弄清楚我的意图,即我只想将这些数据转储到这个地址。
另一个观察:
clrw %d0 → andil #65535,%d0 → movel %d0,c00000。为什么不直接clrl 移动?
【问题讨论】:
-
-Os有用吗?这优化了大小(但不以牺牲速度为代价)。或者-O3 -fno-unroll-loops? 68k 没有被广泛使用,所以我并不感到惊讶,有一些面部级别的错过窥视孔和其他优化直到现在没人注意到,除非有一些微架构的原因让他们使用 tune=generic 关心一些 CPU。您是否尝试过-march=68020或其他?无论如何,我猜 GCC 开发人员会欣赏关于错过优化的错误报告 (gcc.gnu.org/bugzilla),尤其是带有可重复的小示例的简单窥视孔。 -
(每个错误报告都错过了一次优化,即使它们都是来自编译相同的代码。)
-
完全展开小常数迭代可能由不同的 GCC 选项控制。也许
-fno-peel-loops?是的,the documentation 看起来它适用于您的 0..15 循环,并且它仅在-O3启用,而不是在-O2启用。 GCC 的调整启发式甚至可以设置何时执行此操作的阈值:How to ask GCC to completely unroll this loop (i.e., peel this loop)? -
m68k可以做内存到内存
movel吗?此外,这不适用于您的情况,因为您需要从 16 位unsigned int零扩展至 32 位unsigned long。 -
没有完全展开(也称为剥离)导致执行的总指令数减少?只需在寄存器和
movel %d0,c00000中构造每个常量值,没有 cmp / 分支开销。如果这是在适合指令缓存的热循环中,那可能是一个胜利。回复:m68k 将内存移动到内存:是的,与大多数 ISA 不同,m68k 可以使用一条move指令将内存复制到内存。 mrjester.hapisan.com/04_MC68/Sect01Part05/Index.html。唯一的障碍是需要 16 位加载/32 位存储;您可以尝试使用unsigned long调色板数组来查看 gcc 的作用。
标签: c loops gcc assembly 68000