在 x86-64 上使用 AT&T 語法,我希望組裝c = a b;為
add %[a], %[b], %[c]
不幸的是,GNU 的匯編器不會這樣做。為什么不?
細節
根據英特爾的軟體開發人員手冊, rev。75(2021 年 6 月),卷。2,第 2.5 節,
VEX 編碼的通用暫存器指令具有 ... 對三個可編碼運算元的指令語法支持。
VEX 前綴是 AVX 功能,因此從 Sandy Bridge/Bulldozer 開始的 x86-64 CPU 實作了它。那是十年前的事了,所以 GNU 的匯編器應該匯編我的三運算元指令,不是嗎?
為了澄清起見,我知道人們可以用舊樣式將其寫為
mov %[a], %[c]
add %[b], %[c]
但是,我希望以新的 VEX 風格撰寫它。順便說一句,我通過向 GCC 發出-march=skylake命令列選項通知匯編程式我有一個現代 CPU 。
請問我的錯誤是什么?
示例代碼
在 C 包裝器中,
#include <cstddef>
#include <iostream>
int main()
{
volatile int a{8};
volatile int b{5};
volatile int c{0};
//c = a b;
asm volatile (
//"mov %[a], %[c]\n\t"
//"add %[b], %[c]\n\t"
"add %[a], %[b], %[c]\n\t"
: [c] "=&r" (c)
: [a] "r" (a), [b] "r" (b)
: "cc"
);
std::cout << c << "\n";
}
uj5u.com熱心網友回復:
只有少數特定的 GPR 指令具有 VEX 編碼,主要是在 AVX 已經存在之后添加的BMI1/BMI2指令。見表 2-28 中的串列,其中有ANDN, BEXTR, BLSI, BLSMSK, BLSR, BZHI, MULX, PDEP, PEXT, RORX, SARX, SHLX, SHRX,以及 5.1.16.1 中的相同串列。例如,andn的手動輸入僅列出 VEX 編碼,and的手動輸入不列出任何編碼。
因此,英特爾(不幸的是)沒有為整個指令集引入全新的三運算元備用編碼。他們只是介紹了一些需要三個運算元并使用 VEX 的特定指令。在某些情況下,這些指令與現有指令具有相似或等效的功能,例如,SHLX對于SHL可變計數,因此有效地提供了前兩個運算元指令的三運算元版本,但僅在那些特殊情況下。沒有全面的等效說明。
“舊式”二運算元形式仍然是add指令的唯一版本。但是,正如 fuz 在評論中指出的那樣,lea將兩個暫存器相加并將結果寫入第三個暫存器是一種好方法,但受運算元大小的一些限制。
請參閱對不是地址/指標的值使用 LEA?對于 LEA 可以做的更一般的事情,例如將常量復制并添加到暫存器,或移位并添加。編譯器已經知道這一點,并將lea在適當的時候使用,只要它保存指令。(或者使用一些調整選項,例如-mtune=atom舊的有序 Atom,lea即使他們本可以使用,也會使用add。)
如果不是添加其他常見的整數指令更靈活的編碼存在,如and/ xor/ sub, gcc -O3 -march=skylake就已經使用了他們自己的ASM輸出,而不需要行內匯編。或者,如果替代指令可以完成作業,例如leafor add,就會這樣做,因此查看編譯器輸出以了解它知道哪些技巧是有意義的。自己嘗試一下會更有意義,因為它可以在.s僅進行退出系統呼叫的獨立檔案中進行操作,或者只是單步執行,從而消除使用行內 asm 的復雜性。(默認情況下,GAS 不限制指令集。 gcc -march=skylake不將其傳遞給匯編程式,as。)
在您的行內匯編中,您的c運算元應該是僅輸出:=r而不是 r. 舊值已被覆寫,因此無需告訴編譯器將其生成為輸入。(就像你說的,你要c = a b沒有c = a b。)
使用單個lea作為 asm 模板意味著您不需要=&r早期破壞輸出,因為您的 asm 將在寫入該輸出之前讀取其所有輸入。在您的情況下,將其作為輸入/輸出可能會阻止編譯器選擇與輸入之一相同的暫存器,這可能會與mov; add.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/365376.html
上一篇:如果目標檔案定義了_start并且不使用任何庫,為什么我仍然需要鏈接它才能執行它?
下一篇:故意在RISC-V中引發非法指令
