如何防止 Atmel Studio gcc 6.3.1 将 4 字节 memcmp() 优化为 4 字节直接比较?

How to prevent Atmel Studio gcc 6.3.1 from optimizing 4-byte memcmp() to a 4-byte direct comparison?

运行 Atmel Studio 及其提供的 gcc 6.3.1 为 Atmel/Microchip SAMV70 (ARM Cortex-M7) 芯片构建固件。我有使用 memcmp() 将 4 字节输入数组与 4 字节本地数组进行比较的代码。当使用 -O0 编译以禁用优化时,它工作正常。当使用 -Os 编译以优化大小或使用 -O3 进行最大优化时,编译器将 memcmp() 调用替换为直接 4 字节比较(通过检查反汇编进行验证)。不幸的是,优化有时也会将本地 4 字节数组移动到未对齐的起始地址,因此虽然 memcmp() 可以正常工作,但由于未对齐访问,直接比较会触发 HardFault

在我看来,这 100% 是一个编译器优化错误(可能是 gcc,可能是 Atmel 添加的东西),但我坚持使用提供的编译器,因此无法进行更新。所以这是我的实际问题:有没有办法保持优化启用但禁用此特定优化?否则,我将不得不强制本地 4 字节数组进行 4 字节对齐或寻找其他解决方法。

编译器版本: gcc version 6.3.1 20170620 (release) [ARM/embedded-6-branch revision 249437] (Atmel build: 508)

这是一个可能触发错误的示例函数:

bool example(uint8_t *input_data)
{
    uint8_t local_data[4] = { 0x00, 0x01, 0x02, 0x03 };

    return (memcmp(input_data, local_data, 4) == 0);
}

我的代码总是传入一个 4 字节对齐的 input_data,所以这不是问题,但对于编译器优化而言,再一次将其视为理所当然的错误形式。

回答我自己的问题,因为 Eugene 没有 post 官方回答:

来自 gcc ARM 选项

By default unaligned access is disabled for all pre-ARMv6, all ARMv6-M and for ARMv8-M Baseline architectures, and enabled for all other architectures.

这意味着 ARMv7-M 默认允许 未对齐访问。事实证明这是有道理的,因为来自 ARMv7-M Architecture Reference Manual:

The following data accesses support unaligned addressing, and only generate alignment faults when the CCR.UNALIGN_TRP bit is set to 1, see Configuration and Control Register:

• Non halfword-aligned LDR{S}H{T} and STRH{T}.

• Non halfword-aligned TBH.

• Non word-aligned LDR{T} and STR{T}.

这意味着 ARMv7-M 支持一组有限的未对齐访问。 但是,它不支持所有个非对齐访问:

The following data accesses always generate an alignment fault:

• Non halfword-aligned LDREXH and STREXH.

• Non word-aligned LDREX and STREX.

• Non word-aligned LDRD, LDMIA, LDMDB, POP, LDC, VLDR, VLDM, and VPOP.

• Non word-aligned STRD, STMIA, STMDB, PUSH, STC, VSTR, VSTM, and VPUSH.

还有:

Accesses to Strongly Ordered and Device memory types must always be naturally aligned

所以这是促使我提出最初问题的失败情况:

  1. 启用优化的 gcc 正在用直接比较替换 4 字节 memcmp(),默认情况下 允许的,因为默认情况下允许未对齐的访问。所以这不是编译器错误。
  2. 包含 memcmp() 数据的闪存区域位于声明 Strongly Ordered 的 MPU 段中, 不支持 未对齐的访问。因此,当 memcmp() 被直接比较替换,并且数据落在未对齐的地址上时,比较会触发 HardFault.

Eugene 在他最初的评论中得到正确的修复是将 -mno-unaligned-access 添加到编译器选项。在我的例子中,这仍然允许编译器用直接 4 字节比较替换 memcmp(),但它也强制数据对齐 4 字节,允许比较成功而不会触发错误条件。