栈指针rsp在函数返回值传递中是否有用?汇编代码疑问
我了解汇编中返回值的几种方式:
- 存入rax寄存器
- 存入xmm0、xmm1寄存器
- 存入栈中(类似C++的返回值优化,有时会用rdi寄存器保存调用者的栈地址,直接将返回值写入该地址指向的位置)
我有一段汇编代码,用于计算3×3矩阵与3D向量的乘积,最终返回一个3维浮点向量(每个元素4字节,共12字节)。计算过程逻辑清晰,但最后返回值的代码逻辑非常怪异,我无法理解其用途。
假设计算得到的3维向量最终存储在xmm6、xmm7、xmm8寄存器中,相关代码如下:
注:该函数开头和结尾没有
sub rsp, xxx与add rsp, xxx指令,计算矩阵向量乘积的全程并未使用栈。
movss [rsp-28h],xmm6 movss [rsp-24h],xmm7 movss [rsp-20h],xmm8 mov eax,[rsp-24h] shl eax,20h mov ebx,[rsp-28h] or rax,rbx mov [rsp-18h],rax movd rcx,xmm8 mov [rsp-10h],rcx mov ebx,eax shr rax,20h mov [rsp-28h],eax mov [rsp-24h],ebx mov [rsp-20h],ecx movss xmm1,[rsp-10h] movsd xmm0,[rsp-28h]
我将这段代码嵌入C程序的asm()中编译运行后发现,它只是把xmm6、xmm7、xmm8的值复制到三个位置:[rsp-28h]、[rsp-18h]以及xmm0和xmm1,所有位置的浮点数值完全一致。
我知道xmm0和xmm1大概率是返回值存储位置,但[rsp-28h]和[rsp-18h]在返回值传递中是否有实际作用?为何程序要用这种怪异的方式(先复制到栈,再复制到rax等寄存器,最后又复制回栈)来拷贝结果?
另外,调用者代码并未使用xmm0和xmm1,我不确定是调用者丢弃了返回值,还是返回值实际存储在调用者的栈中。
解答
栈上的
[rsp-28h]和[rsp-18h]无实际作用
这段代码里的栈操作完全是冗余的——反复将寄存器值写入栈、再读回寄存器重组、最后又写回栈,但这些栈地址后续既没有被函数本身的其他逻辑使用,调用者也没有读取这些位置。如果是编译器生成的代码,这大概率是优化不彻底留下的垃圾代码;如果是手写汇编,可能是作者调试时留下的无用操作,或者尝试调整返回格式时的残留代码。怪异拷贝逻辑的原因
从代码来看,它试图把三个单精度浮点值重新打包:将xmm7(第二个向量元素)左移后和xmm6(第一个元素)合并到rax,再拆分回栈,最后把前两个元素打包到xmm0(用movsd按双精度读取8字节),第三个元素放到xmm1。但整个过程没有改变数值本身,只是做了无意义的内存/寄存器搬运。要么是编译器对返回值的ABI处理出现了冗余步骤,要么是手写汇编时的逻辑失误。返回值的去向判断
既然调用者没有使用xmm0和xmm1,只有两种可能:- 调用者确实主动丢弃了返回值(比如C代码中调用函数但不接收返回值);
- 这个函数原本应该使用调用者栈传递返回值的方式(也就是你提到的第三种返回方式),但这段汇编的逻辑写错了——正常这种方式会用rdi传递调用者预先分配的栈地址,直接将返回值写入该地址,但这段代码完全没用到rdi,所以这种可能性极低。
内容的提问来源于stack exchange,提问作者zmz

