【问题标题】:What happens to %rax after an empty return statement in a void function?void 函数中的空 return 语句后 %rax 会发生什么?
【发布时间】:2020-03-25 15:01:39
【问题描述】:

我的理解是,如果执行 void 函数没有返回语句,那么 %rax 仍将存储从前一个非 void 函数返回的任何内容。

也就是说,如果:

int a(int param){
    return param;
}

void b(){
}

int main(){
    a(5);
    b();
    return 1;
}

那么在 main 执行完成后返回之前,%rax 中存储的值将是 5。

我的问题是,如果 b 有一个空的 return 语句会发生什么? 也就是说,

void b(){
    return;
}

这会清除 %rax 吗?还是 %rax 仍然保留其先前的值?

【问题讨论】:

  • 没有必要清除%rax 的无效回报,因为没有人应该看它。也不知道它以前的值是什么,因为编译后的代码可以将它用于任何事情。
  • 这是一个人为的例子,当然,但我记得有一次遇到一个错误,因为 int 函数中的一个分支没有返回语句,返回的值实际上是错误的之前调用的函数。那么,为了健壮性,我们是否应该尽可能地清除 %rax 以避免这样的错误?
  • 不,为了稳健性而清除该寄存器不是我们想要的。我们想要的是无法返回值的编译时错误,因此我们可以实现并修复该错误。我相信现代 C 编译器会做到这一点。

标签: assembly x86-64 calling-convention


【解决方案1】:

那么 %rax 仍将存储从前一个非 void 函数返回的任何内容。

不,你的理解是错误的。

当 RAX 不保存返回值(void 或 FP 函数)时,它是一个调用破坏寄存器,例如 RCX 或 RSI。阅读调用约定文档或查看编译器输出。

顺便说一句,明确的return 声明与到达底部的} 完全无关。 (除了从 non-void 函数的末尾掉出的是 UB,一些编译器会将其编译为 ud2 非法指令。)

那么在 main 执行完成后返回之前,%rax 中存储的值将是 5。

这是 100% 的实现细节;编译器可以对函数中的寄存器做任何它想做的事情,比如使用 RAX 来评估非函数调用表达式。调用约定只是明确了函数之间的界限。

【讨论】:

  • 仅当调用者使用返回值时,从非 void 函数中退出才是未定义的行为(参见 ISO/IEC 9899:2011 §6.9.1 ¶12)。
  • @fuz:谢谢。在这种情况下,clang (IIRC) 中的 ud2 可能仅在 C++ 中。我很确定我在函数本身中看到了ud2。我经常在 C++ 模式下使用 Godbolt。 (这个问题没有标记 C 或 C++,所以我们不知道哪个适用。)
猜你喜欢
  • 1970-01-01
  • 2013-11-23
  • 2013-08-17
  • 2012-12-09
  • 2017-12-04
  • 1970-01-01
  • 2013-04-29
  • 2020-04-29
  • 1970-01-01
相关资源
最近更新 更多