【问题标题】:function arguments loading to registers on x64函数参数加载到 x64 上的寄存器
【发布时间】:2015-04-05 17:02:30
【问题描述】:

我有这个小 C 代码

void decode(int *xp,int *yp,int *zp)
{
      int a,b,c;
      a=*yp;
      b=*zp;
      c=*xp;
      *yp=c;
      *zp=a;
      *xp=b;
}

然后我使用 gcc -c -O1 decode.c 将其编译为目标文件,然后使用 objdump -M intel -d decode.o 转储该对象,等效的汇编代码是

mov ecx,DWORD PTR [rsi]
mov eax,DWORD PTR [rdx]
mov r8d,DWORD PTR [rdi]
mov DWORD PTR [rsi],r8d
mov DWORD PTR [rdx],ecx
mov DWORD PTR [rdi],eax
ret

我注意到它根本不使用堆栈。但是首先仍然需要将值加载到寄存器中。所以我的问题是如何将参数加载到寄存器中?编译器会自动将参数加载到幕后的寄存器中吗?还是发生了其他事情?因为没有将参数加载到寄存器中的指令。


还有一点离题。当您增加对编译原始源代码和机器代码之间关系的优化时,会增加将机器代码与源代码相关联的难度。默认情况下,如果您没有为 GCC 指定优化标志,它不会优化代码。因此,我尝试在不进行任何优化的情况下进行编译以从源代码中获得预期的结果,但我得到的是与源代码无关且易于理解的 4-5 倍大的机器代码。但是当我应用 1 级优化时,代码看起来可以理解并且与源代码相关。但为什么呢?

【问题讨论】:

  • 不要同时问两个问题。
  • @EOF 是的,我知道,前 6 个 int 或 ptr 参数被传递给寄存器。但我不明白怎么做?我的意思是寄存器不能填满来自空中的数据,传递必须转换为一些机器代码,或者我非常缺少一些东西?
  • @simple16 当参数在堆栈上时,您认为它们来自哪里?您认为堆栈中充满了来自空气的数据吗?当参数由寄存器传递时,值来自完全相同的位置。
  • @PascalCuoq 哦,现在我明白了将参数加载到寄存器的指令发生在调用者中。 :)

标签: c gcc assembly operating-system cpu-architecture


【解决方案1】:

参数被加载到调用者的寄存器中。示例:

int a;
int b;

int f(int, int);

int g(void) {
  return f(a, b);
}

查看为g生成的代码:

$ gcc -O1 -S t.c
$ cat t.s
…
movl    b(%rip), %esi
movl    a(%rip), %edi
call    f

第二个问题:

所以我尝试在不进行任何优化的情况下进行编译以从源代码中获得预期的结果,但我得到的是与源代码无关且易于理解的 4-5 倍大的机器代码。

发生这种情况是因为未优化的代码很愚蠢。它是中间表示的直接翻译,其中每个变量都存储在堆栈中,即使它不需要,每次转换都由显式操作表示,即使不需要,等等。 -O1 是读取生成程序集的最佳优化级别。也可以禁用帧指针,这样可以将简单功能的开销降至最低。

【讨论】:

  • 我写了 main 来测试程序{int a,b,c; decode(&a,&b,&c);} 但是当我从 objdump 查看主要机器代码时,它所做的只是一些奇怪的指令 retz ret ,仅此而已。你是什​​么意思?
  • @simple16 最有可能的是您的函数decode 内联在main 中。为了给你举个例子,我不得不删除 f 的实现,否则 GCC 会一直内联它。如果这不能解释它,那么请提出一个新问题。
  • 有道理。我还注意到没有将局部变量加载到寄存器的代码。编译器管理变量到寄存器的分配?
  • @simple16 如果局部变量很少,它们可以一直存在于寄存器中并且没有保留的堆栈槽。此外,编译器可以为不同时存在的局部变量使用相同的堆栈槽。曾经有一段时间,唯一阻止编译器执行此操作的就是让必须在调试器中执行代码的人保持清醒,但如今,理论上,DWARF3 调试信息格式允许准确存储此类信息。 dwarfstd.org/Dwarf3Std.php
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-04
相关资源
最近更新 更多