【问题标题】:Is it possible to keep an entire array in cpu register是否可以将整个数组保存在 cpu 寄存器中
【发布时间】:2013-06-24 22:31:41
【问题描述】:

在下面的代码中,

int main( )
{
    register int arr[4];
    /* ... */
}

'arr' 是否有可能在某个 cpu 寄存器中分配。 (考虑 cpu 有 4 个或更多寄存器)。

否则编译器将忽略 register 数组的存储类。

【问题讨论】:

  • 可以想象一个优化编译器将整个 char[8] 数组保存在一个寄存器中,但我认为当前的任何编译器都不会这样做。
  • 如今,register 关键字与 CPU 寄存器无关……编译器会忽略它,只是不允许获取声明为 register 的变量的地址。
  • 在极端情况下是可以的,有时可以,但一般不会发生。

标签: c compiler-construction embedded microcontroller microprocessors


【解决方案1】:

根据我的理解,答案是YESNO

没有因为,

  1. 任何数组元素都必须是可显式寻址的(例如,对于 16 位 uC/uP,其地址应始终位于 0x0000 到 0xFFFF 地址空间之间。)

  2. 使用 register direct 寻址模式(例如 mov r2,#100)访问 CPU 寄存器。 这种寻址方式没有有效地址。 (即使它不被认为是一种寻址模式

  3. 数组元素必须驻留在连续的内存位置。 (对于指针运算,使用数组的主要原因)

并且是的因为,

  1. 编译器可以为上面的数组分配寄存器,这样我们就可以对其进行一些有限的操作。 但不能使用内部使用地址进行优化的操作。

见下面的代码。

int main( )
{
  register int arr[4];
  int i;

  arr[0] = 10;      /* OK */
  arr[1] = 20;      /* OK */
  arr[2] = 30;      /* OK */
  arr[3] = 40;      /* OK */

  for(i=0;i<4;i++)
    arr[i]=10;    /* Error : "address of register variable 'arr' requested" */

  return 0;
}

所以我的最终结论是,理想情况下,register 存储类不应该与 array 一起使用,即使您的编译器允许这样做。

请纠正我或提供更多意见。 :-)

【讨论】:

  • 你的 for 循环可以由编译器展开并且只使用寄存器,编译器一直在做这种事情(甚至在一个循环中计算所有数学,并删除大块但当然,如果你的函数的参数是数组的索引,这是在编译时无法计算的,那么你仍然可以使用带有开关的寄存器,如 if-then-else 树或跳转表,但可能是更聪明的只是使用内存,但那是特定于代码的。
  • 数组元素只需要在 C 模型中是可寻址的。由于 C 标准中的“好像”规则,它不需要在编译器生成的实际代码中可寻址。同样,数组元素只需要在 C 模型中的连续内存位置中,而不是在实际内存中。
  • 假设 2 通常不成立。存在内存映射寄存器。由于 1 和 3 取决于假设 2,因此 NO 答案应该是 YES
【解决方案2】:

不要将register 作为关键字与 CPU 寄存器混用。你的代码

register int arr[4];

使arr 完全无法访问,因为您无法获取对象的地址。基本上你唯一能用它做的就是sizeof arr

【讨论】:

    【解决方案3】:

    我写了这段代码:

    int foo(int x, int y)
    {
        register int a[2] = {x, y};
        return a[0] + a[1];
    }
    

    并用cc -O3 -std=c99 -S 用Apple clang 4.0 编译它,它会生成这个程序集(省略了各种调试和不相关的装饰):

    _foo:
        pushq   %rbp
        movq    %rsp, %rbp
        addl    %esi, %edi
        movl    %edi, %eax
        popq    %rbp
        ret
    

    因此,从某种意义上说,数组保存在寄存器中。然而,这更多是优化的产物,并且所有对数组的引用都是通过常量索引而不是由于register 关键字。所以答案是“嗯,理论上,它可能会发生。但它几乎没有实际用途,而且你通常不能依赖它。”

    某些处理器具有可索引寄存器,例如 ARM 处理器上的 NEON 寄存器,其中包含多个值,这些值可以在某些指令中独立寻址(通过立即值)。我可以设想一个编译器在 NEON 寄存器中保留一个小数组值并独立访问它们,前提是源代码引用可以在编译时解析为常量。

    【讨论】:

      【解决方案4】:

      C89 标准不允许对寄存器存储类的变量进行地址或任何类似操作(至少我的草案没有:3.5.1,请参阅注释 49)。它甚至提到只有 sizeof 运算符对这样的数组有效。 C99 标准引用了 6.7.1 中的寄存器存储类,其中注释 103 声明与 C89 草案完全相同。

      所以总而言之,寄存器存储类和数组真的不应该混用。声明本身是有效的,但从技术上讲它是无用的。

      否则,通常当您有疑问时,请查看反汇编列表。一些针对 8 位控制器的编译器可能会做一些令人惊讶的事情。

      【讨论】:

        【解决方案5】:

        优化编译器可能会临时将任何它认为适合的数组元素分配给 CPU 寄存器,并在那里执行操作。

        但是如果您问是否可以强制该数组注册,那么我不知道如何做到这一点。对于单个变量(在 gcc 中),您可以使用“显式寄存器变量”(参见 http://gcc.gnu.org/onlinedocs/gcc/Explicit-Reg-Vars.html)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-08-01
          • 2020-04-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-25
          • 1970-01-01
          相关资源
          最近更新 更多