Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/62.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
臂组件:can’;在类‘中找不到寄存器;一般规则’;重新加载时‘;asm&x2019;_C_Gcc_Arm_Inline Assembly - Fatal编程技术网

臂组件:can’;在类‘中找不到寄存器;一般规则’;重新加载时‘;asm&x2019;

臂组件:can’;在类‘中找不到寄存器;一般规则’;重新加载时‘;asm&x2019;,c,gcc,arm,inline-assembly,C,Gcc,Arm,Inline Assembly,我试图在ARM Cortex-a8上的ARM assembly中实现一个将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,

我试图在ARM Cortex-a8上的ARM assembly中实现一个将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列表,但我仍然得到了相同的错误

一个简单的解决方案是打破这一点,不使用“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操作时,才应该使用clobber。你也可以使用类似

register int *p1 asm ("r0");
并将其用作输出。然而,除了那些可能改变堆栈的ARM指令之外,我不知道还有任何类似的ARM指令,当然,您的代码不使用这些指令和进位指令

GCC知道,如果内存被列为输入/输出,那么它会发生变化,所以您不需要内存阻塞器。事实上,这是有害的,因为内存阻塞器是一个错误,这将导致在编译器可以为后者调度内存时写入内存


寓意是使用gcc内联汇编程序与编译器一起工作。如果您在汇编程序中编写代码,并且有大量例程,那么寄存器的使用可能会变得复杂和混乱。典型的汇编代码编写者在每个例程的寄存器中只保留一个东西,但这并不总是寄存器的最佳用途。当代码大小变大时,编译器将以一种相当智能的方式对数据进行洗牌,这是很难克服的(而且对于手工编写代码IMO来说也不是很令人满意)


您可能想看看哪种方法可以有效地解决代码中的一些问题。

asm语句有一个更大的问题。您需要将在asm语句中显式指定的所有寄存器添加到clobber列表中(该列表还需要包含“cc”)。这些clobber加上保存输入和输出操作数所需的所有寄存器(也需要标记为早期clobber)意味着您使用的寄存器比ARM多得多。您上次的尝试只会让问题变得更糟。@RossRidge有没有办法在输入之前使用另一种符号而不是
“r”
,从而得到正确的结果?我的意思是像
“g”
“m”
?你真的需要一个循环[迭代次数为8]而不是你正在做的。重新考虑:如果你的输入向量有20000个元素,你会怎么做?您需要为标量
A
值设置reg,为
B
ptr设置reg,为
res
ptr设置reg,为迭代计数设置reg,以及为每个循环迭代设置umull等人(可能需要另外设置4-6个)所需的任何其他reg,因此总数为~10。实际上,向量大小为2-3的regs已经用完,更不用说8了。为了让你的向量算法更清晰,我们来编写一个C fnc,它可以做到这一点[也可以作为你asm fnc的参考]。首先感谢你的简短回答。我有一个问题,如果我想从这个函数中获得最佳性能,在内联汇编中实现所有代码不是更好吗?我知道这里的寄存器可能用完了,但我正在考虑将指针(
*B
*res
)放在两个寄存器中,并使用
ldr
指令访问每个
uint32\t
数组。我不确定这是否可能,但性能对我来说非常重要是的,您可以在代码中使用
ldr
;我想这是一个基准点。编译器具有交错ALU和加载/存储(
ldr/str
)指令的特性和智能。它甚至可以执行
ldm/stm
和/或
ldrd/strd
,这取决于内存顺序、可用寄存器以及CPU类别(您将为A8硬编码,不执行加载/存储将允许编译器选择)。无论如何,您可以执行一个大型例程并使用
ldr
,因为ARM寄存器已经用完了。但是,我认为如果您拆分并查看输出,您会发现很多。您可能会惊讶于您的算法是内存受限/占主导地位的,而不是CPU受限/占主导地位的。可以肯定的是,您当前的大型例程不允许这些操作并行进行(人们也说内存暂停)。我相信,在看到编译器的输出后,您可以击败它。如果我看不到它能做些什么的话,我很难(在更大的程序中)打败它。目前,您有9/25~=36%的指令作为
mov
语句,并且还没有考虑编译器加载/存储东西所做的工作。再次感谢您,我将按照您所说的那样实现它,并将性能与我的C实现进行比较,并将结果放在这里。使用进位时,这有一些限制。在
asm
语句之间不能有分支(这可能会设置进位)。因此,最纯的“C”可能会被拒绝(但它已经是asm)。在任何情况下,它都提供了一个更好的起点(编译器寄存器分配和内存调度)