Assembly 使用SSE(IA32汇编)执行简单的算术运算
在我的大学里,我们刚刚被介绍到IA32 SSE。我要做的是添加两个向量(他们称之为“压缩值”,这意味着向量包含四个32位单精度浮点数。一个verctor的大小是128位)。下面是我要做的:Assembly 使用SSE(IA32汇编)执行简单的算术运算,assembly,x86,sse,Assembly,X86,Sse,在我的大学里,我们刚刚被介绍到IA32 SSE。我要做的是添加两个向量(他们称之为“压缩值”,这意味着向量包含四个32位单精度浮点数。一个verctor的大小是128位)。下面是我要做的: %xmm0 | 5.5 | 1.2 | 2.4 | 7.0 | %xmm1 | 3.0 | 1.5 | 3.5 | 2.2 | | | | | + + + +
%xmm0 | 5.5 | 1.2 | 2.4 | 7.0 |
%xmm1 | 3.0 | 1.5 | 3.5 | 2.2 |
| | | |
+ + + +
| | | |
V V V V
%xmm0 | 8.5 | 2.7 | 5.9 | 9.2 |
然而,在幻灯片上,它们只显示了我根本无法使用的以下代码片段:
# %eax and %ebx contain the addresses of the two vectors that are to be added
movups (%eax), %xmm0
movups (%ebx), %xmm1
addps %xmm1, %xmm0
movups %xmm0, result
这提出了两个问题:
1。我如何首先创建这些向量,以及如何使%eax和%ebx指向它们?
2。如何打印结果以检查操作是否成功?
这是我试过的。下面的代码经过编译,运行时不会崩溃。但是,根本没有输出…:/
.data
x0: .float 7.0
x1: .float 2.4
x2: .float 1.2
x3: .float 5.5
y0: .float 2.2
y1: .float 3.5
y2: .float 1.5
y3: .float 3.0
result: .float 0
intout: .string "Result: %f.\n"
.text
.global main
main:
pushl x3
pushl x2
pushl x1
pushl x0
movl %esp, %eax
pushl y3
pushl y2
pushl y1
pushl y0
movl %esp, %ebx
movups (%eax), %xmm0
movups (%ebx), %xmm1
addps %xmm1, %xmm0
movups %xmm0, result
pushl result
pushl $intout
call printf
addl $40, %esp
movl $1, %eax
int $0x80
您似乎对如何在多个数据项上声明标签以及如何将标签加载到寄存器中感到困惑。标签只是一个地址——内存中的一个点——没有任何大小或与之相关的任何东西。标签后面的内容在内存中是连续的地址。因此,您将引用向量的标签声明为:
x:
.float 7.0
.float 2.4
.float 1.2
.float 5.5
现在,您可以通过简单的移动将该地址加载到寄存器中,然后使用寄存器加载向量:
movl $x, %eax
movups (%eax), %xmm0
或者,可以直接从标签加载
movups x, %xmm0
printf
的%f
说明符表示双参数,而不是浮点参数。因此,您需要转换结果向量中的单个浮点数,并将它们移动到堆栈中。我会这样做:
.section ".rodata"
fmt: .string "%f %f %f %f\n"
.align 16
vec1:
.float 7.0
.float 2.4
.float 1.2
.float 5.5
vec2:
.float 2.2
.float 3.5
.float 1.5
.float 3.0
.data
.align 16
result:
.float 0.0
.float 0.0
.float 0.0
.float 0.0
.text
.globl main
main:
movl %esp, %ebp
andl $-16, %esp # align stack
movaps vec1, %xmm0
movaps vec2, %xmm1
addps %xmm1, %xmm0
movaps %xmm0, result
subl $36, %esp
movl $fmt, (%esp)
movss result, %xmm0
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 4(%esp)
movss result+4, %xmm0
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 12(%esp)
movss result+8, %xmm0
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 20(%esp)
movss result+12, %xmm0
cvtss2sd %xmm0, %xmm0
movsd %xmm0, 28(%esp)
call printf
addl $36, %esp
xorl %eax, %eax
movl %ebp, %esp
ret
movups%xmm0,result
将xmm0
的所有128位写入result
,但您已将result
声明为float
(32位),因此它将覆盖输入的部分字符串。您的结果应该足够长,可以包含4个值,现在movups%xmm0,结果
也会破坏您的字符串。感谢您的快速响应!我如何声明结果
的大小为128位?非常感谢,克里斯!然而,我还有两个问题:它不是必须是lealx,%eax
?我们希望%eax
保存x的地址,而不是在那里可以找到的值,对吗?其次,我如何打印结果以确保其有效?@baerenfaenger:是的,您需要一个$
来获取标签的立即值(而不是从标签加载),我最初输入错误(重要的打字错误)。或者你可以使用leal
。谢谢你,这对我有用!我知道由于%f
需要双精度浮点数,因此我们必须执行转换。然而,难道没有一个通配符要求单精度吗?这将使整个事情变得容易得多!确实会,但没有这样的说明符。请注意,C将透明地进行转换,因此从打算使用的printf
级别看不到任何笨拙之处。您忘记保存/还原调用方的%ebp
。此外,如果您低于$36
(不是16的倍数),将%esp
与16对齐的目的将失败,从而导致调用
指令未对齐。(i386 System V ABI的现代版本要求在函数调用之前进行16字节堆栈对齐。)但是,是的,否则使用SSE2存储%f
的堆栈参数就是一个很好的例子。(有关%f
使用双精度以及C如何工作的更多详细信息,请参阅)