Primeros pasos con llvm
Compilación de una función simple en llvm 4.0
Entonces, lo que intentaremos hacer es compilar la siguiente función
int sum(int a, int b) {
return a + b + 2;
}
sobre la marcha. Y aquí está todo el ejemplo .cpp
:
#include <iostream>
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Verifier.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/Support/TargetSelect.h"
// Optimizations
#include "llvm/Transforms/Scalar.h"
#include "llvm/Analysis/BasicAliasAnalysis.h"
using namespace llvm;
llvm::Function* createSumFunction(Module* module) {
/* Builds the following function:
int sum(int a, int b) {
int sum1 = 1 + 1;
int sum2 = sum1 + a;
int result = sum2 + b;
return result;
}
*/
LLVMContext &context = module->getContext();
IRBuilder<> builder(context);
// Define function's signature
std::vector<Type*> Integers(2, builder.getInt32Ty());
auto *funcType = FunctionType::get(builder.getInt32Ty(), Integers, false);
// create the function "sum" and bind it to the module with ExternalLinkage,
// so we can retrieve it later
auto *fooFunc = Function::Create(
funcType, Function::ExternalLinkage, "sum", module
);
// Define the entry block and fill it with an appropriate code
auto *entry = BasicBlock::Create(context, "entry", fooFunc);
builder.SetInsertPoint(entry);
// Add constant to itself, to visualize constant folding
Value *constant = ConstantInt::get(builder.getInt32Ty(), 0x1);
auto *sum1 = builder.CreateAdd(constant, constant, "sum1");
// Retrieve arguments and proceed with further adding...
auto args = fooFunc->arg_begin();
Value *arg1 = &(*args);
args = std::next(args);
Value *arg2 = &(*args);
auto *sum2 = builder.CreateAdd(sum1, arg1, "sum2");
auto *result = builder.CreateAdd(sum2, arg2, "result");
// ...and return
builder.CreateRet(result);
// Verify at the end
verifyFunction(*fooFunc);
return fooFunc;
};
int main(int argc, char* argv[]) {
// Initilaze native target
llvm::TargetOptions Opts;
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
LLVMContext context;
auto myModule = make_unique<Module>("My First JIT", context);
auto* module = myModule.get();
std::unique_ptr<llvm::RTDyldMemoryManager> MemMgr(new llvm::SectionMemoryManager());
// Create JIT engine
llvm::EngineBuilder factory(std::move(myModule));
factory.setEngineKind(llvm::EngineKind::JIT);
factory.setTargetOptions(Opts);
factory.setMCJITMemoryManager(std::move(MemMgr));
auto executionEngine = std::unique_ptr<llvm::ExecutionEngine>(factory.create());
module->setDataLayout(executionEngine->getDataLayout());
// Create optimizations, not necessary, whole block can be ommited.
// auto fpm = llvm::make_unique<legacy::FunctionPassManager>(module);
// fpm->add(llvm::createBasicAAWrapperPass());
// fpm->add(llvm::createPromoteMemoryToRegisterPass());
// fpm->add(llvm::createInstructionCombiningPass());
// fpm->add(llvm::createReassociatePass());
// fpm->add(llvm::createNewGVNPass());
// fpm->add(llvm::createCFGSimplificationPass());
// fpm->doInitialization();
auto* func = createSumFunction(module); // create function
executionEngine->finalizeObject(); // compile the module
module->dump(); // print the compiled code
// Get raw pointer
auto* raw_ptr = executionEngine->getPointerToFunction(func);
auto* func_ptr = (int(*)(int, int))raw_ptr;
// Execute
int arg1 = 5;
int arg2 = 7;
int result = func_ptr(arg1, arg2);
std::cout << arg1 << " + " << arg2 << " + 1 + 1 = " << result << std::endl;
return 0;
}
Debería funcionar bien cuando se compila con clang++-4.0 con los siguientes indicadores:
$ llvm-config-4.0 --cxxflags --libs core
Instalación o configuración
Siempre se recomienda ir al sitio web oficial de LLVM y seguir las guías de instalación según su sistema operativo.
Si está trabajando en posix, en resumen, debe agregar uno de [los repositorios oficiales de paquetes LLVM] 2. Por ejemplo, si trabaja en Ubuntu Xenial (16.04), agrega una entrada deb
y deb-src
a su archivo /etc/apt/sources.list
:
$ sudo su
$ echo deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main \ >> /etc/apt/sources.list
$ echo deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main \ >> /etc/apt/sources.list
y una vez que lo haga, la instalación es tan simple como llamar
$ sudo apt update
$ sudo apt install clang-X
donde X
es la versión que está buscando (4.0 es actual al momento de escribir esta publicación).
Tenga en cuenta que clang es un compilador C/C++ escrito sobre LLVM (y en realidad ahora está autohospedado) y viene junto con todas las bibliotecas LLVM. Una vez que hagas eso, puedes ir a cualquier turorial y comenzar a codificar.
Si lo desea, puede instalar las bibliotecas LLVM manualmente. Para eso solo tienes que apt install llvm-Y
donde Y
es una biblioteca que estás buscando. Sin embargo, recomiendo compilar LLVM usando proyectos con clang.
Una vez que haga eso, debería tener la herramienta llvm-config
. Es muy útil obtener los indicadores del compilador necesarios para la correcta compilación del proyecto LLVM. Entonces, la primera prueba de que funcionó sería llamando
$ llvm-config-4.0 --cxxflags --libs engine
-I/usr/lib/llvm-4.0/include -std=c++0x -gsplit-dwarf -Wl,-fuse-ld=gold -fPIC -fvisibility-inlines-hidden -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wno-maybe-uninitialized -Wdelete-non-virtual-dtor -Wno-comment -Werror=date-time -std=c++11 -ffunction-sections -fdata-sections -O2 -g -DNDEBUG -fno-exceptions -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
-lLLVM-4.0
Puede obtener un conjunto diferente de banderas, no se preocupe por eso. Mientras no falle con comando no encontrado
, debería estar bien.
El siguiente paso es probar la propia biblioteca LLVM real. Así que vamos a crear un archivo llvmtest.cpp
simple:
#include <iostream>
#include "llvm/IR/LLVMContext.h"
int main() {
llvm::LLVMContext context;
std::cout << &context << std::endl;
return 0;
};
Tenga en cuenta que uso std::cout
para que en realidad usemos la variable context
(para que el compilador no la elimine durante la fase de compilación). Ahora compila el archivo con
$ clang++-4.0 -o llvmtest `llvm-config-4.0 --cxxflags --libs engine` llvmtest.cpp
y probarlo
$ ./llvmtest
0x7ffd85500970
¡Felicidades! Está listo para usar LLVM.