将函数标记为脏(指定调用方应保存所有寄存器)
marking a function dirty (specifying that caller should save all registers)
我有一个函数f()
,它会破坏函数调用中的所有寄存器,堆栈指针除外。如何将此信息传达给gcc
,以便调用方在调用f()
之前保存它想要保存的所有寄存器?
编辑:我正在编写一个协程处理程序,它需要在切换到另一个协程前保存状态,我希望保存尽可能少的状态。假设我们有一个函数yield()
,标记为"脏",它会破坏除堆栈指针之外的所有内容。这个函数将在控制返回到我们的协程后神奇地返回,但最好是调用方而不是被调用方(因为调用方最清楚需要保存哪些寄存器(在调用yield()
之前保存所有需要保存的寄存器,然后再恢复它们。
这个想法违反了最小惊奇原则,因此应该避免。
作为调用方,我不希望在调用函数时担心注册内容。此外,如果您希望调用者处理它,为什么不在f((内部处理它呢?调用方不仅不需要知道,而且只需要一个实现。根据您的建议,每个调用方都需要处理保存/恢复上下文,这可能非常容易出错。
基本解决方案可以使用RAII在f((中处理此问题。类似于:
class ContextGuard {
public:
// Stores the current context
ContextGuard() { ... }
// Restores the current context
~ContextGuard() { ... }
private:
...
};
...
void f() {
ContextGuard contextGuard; // Saves the current context
... // Do stuff that mucks with the registers
} <-- contextGuard is destroyed here and the context is restored.
如何处理存储/恢复上下文是另一回事。Boost.Context可以为你提供你需要的东西。
最后一点,除了堆栈指针之外,还有一些关于寄存器的其他复杂性。在这里,您需要考虑特定于平台的函数调用约定,可能还有其他因素。如果你以这样一种方式处理寄存器,以至于你认为它们需要恢复,那么你必须真正理解你打算在其上运行此代码的所有平台的复杂性。
您还可以使用C函数setjmp和longjump。
setjmp
就是这么做的。它将上下文存储到jmp_buf
变量中。
之后,您可以直接调用longjump
来恢复上下文。
示例实现类似于:
void f()
{
jmp_buf my_jump_buffer;
if (setjmp(my_jump_buffer) == 0) {
// bla bla destroying any register you want (except sp)
// if you need to change sp, you have to make the buffer a global var
longjmp(my_jump_buffer, 1); // Will restore the context
}
}
最后,我用了这个:
#if defined(__GNUC__)
# if defined(i386) || defined(__i386) || defined(__i386__)
# define clobber_all() asm volatile ("":::"eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "cc");
# elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
# define clobber_all() asm volatile ("":::"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "cc");
# elif defined(__arm__)
# define clobber_all() asm volatile ("":::"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "lr", "cc");
# elif defined(__aarch64__)
# define clobber_all() asm volatile ("":::"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "cc");
# else
# error "unsupported architecture"
# endif
#else
# error "unsupported compiler"
#endif
阻塞sp
显示一个警告,这就是为什么我没有包括sp
。您可以将clobber_all()
包含在您的函数序言中:
void f()
{
clobber_all();
// blah, blah, ...
}
它将通知编译器(几乎(所有通用寄存器的内容都已被破坏。
- 在全局变量中保存类的实例以重新创建类(创建"backup")
- 如何在选项卡视图Qt中设置一个新项目,并保存以前的项目
- 嵌入方指针压缩已禁用
- 如何使用OpenCV将RBG图像转换为HSV,并将H、S和V值保存为C++中的3个独立图像
- 将字符指针十六进制转换为字符串并保存在文本文件C++中
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- EvtExportLogneneneba API正在将远程计算机的事件日志保存到远程PC本身.如何将其保存到主机
- 在C++中将类(带有Vector成员)保存为二进制文件
- 在c++中实现LinkedList时,应出现未处理的错误
- C++.我想将更改后的特定字符行保存在字符串中
- 使用FFMPEG将RGB图像序列保存到.mp4时出现问题
- 将RGB图像保存为PPM格式
- 为什么是谷神星协方差.计算()似乎永远运行而不返回?
- 哈希文件递归并保存到矢量Cryptopp中
- 如何将二进制格式的 C++ 对象的 std::vector 保存到磁盘?
- 从类型std::函数传递变量失败,尽管调用方期望的类型完全相同
- 无法通过头文件保存变量的值
- 使用共享指针的函数调用,其对象应为 const
- 阵列不能两次将相同的名称保存,当要添加新字符串时,程序应检查以查看该名称是否已经存在
- 将函数标记为脏(指定调用方应保存所有寄存器)