Created
December 14, 2020 13:00
-
-
Save ateska/e048fa6541cc91fb414ae53a86109918 to your computer and use it in GitHub Desktop.
Demo of LLVM in Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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