c - 如何防止 ARM Compiler 5 armcc 内联汇编程序中的 LDM/STM 指令扩展?

标签 c assembly arm embedded armcc

我正在尝试使用 ARM Compiler 5 armcc 编译的 .c 文件中的内联汇编中的 STM/LDM 指令生成 AXI 总线突发访问。

inline void STMIA2(uint32_t addr, uint32_t w0, uint32_t w1)
{
    __asm {
        STMIA addr!, { w0, w1 }
    }
}

但是 ARM 编译器 armcc 用户指南第 7.18 段说: “所有 LDM 和 STM 指令都扩展为一系列具有等效效果的 LDR 和 STR 指令。但是,编译器随后可能会在优化期间将单独的指令重新组合为 LDM 或 STM。”

这就是实践中真正发生的事情,LDM/STM 在某些情况下被扩展为一组 LDR/STR,并且这些指令的顺序是任意的。 这会影响性能,因为我们使用针对突发处理优化的硬件。这也破坏了功能正确性,因为我们使用的硬件考虑了单词序列并忽略了偏移量(但编译器认为更改指令顺序是安全的)。

要解决此问题,可以使用嵌入式汇编器而不是内联汇编器,但这会导致额外的函数调用 - 返回影响性能的内容。

所以我想知道是否有一种方法可以在不损失性能的情况下正确生成 LDM/STM?我们能够在 GCC 中执行此操作,但没有找到任何适用于 armcc 的解决方案。

objective-c PU:Cortex M0+ (ARMv6-M)。

编辑: 从设备都是片上设备,大部分是非内存设备。对于支持地址空间突发访问区域的非内存从属寄存器的每个寄存器都被保留(例如[0x10000..0x10100]),我不完全确定为什么,也许CPU或总线不支持固定(非增量) ) 地址。 HW 忽略该区域内的偏移量。例如,完整请求可以是 16 个字节,完整请求的第一个字是写入的第一个字(即使偏移量不为零)。

最佳答案

So I'm wondering if there is a way to generate LDM/STM properly without losing performance? We were able to do this in GCC, but didn't find any solution for armcc.

一些关于编译器优化的内容。 Register allocation是最艰巨的工作之一。任何编译器代码生成的核心可能都围绕着它分配物理 CPU 寄存器的时间。大多数编译器都使用 Single static assignment or SSA将您的“C”变量重命名为一堆伪变量(或时间顺序变量)。

为了让您的 STMIA 和 LDMIA 正常工作,您需要加载和存储保持一致。即,如果它是 stmia [rx], {r3,r7} 和像 ldmia [rx], {r4,r8} 这样的恢复,'r3' 映射到新的“r4”和存储的“r7”映射到恢复的“r8”。这对于任何编译器来说都不容易实现,因为“C”变量将根据需要分配。同一变量的不同版本可能在不同的寄存器中。要使 stm/ldm 工作,必须分配这些变量,以便寄存器以正确的顺序递增。即,对于上面的ldmia,如果编译器想要在r0中存储r7(也许是一个返回值?),这是没有办法的在不生成额外代码的情况下创建良好的 ldm 指令。

您可能已经让 gcc 生成了这个,但这可能是运气。如果您只使用 gcc,您可能会发现它也不能正常工作。

参见:ldm/stm and gcc对于 GCC stm/ldm 的问题。

以你为例,

inline void STMIA2(uint32_t addr, uint32_t w0, uint32_t w1)
{
    __asm {
        STMIA addr!, { w0, w1 }
    }
}

inline的值(value)在于可以将整个函数体放在代码中。调用者可能在寄存器 R8 和 R4 中有 w0w1。如果函数不是 inline,则编译必须将它们放在 R1 和 R2 中,但可能会生成额外的移动。任何编译器都很难通用地满足 ldm/stm 的要求。

This affects performance since HW we use optimized for bursts processing. Also this breaks functional correctness because HW we use takes into consideration sequence of words and ignores offsets (but compiler think that it's safe to change the order of instructions).

如果硬件是总线上特定的非内存从属外围设备,那么您可以将写入此从属设备的功能包装在外部包装器中并强制分配寄存器(参见 AAPCS )以便 ldm/stm 将起作用。这将导致性能下降,但可以通过设备驱动程序中的一些自定义汇编器来减轻。

但是,听起来设备可能是内存?在这种情况下,您遇到了问题。通常,像这样的存储设备只会使用缓存?如果您的 CPU 有一个 MPU(内存保护单元)并且可以同时启用数据和代码缓存,那么您可能会解决这个问题。缓存行将始终是突发访问。只需要注意设置 MPU 和数据缓存的代码。 OPs Cortex-M0+ 没有缓存并且设备是非内存的,因此这是不可能的(也不需要)。

如果您的设备是内存且没有数据缓存,那么您的问题可能无法解决(无需付出大量努力)并且您需要不同的硬件。或者您可以像外围设备一样包装它并降低性能;失去了存储设备随机访问的好处。

关于c - 如何防止 ARM Compiler 5 armcc 内联汇编程序中的 LDM/STM 指令扩展?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33183405/

相关文章:

c - C有模板吗?

c - 在用户输入旁边打印输出

assembly - 使用 Mips Assembly 从 txt 文件中读取和打印内容

c++ - "new"operator in multiple threads cause Segmentation Fault

c++ - 修复关于 C/C++ 和寄存器访问的知识差距

c - 使用指针将嵌套结构传递给函数

c - 为什么 gdb 会抛出内部错误并告诉我存在错误?

gcc - 二进制 AND (&) 的无效操作数

c - 不同语言的切入点

android - 如何配置 IBM Worklight Studio 以构建 ARM 和 MIPS 设备?