x86 NASM 将 printf 用于压缩双打
x86 NASM use printf for packed doubles
我刚开始使用 SIMD 指令,正在尝试使用 printf 打印浮点数。我已经查看了许多可能的解决方案,但似乎这段代码在 运行 时不会打印任何内容。相关代码如下:
extern _printf
section .text
global _main
_main:
...
movapd xmm0, oword [rel v1]
movapd xmm1, oword [rel v2]
addpd xmm0, xmm1
movapd xmm1, xmm0
psrldq xmm1, 8
mov rax, 2
mov rdi, fmt
call _printf
...
section .data
fmt: db "%f %f\n", 0
v1: dq 1.1
dq 2.2
v2: dq 3.3
dq 4.4
我正在开发 mac,这是我用于 assemble 和 link 的命令:
nasm -g -f macho64 -o prog.o prog.asm
ld -lc -macosx_version_min 10.13 -lSystem -o prog prog.o
您可能没有刷新 stdio 缓冲区就退出了。默认情况下,stdout
在连接到终端时是行缓冲的(否则是全缓冲的)。
您的格式字符串不以换行符结尾:它以文字反斜杠和 n
结尾,因为 NASM 不处理 C双引号内的转义序列。为此使用反引号:
fmt: db `%f %f\n`, 0
或使用数字ASCII码fmt: db "%f %f", 10, 0
。
当您使用 C stdio 函数调用时,您应该通过从 main
返回或调用 libc 的 exit
函数退出,不是 通过直接进行 sys_exit
系统调用 。库函数首先刷新 stdio 缓冲区并运行析构函数和其他任何东西;系统调用刚刚退出。
我在这里假设您的程序正在干净地退出而不是在 printf 内部崩溃(如果 rsp
在 call
之前不是 16 字节对齐,则可能会使用 movaps
将 FP arg 传递寄存器存储到堆栈,作为通常的可变参数函数代码生成的一部分。)
运行 你的程序在 strace
或 ltrace
下解码系统调用或库函数调用(如果 OS X 有这两种工具)。
您的原始代码(在更新以解决此问题之前)应该从 xmm0 打印低 double
并从堆栈中取出 8 个字节的数据用于第二次 %f
转换(因为 al=1
表示寄存器中有一个 FP arg,堆栈中有任何剩余的 FP arg。)
或者这就是它在您退出之前放入 stdout I/O 缓冲区的内容。
顺便说一句,不要忘记 ALIGN 16
您要使用对齐加载的数据。此外,您选择了一种低效的解包方式(此处不需要整数洗牌,如果您要进行整数洗牌,请使用 pshufd
进行复制+洗牌)。你可以这样做:
DEFAULT REL
...
movapd xmm0, [rel v1]
addpd xmm0, [rel v2]
movhlps xmm1, xmm0 ; false dependency on the old value of xmm0
或
...
movapd xmm1, xmm0 ; copy
unpckhpd xmm1, xmm1 ; broadcast the high half
x86-64 System V 调用约定不要求 arg 传递寄存器的上半部分为零,包括 xmm regs,因此您可以留下任何您想要的高垃圾。 ()
我刚开始使用 SIMD 指令,正在尝试使用 printf 打印浮点数。我已经查看了许多可能的解决方案,但似乎这段代码在 运行 时不会打印任何内容。相关代码如下:
extern _printf
section .text
global _main
_main:
...
movapd xmm0, oword [rel v1]
movapd xmm1, oword [rel v2]
addpd xmm0, xmm1
movapd xmm1, xmm0
psrldq xmm1, 8
mov rax, 2
mov rdi, fmt
call _printf
...
section .data
fmt: db "%f %f\n", 0
v1: dq 1.1
dq 2.2
v2: dq 3.3
dq 4.4
我正在开发 mac,这是我用于 assemble 和 link 的命令:
nasm -g -f macho64 -o prog.o prog.asm
ld -lc -macosx_version_min 10.13 -lSystem -o prog prog.o
您可能没有刷新 stdio 缓冲区就退出了。默认情况下,stdout
在连接到终端时是行缓冲的(否则是全缓冲的)。
您的格式字符串不以换行符结尾:它以文字反斜杠和 n
结尾,因为 NASM 不处理 C双引号内的转义序列。为此使用反引号:
fmt: db `%f %f\n`, 0
或使用数字ASCII码fmt: db "%f %f", 10, 0
。
当您使用 C stdio 函数调用时,您应该通过从 main
返回或调用 libc 的 exit
函数退出,不是 通过直接进行 sys_exit
系统调用 。库函数首先刷新 stdio 缓冲区并运行析构函数和其他任何东西;系统调用刚刚退出。
我在这里假设您的程序正在干净地退出而不是在 printf 内部崩溃(如果 rsp
在 call
之前不是 16 字节对齐,则可能会使用 movaps
将 FP arg 传递寄存器存储到堆栈,作为通常的可变参数函数代码生成的一部分。)
运行 你的程序在 strace
或 ltrace
下解码系统调用或库函数调用(如果 OS X 有这两种工具)。
您的原始代码(在更新以解决此问题之前)应该从 xmm0 打印低 double
并从堆栈中取出 8 个字节的数据用于第二次 %f
转换(因为 al=1
表示寄存器中有一个 FP arg,堆栈中有任何剩余的 FP arg。)
或者这就是它在您退出之前放入 stdout I/O 缓冲区的内容。
顺便说一句,不要忘记 ALIGN 16
您要使用对齐加载的数据。此外,您选择了一种低效的解包方式(此处不需要整数洗牌,如果您要进行整数洗牌,请使用 pshufd
进行复制+洗牌)。你可以这样做:
DEFAULT REL
...
movapd xmm0, [rel v1]
addpd xmm0, [rel v2]
movhlps xmm1, xmm0 ; false dependency on the old value of xmm0
或
...
movapd xmm1, xmm0 ; copy
unpckhpd xmm1, xmm1 ; broadcast the high half
x86-64 System V 调用约定不要求 arg 传递寄存器的上半部分为零,包括 xmm regs,因此您可以留下任何您想要的高垃圾。 (