[英]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 的優化器決定內聯函數(想要的屬性)並在下一個代碼中重用寄存器變量src
和dst
。 顯然,由於ldm %0!
和stm %1!
指令src
和dst
在離開 switch 語句時包含不同的地址。
如何解決?
我不知道如何通知 GCC 用於src
和dst
寄存器在最后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 選擇臨時寄存器,您將獲得更好的寄存器分配器行為,但我不知道如何告訴它按照ldm
和stm
要求以特定數字順序選擇臨時寄存器,或者如何告訴它只使用 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 在不同的寄存器中制作了src
和dst
的副本,並在最后一個UNPACK
宏之后正確使用它們。 兩個評論:
lsrs
是因為它編譯為 2 字節 Cortex-M3 本機lsrs
。 如果我使用標志不變lsr
版本,它會編譯為 4 字節mov.w r3, r2, lsr #16
-> 16 位 Thumb 2 lsr
默認帶有 's'。 如果沒有“s”,則必須使用 32 位 Thumb 2(我必須檢查它)。 無論如何,在這種情況下,我應該在clobbers中添加“cc”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.