Kin*_*nch 6 python llvm llvm-py
我在python中编写了一个语言词法分析器/解析器/编译器,它应该llvm-py稍后在LLVM JIT-VM(使用)中运行.前两个步骤现在非常简单,但是(即使我还没有启动编译任务)我看到一个问题,当我的代码想调用Python-Code(一般),或者与Python lexer交互时/ parser/compiler(特殊)分别.我主要担心的是,代码应该能够在运行时动态地将其他代码加载到VM中,因此它必须从VM中触发Python中的整个词法分析器/解析器/编译器链.
首先:这是否可能,或者VM一旦启动就"不可改变"?
如果是,我目前看到3种可能的解决方案(我愿意接受其他建议)
就像Eli所说的那样,并没有阻止你调用Python C-API.当您从LLVM JIT内部调用外部函数时,它实际上仅用于dlopen()进程空间,因此如果您从llvmpy内部运行,您已经可以访问所有Python解释器符号,您甚至可以与调用的活动解释器进行交互ExecutionEngine或者你可以根据需要旋转一个新的Python解释器.
为了帮助您入门,请使用我们的评估程序创建一个新的C文件.
#include <Python.h>
void python_eval(const char* s)
{
PyCodeObject* code = (PyCodeObject*) Py_CompileString(s, "example", Py_file_input);
PyObject* main_module = PyImport_AddModule("__main__");
PyObject* global_dict = PyModule_GetDict(main_module);
PyObject* local_dict = PyDict_New();
PyObject* obj = PyEval_EvalCode(code, global_dict, local_dict);
PyObject* result = PyObject_Str(obj);
// Print the result if you want.
// PyObject_Print(result, stdout, 0);
}
Run Code Online (Sandbox Code Playgroud)
这是一个用于编译的Makefile:
CC = gcc
LPYTHON = $(shell python-config --includes)
CFLAGS = -shared -fPIC -lpthread $(LPYTHON)
.PHONY: all clean
all:
$(CC) $(CFLAGS) cbits.c -o cbits.so
clean:
-rm cbits.c
Run Code Online (Sandbox Code Playgroud)
然后我们从LLVM的常用样板开始,但是使用ctypes将cbits.so共享库的共享对象加载到全局进程空间中,以便我们有python_eval符号.然后只需创建一个带有函数的简单LLVM模块,使用ctypes分配带有一些Python源的字符串,并将指针传递给运行我们模块的JIT'd函数的ExecutionEngine,后者依次将Python源传递给C函数.调用Python C-API然后返回LLVM JIT.
import llvm.core as lc
import llvm.ee as le
import ctypes
import inspect
ctypes._dlopen('./cbits.so', ctypes.RTLD_GLOBAL)
pointer = lc.Type.pointer
i32 = lc.Type.int(32)
i64 = lc.Type.int(64)
char_type = lc.Type.int(8)
string_type = pointer(char_type)
zero = lc.Constant.int(i64, 0)
def build():
mod = lc.Module.new('call python')
evalfn = lc.Function.new(mod,
lc.Type.function(lc.Type.void(),
[string_type], False), "python_eval")
funty = lc.Type.function(lc.Type.void(), [string_type])
fn = lc.Function.new(mod, funty, "call")
fn_arg0 = fn.args[0]
fn_arg0.name = "input"
block = fn.append_basic_block("entry")
builder = lc.Builder.new(block)
builder.call(evalfn, [fn_arg0])
builder.ret_void()
return fn, mod
def run(fn, mod, buf):
tm = le.TargetMachine.new(features='', cm=le.CM_JITDEFAULT)
eb = le.EngineBuilder.new(mod)
engine = eb.create(tm)
ptr = ctypes.cast(buf, ctypes.c_voidp)
ax = le.GenericValue.pointer(ptr.value)
print 'IR'.center(80, '=')
print mod
mod.verify()
print 'Assembly'.center(80, '=')
print mod.to_native_assembly()
print 'Result'.center(80, '=')
engine.run_function(fn, [ax])
if __name__ == '__main__':
# If you want to evaluate the source of an existing function
# source_str = inspect.getsource(mypyfn)
# If you want to pass a source string
source_str = "print 'Hello from Python C-API inside of LLVM!'"
buf = ctypes.create_string_buffer(source_str)
fn, mod = build()
run(fn, mod, buf)
Run Code Online (Sandbox Code Playgroud)
您应该输出以下内容:
=======================================IR=======================================
; ModuleID = 'call python'
declare void @python_eval(i8*)
define void @call(i8* %input) {
entry:
call void @python_eval(i8* %input)
ret void
}
=====================================Result=====================================
Hello from Python C-API inside of LLVM!
Run Code Online (Sandbox Code Playgroud)
您可以从 LLVM JIT 代码调用外部 C 函数。你还需要什么?
这些外部函数将在执行过程中找到,这意味着如果您将Python链接到VM中,您就可以调用Python的C API函数。
“VM”可能没有您想象的那么神奇:-) 最后,它只是在运行时发送到缓冲区并从那里执行的机器代码。只要该代码可以访问其运行的进程中的其他符号,它就可以执行该进程中任何其他代码可以执行的所有操作。