![](/img/trans.png)
[英]GNU inline asm: same register for different output operands allowed?
[英]Copy a byte to another register in GNU C inline asm, where the compiler chooses registers for both operands
我試圖在 c 的內聯 asm 中處理字符串。 我能夠理解 strcpy 的工作原理(如下所示):
static inline char *strcpy(char *dest, char *src)
{
int d0, d1, d2;
char temp;
asm volatile(
"loop: lodsb;" /* load value pointed to by %si into %al, increment %si */
" stosb;" /* move %al to address pointed to by %di, increment %di */
" testb %%al, %%al;"
" jne loop;"
: "=&S" (d0), "=&D" (d1), "=&a" (d2)
: "0" (src), "1" (dest)
: "memory"
);
}
我正在嘗試使用此結構來制作它,以便我可以在返回字符串之前修改字符串的各個字符。 因此,我正在嘗試如下所示的內容:
static inline char *strcpy(char *dest, char *src)
{
int d0, d1, d2;
char temp;
asm volatile(
"loop: lodsb;" /* load value pointed to by %si into %al, increment %si */
" mov %2, %3;" /* move al into temp */
/*
*
* Do and comparisons and jumps based off how I want to change the characters
*
*/
" stosb;" /* move %al to address pointed to by %di, increment %di */
" testb %%al, %%al;"
" jne loop;"
: "=&S" (d0), "=&D" (d1), "=&a" (d2), "+r" (temp)
: "0" (src), "1" (dest)
: "memory"
);
}
我基本上是將lodsb
指令放入%al
的字節移動到臨時變量中,然后在那里進行任何處理。 但是,由於某種我無法弄清楚的原因,該角色似乎從未真正存儲在 temp 中。
你的第二個版本甚至不會組裝,因為temp
和d2
的大小不同,所以你最終得到 GCC 的mov %eax, %dl
: https : //godbolt.org/z/tng4g4 。 當內聯 asm 沒有執行您想要的操作時,請始終查看編譯器生成的 asm以查看編譯器實際替換到您的模板中的內容(以及它為哪個操作數選擇的寄存器)。
這與您描述的內容不匹配(運行但不起作用),因此它不是您正在做的事情的 MCVE。 但真正的問題仍然是可以回答的。
一種簡單的方法是聲明兩個 C 臨時文件的大小相同,以便 GCC 選擇相同寬度的寄存器。
或者您可以使用大小覆蓋,如mov %k2, %k3
來獲取movl
或mov %b2, %b3
來獲取movb
(8 位操作數大小)。
奇怪的是,您為"=a"
臨時選擇了int
所以編譯器選擇了 EAX,即使您只加載了一個char
。
我實際上建議movzbl %2b, %3k
使用與聲明變量的方式相反的大小; 這比將一個字節合並到目標的低字節更有效,並且避免在 P6 系列、早期 Sandybridge 系列和不進行任何部分寄存器重命名的 CPU 上引入(或添加更多)部分寄存器問題。 另外,英特爾因為 Ivybridge 可以對它進行移動消除。
順便說一句,您的第一個 strcpy 版本看起來安全且正確,不錯。 是的, "memory"
破壞是必要的。
呃,至少內聯匯編是正確的。 但是,由於在沒有return
語句的情況下從非void
函數的末尾脫落,您有 C 未定義的行為。
您可以使用"+&S"(src)
讀/寫操作數而不是虛擬輸出來簡化 asm 操作數,因為您在包裝函數中(因此可以修改此函數的本地src
)。 但是,具有匹配約束的虛擬輸出是在要銷毀的寄存器中獲取輸入的規范方法。
(如果你想像ISO C 設計糟糕的strcpy
,你需要在 asm 語句之前加上char *retval = dst
,如果你要使用上面的"+S"
和"+D"
操作數建議. 更好的主意是將其稱為stpcpy
並返回指向目標末尾的指針。此外,您的 src 應該是const char*
。)
當然,在循環中使用 lodsb/stosb 並不是特別有效,尤其是在不將 AL 與 RAX 分開重命名的 CPU 上,因此每個負載也需要一個 ALU uop 來合並到 RAX 中。 但是一次字節一次比 SSE2 更糟糕,所以用movzx
加載優化它,也許索引尋址模式可能不值得麻煩。 見https://agner.org/optimize/和其他優化鏈接https://stackoverflow.com/tags/x86/info ,尤其是https://uops.info/的指令延遲/吞吐量/微指令計數。 ( stosb
是 3 個stosb
而mov
store + inc edi
總共是 2 個。)
如果您實際上正在優化代碼大小而不是速度,只需使用 8 位或 32 位mov
來復制寄存器,而不是movzbl
。
順便說一句,有這么多操作數,您可能希望在約束中使用命名操作數,例如[src] "+&S"(src)
,然后在模板中使用%[src]
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.