SIMD编程:addps浮点向量加法结果异常,求排查与修复
SIMD
addps指令使用异常问题排查与修复 问题背景
正在学习SIMD指令,目标是用addps实现两个4元素float向量的对应元素相加,结果存入result向量。使用movaps加载向量到XMM寄存器后,执行结果输出异常,但用movlpd逐个加载元素能正确显示向量a、b的值,且C内嵌汇编复现该操作时结果正常。
汇编代码
section .data a dd 1.1f, 2.2f, 3.3f, 4.4f b dd 0.4f, 0.5f, 0.6f, 0.7f result dd 0.0f, 0.0f, 0.0f, 0.0f format db "Content = {%.1f, %.1f, %.1f, %.1f}", 10, 0 formatResult db "Result = {%.1f, %.1f, %.1f, %.1f}", 10, 0 section .text global _start _start: movaps xmm0, [a] movaps xmm1, [b] addps xmm0, xmm1 movaps [result], xmm0 mov rdi, formatResult movaps xmm0, [result] call printf mov eax, 60 syscall section .externals extern printf
编译执行命令
nasm -f elf64 -o file.o file.asm gcc -nostartfiles -o file file.o -no-pie ./file
错误输出
Content = {1.1, 2.2, 3.3, 4.4} Content = {0.4, 0.5, 0.6, 0.7} Result = {172.8, 0.4, 0.0, 0.0}
问题原因
错误根源在于调用printf时违反了x86-64 System V ABI规范:
- 浮点参数类型提升:C标准中,变参函数(如
printf)接收float参数时会自动将其提升为double类型。直接传递32位float会导致printf把XMM寄存器中64位的垃圾数据当成double解析,出现异常值。 - 参数传递方式错误:x86-64 System V ABI规定,变参函数的浮点参数需依次放入
XMM0~XMM7寄存器,每个寄存器对应一个参数。你将4个float打包到单个XMM0寄存器,printf只会读取XMM0的低64位(且错误解析为double),剩余参数未正确传递,导致读取栈上垃圾数据。
而内嵌汇编正常的原因是:C编译器自动处理了float到double的类型提升,以及参数的寄存器分配,符合ABI规范。
修复方案
修改printf调用前的参数准备逻辑,将result中的每个float转换为double,并分别放入对应XMM寄存器:
_start: movaps xmm0, [a] movaps xmm1, [b] addps xmm0, xmm1 movaps [result], xmm0 ; 准备printf参数 mov rdi, formatResult ; 提取每个float并转换为double,放入对应XMM寄存器 movss xmm0, [result] ; 加载第一个float到XMM0低32位 cvtss2sd xmm0, xmm0 ; 转换为double movss xmm1, [result+4] ; 第二个float(每个float占4字节) cvtss2sd xmm1, xmm1 movss xmm2, [result+8] cvtss2sd xmm2, xmm2 movss xmm3, [result+12] cvtss2sd xmm3, xmm3 call printf mov eax, 60 syscall
若想验证addps的计算结果本身是否正确,可通过gdb查看XMM寄存器的值,确认向量相加逻辑无误。
内容的提问来源于stack exchange,提问作者eightShirt
相关产品推荐
相关产品推荐

