在 LLVM 中,通常您将退出生成的函数CreateRet
,但是,我想为函数中实例化的本地对象添加清理析构函数。
我的问题是:我假设我必须在插入之前插入清理函数调用CreateRet
,但是,我想知道返回值是否是本地值之一(假设我们按值返回)然后我们不能在返回之前破坏这个值,但是返回后本地也不会被破坏,所以我想说我对本地人的生命周期以及在哪里正确插入清理有点困惑
在 LLVM 中,通常您将退出生成的函数CreateRet
,但是,我想为函数中实例化的本地对象添加清理析构函数。
我的问题是:我假设我必须在插入之前插入清理函数调用CreateRet
,但是,我想知道返回值是否是本地值之一(假设我们按值返回)然后我们不能在返回之前破坏这个值,但是返回后本地也不会被破坏,所以我想说我对本地人的生命周期以及在哪里正确插入清理有点困惑
看一些 C++ 反汇编!:)
通常,您将返回值保存在堆栈中,调用所有本地对象的析构函数,然后将返回值从堆栈移动到eax
寄存器中(cdecl 调用约定)。保存是必要的eax
,因为允许析构函数更改该寄存器。
以这个非常假设的伪组件为例:
// inside imaginary function
mov [ebp-0Ch],eax; // save eax register
lea ecx, [ebp-4]; // [ebp-4] == your object address
call Foo::~Foo(); // call the destructor
mov eax,[ebp-0Ch]; // retrieve the saved return value
ret; // now return
如果返回的值是一个本地对象,那么你当然首先需要将该对象复制到准备好的空间中。
// pseudo function call
int i = func();
在这里,func
将在堆栈上提供返回值的空间,这就是您复制返回值的地方。之后,如上所示继续。
您可以将源语言return
语句/表达式作为副本编码生成到返回存储中,然后分支到指定的返回基本块,这将破坏本地。如果您生成的 LLVM 函数返回值本身,而不是遵循其自己的协议(通过第一个参数或类似的返回值),您可以将返回值保存到alloca
第一个,然后加载该值alloca
并使用ret
. 通过第一个参数返回的示例,%valuetype
表示在语言运行时中存储值的结构
define void @myfn(%valuetype *%ret) {
; use and create whatever locals you need
; source-language: return somelocal
store %valuetype %local1, %valuetype *%ret
br label %retlabel
retlabel:
; emit code to destruct locals ..., then return
ret void
}