llvmpy is a Python binding to LLVM. Since both Python and LLVM are very interesting for me, llvmpy is a cool technology to play with. What follows is a basic description of how to get started with llvmpy.

Tool versions:

  • OS: Ubuntu 12.04 64-bit
  • LLVM: 3.1 release
  • Python: 2.7
  • llvmpy: today's trunk from Github

First of all, download the LLVM 3.1 release sources (or check them out of SVN). Build LLVM as follows:

$ configure --enable-shared=yes --enable-pic --disable-libffi
$ make -j
$ sudo make install

This installs LLVM into the system. You can also install it locally, but then you will have to tell llvmp about it explicitly when building it.

Now build & install llvmpy with:

$ sudo python setup.py install

-------- Update (09.03.2013): LLVM is a moving target, and to follow it llvmpy has to be too. So here's an update for building the latest llvmpy from trunk with LLVM 3.2:

Download LLVM 3.2 and build as before. Updates:

  1. Best to use --prefix to install LLVM somewhere locally
  2. Run make with: REQUIRES_RTTI=1 make -j<N>

Installing llvmpy requires to find llvm-config, so if it's not installed globally use:

$ LLVM_CONFIG_PATH=<path> python setup.py install

This may need sudo if not installing into a virtualenv.

When running actual samples with llvmpy, use LD_LIBRARY_PATH to point to the lib of LLVM (if not installed globally).

-------- End of update

Now we can run some of the examples and tests from the test directory. llvmpy's tests are organized in a weird way, so hooray for the unittest discovery in Python 2.7:

$ cd test
$ python -m unittest discover -vp "*.py"
Ran 36 tests in 0.193s


We can also write a simple example ourselves (this is slightly modified from one of the documentation examples):

from llvm.core import Module, Type, Builder
from llvm.ee import ExecutionEngine, GenericValue

# Create a new module with a function implementing this:
# int sum(int a, int b) {
#   return a + b;
# }
my_module = Module.new('my_module')
ty_int = Type.int()
ty_func = Type.function(ty_int, [ty_int, ty_int])
f_sum = my_module.add_function(ty_func, "sum")
f_sum.args[0].name = "a"
f_sum.args[1].name = "b"
bb = f_sum.append_basic_block("entry")

# IRBuilder for our basic block
builder = Builder.new(bb)
tmp = builder.add(f_sum.args[0], f_sum.args[1], "tmp")

# Create an execution engine object. This will create a JIT compiler
# on platforms that support it, or an interpreter otherwise
ee = ExecutionEngine.new(my_module)

# Each argument needs to be passed as a GenericValue object, which is a kind
# of variant
arg1 = GenericValue.int(ty_int, 100)
arg2 = GenericValue.int(ty_int, 42)

# Now let's compile and run!
retval = ee.run_function(f_sum, [arg1, arg2])

# The return value is also GenericValue. Let's print it.
print "returned", retval.as_int()

This prints 142, as expected. The above 36 lines of commented Python code have created some LLVM IR on the fly, JITed it into a buffer, and executed it.


comments powered by Disqus