【问题标题】:gcc: strange asm generated for simple loopgcc:为简单循环生成的奇怪 asm
【发布时间】: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 %d0andil #65535,%d0movel %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


【解决方案1】:

我一直在使用 GCC 和 68k 代码生成,我发现它无法再为 68k 系列生成像样的代码,尤其是 68000。

代码几乎不正确,但没有优化(或者我应该说,它似乎是 DE 优化的?)。您应该首先尝试使用 -Os 而不是 -O2。即使这样你也会在生成的代码中遇到很多无用的insn。

我的猜测是,虽然 GCC 中的实际架构支持迅速发展,但 68k 的后端没有得到适当的维护,只是以最小的努力保持正确。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-02
    • 1970-01-01
    • 1970-01-01
    • 2015-08-14
    • 2021-04-22
    • 1970-01-01
    • 2012-08-17
    • 2019-02-11
    相关资源
    最近更新 更多