簡體   English   中英

將一個字節復制到 GNU C 內聯 asm 中的另一個寄存器,編譯器在其中為兩個操作數選擇寄存器

[英]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 中。

你的第二個版本甚至不會組裝,因為tempd2的大小不同,所以你最終得到 GCC 的mov %eax, %dlhttps : //godbolt.org/z/tng4g4 當內聯 asm 沒有執行您想要的操作時,請始終查看編譯器生成的 asm以查看編譯器實際替換到您的模板中的內容(以及它為哪個操作數選擇的寄存器)。

這與您描述的內容不匹配(運行但不起作用),因此它不是您正在做的事情的 MCVE。 但真正的問題仍然是可以回答的。

一種簡單的方法是聲明兩個 C 臨時文件的大小相同,以便 GCC 選擇相同寬度的寄存器。

或者您可以使用大小覆蓋,如mov %k2, %k3來獲取movlmov %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 個stosbmov store + inc edi總共是 2 個。)

如果您實際上正在優化代碼大小而不是速度,只需使用 8 位或 32 位mov來復制寄存器,而不是movzbl


順便說一句,有這么多操作數,您可能希望在約束中使用命名操作數,例如[src] "+&S"(src) ,然后在模板中使用%[src]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM