2015-08-15 132 views
1

在GCC手册中,部分6.43.2.5 Extended Asm - Clobbers,在“记忆”撞的解释寄存器,被提及,避免冲洗寄存器一招:避免冲洗内存访问(GCC内联汇编)

如果你知道存储器的大小在编译时被访问,可以指定一个存储器输入这样的:

{"m"(({ struct { char x[10]; } *p = (void *)ptr ; *p; }))} 

(假设你访问的10个字节的字符串。)

我想我理解这个想法,但是我不完全清楚如何使用它,以及这个诀窍会产生什么影响 - 除了向GCC提供更多类型的信息之外。 最重要的三个问题就出来了:

  • 我还以此为输出或输入/输出操作数和修改数据?
  • 如果我使用这个技巧,我是否仍然需要“内存”clobber?
    我想我不这样做,因为我宣称内存块是输入/输出,但我不确定。
  • 只有在访问内存时,我可以安全地删除所需的volatile限定符吗?
    我想我可以,因为它会被声明为输出。

因为我们喜欢的例子:这段代码是否有意义,它是合法的吗?它似乎工作。

#include <iostream> 
#include <cstdint> 

void add_assembly(std::uint64_t * x) { 
    struct memory { std::uint64_t data[2]; } * p = reinterpret_cast<memory*>(x); 
    __asm__ (
     "addq $1, %[x] \t\n" 
     "addq $5, 8%[x] \t\n" 
     : [x] "+m" (*p) // Bonus question: Why don't I need a "&" here? 
     : "m" (*p) 
     : "cc" 
); 
} 

int main() { 
    std::uint64_t x[2]; 
    x[0] = 3000; 
    x[1] = 7253; 
    std::cout << "before: " << x[0] << " " << x[1] << std::endl; 
    add_assembly(&x[0]); // add 1 to x[0], add 5 to x[1] 
    std::cout << "after: " << x[0] << " " << x[1] << std::endl; 
    return 0; 
} 

回答

1

我不是100%确定这个答案的所有内容,但我最近自己也看过这个问题。

我也可以用它作为输出或输入/输出操作数并修改数据吗?

是的,但你可能是一个单独的r(注册)或m(可能在复杂的寻址模式)约束更好。这是特别的。如果您想在循环中增加指针,并因此需要它在寄存器中,则为true。 m约束可以使%0扩展到(%rsi, %rdx, 4)什么的。

如果我使用这个技巧,我还需要“内存”clobber吗?

不。这告诉gcc哪个内存可能被修改。 "memory"表示全部内存可能被修改。

当只访问内存时,可以安全地删除所需的volatile限定符吗?

您的意思是asm volatile (...)?是。 volatile只有当周围的代码不使用它的输出(或没有)时才需要让gcc移动或消除asm语句。只要你告诉gcc你的asm在内存中产生的结果,并使用该结果,你不应该使用volatile,所以gcc可以围绕任何其他黑盒函数进行优化。 (当然,假设你的asm是一个纯粹的函数,它只依赖于它所声明的输入)。


例子很好。 :)你的看起来是正确的。

我认为你是正确的,指定一个读写输入操作数是不够的;您需要将它指定为输入和输出两次。 +约束的措辞使我认为“读写”意味着gcc将你留在其中的值视为变量的新值,但我认为情况并非如此。

我认为在x86上,m约束等同于o(可抵消)约束。但要注意语法;你不希望8%[x]变成88(%rsp)。那么也许8 + %[x]?不确定。留下一个空格,所以这是一个语法错误,而不是一个不同的数字。

\t\n很愚蠢。你想要在每一行的开头选项卡。

asm (
    "inc  %[x] \n\t" // inc is shorter than `add` 
    "addq $5, 8 %[x] \n\t" // this `add` writes all flags, preventing partial-flag stalls/slowdowns 
    : [x] "+m" (*p) // Bonus question: Why don't I need a "&" here? 
     // because you aren't writing a temporary to the output before reading the input 
    : "m" (*p) 
    : "cc" 
);