用于双重否定的奇怪的SSE汇编程序指令

时间:2017-07-17 09:30:14

标签: gcc assembly x86-64 sse magic-numbers

GCC和Clang编译器似乎采用了一些黑魔法。 C代码只是否定double的值,但汇编指令涉及逐位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处的汇编程序指令表示&#34;此行&#34;但是我无法理解它是如何工作的。 xorpd/xorps指令应该是按位XORPTR [rip]是指令指针。

我怀疑在执行时rip指向0f 57 05 00 00 00 0f字节附近的某个位置,但我无法弄清楚,这是如何工作的以及两个编译器的原因选择这种方法。

P.S。我应该指出,这是使用-O3

编译的

2 个答案:

答案 0 :(得分:7)

对我而言gcc的输出与-S -O3选项的相同代码为:

    .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指令使用指令指针相对寻址,偏移量指向带有64位值.LC0的{​​{1}}标签(第63位设置为1)。

0x8000000000000000

如果你的编译器是big endian这些swaped的行。

使用.LC0: .long 0 .long -2147483648 对double值进行xoring,将符号位(第63位)设置为负值。

clang使用0x8000000000000000指令的方式与x值为double值的前32位相同。

如果使用xorps选项运行对象转储,它将显示在运行之前应该对程序执行的重定位。

-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 我们的重定位类型为R_X86_64_PC32。

PS:我正在使用gcc 6.3.0

答案 1 :(得分:4)

  

xorps xmm0,XMMWORD PTR [rip+0x0]

[]包围的指令的任何部分都是对内存的间接引用。 在这种情况下,引用地址RIP+0的内存 (我怀疑它实际上是RIP+0,你可能已经编辑了实际的偏移量)

X64指令集添加instruction pointer relative addressing。这意味着您可以在程序中拥有(通常只读)数据,即使程序在内存中移动也可以轻松解决。

XOR xmm0,Y反转xmm0中Y中设置的所有位 否定涉及反转符号位,因此这就是使用xor的原因。特别是xorpd/s,因为我们正在处理双重呼吁。单花车。