3

我正在开发一个虚拟机,我想让调用编译函数成为可能。但是,由于每个函数最终可能具有不同的签名,我的计划是将所有调用概括为 2 种可能的场景 - 调用没有返回和没有参数的函数,以及调用带有一个void *参数的函数。

计划是使用它类似于thiscall- 所有参数在传递的指针的位置正确对齐,并且通过间接检索参数。至少 IMO 不应该比从堆栈中读取它们慢。

所以而不是:

int foo(int a, int b) { return a+b; }

我可以有类似的东西:

void foo2(void *p) {
   *(int*)p = *(int*)(p + 4) + *(int*)(p + 8);
}

所以我的问题是使用这种方法可能会出现什么问题?我可以马上说的是它“在黑暗中”工作,因此正确计算偏移量至关重要。这也有点不方便,因为所有临时人员都需要由用户提供。假设我的 VM 编译器将处理这两个问题,我最关心的是性能——我不想创建一个普通函数,并且为每个普通函数创建一个void *包装器——我想直接对所有函数使用该约定,所以我不禁想知道编译器在编译代码中使用时内联函数的工作有多好?我是否会忽略任何其他可能的性能影响(不包括__fastcall哪个会使用更多的寄存器和更少的间接)?

4

2 回答 2

0

在性能方面(和易用性)你可能会得到最好的结果cdecl——一切都在堆栈中。C 标准允许您使用任意参数指定函数原型

typedef void (__cdecl * function_with_any_parameters)();

您必须确保将要调用的所有函数定义为:

void __cdecl f(type1 arg1, type2 arg2, type3 arg3); // any amount of arguments

只需使用适量的参数调用它们:

f(arg1, arg2, arg3, arg4);

如果您希望通过单个指针,那么您确实有额外的开销:一个指针。最简单的方法是将所有函数定义为接受指向匿名结构的指针:

void f(struct {type1 a; type2 b;} * args);

然后,您可以使用指向适当结构的指针调用该函数,以避免任何错位。

struct {type1 a; type2 b;} args = {arg1, arg2};
f(&args);

您正在有效地cdecl自行实施。

于 2013-09-09T10:51:10.657 回答
0

在运行了一些基准测试之后,我会说编译器在优化类似的指针函数方面做得很好。该函数与函数和常规运算符void *一样快。add+

似乎这种约定对于提供必要的调用抽象很有用,而不会损害优化和整体性能。唯一的牺牲是安全性,这可能是也可能不是主要问题,具体取决于应用程序上下文。

于 2013-09-10T12:52:00.067 回答