gpt4 book ai didi

python - 从 LLVM JIT 调用 Python 代码

转载 作者:太空狗 更新时间:2023-10-29 22:27:10 25 4
gpt4 key购买 nike

我用 python 编写了一个语言分析器/解析器/编译器,稍后应该在 LLVM JIT-VM(使用 llvm-py)中运行。前两个步骤现在非常简单,但是(即使我还没有开始编译任务)我看到一个问题,当我的代码想要调用 Python 代码(通常),或者与 Python 词法分析器交互时/parser/compiler(特别是)分别。我主要担心的是,代码应该能够在运行时将额外代码动态加载到 VM 中,因此它必须从 VM 内部触发 Python 中的整个词法分析器/解析器/编译器链。

首先:这是否可能,或者虚拟机启动后是否“不可更改”?

如果是,我目前看到 3 种可能的解决方案(我愿意接受其他建议)

  • “突破”虚拟机并使其可以直接调用主进程的 Python 函数(可能通过将其注册为 LLVM 函数,以某种方式重定向到主进程)。我没有发现任何关于此的信息,而且我不确定这是否是个好主意(安全等)。
  • 将运行时(运行时静态或动态)编译成 LLVM-Assembly/-IR。这要求 IR 代码能够修改它在其中运行的 VM
  • 将运行时(静态)编译成一个库并将其直接加载到 VM 中。同样,它必须能够向其运行的 VM 添加功能(等)。

最佳答案

正如 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);
}

这里有一个小的 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

然后我们从 LLVM 的常用样板开始,但使用 ctypes 将我们的 cbits.so 共享库的共享对象加载到全局进程空间中,这样我们就有了 python_eval符号。然后只需创建一个带有函数的简单 LLVM 模块,使用 ctypes 分配一个带有一些 Python 源代码的字符串,并将指针传递给运行我们模块中的 JIT 函数的 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)

您应该得到以下输出:

=======================================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!

关于python - 从 LLVM JIT 调用 Python 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15169015/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com