llvmEmpezando con llvm


Observaciones

Esta sección proporciona una descripción general de qué es llvm y por qué un desarrollador puede querer usarlo.

También debe mencionar cualquier tema importante dentro de llvm y vincular a los temas relacionados. Dado que la Documentación para llvm es nueva, es posible que deba crear versiones iniciales de los temas relacionados.

Recopilación de una función simple en llvm 4.0

Entonces, lo que intentaremos hacer es compilar una función siguiente

int sum(int a, int b) {
    return a + b + 2;
}
 

sobre la marcha. Y aquí está el ejemplo completo de .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 las siguientes banderas:

$ 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 . 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 haces, 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 está vigente al momento de escribir esta publicación).

Tenga en cuenta que clang es un compilador de C / C ++ escrito sobre LLVM (y en realidad ahora está alojado automáticamente) y viene junto con todas las bibliotecas de LLVM. Una vez que haces eso puedes ir a cualquier turorial y comenzar a codificar.

Si lo desea puede instalar bibliotecas LLVM manualmente. Para eso, solo tiene que apt install llvm-Y donde Y es la biblioteca que está buscando. Sin embargo, sí recomiendo compilar LLVM usando proyectos con clang.

Una vez que hagas eso deberías tener la herramienta llvm-config . Es muy útil obtener los indicadores del compilador necesarios para la compilación correcta del proyecto LLVM. Así que la primera prueba 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 ello. Siempre y cuando no falle con el command not found , debería estar bien.

El siguiente paso es probar la biblioteca LLVM real en sí. Así que vamos a crear un simple archivo llvmtest.cpp :

#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 utilicemos la variable de 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! Usted está listo para usar LLVM.