Skip to content

Instantly share code, notes, and snippets.

@ateska
Created December 14, 2020 13:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ateska/e048fa6541cc91fb414ae53a86109918 to your computer and use it in GitHub Desktop.
Save ateska/e048fa6541cc91fb414ae53a86109918 to your computer and use it in GitHub Desktop.
Demo of LLVM in Python
import sys
from llvmlite import ir
import llvmlite.binding as llvm
import ctypes
# All these initializations are required for code generation!
llvm.initialize()
llvm.initialize_native_target()
llvm.initialize_native_asmprinter() # yes, even this one
int8ptr = ir.IntType(8).as_pointer()
int64 = ir.IntType(64)
v_str_x = b'hello world\n\0'
v_str_const = ir.Constant(
ir.ArrayType(ir.IntType(8), len(v_str_x)),
bytearray(v_str_x)
)
module = ir.Module(name=__file__)
puts_func = ir.Function(
module,
ir.FunctionType(ir.IntType(32), [int8ptr]),
name="puts"
)
main_func = ir.Function(
module,
ir.FunctionType(ir.VoidType(), []),
name="main"
)
v_str_val = ir.GlobalVariable(
module,
v_str_const.type,
name='.str4'
)
v_str_val.linkage = 'internal'
v_str_val.global_constant = True
v_str_val.initializer = v_str_const
main_func_block = main_func.append_basic_block('main')
builder = ir.IRBuilder(main_func_block)
cast210 = builder.gep(v_str_val, [int64(0), int64(0)])
builder.call(puts_func, [cast210])
builder.ret_void()
llvm_ir = str(module)
print(">>", llvm_ir)
# llvm_ir = r"""
# @.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00"
# declare i32 @puts(i8* nocapture) nounwind
# define i32 @main() { ; i32()*
# ; Convert [13 x i8]* to i8 *...
# %cast210 = getelementptr [13 x i8],[13 x i8]* @.str, i64 0, i64 0
# ; Call puts function to write out the string to stdout.
# call i32 @puts(i8* %cast210)
# ret i32 0
# }
# """
def create_execution_engine():
"""
Create an ExecutionEngine suitable for JIT code generation on
the host CPU. The engine is reusable for an arbitrary number of
modules.
"""
# Create a target machine representing the host
target = llvm.Target.from_default_triple()
target_machine = target.create_target_machine()
# And an execution engine with an empty backing module
backing_mod = llvm.parse_assembly("")
engine = llvm.create_mcjit_compiler(backing_mod, target_machine)
return engine
def compile_ir(engine, llvm_ir):
"""
Compile the LLVM IR string with the given engine.
The compiled module object is returned.
"""
# Create a LLVM module object from the IR
mod = llvm.parse_assembly(llvm_ir)
mod.verify()
# Now add the module and make sure it is ready for execution
engine.add_module(mod)
engine.finalize_object()
engine.run_static_constructors()
return mod
engine = create_execution_engine()
mod = compile_ir(engine, llvm_ir)
# Look up the function pointer (a Python int)
func_ptr = engine.get_function_address("main")
print("func_ptr", func_ptr)
# Run the function via ctypes
cfunc = ctypes.CFUNCTYPE(ctypes.c_int32)(func_ptr)
cfunc()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment