考虑以下小函数:
void foo(int* iptr) {
iptr[10] = 1;
__asm__ volatile ("nop"::"r"(iptr):);
iptr[10] = 2;
}
使用gcc编译,编译结果如下:
foo:
nop
mov DWORD PTR [rdi+40], 2
ret
需要特别注意的是,iptr
的第一次写入 iptr[10] = 1
没有发生:内联汇编 nop
是函数中的第一件事情,只有在 ASM 调用之后才出现最终的写入 2
。显然,编译器决定仅需提供 iptr
值本身的最新版本,而不需要指向的内存。
我可以通过使用 memory
修饰符来告诉编译器,内存必须与更新一致,像这样:
void foo(int* iptr) {
iptr[10] = 1;
__asm__ volatile ("nop"::"r"(iptr):"memory");
iptr[10] = 2;
}
这将导致预期的代码:
foo:
mov DWORD PTR [rdi+40], 1
nop
mov DWORD PTR [rdi+40], 2
ret
然而,这个条件太强了,因为它告诉编译器需要写入所有的内存。例如,在下面的函数中:
void foo2(int* iptr, long* lptr) {
iptr[10] = 1;
lptr[20] = 100;
__asm__ volatile ("nop"::"r"(iptr):);
iptr[10] = 2;
lptr[20] = 200;
}
期望的行为是让编译器优化掉对 lptr[20]
的第一次写入,但不是对 iptr[10]
的第一次写入。使用 "memory"
选项不能实现这一点,因为它意味着两个写入都必须发生:
foo2:
mov DWORD PTR [rdi+40], 1
mov QWORD PTR [rsi+160], 100 ; lptr[10] written unecessarily
nop
mov DWORD PTR [rdi+40], 2
mov QWORD PTR [rsi+160], 200
ret
有没有一种方法可以告诉gcc扩展的asm语法编译器,输入到asm中包括指针和它所指向的任何内容?
=
符号的一个副作用,意味着汇编可能会改变指针的值,因此gcc
必须进行两次写操作(因为它们可能写入不同的位置)。然而,这并不意味着gcc在调用内联汇编之前必须执行写操作(尽管在这种情况下确实如此),因此它在一般情况下不起作用(您可以构造一个类似的示例,其中它失败)。 - BeeOnRope"+r"
吗?使用"=r"
甚至不需要编译器将指针值传递到汇编中,我认为是这样的? - BeeOnRopem
((*(const float (*)[]) fptr)
)会告诉编译器整个数组对象是一个任意长度的输入”。 - Jester