Jump to content

April 2017 »

S M T W T F S
      1
2345678
9101112131415
16171819202122
232425 26 272829
30      
- - - - -

LLVM... it LIVES!


Epoch LLVM
4: Adsense

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

Feb 27 2012 01:02 PM
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.
Feb 27 2012 01:13 PM
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.
Feb 27 2012 07:11 PM
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!
Feb 27 2012 07:25 PM
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.
Feb 28 2012 02:15 PM
I wanted to mess with LLVM, but don't you have to install 4 GB of stuff just to get it up and running?
Feb 28 2012 06:22 PM
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.

Note: GameDev.net moderates comments.