2016-02-04 80 views
2

是否可以重写或改进此函数,以便在其内联汇编中不需要volatile或通用内存clobber?将GCC内联汇编重写为不需要volatile或内存clobber

// 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函数调用具有相同的调用约定。经过一些试验和错误之后,我已经到达了“有效”的上述内容,但我还不确定它是否正确并且始终有效,这取决于优化编译器的突发奇想和幻想。

我希望能够删除“内存”clobber并告诉GCC究竟哪个内存将被修改,但GCC Extended Asm文档讨论了如何将值分配给特定的寄存器,然后是内存约束,但不是如果他们都可以合并。到目前为止,从上面的例子中删除“内存”clobber可能会导致GCC不能使用前面的代码中的输出。

我还希望能够在不使用输出的情况下删除volatile。但是到目前为止,从上面的例子中删除volatile会导致GCC根本不发射程序集。

通过将代码移动到外部编译单元来手动添加额外的内联汇编以将系统调用参数移动到r0/r1是非常浪费的替代方法,我宁愿避免这种方法。

+0

1)是的,你可以有1个约束是一个寄存器,另一个是内存,是的,两个约束可以重叠。 2)如果没有volatile,gcc认为asm是计算'r0'和'r1'新值的方式。但是由于优化器可以看到变量'r0'和'r1'在asm之后不再被使用,因此asm被丢弃。 –

回答

2

长话短说:这是"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汇编指定特定的寄存器,除了上面的方法有点笨拙。