我需要一些关于向被调用方“转发”参数的建议(在LLVM-IR中)。
假设我有一个函数F
,它在模块中所有其他函数的开头调用。从F
我需要访问(读取)传递给其直接调用者的参数。
现在要执行此操作,我将结构中的调用者中的所有参数都包装起来,并将i8*
指针传递给结构F
,旁边还有一个标识符,告诉哪个调用者F
正在来自。 F
然后有一个巨大的开关,分支到相应的拆箱代码。必须这样做,因为模块中的函数具有不同的签名(不同的参数/返回值计数和类型;甚至不同的调用约定),但它显然是次优的(从性能和代码大小的角度来看)因为我需要在堆栈上分配结构,复制其中的参数,将另一个指针传递给F
,然后执行拆箱。
我想知道是否有更好的方法来做到这一点,即从函数访问其直接调用者的堆栈帧的方式(知道,由于标识符,函数是哪个调用者来自)或者更一般地说,在其直接调用者中定义的任意值。有什么建议吗?
注意:我正在研究的重点是拥有单个函数F
来完成所有这些工作;分裂/内联/专门化/模板化F
不是一种选择。
澄清一下,假设我们有以下函数FuncA
和FuncB
(注意:下面的内容只是伪C代码,请记住我们正在谈论LLVM-IR!)
Type1 FuncA(Type2 ArgA1) {
F();
// ...
}
Type3 FuncB(Type4 ArgB1, Type5 ArgB2, Type6 ArgB3) {
F();
// ...
}
我需要的是函数F
执行以下操作的有效方法:
void F() {
switch (caller) {
case FuncA:
// do something with ArgA1
break;
case FuncB:
// do something with ArgB1, ArgB2, ArgB3
break;
}
}
正如我在第一部分中解释的那样,现在我的F
看起来像这样:
struct Args_FuncA { Type2 ArgA1 };
struct Args_FuncB { Type4 ArgB1, Type5 ArgB2, Type6 ArgB3 };
void F(int callerID, void *args) {
switch (callerID) {
case ID_FuncA:
Args_FuncA *ArgsFuncA = (Args_FuncA*)args;
Type2 ArgA1 = ArgsFuncA->ArgA1;
// do something with ArgA1
break;
case ID_FuncB:
Args_FuncB *ArgsFuncB = (Args_FuncB*)args;
Type4 ArgB1 = ArgsFuncB->ArgB1;
Type5 ArgB2 = ArgsFuncB->ArgB2;
Type6 ArgB3 = ArgsFuncB->ArgB3;
// do something with ArgB1, ArgB2, ArgB3
break;
}
}
并且这两个函数变为:
Type1 FuncA(Type2 ArgA1) {
Args_FuncA args = { ArgA1 };
F(ID_FuncA, (void*)&args);
// ...
}
Type3 FuncB(Type4 ArgB1, Type5 ArgB2, Type6 ArgB3) {
Args_FuncB args = { ArgB1, ArgB2, ArgB3 };
F(ID_FuncB, (void*)&args);
// ...
}
答案 0 :(得分:1)
恕我直言,你做得对。虽然机器码装配有解决方案,但我担心LLVM装配可能没有解决方案,因为它是“更高级别”。如果你想在某些函数的开头运行一个函数,你考虑过检查
我知道这不是直接回答,但我希望它可能在某种程度上有所帮助;)。
答案 1 :(得分:1)
不确定这是否有帮助,但我遇到了类似的问题,并通过使用llvm向量存储中间值来解决LLVM的tbaa分析的局限性。 LLVM优化通过后来能够优化向量加载/存储到标量寄存器中。
我记得有一些警告。如果你探索这条路线,请告诉我,我可以挖掘一些代码。