我很难理解某些特定的影响 GCC 中内联汇编的约束。
在下面的示例中,如果我在输出上运行“=X”并在所有输入上运行“X”,则 2 会打印输出
0x562f39629260, 100
0x14, 100
这表明指向我分配的缓冲区的指针已更改。尝试读取缓冲区的内容会导致段错误 在我的汇编代码之后。
相反,如果我在输出中输入“+X”或在输入中输入“m”,则 地址保持不变,打印输出:
0x55571bb83260, 100
0x55571bb83260, 100
而且我可以安全地读取缓冲区,而不会出现段错误。
我不明白如何或为什么应该/可以修改该指针? 有没有办法安全地选择约束? gcc 在线文档 对此没有给出太多见解。
非常感谢,
int main() {
long size = 100;
char * buffer = (char*)malloc(size*sizeof(char));
printf("%p, %d\n",buffer, size);
__asm__(
"mov %[out], %%rcx \n"
"mov %[size], %%rbx \n"
"loop: \n"
"movb $1, (%%rcx) \n"
"add $1, %%rcx \n"
"sub $1, %%rbx \n"
"jnz loop \n"
: "=X"(buffer) //outputs
: [out]"X"(buffer), [size]"X"(size) //inputs
: "rbx", "rcx" //clobbers
);
printf("%p, %d\n",buffer, size);
return 0;
}
最佳答案
=
在=X
意味着这是一个 OUTPUT ONLY 约束(与 +
的更新约束相反)。这意味着汇编代码预计会向操作数写入一些内容(写入 %0
),并且这将是输出的值。但由于您的汇编代码永远不会写入 %0
,你会得到该位置中发生的任何垃圾(可能是寄存器分配器选择的寄存器)。
尝试添加 mov %%rcx,%0
运行 asm 代码看看实际发生了什么。
很可能您真正想要的更像是:
__asm__ volatile (
"mov %[size], %%rbx \n"
"loop: \n"
"movb $1, (%[out]) \n"
"add $1, %[out] \n"
"sub $1, %%rbx \n"
"jnz loop \n"
: [out]"+r"(buffer) //outputs
: [size]"X"(size) //inputs
: "rbx", "memory" //clobbers
);
请注意,这留下 buffer
指向插入的值之后(在缓冲区的末尾)——不清楚这是否是您想要的。您可以对尺寸执行相同的操作,使其变得更加简单:
__asm__ volatile (
"loop: \n"
"movb $1, (%[out]) \n"
"add $1, %[out] \n"
"sub $1, %[size] \n"
"jnz loop \n"
: [out]"+r"(buffer), [size]"+X"(size) //outputs
: //inputs
: "memory" //clobbers
);
尽管根本不使用 asm 会更简单(对优化器来说更好):
do { *buffer++ = '\1'; } while (--size);
总结一下下面的所有评论,您可能想要的是:
long size = 100;
char buffer[100];
char *temp;
__asm__(
"loop: \n"
"movb $1, (%[out]) \n"
"add $1, %[out] \n"
"sub $1, %[size] \n"
"jnz loop \n"
: [out]"=r"(temp), [size]"+X"(size), "=m"(buffer) //outputs
: "0"(buffer) // inputs
) // no clobbers
- 使用
"=m"
对整个缓冲区的约束而不是内存破坏和 volatile 意味着如果没有使用任何结果,则可以消除死代码 - 使用临时指针在缓冲区上前进意味着可以保留原始缓冲区(起始)值。
- 如果必须对缓冲区使用 malloc
"=m"(*(char (*)[100])buffer)
可用于获取整个缓冲区的约束。
然而,我坚持我之前的评论,即不使用 asm 编写它更好;它更简单、更容易理解,编译器的优化器可能会为您对其进行矢量化。
关于c - ASM 约束副作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55191718/