Как вызвать функцию JITed LLVM с неизвестным типом?

Я реализую интерфейс для JIT-компилятора с использованием LLVM. Я начал с примера с калейдоскопом в учебнике по LLVM. Я знаю, как сгенерировать LLVM IR и JIT с помощью LLVM C ++ API. Я также знаю, как вызвать функцию JITed, используя метод getPointerToFunction из llvm :: ExecutionEngine.

getPointerToFunction возвращает void *, которое я затем должен привести к правильному типу функции. Например, в моем компиляторе есть модульный тест, который выглядит следующим образом:

void* compiled_func = compiler.get_function("f");   
auto f = reinterpret_cast<int32_t(*)(int32_t)>(compiled_func);
int32_t result = f(10);

Проблема в том, что я должен заранее знать сигнатуру функции. В приведенном выше примере у меня есть функция «f», которая принимает 32-битное целое число и возвращает 32-битное целое число. Поскольку я сам создаю "f", я знаю, что это за функция, поэтому я могу вызвать функцию JIT. Однако в целом я не знаю, что такое сигнатура функции (или какие типы структур), которые вводятся пользователем. Пользователь может создавать произвольные функции с произвольными аргументами и возвращаемыми типами, поэтому я не знаю, какой тип указателя функции нужно преобразовать в void * из getPointerToFunction LLVM. Моя среда выполнения должна иметь возможность вызывать эти функции (например, для цикла Read-Evaluate-Print). Как я могу обрабатывать такие произвольные функции из моей среды выполнения JIT?

Спасибо


person n00b101    schedule 07.04.2013    source источник


Ответы (1)


От compiled_func можно получить не так много информации - как вы писали, это просто void*. Но когда вы пишете «в общем, я не знаю, какова сигнатура функции», это неверно - вы только что скомпилировали эту функцию, поэтому у вас должен быть доступ к объекту LLVM Function, у которого можно запросить его тип. Это правда, что это тип LLVM IR, а не тип C ++, но вы часто можете знать, какой из них переводится.

Например, если мы заимствуем код из раздела руководства по JITting Kaleidoscope < / а>:

if (Function *LF = F->Codegen()) {
  LF->dump();  // Dump the function for exposition purposes.

  // JIT the function, returning a function pointer.
  void *FPtr = TheExecutionEngine->getPointerToFunction(LF);

  // Cast it to the right type (takes no arguments, returns a double) so we
  // can call it as a native function.
  double (*FP)() = (double (*)())(intptr_t)FPtr;
  fprintf(stderr, "Evaluated to %f\n", FP());
}

Тогда да, FPtr "предполагалось" иметь тип double (), но здесь также LF типа Function*, так что вы могли сделать что-то вроде:

Type* RetTy = LF->getReturnType();
if (RetTy->isDoubleTy()) {
  double (*FP)() = (double (*)())(intptr_t)FPtr;
  fprintf(stderr, "Evaluated to %f\n", FP());
} else if (RetTy->isIntegerTy(32)) {
  int (*FP)() = (int (*)())(intptr_t)FPtr;
  fprintf(stderr, "Evaluated to %d\n", FP());
} else ...

Точно так же вы можете запросить функцию о типах ее параметров.

Немного громоздко? Вы можете использовать свой механизм выполнения для вызова функции через его удобный runFunction , который получает вектор GenericValue и возвращает GenericValue. Вы все равно должны запросить тип Function, чтобы узнать, каким должен быть базовый тип под каждым GenericValue.

person Oak    schedule 08.04.2013
comment
Да, вы правы, что я знаю сигнатуру функции во время выполнения. Предложенное вами решение имеет смысл. Я вижу, что runFunction в сочетании с запросом к типу GenericValue должна позволить мне JIT и выполнять произвольные функции с произвольными значениями. Спасибо! - person n00b101; 08.04.2013