ai-content-maker/.venv/Lib/site-packages/llvmlite/binding/executionengine.py

331 lines
11 KiB
Python

import platform
from ctypes import (POINTER, c_char_p, c_bool, c_void_p,
c_int, c_uint64, c_size_t, CFUNCTYPE, string_at, cast,
py_object, Structure)
from llvmlite.binding import ffi, targets, object_file
# Just check these weren't optimized out of the DLL.
ffi.lib.LLVMPY_LinkInMCJIT
def create_mcjit_compiler(module, target_machine, use_lmm=None):
"""
Create a MCJIT ExecutionEngine from the given *module* and
*target_machine*.
*lmm* controls whether the llvmlite memory manager is used. If not supplied,
the default choice for the platform will be used (``True`` on 64-bit ARM
systems, ``False`` otherwise).
"""
if use_lmm is None:
use_lmm = platform.machine() in ('arm64', 'aarch64')
with ffi.OutputString() as outerr:
engine = ffi.lib.LLVMPY_CreateMCJITCompiler(
module, target_machine, use_lmm, outerr)
if not engine:
raise RuntimeError(str(outerr))
target_machine._owned = True
return ExecutionEngine(engine, module=module)
def check_jit_execution():
"""
Check the system allows execution of in-memory JITted functions.
An exception is raised otherwise.
"""
errno = ffi.lib.LLVMPY_TryAllocateExecutableMemory()
if errno != 0:
raise OSError(errno,
"cannot allocate executable memory. "
"This may be due to security restrictions on your "
"system, such as SELinux or similar mechanisms."
)
class ExecutionEngine(ffi.ObjectRef):
"""An ExecutionEngine owns all Modules associated with it.
Deleting the engine will remove all associated modules.
It is an error to delete the associated modules.
"""
_object_cache = None
def __init__(self, ptr, module):
"""
Module ownership is transferred to the EE
"""
self._modules = set([module])
self._td = None
module._owned = True
ffi.ObjectRef.__init__(self, ptr)
def get_function_address(self, name):
"""
Return the address of the function named *name* as an integer.
It's a fatal error in LLVM if the symbol of *name* doesn't exist.
"""
return ffi.lib.LLVMPY_GetFunctionAddress(self, name.encode("ascii"))
def get_global_value_address(self, name):
"""
Return the address of the global value named *name* as an integer.
It's a fatal error in LLVM if the symbol of *name* doesn't exist.
"""
return ffi.lib.LLVMPY_GetGlobalValueAddress(self, name.encode("ascii"))
def add_global_mapping(self, gv, addr):
ffi.lib.LLVMPY_AddGlobalMapping(self, gv, addr)
def add_module(self, module):
"""
Ownership of module is transferred to the execution engine
"""
if module in self._modules:
raise KeyError("module already added to this engine")
ffi.lib.LLVMPY_AddModule(self, module)
module._owned = True
self._modules.add(module)
def finalize_object(self):
"""
Make sure all modules owned by the execution engine are fully processed
and "usable" for execution.
"""
ffi.lib.LLVMPY_FinalizeObject(self)
def run_static_constructors(self):
"""
Run static constructors which initialize module-level static objects.
"""
ffi.lib.LLVMPY_RunStaticConstructors(self)
def run_static_destructors(self):
"""
Run static destructors which perform module-level cleanup of static
resources.
"""
ffi.lib.LLVMPY_RunStaticDestructors(self)
def remove_module(self, module):
"""
Ownership of module is returned
"""
with ffi.OutputString() as outerr:
if ffi.lib.LLVMPY_RemoveModule(self, module, outerr):
raise RuntimeError(str(outerr))
self._modules.remove(module)
module._owned = False
@property
def target_data(self):
"""
The TargetData for this execution engine.
"""
if self._td is not None:
return self._td
ptr = ffi.lib.LLVMPY_GetExecutionEngineTargetData(self)
self._td = targets.TargetData(ptr)
self._td._owned = True
return self._td
def enable_jit_events(self):
"""
Enable JIT events for profiling of generated code.
Return value indicates whether connection to profiling tool
was successful.
"""
ret = ffi.lib.LLVMPY_EnableJITEvents(self)
return ret
def _find_module_ptr(self, module_ptr):
"""
Find the ModuleRef corresponding to the given pointer.
"""
ptr = cast(module_ptr, c_void_p).value
for module in self._modules:
if cast(module._ptr, c_void_p).value == ptr:
return module
return None
def add_object_file(self, obj_file):
"""
Add object file to the jit. object_file can be instance of
:class:ObjectFile or a string representing file system path
"""
if isinstance(obj_file, str):
obj_file = object_file.ObjectFileRef.from_path(obj_file)
ffi.lib.LLVMPY_MCJITAddObjectFile(self, obj_file)
def set_object_cache(self, notify_func=None, getbuffer_func=None):
"""
Set the object cache "notifyObjectCompiled" and "getBuffer"
callbacks to the given Python functions.
"""
self._object_cache_notify = notify_func
self._object_cache_getbuffer = getbuffer_func
# Lifetime of the object cache is managed by us.
self._object_cache = _ObjectCacheRef(self)
# Note this doesn't keep a reference to self, to avoid reference
# cycles.
ffi.lib.LLVMPY_SetObjectCache(self, self._object_cache)
def _raw_object_cache_notify(self, data):
"""
Low-level notify hook.
"""
if self._object_cache_notify is None:
return
module_ptr = data.contents.module_ptr
buf_ptr = data.contents.buf_ptr
buf_len = data.contents.buf_len
buf = string_at(buf_ptr, buf_len)
module = self._find_module_ptr(module_ptr)
if module is None:
# The LLVM EE should only give notifications for modules
# known by us.
raise RuntimeError("object compilation notification "
"for unknown module %s" % (module_ptr,))
self._object_cache_notify(module, buf)
def _raw_object_cache_getbuffer(self, data):
"""
Low-level getbuffer hook.
"""
if self._object_cache_getbuffer is None:
return
module_ptr = data.contents.module_ptr
module = self._find_module_ptr(module_ptr)
if module is None:
# The LLVM EE should only give notifications for modules
# known by us.
raise RuntimeError("object compilation notification "
"for unknown module %s" % (module_ptr,))
buf = self._object_cache_getbuffer(module)
if buf is not None:
# Create a copy, which will be freed by the caller
data[0].buf_ptr = ffi.lib.LLVMPY_CreateByteString(buf, len(buf))
data[0].buf_len = len(buf)
def _dispose(self):
# The modules will be cleaned up by the EE
for mod in self._modules:
mod.detach()
if self._td is not None:
self._td.detach()
self._modules.clear()
self._object_cache = None
self._capi.LLVMPY_DisposeExecutionEngine(self)
class _ObjectCacheRef(ffi.ObjectRef):
"""
Internal: an ObjectCache instance for use within an ExecutionEngine.
"""
def __init__(self, obj):
ptr = ffi.lib.LLVMPY_CreateObjectCache(_notify_c_hook,
_getbuffer_c_hook,
obj)
ffi.ObjectRef.__init__(self, ptr)
def _dispose(self):
self._capi.LLVMPY_DisposeObjectCache(self)
# ============================================================================
# FFI
ffi.lib.LLVMPY_CreateMCJITCompiler.argtypes = [
ffi.LLVMModuleRef,
ffi.LLVMTargetMachineRef,
c_bool,
POINTER(c_char_p),
]
ffi.lib.LLVMPY_CreateMCJITCompiler.restype = ffi.LLVMExecutionEngineRef
ffi.lib.LLVMPY_RemoveModule.argtypes = [
ffi.LLVMExecutionEngineRef,
ffi.LLVMModuleRef,
POINTER(c_char_p),
]
ffi.lib.LLVMPY_RemoveModule.restype = c_bool
ffi.lib.LLVMPY_AddModule.argtypes = [
ffi.LLVMExecutionEngineRef,
ffi.LLVMModuleRef
]
ffi.lib.LLVMPY_AddGlobalMapping.argtypes = [ffi.LLVMExecutionEngineRef,
ffi.LLVMValueRef,
c_void_p]
ffi.lib.LLVMPY_FinalizeObject.argtypes = [ffi.LLVMExecutionEngineRef]
ffi.lib.LLVMPY_GetExecutionEngineTargetData.argtypes = [
ffi.LLVMExecutionEngineRef
]
ffi.lib.LLVMPY_GetExecutionEngineTargetData.restype = ffi.LLVMTargetDataRef
ffi.lib.LLVMPY_TryAllocateExecutableMemory.argtypes = []
ffi.lib.LLVMPY_TryAllocateExecutableMemory.restype = c_int
ffi.lib.LLVMPY_GetFunctionAddress.argtypes = [
ffi.LLVMExecutionEngineRef,
c_char_p
]
ffi.lib.LLVMPY_GetFunctionAddress.restype = c_uint64
ffi.lib.LLVMPY_GetGlobalValueAddress.argtypes = [
ffi.LLVMExecutionEngineRef,
c_char_p
]
ffi.lib.LLVMPY_GetGlobalValueAddress.restype = c_uint64
ffi.lib.LLVMPY_MCJITAddObjectFile.argtypes = [
ffi.LLVMExecutionEngineRef,
ffi.LLVMObjectFileRef
]
class _ObjectCacheData(Structure):
_fields_ = [
('module_ptr', ffi.LLVMModuleRef),
('buf_ptr', c_void_p),
('buf_len', c_size_t),
]
_ObjectCacheNotifyFunc = CFUNCTYPE(None, py_object,
POINTER(_ObjectCacheData))
_ObjectCacheGetBufferFunc = CFUNCTYPE(None, py_object,
POINTER(_ObjectCacheData))
# XXX The ctypes function wrappers are created at the top-level, otherwise
# there are issues when creating CFUNCTYPEs in child processes on CentOS 5
# 32 bits.
_notify_c_hook = _ObjectCacheNotifyFunc(
ExecutionEngine._raw_object_cache_notify)
_getbuffer_c_hook = _ObjectCacheGetBufferFunc(
ExecutionEngine._raw_object_cache_getbuffer)
ffi.lib.LLVMPY_CreateObjectCache.argtypes = [_ObjectCacheNotifyFunc,
_ObjectCacheGetBufferFunc,
py_object]
ffi.lib.LLVMPY_CreateObjectCache.restype = ffi.LLVMObjectCacheRef
ffi.lib.LLVMPY_DisposeObjectCache.argtypes = [ffi.LLVMObjectCacheRef]
ffi.lib.LLVMPY_SetObjectCache.argtypes = [ffi.LLVMExecutionEngineRef,
ffi.LLVMObjectCacheRef]
ffi.lib.LLVMPY_CreateByteString.restype = c_void_p
ffi.lib.LLVMPY_CreateByteString.argtypes = [c_void_p, c_size_t]