为何内联ASM函数会重复累加第一个输入操作数?
问题分析与解答
关于x86_64架构下的内联汇编位数问题
你的理解有误:Windows x86_64系统上,GNU g++完全支持64位内联汇编。你使用addl这类32位指令,只是因为操作的是int(32位)类型变量,对应32位指令;如果需要操作64位变量(比如long long),可以使用addq等64位指令,内联汇编并非只能是32位。
核心问题:为什么添加addl %1, %0;会得到a+a+b+c?
问题出在你对GCC内联汇编约束的误解:
=r是只写输出约束:编译器会为result分配一个寄存器,但不会保证这个寄存器的初始值——哪怕你在C代码里写了int result = 0;,编译器可能会优化掉这个赋值,或者直接复用存储了a的寄存器作为输出寄存器(因为=r表示你只会往这个寄存器里写,不会读初始值)。- 输入约束
r(a)的寄存器分配是编译器自主决定的,它完全可能把%0(输出)和%1(输入a)分配到同一个寄存器。
在你的测试场景中,编译器恰好把%0和%1分配到了同一个寄存器:
- 初始时
%0的值就是a(而非你预期的0) - 执行
addl %1, %0;后,%0变成a+a - 后续执行
addl %2, %0;和addl %3, %0;,最终结果就是a+a+b+c
正确的写法
如果你需要输出寄存器初始为0,有两种可靠的方式:
方法1:使用读写约束+r替换=r
+r是读写约束,告诉编译器这个操作数需要先读初始值再写,此时编译器会保证%0的初始值是C代码中初始化的0:
asm( "addl %1, %0;" "addl %2, %0;" "addl %3, %0;" : "+r" (result) : "r" (a), "r" (b), "r" (c) );
方法2:在汇编代码中显式清零输出寄存器
直接在汇编开头添加清零指令,不依赖C代码的初始化:
asm( "movl $0, %0;" "addl %1, %0;" "addl %2, %0;" "addl %3, %0;" : "=r" (result) : "r" (a), "r" (b), "r" (c) );
内容的提问来源于stack exchange,提问作者MAA1117
相关产品推荐
相关产品推荐

