【问题标题】:why does compiler store variables in register? [duplicate]为什么编译器将变量存储在寄存器中? [复制]
【发布时间】:2013-11-11 15:29:51
【问题描述】:

您好,我一直在阅读各种文档中的此类内容

register

告诉编译器将声明的变量存储在 CPU 寄存器中。

在标准 C 方言中,关键字 register 使用以下语法:

register data-definition;

register 类型修饰符告诉编译器将声明的变量存储在 CPU 寄存器中(如果可能),以优化访问。例如,

register int i;

注意TIGCC会在优化开启时自动将常用变量存储到CPU寄存器中,但是即使优化关闭,关键字register也会强制存储到寄存器中。但是,如果编译器断定在此位置没有足够的空闲寄存器可供使用,则将数据存储在寄存器中的请求可能会被拒绝。

http://tigcc.ticalc.org/doc/keywords.html#register

我的观点不仅仅是关于注册。我的观点是为什么编译器会将变量存储在内存中。编译器业务只是编译并生成一个目标文件。在运行时发生实际的内存分配。为什么编译器会做这件事。我的意思是如果不通过编译文件本身来运行目标文件,在 C 的情况下会发生内存分配吗?

【问题讨论】:

  • 我不明白你在问什么。变量必须在寄存器中才能执行指令。这就是 CPU 的工作方式。
  • 不清楚你到底在问什么。您正在阅读的文档讨论了编译器生成的代码。即,从您的 C 代码中,编译器需要生成告诉 CPU 如何操作的代码,其中包括变量在内存中的位置,执行指令时使用哪个 CPU 寄存器等等。一旦编译器生成了该代码,实际的内存/寄存器分配和使用就会在您运行编译后的代码时发生。

标签: c compiler-construction cpu-architecture cpu-registers


【解决方案1】:

编译器正在生成机器码,机器码用于运行您的程序。编译器决定它生成什么机器代码,因此决定在运行时会发生什么样的分配。当您键入 gcc foo.c 时,它不会执行它们,但稍后,当您运行可执行文件时,它会运行 GCC 生成的代码。

这意味着编译器希望生成尽可能快的代码,并在编译时做出尽可能多的决定,包括如何分配事物。

【讨论】:

  • 补充一点,register 不会强制编译器将变量实际分配给寄存器,但这只是表明它是有用的。如果我读到的一些 cmets 是真的,那么现代编译器已经如此优化,以至于他们可以更好地猜测哪些变量更有用 register 并且大多忽略关键字。
  • 任何编译器都是一样的。运行时始终运行实际业务的代码,它只运行编译器生成的代码(可能在运行时可能会发生很少的决定),但没有其他语言文档说编译器会这样做和那样做。他们说编译器生成代码,运行时运行代码。即使是解释器语言也是一样的。我知道 c 生成机器代码,但我的观点是编译器只是将源代码编译成机器可以理解的,但如果必须进行实际执行,则必须运行它。为什么 C 文档与其他文档如此不同?
  • @MuralidharYaragalla 因为在每个 C 编译器中,编译器都决定生成执行这些操作的代码。就好比说“指挥官要攻打那个岛”,指挥官可能不会亲自动手,而是下达命令
  • 好的。谢谢你。它感觉它是指挥官,因为它正在为机器生成指令。在像 java 这样的解释器语言中,它们依赖于运行时来生成机器代码。无论如何,非常感谢你的澄清。
【解决方案2】:

编译器不会运行代码(除非它进行几轮分析和更好的代码执行),但它必须准备它——这包括如何保留程序定义的变量,是否使用快速高效存储为寄存器,或使用较慢(且更容易产生副作用)的内存。

最初,您的局部变量将被简单地分配到堆栈帧上的位置(当然,您明确使用动态分配的内存除外)。如果您的函数分配了一个 int,您的编译器可能会告诉堆栈增加几个额外的字节,并使用该内存地址来存储该变量并将其作为操作数传递给您的代码对该变量执行的任何操作。
但是,由于内存速度较慢(即使在缓存时),并且操作它会对 CPU 造成更多限制,所以在稍后阶段,编译器可能会决定尝试将一些变量移动到寄存器中。这种分配是通过一个复杂的算法完成的,该算法试图选择最重用和延迟关键的变量,这些变量可以适合您的架构现有的逻辑寄存器数量(同时确认各种限制,例如一些指令要求操作数在此或那个寄存器)。

还有一个复杂的问题 - 一些内存地址可能会以编译时未知的方式与外部指针产生别名,在这种情况下,您无法将它们移动到寄存器中。编译器通常是一群非常谨慎的人,他们中的大多数会避免危险的优化(否则他们需要进行一些特殊的检查以避免讨厌的事情)。

毕竟,编译器仍然很有礼貌地让你建议哪个变量对你很重要和关键,以防他错过它,并用register关键字标记你'基本上是在要求他尝试通过使用寄存器来优化这个变量,假设有足够的寄存器可用并且没有别名是可能的。

这里有一个小例子: 以下面的代码为例,两次做同样的事情,但情况略有不同:

#include "stdio.h"

int j;
int main() {
    int i;
    for (i = 0; i < 100; ++i) {
        printf ("i'm here to prevent the loop from being optimized\n");
    }
    for (j = 0; j < 100; ++j) {
        printf ("me too\n");
    }
}

注意 i 是本地的,j 是全局的(因此编译器不知道在运行期间是否有其他人可以访问他)。

在 gcc 中使用 -O3 编译会为 main 生成以下代码:

0000000000400540 <main>:
  400540:       53                      push   %rbx
  400541:       bf 88 06 40 00          mov    $0x400688,%edi
  400546:       bb 01 00 00 00          mov    $0x1,%ebx
  40054b:       e8 18 ff ff ff          callq  400468 <puts@plt>
  400550:       bf 88 06 40 00          mov    $0x400688,%edi
  400555:       83 c3 01                add    $0x1,%ebx            # <-- i++
  400558:       e8 0b ff ff ff          callq  400468 <puts@plt>
  40055d:       83 fb 64                cmp    $0x64,%ebx
  400560:       75 ee                   jne    400550 <main+0x10>
  400562:       c7 05 80 04 10 00 00    movl   $0x0,1049728(%rip)        # 5009ec <j>
  400569:       00 00 00
  40056c:       bf c0 06 40 00          mov    $0x4006c0,%edi
  400571:       e8 f2 fe ff ff          callq  400468 <puts@plt>
  400576:       8b 05 70 04 10 00       mov    1049712(%rip),%eax        # 5009ec <j> (loads j)
  40057c:       83 c0 01                add    $0x1,%eax            # <-- j++
  40057f:       83 f8 63                cmp    $0x63,%eax
  400582:       89 05 64 04 10 00       mov    %eax,1049700(%rip)        # 5009ec <j> (stores j back)
  400588:       7e e2                   jle    40056c <main+0x2c>
  40058a:       5b                      pop    %rbx
  40058b:       c3                      retq

如您所见,第一个循环计数器位于 ebx 中,并在每次迭代时递增并与限制进行比较。
然而,第二个循环是危险的,gcc 决定通过内存传递索引计数器(每次迭代都将其加载到 rax 中)。这个例子展示了你在使用寄存器时会有多好,以及有时你不能这样做。

【讨论】:

  • 谢谢你。它确实有帮助。
【解决方案3】:

编译器需要将代码翻译成机器指令,并告诉计算机如何运行代码。这包括如何进行操作(例如将两个数字相乘)以及如何存储数据(堆栈、堆或寄存器)。

【讨论】:

    猜你喜欢
    • 2020-09-04
    • 1970-01-01
    • 1970-01-01
    • 2013-07-22
    • 2013-04-01
    • 1970-01-01
    • 2016-02-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多