双重否定的奇怪 SSE 汇编指令
Weird SSE assembler instructions for double negation
GCC 和 Clang 编译器似乎使用了一些黑魔法。 C
代码只是否定双精度值,但汇编指令涉及按位 XOR
和指令指针。有人可以解释发生了什么以及为什么它是最佳解决方案。谢谢。
test.c的内容:
void function(double *a, double *b) {
*a = -(*b); // This line.
}
生成的汇编程序指令:
(gcc)
0000000000000000 <function>:
0: f2 0f 10 06 movsd xmm0,QWORD PTR [rsi]
4: 66 0f 57 05 00 00 00 xorpd xmm0,XMMWORD PTR [rip+0x0] # c <function+0xc>
b: 00
c: f2 0f 11 07 movsd QWORD PTR [rdi],xmm0
10: c3 ret
(clang)
0000000000000000 <function>:
0: f2 0f 10 06 movsd xmm0,QWORD PTR [rsi]
4: 0f 57 05 00 00 00 00 xorps xmm0,XMMWORD PTR [rip+0x0] # b <function+0xb>
b: 0f 13 07 movlps QWORD PTR [rdi],xmm0
e: c3 ret
地址0x4
处的汇编指令表示"This line",但我不明白它是如何工作的。 xorpd/xorps
指令应该是按位 XOR
并且 PTR [rip]
是指令指针。
我怀疑在执行时 rip
指向 0f 57 05 00 00 00 0f
字节带附近的某处,但我不太清楚,这是如何工作的以及为什么两个编译器选择这种方法。
P.S。我应该指出,这是使用 -O3
编译的
xorps xmm0,XMMWORD PTR [rip+0x0]
被[]
包围的指令的任何部分都是对内存的间接引用。
在这种情况下,对地址 RIP+0
处的内存的引用
(我怀疑它实际上是 RIP+0
,您可能已经编辑了实际偏移量)
X64指令集增加instruction pointer relative addressing。这意味着您可以在程序中拥有(通常是只读的)数据,即使程序在内存中四处移动,您也可以轻松访问这些数据。
A XOR xmm0,Y
反转在 Y
.
中设置的 xmm0 中的所有位
否定涉及反转符号位,因此这就是使用 xor 的原因。特别是 xorpd/s
因为我们正在处理双重响应。单花车。
对我来说,使用相同代码的 -S -O3
选项的 gcc
的输出是:
.file "test.c"
.text
.p2align 4,,15
.globl function
.type function, @function
function:
.LFB0:
.cfi_startproc
movsd (%rsi), %xmm0
xorpd .LC0(%rip), %xmm0
movsd %xmm0, (%rdi)
ret
.cfi_endproc
.LFE0:
.size function, .-function
.section .rodata.cst16,"aM",@progbits,16
.align 16
.LC0:
.long 0
.long -2147483648
.long 0
.long 0
.ident "GCC: (Ubuntu 6.3.0-12ubuntu2) 6.3.0 20170406"
.section .note.GNU-stack,"",@progbits
这里的xorpd
指令使用指令指针相对寻址,偏移量指向.LC0
标签,64位值0x8000000000000000
(第63位设置为1)。
.LC0:
.long 0
.long -2147483648
如果你的编译器是 big endian 这些行交换了。
将双精度值与 0x8000000000000000
进行异或运算,将符号位(第 63 位)设置为 1 表示负值。
clang 使用 xorps
指令以相同的方式异或 double 值的前 32 位。
如果您 运行 带有 -r
选项的对象转储,它将向您显示在 运行 启动程序之前应该在程序上完成的重定位。
objdump -d test.o -r
test.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <function>:
0: f2 0f 10 06 movsd (%rsi),%xmm0
4: 66 0f 57 05 00 00 00 xorpd 0x0(%rip),%xmm0 # c <function+0xc>
b: 00
8: R_X86_64_PC32 .LC0-0x4
c: f2 0f 11 07 movsd %xmm0,(%rdi)
10: c3 retq
Disassembly of section .text.startup:
0000000000000000 <main>:
0: 31 c0 xor %eax,%eax
2: c3 retq
这里 <function + 0xb>
我们有一个 R_X86_64_PC32 类型的重定位。
PS: 我正在使用 gcc 6.3.0
GCC 和 Clang 编译器似乎使用了一些黑魔法。 C
代码只是否定双精度值,但汇编指令涉及按位 XOR
和指令指针。有人可以解释发生了什么以及为什么它是最佳解决方案。谢谢。
test.c的内容:
void function(double *a, double *b) {
*a = -(*b); // This line.
}
生成的汇编程序指令:
(gcc)
0000000000000000 <function>:
0: f2 0f 10 06 movsd xmm0,QWORD PTR [rsi]
4: 66 0f 57 05 00 00 00 xorpd xmm0,XMMWORD PTR [rip+0x0] # c <function+0xc>
b: 00
c: f2 0f 11 07 movsd QWORD PTR [rdi],xmm0
10: c3 ret
(clang)
0000000000000000 <function>:
0: f2 0f 10 06 movsd xmm0,QWORD PTR [rsi]
4: 0f 57 05 00 00 00 00 xorps xmm0,XMMWORD PTR [rip+0x0] # b <function+0xb>
b: 0f 13 07 movlps QWORD PTR [rdi],xmm0
e: c3 ret
地址0x4
处的汇编指令表示"This line",但我不明白它是如何工作的。 xorpd/xorps
指令应该是按位 XOR
并且 PTR [rip]
是指令指针。
我怀疑在执行时 rip
指向 0f 57 05 00 00 00 0f
字节带附近的某处,但我不太清楚,这是如何工作的以及为什么两个编译器选择这种方法。
P.S。我应该指出,这是使用 -O3
xorps xmm0,XMMWORD PTR [rip+0x0]
被[]
包围的指令的任何部分都是对内存的间接引用。
在这种情况下,对地址 RIP+0
处的内存的引用
(我怀疑它实际上是 RIP+0
,您可能已经编辑了实际偏移量)
X64指令集增加instruction pointer relative addressing。这意味着您可以在程序中拥有(通常是只读的)数据,即使程序在内存中四处移动,您也可以轻松访问这些数据。
A XOR xmm0,Y
反转在 Y
.
中设置的 xmm0 中的所有位
否定涉及反转符号位,因此这就是使用 xor 的原因。特别是 xorpd/s
因为我们正在处理双重响应。单花车。
对我来说,使用相同代码的 -S -O3
选项的 gcc
的输出是:
.file "test.c"
.text
.p2align 4,,15
.globl function
.type function, @function
function:
.LFB0:
.cfi_startproc
movsd (%rsi), %xmm0
xorpd .LC0(%rip), %xmm0
movsd %xmm0, (%rdi)
ret
.cfi_endproc
.LFE0:
.size function, .-function
.section .rodata.cst16,"aM",@progbits,16
.align 16
.LC0:
.long 0
.long -2147483648
.long 0
.long 0
.ident "GCC: (Ubuntu 6.3.0-12ubuntu2) 6.3.0 20170406"
.section .note.GNU-stack,"",@progbits
这里的xorpd
指令使用指令指针相对寻址,偏移量指向.LC0
标签,64位值0x8000000000000000
(第63位设置为1)。
.LC0:
.long 0
.long -2147483648
如果你的编译器是 big endian 这些行交换了。
将双精度值与 0x8000000000000000
进行异或运算,将符号位(第 63 位)设置为 1 表示负值。
clang 使用 xorps
指令以相同的方式异或 double 值的前 32 位。
如果您 运行 带有 -r
选项的对象转储,它将向您显示在 运行 启动程序之前应该在程序上完成的重定位。
objdump -d test.o -r
test.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <function>:
0: f2 0f 10 06 movsd (%rsi),%xmm0
4: 66 0f 57 05 00 00 00 xorpd 0x0(%rip),%xmm0 # c <function+0xc>
b: 00
8: R_X86_64_PC32 .LC0-0x4
c: f2 0f 11 07 movsd %xmm0,(%rdi)
10: c3 retq
Disassembly of section .text.startup:
0000000000000000 <main>:
0: 31 c0 xor %eax,%eax
2: c3 retq
这里 <function + 0xb>
我们有一个 R_X86_64_PC32 类型的重定位。
PS: 我正在使用 gcc 6.3.0