Assembly/ABI:大型 Return 类型的调用者责任

Assembly/ABI: Caller Responsibility for Large Return Types

我的汇编技能很差,但我一直在努力更好地理解汇编,以提高我对分析会话和优化编译器工作方式的理解。

在研究 Windows 的 64 位调用约定时,我从未停止在机器级别真正思考的事情之一是给出如下内容:

struct BigUdt
{
    double buf[16]; // exceeds 64 bits
};
struct BigUdt create_big_udt();

...如果我正确理解文档,create_big_udt 通常会为 struct BigUdt(我假设在堆栈上)和 return 分配内存到它的地址rax.

中的内存

因此,从装配的角度来看,我的问题是:就责任而言,调用者的责任在哪里?我假设有人留下了责任在某个时候将 sizeof(BidUdt) 添加回 rsp。对于大小不是 64 位倍数的类型,64 位堆栈对齐也会在这里成为一个问题,或者会被 struct BigUdt's 填充覆盖吗?

呸,我希望这是一个合理的问题。我发现调用者和被调用者之间的这些 ABI 契约与调用约定相当令人生畏,并且是迄今为止学习汇编最难的部分之一。

documentation说的很清楚:

Otherwise, the caller assumes the responsibility of allocating memory and passing a pointer for the return value as the first argument. Subsequent arguments are then shifted one argument to the right. The same pointer must be returned by the callee in RAX.

所以基本上 foo bar(...) 变成了 foo* bar(foo* retval, ...)。指针可以指向调用者想要结果的任何地方(通常它将是调用者堆栈帧中局部变量的地址),这当然不受调用中涉及的堆栈操作的影响,并且始终是有效的内存、堆栈或除此以外。它永远不会 "hanging in the wind"(如果我正确理解这个词的话),因为那确实会导致灾难。