C语言 重写 GCC 内联程序集,使其不需要易失性或内存破坏



是否可以重写或改进此函数,使其内联程序集中不需要易失性或通用内存破坏器?

// do stuff with the input Foo structure and write the result to the 
// output Bar structure.
static inline void MemFrob(const struct Foo* input, struct Bar* output) {
    register const Foo* r0 asm("r0") = input;
    register Bar* r1 asm("r1") = output;
    __asm__ __volatile__(
        "svc #0x0f0000 nt"
        : "+r" (r0), "+r" (r1)
        :
        : "r2", "r3", "cc", "memory"
        );
}

对于此特定情况,目标平台是 ARM7 系统,代码正在使用 GCC 5.3.0 进行编译。 正在执行的系统调用与 C 函数调用具有相同的调用约定。 经过一些试验和错误,我已经得出了上述"有效"的方法,但我还不确定它是正确的并且将始终有效,这取决于优化编译器的突发奇想和幻想。

我希望能够删除"内存"标记并准确地告诉GCC将修改哪个内存,但是GCC扩展Asm文档讨论了如何将值分配给特定的寄存器,然后是内存约束,但如果它们都可以组合,则不会。 截至目前,从上述示例中删除"内存"clobber 可能会导致 GCC 在后续代码中不使用输出。

我还希望在不使用输出的情况下能够消除易失性。 但截至目前,从上述示例中删除易失性会导致 GCC 根本不发出程序集。

添加额外的内联程序集以手动将系统调用参数移动到 r0/r1 中,或者通过将代码移动到外部编译单元来取消内联是我宁愿避免的浪费解决方法。

长话短说:这就是"m"约束的用途。 通常,如果您使用的是 volatile__volatile__ ,与 asm ,这是因为您的代码中存在错误。 编译器的主要工作之一是流分析,因此只要您为其提供足够的信息来进行正确的流分析,一切都会正常工作。

这是一个固定版本:

void MemFrob(const struct Foo* input, struct Bar* output) {
    register const Foo* r0 asm("r0") = input;
    register Bar* r1 asm("r1") = output;
    __asm__ (
        "svc #0x0f0000"
        : "=m"(*r1) // writes data to *output (but does not read)
        : "m"(*r0), // reads data in *input
          "l"(r0), "l"(r1) // This is necessary to ensure correct register
        : "r2", "r3", "cc"
        );
}

您可以在 https://gcc.godbolt.org/上对其进行测试(建议使用-O2编译器选项(。 输出如下:

svc #0x0f0000
bx lr

显然,当内联时,它应该减少到只有一个指令。

不幸的是,我无法弄清楚在使用内联 ARM 程序集时如何指定特定的寄存器,除了上面的方法有点笨拙。

最新更新