Для чего нужны все слои LLVM?

Я играю с LLVM 3.7 и хотел использовать новые возможности ORC. Но я занимаюсь этим уже несколько часов и до сих пор не понимаю, для чего нужен каждый слой, когда их использовать, как их составлять или, по крайней мере, минимальный набор вещей, которые мне нужны.

Прошел Kaleidoscope учебник, но он не объясняет составные части, просто говорит, поместите это здесь и это здесь (плюс синтаксический анализ и т. д. отвлекает от основных битов LLVM). Хотя это здорово для начала, остается много пробелов. В LLVM есть много документов по разным вещам, но их так много, что это на самом деле граничит с ошеломлением. Такие вещи, как http://llvm.org/releases/3.7.0/docs/ProgrammersManual.html, но я не могу найти ничего, что объясняло бы, как все части сочетаются друг с другом. Еще более запутанным кажется наличие нескольких API для выполнения одной и той же задачи, учитывая MCJIT и более новый ORC API. Я видел пост Лэнга Хеймса, в котором объяснялось довольно много вещей. изменились после патча, который он разместил по этой ссылке.

Итак, для конкретного вопроса, как все эти слои сочетаются друг с другом? Когда я ранее использовал LLVM, я мог довольно легко ссылаться на функции C, используя "Как использовать JIT" в качестве основы, я попытался связать внешнюю функцию extern "C" double doIt, но в итоге получил LLVM ERROR: Tried to execute an unknown external function: doIt.

Посмотрите на этот пример ORC кажется, мне нужно настроить, где он ищет символы. Но TBH, пока я все еще качаюсь в этом, это в основном догадки. Вот что я получил:

#include "llvm/ADT/STLExtras.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/ExecutionEngine/Interpreter.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include "std.hpp"

using namespace llvm;

int main() {

  InitializeNativeTarget();

  LLVMContext Context;

  // Create some module to put our function into it.
  std::unique_ptr<Module> Owner = make_unique<Module>("test", Context);
  Module *M = Owner.get();

  // Create the add1 function entry and insert this entry into module M.  The
  // function will have a return type of "int" and take an argument of "int".
  // The '0' terminates the list of argument types.
  Function *Add1F = cast<Function>(M->getOrInsertFunction("add1", Type::getInt32Ty(Context), Type::getInt32Ty(Context), (Type *) 0));

  // Add a basic block to the function. As before, it automatically inserts
  // because of the last argument.
  BasicBlock *BB = BasicBlock::Create(Context, "EntryBlock", Add1F);

  // Create a basic block builder with default parameters.  The builder will
  // automatically append instructions to the basic block `BB'.
  IRBuilder<> builder(BB);

  // Get pointers to the constant `1'.
  Value *One = builder.getInt32(1);

  // Get pointers to the integer argument of the add1 function...
  assert(Add1F->arg_begin() != Add1F->arg_end()); // Make sure there's an arg
  Argument *ArgX = Add1F->arg_begin();  // Get the arg
  ArgX->setName("AnArg");            // Give it a nice symbolic name for fun.

  // Create the add instruction, inserting it into the end of BB.
  Value *Add = builder.CreateAdd(One, ArgX);

  // Create the return instruction and add it to the basic block
  builder.CreateRet(Add);

  // Now, function add1 is ready.


  // Now we're going to create function `foo', which returns an int and takes no
  // arguments.
  Function *FooF = cast<Function>(M->getOrInsertFunction("foo", Type::getInt32Ty(Context), (Type *) 0));

  // Add a basic block to the FooF function.
  BB = BasicBlock::Create(Context, "EntryBlock", FooF);

  // Tell the basic block builder to attach itself to the new basic block
  builder.SetInsertPoint(BB);

  // Get pointer to the constant `10'.
  Value *Ten = builder.getInt32(10);

  // Pass Ten to the call to Add1F
  CallInst *Add1CallRes = builder.CreateCall(Add1F, Ten);
  Add1CallRes->setTailCall(true);

  // Create the return instruction and add it to the basic block.
  builder.CreateRet(Add1CallRes);

  std::vector<Type *> args;
  args.push_back(Type::getDoubleTy(getGlobalContext()));
  FunctionType *FT = FunctionType::get(Type::getDoubleTy(getGlobalContext()), args, false);

  Function *F = Function::Create(FT, Function::ExternalLinkage, "doIt", Owner.get());

  // Now we create the JIT.
  ExecutionEngine *EE = EngineBuilder(std::move(Owner)).create();

  outs() << "We just constructed this LLVM module:\n\n" << *M;
  outs() << "\n\nRunning foo: ";
  outs().flush();

  // Call the `foo' function with no arguments:
  std::vector<GenericValue> noargs;
  GenericValue gv = EE->runFunction(FooF, noargs);
  auto ax = EE->runFunction(F, noargs);

  // Import result of execution:
  outs() << "Result: " << gv.IntVal << "\n";
  outs() << "Result 2: " << ax.IntVal << "\n";
  delete EE;
  llvm_shutdown();
  return 0;
}

doIt объявлено в std.hpp.


person zcourts    schedule 12.09.2015    source источник


Ответы (1)


Ваш вопрос очень расплывчатый, но, возможно, я могу немного помочь. Этот пример кода представляет собой простую JIT-компиляцию, созданную с помощью Orc. Он хорошо прокомментирован. так что это должно быть легко следовать.

Проще говоря, Orc строится на основе тех же строительных блоков, что и MCJIT (MC для компиляции модулей LLVM в объектные файлы, RuntimeDyld для динамической компоновки во время выполнения), но обеспечивает большую гибкость благодаря своей концепции слоев. Таким образом, он может поддерживать такие вещи, как «ленивая» JIT-компиляция, которую MCJIT не поддерживает. Это важно для сообщества LLVM, потому что "старый JIT", который был удален не так давно, поддерживал эти вещи. Orc JIT позволяет нам получить обратно эти расширенные возможности JIT, продолжая строить поверх MC и, таким образом, не дублируя логику эмиссии кода.

Чтобы получить более точные ответы, я предлагаю вам задавать более конкретные вопросы.

person Eli Bendersky    schedule 13.09.2015
comment
Спасибо, этот пример кода полезен. Неопределенность моего вопроса проистекает из моего непонимания, я думаю. Потратив на это выходные, я думаю, что начинаю понимать, так что, надеюсь, если у меня возникнут какие-либо вопросы в будущем, они будут очень конкретными. - person zcourts; 14.09.2015