GCC内联汇编在-O2优化选项下失效的原因及解决方法
GCC -O2优化下内联汇编模拟memcpy无输出的原因分析
问题现象
开启GCC的-O2优化选项时,用内联汇编模拟memcpy的代码输出为空:
dst:
关闭-O2或使用-O1优化时则正常输出:
dst: 123456789
问题代码示例
以下是出现问题的代码:
#include <stdio.h> int main() { char src[] = "123456789"; char dst[] = "000000000"; /* Inline assembly to mimic memcpy */ __asm__ volatile( "rep movsb\n" : : "D" (dst), "S" (src), "c" (9) : "memory" ); printf("dst: %s\n", dst); return 0; }
优化后可行代码
将rep movsb封装为static inline函数后,-O2下可正常运行:
#include <stdio.h> static inline void *__movsb(void *d, const void *s, size_t n) { asm volatile ("rep movsb" : "=D" (d), "=S" (s), "=c" (n) : "0" (d), "1" (s), "2" (n) : "memory"); return d; } int main() { char src[] = "123456789"; char dst[] = "000000000"; __movsb(dst,src,9); printf("dst: %s\n", dst); return 0; }
反汇编显示__movsb已被内联,且当拷贝长度为运行时随机值时,memcpy会被调用,而__movsb仍保持内联。
原因解析
问题核心是内联汇编的寄存器约束声明不完整:
- 第一个代码仅将
dst、src、计数作为输入约束,但rep movsb指令执行时会修改%rdi(对应dst的寄存器)、%rsi(对应src的寄存器)和%rcx(计数寄存器)的内容,代码却未告知编译器这一点。 -O2优化级别下,GCC会尽可能复用寄存器提升效率。由于编译器不知道这些寄存器已被修改,执行printf时直接复用了已经指向dst+9(字符串末尾)的%rdi寄存器,导致输出空字符串。- 封装后的代码通过
"=D"(d)、"=S"(s)、"=c"(n)明确声明这些寄存器是输出约束,告诉编译器寄存器值会被修改;同时用"0"(d)等语法表示输入输出复用同一寄存器。这样编译器会重新加载dst的首地址到寄存器用于printf,不会复用被修改后的寄存器值,因此能正常输出。 - 注意:虽然两个代码都加了
"memory"clobber(告知编译器内存被修改),但它仅影响内存访问的优化,无法让编译器感知寄存器内容的变化,必须通过输出约束声明寄存器的修改。
内容的提问来源于stack exchange,提问作者vengy
相关产品推荐
相关产品推荐

