簡體   English   中英

如何在擴展 GCC 內聯匯編中標記為損壞的輸入操作數(C 寄存器變量)?

[英]How to mark as clobbered input operands (C register variables) in extended GCC inline assembly?

問題描述

我正在嘗試設計將 uint32_t 元素的數組A解包到 uint32_t 元素的數組B的 C 代碼,其中A每個元素都被解包為B兩個連續元素,以便B[2*i]包含A[i]低 16 位A[i]B[2*i + 1]包含A[i]右移的高 16 位,即,

B[2*i] = A[i] & 0xFFFFul;
B[2*i+1] = A[i] >> 16u;

請注意,數組對齊為 4,長度可變,但A始終包含 uint32_t 的 4 的倍數且大小 <= 32, B有足夠的空間用於解包,我們在 ARM Cortex-M3 上。

當前 GCC 內聯匯編中的錯誤解決方案

由於 GCC 不擅長優化這種解包,我編寫了展開的 C 和內聯匯編,以使其速度優化,代碼大小和寄存器使用可接受。 展開的代碼如下所示:

static void unpack(uint32_t * src, uint32_t * dst, uint8_t nmb8byteBlocks)
{
    switch(nmb8byteBlocks) {
        case 8:
            UNPACK(src, dst)
        case 7:
            UNPACK(src, dst)
        ...
        case 1:
            UNPACK(src, dst)
        default:;
    }
}

在哪里

#define UNPACK(src, dst) \
    asm volatile ( \
        "ldm     %0!, {r2, r4} \n\t" \
        "lsrs    r3, r2, #16 \n\t" \
        "lsrs    r5, r4, #16 \n\t" \
        "stm     %1!, {r2-r5} \n\t" \
        : \
        : "r" (src), "r" (dst) \
        : "r2", "r3", "r4", "r5" \
    );

它會一直工作,直到 GCC 的優化器決定內聯函數(想要的屬性)並在下一個代碼中重用寄存器變量srcdst 顯然,由於ldm %0! stm %1! 指令srcdst在離開 switch 語句時包含不同的地址。

如何解決?

我不知道如何通知 GCC 用於srcdst寄存器在最后case 1:中的最后一個 UNPACK 宏之后無效case 1:

我試圖將它們作為輸出操作數傳遞給所有或僅最后一個宏( "=r" (mem), "=r" (pma) )或以某種方式(如何)將它們包含在內聯 asm clobbers 中,但它只進行寄存器處理糟糕的代碼再次變得更糟。

只有一種解決方案是禁用函數內聯( __attribute__ ((noinline)) ),但在這種情況下,我失去了 GCC 的優勢,如果 nmb8byteBlocks 在編譯時已知,它可以減少適當數量的宏並內聯它。 (同樣的缺點也適用於將代碼重寫為純匯編。)

有沒有可能如何在內聯匯編中解決這個問題?

認為您正在尋找+約束修飾符,這意味着“此操作數既可讀取又可寫入”。 (請參閱 GCC 內聯匯編文檔的“修飾符”部分。)

你還需要告訴GCC這個asm讀寫內存; 最簡單的方法是將"memory"添加到 clobber 列表中。 並且您使用lsrs破壞“條件代碼”,因此還需要"cc"破壞。 嘗試這個:

#define UNPACK(src, dst) \
    asm volatile ( \
        "ldm     %0!, {r2, r4} \n\t" \
        "lsrs    r3, r2, #16 \n\t" \
        "lsrs    r5, r4, #16 \n\t" \
        "stm     %1!, {r2-r5} \n\t" \
        : "+r" (src), "+r" (dst) \
        : /* no input-only operands */ \
        : "r2", "r3", "r4", "r5", "memory", "cc" \
    );

微優化:由於不使用班次中的條件代碼,因此最好使用lsr而不是lsrs 。這也使幾個月后的代碼更易於閱讀;將來您不會撓頭想知道是否有這里實際上需要條件代碼的一些原因。編輯:我被提醒, lsrs編碼比 Thumb 格式的lsr更緊湊,即使不需要條件代碼,這也足以成為使用它的理由。 )

(我想說,如果您讓 GCC 選擇臨時寄存器,您將獲得更好的寄存器分配器行為,但我不知道如何告訴它按照ldmstm要求以特定數字順序選擇臨時寄存器,或者如何告訴它只使用 2 字節 Thumb 指令可訪問的寄存器。)

(可以使用"m"類型的輸入和輸出操作數來准確指定讀取和寫入的內存,但這很復雜,可能不會有太大改善。如果您發現此代碼有效但導致一堆不相關的東西得到不必要地從內存重新加載到寄存器中,請參閱如何指示可以使用內聯 ASM 參數*指向*的內存?

(您可能會得到更好的代碼生成什么unpack內聯到,如果你改變它的函數簽名

static void unpack(const uint32_t *restrict src,
                   uint32_t *restrict dst,
                   unsigned int nmb8byteBlocks)

我還會嘗試添加if (nmb8byteBlocks > 8) __builtin_trap(); 作為函數的第一行。)

非常感謝 zwol,這正是我要找的,但在 GCC 內聯匯編頁面中找不到。 它完美地解決了這個問題——現在 GCC 在不同的寄存器中制作了srcdst的副本,並在最后一個UNPACK宏之后正確使用它們。 兩個評論:

  1. 我使用lsrs是因為它編譯為 2 字節 Cortex-M3 本機lsrs 如果我使用標志不變lsr版本,它會編譯為 4 字節mov.w r3, r2, lsr #16 -> 16 位 Thumb 2 lsr默認帶有 's'。 如果沒有“s”,則必須使用 32 位 Thumb 2(我必須檢查它)。 無論如何,在這種情況下,我應該在clobbers中添加“cc”。
  2. 在上面的代碼中,我刪除了 nmb8byteBlocks 值范圍檢查以使其清楚。 但是,當然,您的最后一句話不僅對所有 C 程序員來說都是一個好點。

暫無
暫無

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

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