【问题标题】:gcc for ARM does a compare with zero after a subtractionARM的gcc在减法后与零进行比较
【发布时间】:2021-12-28 22:01:05
【问题描述】:

我目前正在尝试检查哪些 C 代码确实为 ARM Cortex-M 处理器生成了最佳/高效的代码。特别是对于循环:我想知道我需要如何编写一个循环才能省略汇编中的任何比较语句并使用零标志。

为此,我设置了一个带有循环的演示项目。循环倒计时到零,如下所示:

sum = 0;

    for(int i=ADC_BUFFER_LENGTH-1; i!=0; i--)
    {
      sum += adc_data_buffer[i];
    }

除其他外,代码的反汇编:

...
3b01        subs    r3, #1
2b00        cmp     r3, #0
d1f5        bne.n   14 <main+0x14>
...

可以看出,寄存器 3 中的计数器值将递减,然后与“0”比较,如果不相等则分支(= 未设置零标志)。

这里比较语句的用法让我很困惑,因为减法语句当然也对零标志有影响。

它是使用 -Og 优化选项编译的。当我使用 -O3 时,减法不会发生在分支之前,因此需要进行比较。由于寄存器 3 没有在其他任何地方使用,甚至可以通过稍微重新排列语句来进一步优化程序集。

这让我感到困惑,因为我多次阅读到现在的优化器/编译器非常好,编写自己的程序集不可能更好......有人可以对此有所了解吗?

目标是STM32F030,在Windows 10上使用arm-none-eabi-gcc 10.3.1-2.3.1编译(STM32-for-VSCode)

【问题讨论】:

  • 使用-Og 编译时不要期望最佳代码。此优化级别针对调试体验进行了优化,因此将避免消除部分计算的优化。
  • 使用 32 位指令总线,这为您提供 2 条指令。一些优化的目标可能是减少代码分支的数量,并将循环代码的内容塞进更少的 32 位字中。
  • gcc 的输出自 4.x.x 以来并没有变得更好,虽然有一些改进,但正如我们在很多失败中看到的那样。即使进行了“出色”的优化,仍然可以预期人们可以为一个体面的项目进行手动改进。应该能够找到一些改进。好/坏虽然是相对的。这可能是为了优化调试而有意的,因为调试想要制作一个可以单步执行的二进制文件等,因此,人们不会对这样的事情感到惊讶。
  • 您能否提及 CFLAGS 和 gcc 版本,以便人们可以在编译器资源管理器中使用它?

标签: assembly gcc arm cortex-m


【解决方案1】:
  1. 如果此代码应添加缓冲区中的所有值 - 那么它是无效的,因为它跳过了 adc_data_buffer[0]ADC_BUFFER_LENGTH 定义为 16
    for(int i=ADC_BUFFER_LENGTH-1; i >= 0; i--)
    {
      sum += adc_data_buffer[i];
    }

针对大小或-O2进行了优化。

foo:
        movs    r2, r0
        movs    r3, r0
        movs    r0, #0
        adds    r2, r2, #60
.L2:
        ldr     r1, [r2]
        adds    r0, r0, r1
        movs    r1, r2
        subs    r2, r2, #4
        cmp     r3, r1
        bne     .L2
        bx      lr   lr

为了更正确,您应该使用size_t 而不是int 作为索引。

    for(size_t i=ADC_BUFFER_LENGTH; i; i--)
    {
      sum += adc_data_buffer[i - 1];
    }

(它会编译成和上面一样的代码)

-O3 优化只会展开循环

https://godbolt.org/z/nGEehro5h

【讨论】:

    猜你喜欢
    • 2013-10-01
    • 1970-01-01
    • 2013-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多