2017-07-17 33 views
0

GCC和Clang編譯器似乎使用了一些黑暗魔法。 C代碼只是否定了雙精度值,但彙編器指令涉及位方式XOR和指令指針。有人可以解釋發生了什麼,爲什麼它是最佳解決方案。謝謝。古怪的SSE彙編指令雙重否定

void function(double *a, double *b) { 
    *a = -(*b); // This line. 
} 

將所得的彙編程序指令:

test.c的內容

(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彙編指令表示 「這條線」,然而我無法理解它是如何工作的。 xorpd/xorps指令應該是位明智的XORPTR [rip]是指令指針。

我懷疑在執行的那一刻rip指向接近0f 57 05 00 00 00 0f字節的地方,但我無法弄清楚,這是如何工作的,爲什麼兩個編譯器都選擇這種方法。

P.S.我應該指出,這是通過使用-O3

+0

我無法重現這一點。兩個編譯器都使用一個表示-0.0的常量進行異或操作,它們從內存中加載。這對[RIP相對抵銷]有意義(https://stackoverflow.com/questions/44967075/why-does-this-movss-instruction-use-rip-relative-addressing/44967386#44967386)。沒有意義的是,你的反彙編顯示它們將下一條指令的字節加載爲浮點常量。這是不對的。你有可能以某種方式剝離了反彙編的偏移量嗎?或者你的反彙編人員感到困惑? –

+0

我絕對不會手動更改任何東西。使用上面的代碼逐字和命令'gcc test.c -c -O3 -o test.o'和'objdump -S -M intel test.o'這是我得到的輸出。我會重申一下,我知道'PTR [rip]'是什麼,並且可以通過改變雙精度中的前導位來反轉符號。我所問的原因是因爲這兩個想法並沒有混合在我的腦海中。 – RuRo

+0

你越確定自己沒有犯錯,你做得越有可能。從控制檯複製錯誤的部分。抱歉,彙編程序列表現在應該是正確的。奇怪的'xor'與'rip'仍然存在。 – RuRo

回答

6

爲我的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指令使用指令指針相對與偏移指向.LC0標籤與64位值0x8000000000000000尋址(第63位設爲1)。

.LC0: 
    .long 0 
    .long -2147483648 

如果你的編譯器是big endian這些行交換。

0x8000000000000000 xoring的雙倍值將符號位(第63位)設置爲負值。

鐺使用xorps指令以相同的方式這xors的第一個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

+0

正確,但不是第0位設置爲1,第63位(符號位)設置爲1,因此十六進制值爲0x8000000000000000。 –

+0

我從左邊第0位我將編輯並修復它。 –

+0

沒關係。否則,很好的答案。 –

4

xorps xmm0,XMMWORD PTR [rip+0x0]

通過[]包圍的指令的任何部分是一個間接引用存儲器編譯。 在這種情況下,在地址RIP+0
到內存的引用(我懷疑它實際上是RIP+0,你可能已經編輯了實際偏移)

的X64指令集的instruction pointer relative addressing。這意味着您可以在程序中擁有(通常只讀)數據,即使程序在內存中移動,也可以輕鬆解決。

A XOR xmm0,Y反轉設置在Y中的xmm0中的所有位。
否定涉及反轉符號位,所以這就是使用xor的原因。特別是xorpd/s,因爲我們正在處理雙重責任。單浮體。

+0

我很確定我沒有手動更改任何東西。也許,你可以發佈你得到的輸出,所以我可以嘗試比較。 – RuRo

+3

@RuRo,你可能沒有用'--reloc'選項運行'objdump'。然後你得到沒有重定位的原始指令,它將在執行之前用來重寫一些參數。 –

+0

@FlorianWeimer該死的。你是對的。我只是假設沒有搬遷,因爲沒有什麼可以聯繫的。如果他知道我們的教授會生氣的。我們在課堂上完全瞭解到。 – RuRo