Мне нужен совет по "пересылке" аргументов вызываемой стороне (в LLVM-IR).
Предположим, у меня есть функция F
, которая вызывается в начале всех других функций в модуле.Начиная с F
мне нужно получить доступ (прочитать) аргументы, переданные его непосредственному вызывающему.
Прямо сейчас, чтобы сделать это, я блокирую все аргументы в вызывающем объекте внутри структуры и передаю указатель i8*
на структуруна F
рядом с идентификатором, сообщающим, из какого абонента вызывается F
.F
имеет гигантский переключатель, который переходит к соответствующему коду распаковки.Это должно быть сделано, потому что функции в модуле имеют разные сигнатуры (различное количество аргументов / возвращаемого значения и типы; даже разные соглашения о вызовах), но это, очевидно, неоптимально (как с точки зрения производительности, так и размера кода), потому что янужно выделить структуру в стеке, скопировать аргументы внутри нее, передать дополнительный указатель на F
и затем выполнить распаковку.
Мне было интересно, есть ли лучший способ сделать это, то есть способ доступа из функции стековый фрейм ее непосредственного вызывающего (зная, благодаря идентификатору, из которого вызывается функция) или, в более общем случае, произвольные значения, определенные в его непосредственномабонент .Любые предложения?
примечание: весь смысл того, над чем я работаю, заключается в том, что single функция 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);
// ...
}