0

考虑以下不会内联的函数并假设 x86 作为平台:

void doSomething(int & in){
//do something
}

首先,我不确定这种情况是否会发生,但是因为我认为有可能我会问,所以如果在任何调用者中调用此函数时,要提供的参数恰好位于调用者堆栈帧的顶部,因此在在汇编语言中通过 ebp 寄存器(在被调用者将 esp 的内容移动到 ebp 之后)被调用的函数访问它是可能的你建议我们忽略为函数声明一个参数并在这种特殊情况下使用汇编来访问我们的参数还是只是保留函数定义并让编译器完成它的工作?因为我还没有读到任何编译器会认为这种特殊情况作为调用约定的因素的地方,我认为它只会生成代码以将指向参数的指针传递给被调用者堆栈帧或寄存器之一

4

3 回答 3

4

首先,它很容易被破坏——例如,你得到一个不同版本的编译器,它生成不同的代码。或者您更改优化功能。不要介意突然需要doSomething在不同的地方使用然后它不起作用的情况,因为变量不再位于堆栈的顶部。

其次,假设函数内部的代码足够短,编译器很可能会内联函数,所以你根本不会“丢失”任何东西。

第三,现代编译器中的单个参数通常无论如何都会在寄存器中传递,因此启用优化时没有任何好处。

如果你真的认为这样做有好处,并且编译器不会内联或以其他方式优化代码[你看过生成的代码吗?],那么尝试使用forceinlinealways_inline在你的编译器中调用它(大多数编译器都有这样的选择)。如果这不起作用,请使用宏手动内联它。或者干脆将代码移动到“copy-n-paste”调用它的位置。

于 2013-07-05T08:21:44.967 回答
2

您的注释“要提供的参数恰好位于调用方堆栈帧的顶部,因此在被调用函数中通过 ebp 寄存器访问该参数”包含一个事实误解。

这是因为以下几点:

  • 您假设一个基于堆栈的调用约定,即push调用者在调用函数之前将函数参数编入堆栈call。通常情况并非如此。即使在 32 位 x86 上,也有非基于堆栈的调用约定(例如,fastcall在 32 位 linux 内核中使用的 Windows 或 GNU GCC )。如果使用了这样的参数,则不会在堆栈顶部找到参数,而是在 ... 用于保存第一个参数的任何寄存器。


    但即使你有基于堆栈的参数传递......仍然:

  • 您至少在 x86 上错过了该call指令将返回地址推入堆栈顶部,因此当函数的第一条指令到达该方式时正在执行,ESP不会指向该函数的第一个 arg ,但到返回地址。
  • 您错过了EBP一个被调用者保存(保留在函数调用上)的寄存器,而不是由体系结构代表您初始化 - 生成的代码有必要显式设置它。因此,想要使用它的函数(即使只是作为帧指针)必须在使用它之前将其保存在某个地方。这意味着正常的序幕将会有push EBP; mov EBP, ESP(你不能这样做MOV EBP, ESP,因为这会覆盖调用者的EBP无效/你可能不会这样做)。因此,如果您想引用函数的第一个参数,则[ EBP + 8 ] 不需要 [ EBP ].
    如果你不是使用帧指针,则第一个参数(由于call用于访问已推送返回地址的函数)位于[ ESP + 4 ] not [ ESP ]

我希望这能澄清一点。

我同意其他发帖人的观点,即澄清这个问题会有所帮助,你究竟想要实现什么,以及为什么你认为汇编语言在这里可能有用。

于 2013-07-15T10:24:54.110 回答
1

我不会。调用约定可能会有所不同(在 x86 和 x86_64 之间);参数可以被推入堆栈或放入寄存器,我不确定你能确定它们会在哪里。

在汇编中编写此代码,除非您真的知道自己在做什么,否则可能会导致未定义的行为代码。

于 2013-07-05T07:47:03.070 回答