llvmDémarrer avec llvm


Remarques

Cette section fournit une vue d'ensemble de ce qu'est llvm et des raisons pour lesquelles un développeur peut vouloir l'utiliser.

Il devrait également mentionner tous les grands sujets dans llvm, et établir un lien avec les sujets connexes. La documentation de llvm étant nouvelle, vous devrez peut-être créer des versions initiales de ces rubriques connexes.

Compilation d'une fonction simple dans llvm 4.0

Donc, ce que nous allons essayer de faire est de compiler une fonction suivante

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

à la volée. Et voici l'exemple entier .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;
}
 

Il devrait fonctionner correctement s'il est compilé avec clang ++ - 4.0 avec les indicateurs suivants:

$ llvm-config-4.0 --cxxflags --libs core
 

Installation ou configuration

Il est toujours recommandé de vous rendre sur le site Web officiel de LLVM et de suivre les guides d'installation en fonction de votre système d'exploitation.

Si vous travaillez sur posix, vous devez ajouter l'un des référentiels officiels de packages LLVM . Par exemple, si vous travaillez sur Ubuntu Xenial (16.04), vous ajoutez une entrée deb et deb-src dans votre fichier /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
 

et une fois que vous faites cela, l'installation est aussi simple que d'appeler

$ sudo apt update
$ sudo apt install clang-X
 

X est la version que vous recherchez (4.0 est à jour au moment de la rédaction de ce post).

Notez que clang est un compilateur C / C ++ écrit sur LLVM (et qui est en réalité auto hébergé maintenant) et qu'il est associé à toutes les bibliothèques LLVM. Une fois que vous faites cela, vous pouvez aller à n'importe quel site et commencer à coder.

Si vous le souhaitez, vous pouvez installer les bibliothèques LLVM manuellement. Pour cela, il vous suffit d' apt install llvm-YY est une bibliothèque que vous recherchez. Cependant, je recommande de compiler LLVM en utilisant des projets avec clang.

Une fois que vous faites cela, vous devriez avoir l'outil llvm-config . Il est très utile d'obtenir les indicateurs du compilateur nécessaires à la compilation de projet LLVM correcte. Donc, le premier test qui a fonctionné serait en appelant

$ 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
 

Vous pouvez obtenir un ensemble de drapeaux différent, ne vous inquiétez pas. Tant que cela n'échoue pas avec la command not found ça devrait aller.

La prochaine étape consiste à tester la bibliothèque LLVM elle-même. Alors, créons un simple fichier llvmtest.cpp :

#include <iostream>
#include "llvm/IR/LLVMContext.h"

int main() {
    llvm::LLVMContext context;
    std::cout << &context << std::endl;
    return 0;
};
 

Notez que j'utilise std::cout pour que nous utilisions la variable context (le compilateur ne le supprimera donc pas pendant la phase de compilation). Maintenant compilez le fichier avec

$ clang++-4.0 -o llvmtest `llvm-config-4.0 --cxxflags --libs engine` llvmtest.cpp
 

et le tester

$ ./llvmtest
0x7ffd85500970
 

Toutes nos félicitations! Vous êtes prêt à utiliser LLVM.