重写 GCC 内联程序集以不需要易失性或内存破坏 [英] Rewriting GCC inline assembly to not require volatile or a memory clobber

查看:21
本文介绍了重写 GCC 内联程序集以不需要易失性或内存破坏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以重写或改进此函数,使其内联汇编中不需要 volatile 或通用内存破坏?

Is it possible to rewrite or improve this function to not require volatile or a generic memory clobber in its inline assembly?

// 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 \n\t"
        : "+r" (r0), "+r" (r1)
        :
        : "r2", "r3", "cc", "memory"
        );
}

针对这个具体情况,目标平台是ARM7系统,代码是用GCC 5.3.0编译的.正在执行的系统调用与 C 函数调用具有相同的调用约定.经过一些反复试验,我得出了上面有效"的方法,但我还不确定它是否正确并且总是有效,这取决于优化编译器的奇思妙想.

For this specific situation, the target platform is an ARM7 system, and the code is being compiled with GCC 5.3.0. The system call being performed has the same calling convention as a C function call. After some trial and error, I've arrived at the above which "works" but I am not yet confident that it is correct and will always work, subject to the whims and fancies of the optimizing compiler.

我希望能够删除内存"破坏并告诉 GCC 确切的内存将被修改,但 GCC 扩展 Asm 文档讨论了如何为特定寄存器赋值,然后是内存约束,但不是如果它们都可以组合.到目前为止,从上面的示例中删除内存"破坏可能会导致 GCC 在处理代码中不使用输出.

I'd like to be able to remove the "memory" clobber and tell GCC exactly which memory will be modified, but the GCC Extended Asm documentation discusses how to assign values to specific registers, and then memory constraints, but not if they both can be combined. As of now, removing the "memory" clobber from the above example can cause GCC to not use the output in proceeding code.

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

I'd also like to be able to remove the volatile in the cases where the output is not used. But as of now, removing volatile from the above example causes GCC to not emit the assembly at all.

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

Adding additional inline assembly to move the system call parameters into r0/r1 manually or un-inlining by moving the code to an external compilation unit are wasteful workarounds that I'd much rather avoid.

推荐答案

长话短说:这就是 "m" 约束的用途.通常,如果您使用 volatile__volatile__asm,那是因为您的代码中存在错误.编译器的主要工作之一是流分析,所以只要你给它足够的信息来做正确的流分析,一切都会正常工作.

Long story short: this is what the "m" constraint is for. Usually, if you are using volatile or __volatile__, with asm, it is because there is an error in your code. One of the compiler's main jobs is flow analysis, so as long as you give it enough information to do the correct flow analysis, everything will work correctly.

这是一个固定版本:

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 推荐的编译器选项).输出如下:

You can test it on https://gcc.godbolt.org/ (-O2 compiler options recommended). The output is as follows:

svc #0x0f0000
bx lr

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

Obviously, when inlined, it should reduce to just the one instruction.

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

Unfortunately, I couldn't figure out how to specify specific registers when using inline ARM assembly, other than the method above which is a bit clumsy.

这篇关于重写 GCC 内联程序集以不需要易失性或内存破坏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆