Jump to content

  • Log In with Google      Sign In   
  • Create Account






LLVM... it LIVES!

Posted by ApochPiQ, 26 February 2012 · 1,047 views

Epoch LLVM
So I decided that I was tired of not working on the VM side of things, and rigged up a simple test harness for LLVM.

Basically, this creates the following situation:
  • getstuff() is a function defined in the native .EXE which returns a simple integer
  • answer() is a function defined in LLVM bitcode at runtime
  • answer() accepts one integer parameter, adds it to the return value of getstuff(), and returns the result
  • The native .EXE will JIT compile answer() to native code, execute it, and print its return value
All this is pretty simple to do:

int _tmain(int argc, _TCHAR* argv[])
{
    using namespace llvm;

    InitializeNativeTarget();

    LLVMContext& Context = getGlobalContext();

    Module* module = new Module("EpochJIT", Context);

    IRBuilder<> Builder(Context);

    std::map<std::string, Value*> NamedValues;


    std::vector<Type*> args(1, Type::getInt32Ty(Context));
    FunctionType* functype = FunctionType::get(Type::getInt32Ty(Context), args, false);

    Function* func = Function::Create(functype, Function::ExternalLinkage, "answer", module);

    std::vector<std::string> Args;
    Args.push_back("a");

    unsigned Idx = 0;
    for(Function::arg_iterator AI = func->arg_begin(); Idx != Args.size(); ++AI, ++Idx)
    {
        AI->setName(Args[Idx]);
        NamedValues[Args[Idx]] = AI;
    }

    BasicBlock* block = BasicBlock::Create(Context, "entry", func);
    Builder.SetInsertPoint(block);

    std::vector<Type*> noargs;
    FunctionType* getstufffunctype = FunctionType::get(Type::getInt32Ty(Context), noargs, false);
    Function* getstufffunc = Function::Create(getstufffunctype, Function::ExternalLinkage, "getstuff", module);

    Value* stuff = Builder.CreateCall(getstufffunc);

    Value* addition = Builder.CreateAdd(NamedValues["a"], stuff, "addtmp");

    Builder.CreateRet(addition);

    verifyFunction(*func);

    module->dump();

    std::string ErrStr;
    ExecutionEngine* ee = EngineBuilder(module).setErrorStr(&ErrStr).create();
    if(!ee)
    {
        std::cout << ErrStr << std::endl;
        return 1;
    }

    void* fptr = ee->getPointerToFunction(func);
    int (*fp)(int) = (int (*)(int))fptr;

    std::wcout << fp(2) << std::endl;

    return 0;
}

And the results speak for themselves:

Attached Image




Ah, LLVM.. I've played with it a bit, such a quality library and if/when I get around to needing/wanting my own scripting language it is hands down my first choice for the backend.
Yeah, I've been extremely impressed by the library so far. Very clean design and stupid powerful.

I honestly was pleasantly surprised to discover that it's possible to go from AST to JIT'ed native code in a lazy evening's worth of work.
I was going to use it for a BASIC dialect I made a while back but I couldn't figure it out and the documentation wasn't of much help.
:/
I might have to relook at it since you claim that it is so easy to use!
Only problem I had at the time was the release didn't support VS2010 out of the box, but that's long since been fixed.

What I found helpful was following the tutorial series on making their toy language; it was a decent intro to the lib and gave you something to jump from into the docs.
I wanted to mess with LLVM, but don't you have to install 4 GB of stuff just to get it up and running?
Well, the compile of the LLVM project does take up a fair chunk of space when you factor in all the .obj files etc; the final library directory I think I was using was somewhere around 700meg, the 'build' directory does apprently take up 8gb however you do have to weight that against how good the library is and what it can do.

In short; while big that really isn't an issue.

August 2014 »

S M T W T F S
     12
3456789
10111213141516
17181920212223
242526 27 282930
31      
PARTNERS