Переадресация аргументов в LLVM - PullRequest
7 голосов
/ 22 августа 2011

Мне нужен совет по "пересылке" аргументов вызываемой стороне (в 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);
  // ...
}

Ответы [ 2 ]

1 голос
/ 23 августа 2011

Не уверен, поможет ли это, но у меня была похожая проблема, и я обошел ограничения анализа tbaa LLVM, используя вектор llvm для хранения промежуточных значений. Проходы оптимизации LLVM впоследствии смогли оптимизировать векторную загрузку / сохранение в скалярных регистрах.

Насколько я помню, было несколько предостережений. Дайте мне знать, если вы исследуете этот маршрут, и я могу выкопать некоторый код.

1 голос
/ 22 августа 2011

ИМХО, вы сделали все правильно.Хотя в сборке машинного кода есть решения, я боюсь, что в сборке LLVM может не быть решения, так как это «более высокий уровень».Если вы хотите запустить функцию в начале некоторых функций, вы подумали о проверке

  • источников отладчика (например, gdb)
  • Бинарные инструменты с Valgrind

Я знаю, что это не прямой ответ, но я надеюсь, что это может быть полезным в некотором роде;).

...