ARM程序集:在重新加载’asm’时无法在类’GENERAL_REGS’中找到寄存器

我试图在ARM Cortex-a8上实现一个函数,该函数将32位操作数与ARM组件中的256位操作数相乘。 问题是我的寄存器用完了,我不知道如何减少这里使用的寄存器的数量。 这是我的function:

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"); } 

编辑1:我根据第一个评论更新了我的clobber列表,但我仍然得到相同的错误

一个简单的解决方案是打破这个,不要使用’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 inline汇编程序’的全部目的不是直接在’C’文件中编译汇编程序。 它是使用编译器的寄存器分配逻辑并做一些在’C’中不能轻易完成的事情。 在你的情况下使用进位逻辑。

通过不使它成为一个巨大的’asm’子句,编译器可以在需要新寄存器时从内存调度负载。 它还将使用加载/存储单元管理“UMULL”ALU活动。

如果指令隐含地破坏了特定的寄存器,则应该只使用clobber。 你也可以使用类似的东西,

 register int *p1 asm ("r0"); 

并将其用作输出。 但是,除了那些可能改变堆栈的代码之外,我不知道任何这样的ARM指令,并且你的代码当然不使用这些指令。

如果将GCC列为输入/输出,GCC知道内存会发生变化,因此您不需要内存崩溃。 实际上它是有害的,因为内存 clobber是编译器内存屏障 ,这将导致在编译器可能为后者安排内存时写入内存。


道德是使用gcc内联汇编程序与编译器一起工作。 如果您使用汇编程序进行编码并且您有大量的例程,那么寄存器的使用会变得复杂和混乱。 典型的汇编程序编码器每个例程只在寄存器中保留一个东西,但这并不总是寄存器的最佳用途。 当代码大小变大时,编译器将以一种相当聪明的方式对数据进行混洗,这种方式很难被击败(并且对手动代码IMO不太满意)。

您可能希望查看GMP库 , 该库有很多方法可以有效地解决代码所具有的一些相同问题。