[英]how to solve error in inline assembly in C: 'can't find a register in class 'GENERAL_REGS' while reloading 'asm''
[英]ARM assembly: can’t find a register in class ‘GENERAL_REGS’ while reloading ‘asm’
我試圖在ARM Cortex-a8上的ARM匯編中實現將32位操作數與256位操作數相乘的函數。 問題是我的寄存器用完了,我不知道如何減少這里使用的寄存器數量。 這是我的功能:
typedef struct UN_256fe{
uint32_t uint32[8];
}UN_256fe;
typedef struct UN_288bite{
uint32_t uint32[9];
}UN_288bite;
void multiply32x256(uint32_t A, UN_256fe* B, UN_288bite* res){
asm (
"umull r3, r4, %9, %10;\n\t"
"mov %0, r3; \n\t"/*res->uint32[0] = r3*/
"umull r3, r5, %9, %11;\n\t"
"adds r6, r3, r4; \n\t"/*res->uint32[1] = r3 + r4*/
"mov %1, r6; \n\t"
"umull r3, r4, %9, %12;\n\t"
"adcs r6, r5, r3; \n\t"
"mov %2, r6; \n\t"/*res->uint32[2] = r6*/
"umull r3, r5, %9, %13;\n\t"
"adcs r6, r3, r4; \n\t"
"mov %3, r6; \n\t"/*res->uint32[3] = r6*/
"umull r3, r4, %9, %14;\n\t"
"adcs r6, r3, r5; \n\t"
"mov %4, r6; \n\t"/*res->uint32[4] = r6*/
"umull r3, r5, %9, %15;\n\t"
"adcs r6, r3, r4; \n\t"
"mov %5, r6; \n\t"/*res->uint32[5] = r6*/
"umull r3, r4, %9, %16;\n\t"
"adcs r6, r3, r5; \n\t"
"mov %6, r6; \n\t"/*res->uint32[6] = r6*/
"umull r3, r5, %9, %17;\n\t"
"adcs r6, r3, r4; \n\t"
"mov %7, r6; \n\t"/*res->uint32[7] = r6*/
"adc r6, r5, #0 ; \n\t"
"mov %8, r6; \n\t"/*res->uint32[8] = r6*/
: "=r"(res->uint32[8]), "=r"(res->uint32[7]), "=r"(res->uint32[6]), "=r"(res->uint32[5]), "=r"(res->uint32[4]),
"=r"(res->uint32[3]), "=r"(res->uint32[2]), "=r"(res->uint32[1]), "=r"(res->uint32[0])
: "r"(A), "r"(B->uint32[7]), "r"(B->uint32[6]), "r"(B->uint32[5]),
"r"(B->uint32[4]), "r"(B->uint32[3]), "r"(B->uint32[2]), "r"(B->uint32[1]), "r"(B->uint32[0]), "r"(temp)
: "r3", "r4", "r5", "r6", "cc", "memory");
}
EDIT-1:我根據第一條評論更新了我的內容清單,但仍然收到相同的錯誤
一個簡單的解決方案是將其分解,而不使用“ Clobber”。 將變量聲明為“ tmp1”,等等。請盡量不要使用任何mov
語句; 如果需要,讓編譯器執行此操作。 編譯器將使用一種算法來找出最佳的信息“流”。 如果您使用'clobber',則它無法重用寄存器。 按照現在的方式,您可以在匯編程序執行之前先加載所有內存。 這很不好,因為您希望將內存/ CPU ALU流水線化。
void multiply32x256(uint32_t A, UN_256fe* B, UN_288bite* res)
{
uint32_t mulhi1, mullo1;
uint32_t mulhi2, mullo2;
uint32_t tmp;
asm("umull %0, %1, %2, %3;\n\t"
: "=r" (mullo1), "=r" (mulhi1)
: "r"(A), "r"(B->uint32[7])
);
res->uint32[8] = mullo1; /* was 'mov %0, r3; */
volatile asm("umull %0, %1, %3, %4;\n\t"
"adds %2, %5, %6; \n\t"/*res->uint32[1] = r3 + r4*/
: "=r" (mullo2), "=r" (mulhi2), "=r" (tmp)
: "r"(A), "r"(B->uint32[6]), "r" (mullo1), "r"(mulhi1)
: "cc"
);
res->uint32[7] = tmp; /* was 'mov %1, r6; */
/* ... etc */
}
“ gcc內聯匯編程序”的全部目的不是直接在“ C”文件中編碼匯編程序。 它是使用編譯器的寄存器分配邏輯,做一些不能在“C”可以輕松完成。 在您的情況下使用進位邏輯。
通過使其不成為一個巨大的“ asm”子句,編譯器可以在需要新寄存器時從內存中調度加載。 它還會將您的“ UMULL” ALU活動與加載/存儲單元進行管道傳輸。
僅當指令隱式破壞特定寄存器時,才應使用Clobber。 您也可以使用類似的方法,
register int *p1 asm ("r0");
並將其用作輸出。 但是,除了那些可能改變堆棧的指令之外,我不知道像這樣的任何ARM指令,並且您的代碼當然不會使用這些指令和進位指令。
GCC知道如果內存被列為輸入/輸出,它就會發生變化,因此您不需要內存破壞者。 實際上,這是有害的,因為內存破壞者是編譯器的內存屏障 ,當編譯器可能為后者安排內存時 ,這將導致寫入內存。
道德是使用gcc內聯匯編器與編譯器一起工作。 如果您在匯編器中編碼並且擁有大量例程,則寄存器的使用可能會變得復雜而混亂。 典型的匯編編碼器每個例程在寄存器中僅保留一件事,但這並不總是寄存器的最佳用途。 編譯器將以相當智能的方式對數據進行混洗,當代碼變大時,這種方式很難克服(並且對於手動編寫IMO不太滿意)。
您可能想看一下GMP庫 ,它具有許多方法來有效解決代碼中看起來相同的問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.