llvmAan de slag met llvm


Opmerkingen

Deze sectie geeft een overzicht van wat llvm is en waarom een ontwikkelaar het misschien wil gebruiken.

Het moet ook alle grote onderwerpen binnen llvm vermelden en naar de gerelateerde onderwerpen verwijzen. Aangezien de documentatie voor llvm nieuw is, moet u mogelijk eerste versies van die gerelateerde onderwerpen maken.

Compilatie van een eenvoudige functie in llvm 4.0

We zullen dus proberen een volgende functie te compileren

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

tijdens de vlucht. En hier is het hele .cpp voorbeeld:

#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;
}
 

Het zou goed moeten werken als het gecompileerd is met clang ++ - 4.0 met de volgende vlaggen:

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

Installatie of instellingen

Het wordt altijd aanbevolen om naar de officiële LLVM-website te gaan en de installatiehandleidingen te volgen, afhankelijk van uw besturingssysteem.

Als u aan posix werkt, moet u in het kort een van de officiële LLVM-pakketrepository's toevoegen . Als u bijvoorbeeld aan Ubuntu Xenial (16.04) werkt, voegt u een deb en deb-src item toe aan uw /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
 

en zodra u dat doet, is de installatie net zo eenvoudig als bellen

$ sudo apt update
$ sudo apt install clang-X
 

waarbij X de versie is die u zoekt (4.0 is actueel op het moment van schrijven van dit bericht).

Merk op dat clang een C / C ++ compiler is die is geschreven over LLVM (en nu zelf wordt gehost) en samen met alle LLVM-bibliotheken wordt geleverd. Als je dat eenmaal hebt gedaan, kun je naar elke willekeurige versie gaan en beginnen met coderen.

Als u wilt, kunt u LLVM-bibliotheken handmatig installeren. Daarvoor moet je gewoon apt install llvm-Y waar Y een bibliotheek is die je zoekt. Ik raad echter wel aan om LLVM te compileren met behulp van projecten met clang.

Zodra u dat doet, zou u het llvm-config moeten hebben. Het is erg handig om compilervlaggen te krijgen die nodig zijn voor een correcte LLVM-projectcompilatie. Dus de eerste test die het werkte zou zijn door te bellen

$ 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
 

U kunt een andere set vlaggen krijgen, maak u er geen zorgen over. Zolang het niet faalt met het command not found , zou het goed moeten zijn.

De volgende stap is het testen van de eigenlijke LLVM-bibliotheek zelf. llvmtest.cpp we dus een eenvoudig llvmtest.cpp bestand maken:

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

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

Merk op dat ik std::cout zodat we de context daadwerkelijk gebruiken (zodat de compiler deze niet zal verwijderen tijdens de compilatiefase). Compileer nu het bestand met

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

en test het

$ ./llvmtest
0x7ffd85500970
 

Gefeliciteerd! U bent klaar om LLVM te gebruiken.