Videre
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Things that rely on the LLVM library
|
||||
"""
|
||||
from .dylib import *
|
||||
from .executionengine import *
|
||||
from .initfini import *
|
||||
from .linker import *
|
||||
from .module import *
|
||||
from .options import *
|
||||
from .newpassmanagers import *
|
||||
from .targets import *
|
||||
from .value import *
|
||||
from .typeref import *
|
||||
from .analysis import *
|
||||
from .object_file import *
|
||||
from .context import *
|
||||
from .orcjit import *
|
||||
from .config import *
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
A collection of analysis utilities
|
||||
"""
|
||||
|
||||
from ctypes import POINTER, c_char_p, c_int
|
||||
|
||||
from llvmlite.binding import ffi
|
||||
from llvmlite.binding.module import parse_assembly
|
||||
|
||||
|
||||
def get_function_cfg(func, show_inst=True):
|
||||
"""Return a string of the control-flow graph of the function in DOT
|
||||
format. If the input `func` is not a materialized function, the module
|
||||
containing the function is parsed to create an actual LLVM module.
|
||||
The `show_inst` flag controls whether the instructions of each block
|
||||
are printed.
|
||||
"""
|
||||
assert func is not None
|
||||
from llvmlite import ir
|
||||
if isinstance(func, ir.Function):
|
||||
mod = parse_assembly(str(func.module))
|
||||
func = mod.get_function(func.name)
|
||||
|
||||
# Assume func is a materialized function
|
||||
with ffi.OutputString() as dotstr:
|
||||
ffi.lib.LLVMPY_WriteCFG(func, dotstr, show_inst)
|
||||
return str(dotstr)
|
||||
|
||||
|
||||
def view_dot_graph(graph, filename=None, view=False):
|
||||
"""
|
||||
View the given DOT source. If view is True, the image is rendered
|
||||
and viewed by the default application in the system. The file path of
|
||||
the output is returned. If view is False, a graphviz.Source object is
|
||||
returned. If view is False and the environment is in a IPython session,
|
||||
an IPython image object is returned and can be displayed inline in the
|
||||
notebook.
|
||||
|
||||
This function requires the graphviz package.
|
||||
|
||||
Args
|
||||
----
|
||||
- graph [str]: a DOT source code
|
||||
- filename [str]: optional. if given and view is True, this specifies
|
||||
the file path for the rendered output to write to.
|
||||
- view [bool]: if True, opens the rendered output file.
|
||||
|
||||
"""
|
||||
# Optionally depends on graphviz package
|
||||
import graphviz as gv
|
||||
|
||||
src = gv.Source(graph)
|
||||
if view:
|
||||
# Returns the output file path
|
||||
return src.render(filename, view=view)
|
||||
else:
|
||||
# Attempts to show the graph in IPython notebook
|
||||
try:
|
||||
__IPYTHON__
|
||||
except NameError:
|
||||
return src
|
||||
else:
|
||||
import IPython.display as display
|
||||
format = 'svg'
|
||||
return display.SVG(data=src.pipe(format))
|
||||
|
||||
|
||||
# Ctypes binding
|
||||
ffi.lib.LLVMPY_WriteCFG.argtypes = [ffi.LLVMValueRef, POINTER(c_char_p), c_int]
|
||||
@@ -0,0 +1,34 @@
|
||||
import atexit
|
||||
|
||||
|
||||
def _encode_string(s):
|
||||
encoded = s.encode('utf-8')
|
||||
return encoded
|
||||
|
||||
|
||||
def _decode_string(b):
|
||||
return b.decode('utf-8')
|
||||
|
||||
|
||||
_encode_string.__doc__ = """Encode a string for use by LLVM."""
|
||||
_decode_string.__doc__ = """Decode a LLVM character (byte)string."""
|
||||
|
||||
|
||||
_shutting_down = [False]
|
||||
|
||||
|
||||
def _at_shutdown():
|
||||
_shutting_down[0] = True
|
||||
|
||||
|
||||
atexit.register(_at_shutdown)
|
||||
|
||||
|
||||
def _is_shutting_down(_shutting_down=_shutting_down):
|
||||
"""
|
||||
Whether the interpreter is currently shutting down.
|
||||
For use in finalizers, __del__ methods, and similar; it is advised
|
||||
to early bind this function rather than look it up when calling it,
|
||||
since at shutdown module globals may be cleared.
|
||||
"""
|
||||
return _shutting_down[0]
|
||||
@@ -0,0 +1,143 @@
|
||||
import os
|
||||
import warnings
|
||||
from functools import cache
|
||||
from ctypes import c_int, c_char_p
|
||||
from llvmlite.binding import ffi
|
||||
|
||||
# these are here as they cannot be lazy bound as the module globals make calls
|
||||
# to the LLVMPY API functions
|
||||
ffi.lib.LLVMPY_HasSVMLSupport.argtypes = ()
|
||||
ffi.lib.LLVMPY_HasSVMLSupport.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_IsStaticLibstdcxxLinkageBuild.argtypes = ()
|
||||
ffi.lib.LLVMPY_IsStaticLibstdcxxLinkageBuild.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_IsDynamicLLVMLinkageBuild.argtypes = ()
|
||||
ffi.lib.LLVMPY_IsDynamicLLVMLinkageBuild.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_PackageFormat.argtypes = ()
|
||||
ffi.lib.LLVMPY_PackageFormat.restype = c_char_p
|
||||
|
||||
ffi.lib.LLVMPY_LlvmAssertionsState.argtypes = ()
|
||||
ffi.lib.LLVMPY_LlvmAssertionsState.restype = c_char_p
|
||||
|
||||
|
||||
def _has_svml():
|
||||
"""
|
||||
Returns True if SVML was enabled at FFI support compile time.
|
||||
"""
|
||||
if ffi.lib.LLVMPY_HasSVMLSupport() == 0:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
has_svml = _has_svml()
|
||||
|
||||
|
||||
def _build_llvm_linkage_type():
|
||||
"""
|
||||
Returns "static" if the FFI support is statically linked against LLVM,
|
||||
returns "dynamic" otherwise.
|
||||
"""
|
||||
if ffi.lib.LLVMPY_IsDynamicLLVMLinkageBuild() == 0:
|
||||
return "static"
|
||||
else:
|
||||
return "dynamic"
|
||||
|
||||
|
||||
build_llvm_linkage_type = _build_llvm_linkage_type()
|
||||
|
||||
|
||||
def _build_libstdcxx_linkage_type():
|
||||
"""
|
||||
Returns "static" if the FFI support is statically linked against libstdc++,
|
||||
returns "dynamic" otherwise.
|
||||
"""
|
||||
if ffi.lib.LLVMPY_IsStaticLibstdcxxLinkageBuild() == 1:
|
||||
return "static"
|
||||
else:
|
||||
return "dynamic"
|
||||
|
||||
|
||||
build_libstdcxx_linkage_type = _build_libstdcxx_linkage_type()
|
||||
|
||||
|
||||
def _package_format():
|
||||
"""
|
||||
Returns "wheel", "conda" or "unspecified"
|
||||
"""
|
||||
return ffi.lib.LLVMPY_PackageFormat().decode()
|
||||
|
||||
|
||||
package_format = _package_format()
|
||||
|
||||
|
||||
def _llvm_assertions_state():
|
||||
"""
|
||||
Returns one of "on", "off" or "unknown". Depending on whether it is
|
||||
determined that LLVM was build with assertions on, off, or is not known.
|
||||
"Is not known" is typically from a dynamic linkage against LLVM in which
|
||||
case it's not easily identified whether LLVM was built with assertions.
|
||||
"""
|
||||
return ffi.lib.LLVMPY_LlvmAssertionsState().decode()
|
||||
|
||||
|
||||
llvm_assertions_state = _llvm_assertions_state()
|
||||
|
||||
|
||||
@cache
|
||||
def get_sysinfo():
|
||||
d = dict()
|
||||
d["ffi_lib_location"] = ffi.lib._name
|
||||
d["package_format"] = package_format
|
||||
d["llvm_linkage_type"] = build_llvm_linkage_type
|
||||
d["libstdcxx_linkage_type"] = build_libstdcxx_linkage_type
|
||||
d["llvm_assertions_state"] = llvm_assertions_state
|
||||
|
||||
# import lief
|
||||
HAVE_LIEF = False
|
||||
try:
|
||||
import lief
|
||||
HAVE_LIEF = True
|
||||
except ImportError:
|
||||
msg = "py-lief package not found, sysinfo is limited as a result"
|
||||
warnings.warn(msg)
|
||||
|
||||
d["lief_probe_status"] = HAVE_LIEF
|
||||
d["linked_libraries"] = None
|
||||
d["canonicalised_linked_libraries"] = None
|
||||
|
||||
def canonicalise_library_type(dso):
|
||||
"""Canonicalises the representation of the binary::libraries as a
|
||||
sequence of strings"""
|
||||
# Note lief v16:
|
||||
# Mach-O .libraries are DylibCommand instances.
|
||||
# Windows PE and Linux ELF .libraries are strings.
|
||||
return [getattr(x, "name", x) for x in dso.libraries]
|
||||
|
||||
def canonicalise_library_spelling(libs):
|
||||
# This adjusts the library "spelling" so that it just contains the
|
||||
# name given to the linker. e.g. `@rpath/somewhere/libfoo.so.1.3`
|
||||
# would be canonicalised to "foo".
|
||||
fixes = []
|
||||
for lib in libs:
|
||||
# some libraries, e.g. Mach-O have an @rpath or system path
|
||||
# prefix in their name, remove it.
|
||||
path_stripped = os.path.split(lib)[-1]
|
||||
# Assume all library names contain at least one dot, even if they
|
||||
# don't it's fine, the first part is the piece of interest.
|
||||
prefix_libname = path_stripped.split(".")[0]
|
||||
linker_name = prefix_libname.replace("lib", "").replace("LIB", "")
|
||||
# further canonicalize by referring to all libraries in lower case.
|
||||
fixes.append(linker_name.lower())
|
||||
return fixes
|
||||
|
||||
if HAVE_LIEF:
|
||||
dso = lief.parse(d["ffi_lib_location"])
|
||||
link_libs = tuple(canonicalise_library_type(dso))
|
||||
d["linked_libraries"] = link_libs
|
||||
canonicalised_libs = canonicalise_library_spelling(link_libs)
|
||||
d["canonicalised_linked_libraries"] = canonicalised_libs
|
||||
|
||||
return d
|
||||
@@ -0,0 +1,31 @@
|
||||
from llvmlite.binding import ffi
|
||||
|
||||
|
||||
def create_context():
|
||||
return ContextRef(
|
||||
ffi.lib.LLVMPY_ContextCreate())
|
||||
|
||||
|
||||
def get_global_context():
|
||||
return GlobalContextRef(
|
||||
ffi.lib.LLVMPY_GetGlobalContext())
|
||||
|
||||
|
||||
class ContextRef(ffi.ObjectRef):
|
||||
def __init__(self, context_ptr):
|
||||
super(ContextRef, self).__init__(context_ptr)
|
||||
|
||||
def _dispose(self):
|
||||
ffi.lib.LLVMPY_ContextDispose(self)
|
||||
|
||||
|
||||
class GlobalContextRef(ContextRef):
|
||||
def _dispose(self):
|
||||
pass
|
||||
|
||||
|
||||
ffi.lib.LLVMPY_GetGlobalContext.restype = ffi.LLVMContextRef
|
||||
|
||||
ffi.lib.LLVMPY_ContextCreate.restype = ffi.LLVMContextRef
|
||||
|
||||
ffi.lib.LLVMPY_ContextDispose.argtypes = [ffi.LLVMContextRef]
|
||||
@@ -0,0 +1,45 @@
|
||||
from ctypes import c_void_p, c_char_p, c_bool, POINTER
|
||||
|
||||
from llvmlite.binding import ffi
|
||||
from llvmlite.binding.common import _encode_string
|
||||
|
||||
|
||||
def address_of_symbol(name):
|
||||
"""
|
||||
Get the in-process address of symbol named *name*.
|
||||
An integer is returned, or None if the symbol isn't found.
|
||||
"""
|
||||
return ffi.lib.LLVMPY_SearchAddressOfSymbol(_encode_string(name))
|
||||
|
||||
|
||||
def add_symbol(name, address):
|
||||
"""
|
||||
Register the *address* of global symbol *name*. This will make
|
||||
it usable (e.g. callable) from LLVM-compiled functions.
|
||||
"""
|
||||
ffi.lib.LLVMPY_AddSymbol(_encode_string(name), c_void_p(address))
|
||||
|
||||
|
||||
def load_library_permanently(filename):
|
||||
"""
|
||||
Load an external library
|
||||
"""
|
||||
with ffi.OutputString() as outerr:
|
||||
if ffi.lib.LLVMPY_LoadLibraryPermanently(
|
||||
_encode_string(filename), outerr):
|
||||
raise RuntimeError(str(outerr))
|
||||
|
||||
# ============================================================================
|
||||
# FFI
|
||||
|
||||
|
||||
ffi.lib.LLVMPY_AddSymbol.argtypes = [
|
||||
c_char_p,
|
||||
c_void_p,
|
||||
]
|
||||
|
||||
ffi.lib.LLVMPY_SearchAddressOfSymbol.argtypes = [c_char_p]
|
||||
ffi.lib.LLVMPY_SearchAddressOfSymbol.restype = c_void_p
|
||||
|
||||
ffi.lib.LLVMPY_LoadLibraryPermanently.argtypes = [c_char_p, POINTER(c_char_p)]
|
||||
ffi.lib.LLVMPY_LoadLibraryPermanently.restype = c_bool
|
||||
@@ -0,0 +1,330 @@
|
||||
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]
|
||||
@@ -0,0 +1,410 @@
|
||||
import sys
|
||||
import ctypes
|
||||
import threading
|
||||
import importlib.resources as _impres
|
||||
|
||||
from llvmlite.binding.common import _decode_string, _is_shutting_down
|
||||
from llvmlite.utils import get_library_name
|
||||
|
||||
|
||||
def _make_opaque_ref(name):
|
||||
newcls = type(name, (ctypes.Structure,), {})
|
||||
return ctypes.POINTER(newcls)
|
||||
|
||||
|
||||
LLVMContextRef = _make_opaque_ref("LLVMContext")
|
||||
LLVMModuleRef = _make_opaque_ref("LLVMModule")
|
||||
LLVMValueRef = _make_opaque_ref("LLVMValue")
|
||||
LLVMTypeRef = _make_opaque_ref("LLVMType")
|
||||
LLVMExecutionEngineRef = _make_opaque_ref("LLVMExecutionEngine")
|
||||
LLVMPassManagerBuilderRef = _make_opaque_ref("LLVMPassManagerBuilder")
|
||||
LLVMPassManagerRef = _make_opaque_ref("LLVMPassManager")
|
||||
LLVMTargetDataRef = _make_opaque_ref("LLVMTargetData")
|
||||
LLVMTargetLibraryInfoRef = _make_opaque_ref("LLVMTargetLibraryInfo")
|
||||
LLVMTargetRef = _make_opaque_ref("LLVMTarget")
|
||||
LLVMTargetMachineRef = _make_opaque_ref("LLVMTargetMachine")
|
||||
LLVMMemoryBufferRef = _make_opaque_ref("LLVMMemoryBuffer")
|
||||
LLVMAttributeListIterator = _make_opaque_ref("LLVMAttributeListIterator")
|
||||
LLVMElementIterator = _make_opaque_ref("LLVMElementIterator")
|
||||
LLVMAttributeSetIterator = _make_opaque_ref("LLVMAttributeSetIterator")
|
||||
LLVMGlobalsIterator = _make_opaque_ref("LLVMGlobalsIterator")
|
||||
LLVMFunctionsIterator = _make_opaque_ref("LLVMFunctionsIterator")
|
||||
LLVMBlocksIterator = _make_opaque_ref("LLVMBlocksIterator")
|
||||
LLVMArgumentsIterator = _make_opaque_ref("LLVMArgumentsIterator")
|
||||
LLVMInstructionsIterator = _make_opaque_ref("LLVMInstructionsIterator")
|
||||
LLVMOperandsIterator = _make_opaque_ref("LLVMOperandsIterator")
|
||||
LLVMIncomingBlocksIterator = _make_opaque_ref("LLVMIncomingBlocksIterator")
|
||||
LLVMTypesIterator = _make_opaque_ref("LLVMTypesIterator")
|
||||
LLVMObjectCacheRef = _make_opaque_ref("LLVMObjectCache")
|
||||
LLVMObjectFileRef = _make_opaque_ref("LLVMObjectFile")
|
||||
LLVMSectionIteratorRef = _make_opaque_ref("LLVMSectionIterator")
|
||||
LLVMOrcLLJITRef = _make_opaque_ref("LLVMOrcLLJITRef")
|
||||
LLVMOrcDylibTrackerRef = _make_opaque_ref("LLVMOrcDylibTrackerRef")
|
||||
LLVMTimePassesHandlerRef = _make_opaque_ref("LLVMTimePassesHandler")
|
||||
LLVMPipelineTuningOptionsRef = _make_opaque_ref("LLVMPipeLineTuningOptions")
|
||||
LLVMModulePassManagerRef = _make_opaque_ref("LLVMModulePassManager")
|
||||
LLVMFunctionPassManagerRef = _make_opaque_ref("LLVMFunctionPassManager")
|
||||
LLVMPassBuilderRef = _make_opaque_ref("LLVMPassBuilder")
|
||||
|
||||
|
||||
class _LLVMLock:
|
||||
"""A Lock to guarantee thread-safety for the LLVM C-API.
|
||||
|
||||
This class implements __enter__ and __exit__ for acquiring and releasing
|
||||
the lock as a context manager.
|
||||
|
||||
Also, callbacks can be attached so that every time the lock is acquired
|
||||
and released the corresponding callbacks will be invoked.
|
||||
"""
|
||||
def __init__(self):
|
||||
# The reentrant lock is needed for callbacks that re-enter
|
||||
# the Python interpreter.
|
||||
self._lock = threading.RLock()
|
||||
self._cblist = []
|
||||
|
||||
def register(self, acq_fn, rel_fn):
|
||||
"""Register callbacks that are invoked immediately after the lock is
|
||||
acquired (``acq_fn()``) and immediately before the lock is released
|
||||
(``rel_fn()``).
|
||||
"""
|
||||
self._cblist.append((acq_fn, rel_fn))
|
||||
|
||||
def unregister(self, acq_fn, rel_fn):
|
||||
"""Remove the registered callbacks.
|
||||
"""
|
||||
self._cblist.remove((acq_fn, rel_fn))
|
||||
|
||||
def __enter__(self):
|
||||
self._lock.acquire()
|
||||
# Invoke all callbacks
|
||||
for acq_fn, rel_fn in self._cblist:
|
||||
acq_fn()
|
||||
|
||||
def __exit__(self, *exc_details):
|
||||
# Invoke all callbacks
|
||||
for acq_fn, rel_fn in self._cblist:
|
||||
rel_fn()
|
||||
self._lock.release()
|
||||
|
||||
|
||||
class _suppress_cleanup_errors:
|
||||
def __init__(self, context):
|
||||
self._context = context
|
||||
|
||||
def __enter__(self):
|
||||
return self._context.__enter__()
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
try:
|
||||
return self._context.__exit__(exc_type, exc_value, traceback)
|
||||
except PermissionError:
|
||||
pass # Resource dylibs can't be deleted on Windows.
|
||||
|
||||
|
||||
class _lib_wrapper(object):
|
||||
"""Wrap libllvmlite with a lock such that only one thread may access it at
|
||||
a time.
|
||||
|
||||
This class duck-types a CDLL.
|
||||
"""
|
||||
__slots__ = ['_lib_handle', '_fntab', '_lock']
|
||||
|
||||
def __init__(self):
|
||||
self._lib_handle = None
|
||||
self._fntab = {}
|
||||
self._lock = _LLVMLock()
|
||||
|
||||
def _load_lib(self):
|
||||
test_sym = "LLVMPY_GetVersionInfo"
|
||||
mod_name = __name__.rpartition(".")[0]
|
||||
lib_name = get_library_name()
|
||||
|
||||
with _suppress_cleanup_errors(_importlib_resources_path(
|
||||
mod_name, lib_name)) as lib_path:
|
||||
try:
|
||||
self._lib_handle = ctypes.CDLL(str(lib_path))
|
||||
# Check that we can look up expected symbols.
|
||||
getattr(self._lib_handle, test_sym)()
|
||||
except OSError:
|
||||
# OSError may be raised if the file cannot be opened, or is not
|
||||
# a shared library.
|
||||
msg = (f"Could not find/load shared object file '{lib_name}' "
|
||||
f"from resource location: '{mod_name}'. This could mean "
|
||||
"that the library literally cannot be found, but may "
|
||||
"also mean that the permissions are incorrect or that a "
|
||||
"dependency of/a symbol in the library could not be "
|
||||
"resolved.")
|
||||
raise OSError(msg)
|
||||
except AttributeError:
|
||||
# AttributeError is raised if the test_sym symbol does not
|
||||
# exist.
|
||||
msg = ("During testing of symbol lookup, the symbol "
|
||||
f"'{test_sym}' could not be found in the library "
|
||||
f"'{lib_path}'")
|
||||
raise OSError(msg)
|
||||
|
||||
@property
|
||||
def _lib(self):
|
||||
# Not threadsafe.
|
||||
if not self._lib_handle:
|
||||
self._load_lib()
|
||||
return self._lib_handle
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self._fntab[name]
|
||||
except KeyError:
|
||||
pass
|
||||
# Lazily wraps new functions as they are requested
|
||||
cfn = getattr(self._lib, name)
|
||||
wrapped = _lib_fn_wrapper(self._lock, cfn)
|
||||
self._fntab[name] = wrapped
|
||||
return wrapped
|
||||
|
||||
@property
|
||||
def _name(self):
|
||||
"""The name of the library passed in the CDLL constructor.
|
||||
|
||||
For duck-typing a ctypes.CDLL
|
||||
"""
|
||||
return self._lib._name
|
||||
|
||||
@property
|
||||
def _handle(self):
|
||||
"""The system handle used to access the library.
|
||||
|
||||
For duck-typing a ctypes.CDLL
|
||||
"""
|
||||
return self._lib._handle
|
||||
|
||||
|
||||
class _lib_fn_wrapper(object):
|
||||
"""Wraps and duck-types a ctypes.CFUNCTYPE to provide
|
||||
automatic locking when the wrapped function is called.
|
||||
|
||||
TODO: we can add methods to mark the function as threadsafe
|
||||
and remove the locking-step on call when marked.
|
||||
"""
|
||||
__slots__ = ['_lock', '_cfn']
|
||||
|
||||
def __init__(self, lock, cfn):
|
||||
self._lock = lock
|
||||
self._cfn = cfn
|
||||
|
||||
@property
|
||||
def argtypes(self):
|
||||
return self._cfn.argtypes
|
||||
|
||||
@argtypes.setter
|
||||
def argtypes(self, argtypes):
|
||||
self._cfn.argtypes = argtypes
|
||||
|
||||
@property
|
||||
def restype(self):
|
||||
return self._cfn.restype
|
||||
|
||||
@restype.setter
|
||||
def restype(self, restype):
|
||||
self._cfn.restype = restype
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
with self._lock:
|
||||
return self._cfn(*args, **kwargs)
|
||||
|
||||
|
||||
def _importlib_resources_path_repl(package, resource):
|
||||
"""Replacement implementation of `import.resources.path` to avoid
|
||||
deprecation warning following code at importlib_resources/_legacy.py
|
||||
as suggested by https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy
|
||||
|
||||
Notes on differences from importlib.resources implementation:
|
||||
|
||||
The `_common.normalize_path(resource)` call is skipped because it is an
|
||||
internal API and it is unnecessary for the use here. What it does is
|
||||
ensuring `resource` is a str and that it does not contain path separators.
|
||||
""" # noqa E501
|
||||
return _impres.as_file(_impres.files(package) / resource)
|
||||
|
||||
|
||||
_importlib_resources_path = (_importlib_resources_path_repl
|
||||
if sys.version_info[:2] >= (3, 10)
|
||||
else _impres.path)
|
||||
|
||||
|
||||
lib = _lib_wrapper()
|
||||
|
||||
|
||||
def register_lock_callback(acq_fn, rel_fn):
|
||||
"""Register callback functions for lock acquire and release.
|
||||
*acq_fn* and *rel_fn* are callables that take no arguments.
|
||||
"""
|
||||
lib._lock.register(acq_fn, rel_fn)
|
||||
|
||||
|
||||
def unregister_lock_callback(acq_fn, rel_fn):
|
||||
"""Remove the registered callback functions for lock acquire and release.
|
||||
The arguments are the same as used in `register_lock_callback()`.
|
||||
"""
|
||||
lib._lock.unregister(acq_fn, rel_fn)
|
||||
|
||||
|
||||
class _DeadPointer(object):
|
||||
"""
|
||||
Dummy class to make error messages more helpful.
|
||||
"""
|
||||
|
||||
|
||||
class OutputString(object):
|
||||
"""
|
||||
Object for managing the char* output of LLVM APIs.
|
||||
"""
|
||||
_as_parameter_ = _DeadPointer()
|
||||
|
||||
@classmethod
|
||||
def from_return(cls, ptr):
|
||||
"""Constructing from a pointer returned from the C-API.
|
||||
The pointer must be allocated with LLVMPY_CreateString.
|
||||
|
||||
Note
|
||||
----
|
||||
Because ctypes auto-converts *restype* of *c_char_p* into a python
|
||||
string, we must use *c_void_p* to obtain the raw pointer.
|
||||
"""
|
||||
return cls(init=ctypes.cast(ptr, ctypes.c_char_p))
|
||||
|
||||
def __init__(self, owned=True, init=None):
|
||||
self._ptr = init if init is not None else ctypes.c_char_p(None)
|
||||
self._as_parameter_ = ctypes.byref(self._ptr)
|
||||
self._owned = owned
|
||||
|
||||
def close(self):
|
||||
if self._ptr is not None:
|
||||
if self._owned:
|
||||
lib.LLVMPY_DisposeString(self._ptr)
|
||||
self._ptr = None
|
||||
del self._as_parameter_
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
|
||||
def __del__(self, _is_shutting_down=_is_shutting_down):
|
||||
# Avoid errors trying to rely on globals and modules at interpreter
|
||||
# shutdown.
|
||||
if not _is_shutting_down():
|
||||
if self.close is not None:
|
||||
self.close()
|
||||
|
||||
def __str__(self):
|
||||
if self._ptr is None:
|
||||
return "<dead OutputString>"
|
||||
s = self._ptr.value
|
||||
assert s is not None
|
||||
return _decode_string(s)
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self._ptr)
|
||||
|
||||
__nonzero__ = __bool__
|
||||
|
||||
@property
|
||||
def bytes(self):
|
||||
"""Get the raw bytes of content of the char pointer.
|
||||
"""
|
||||
return self._ptr.value
|
||||
|
||||
|
||||
def ret_string(ptr):
|
||||
"""To wrap string return-value from C-API.
|
||||
"""
|
||||
if ptr is not None:
|
||||
return str(OutputString.from_return(ptr))
|
||||
|
||||
|
||||
def ret_bytes(ptr):
|
||||
"""To wrap bytes return-value from C-API.
|
||||
"""
|
||||
if ptr is not None:
|
||||
return OutputString.from_return(ptr).bytes
|
||||
|
||||
|
||||
class ObjectRef(object):
|
||||
"""
|
||||
A wrapper around a ctypes pointer to a LLVM object ("resource").
|
||||
"""
|
||||
_closed = False
|
||||
_as_parameter_ = _DeadPointer()
|
||||
# Whether this object pointer is owned by another one.
|
||||
_owned = False
|
||||
|
||||
def __init__(self, ptr):
|
||||
if ptr is None:
|
||||
raise ValueError("NULL pointer")
|
||||
self._ptr = ptr
|
||||
self._as_parameter_ = ptr
|
||||
self._capi = lib
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close this object and do any required clean-up actions.
|
||||
"""
|
||||
try:
|
||||
if not self._closed and not self._owned:
|
||||
self._dispose()
|
||||
finally:
|
||||
self.detach()
|
||||
|
||||
def detach(self):
|
||||
"""
|
||||
Detach the underlying LLVM resource without disposing of it.
|
||||
"""
|
||||
if not self._closed:
|
||||
del self._as_parameter_
|
||||
self._closed = True
|
||||
self._ptr = None
|
||||
|
||||
def _dispose(self):
|
||||
"""
|
||||
Dispose of the underlying LLVM resource. Should be overriden
|
||||
by subclasses. Automatically called by close(), __del__() and
|
||||
__exit__() (unless the resource has been detached).
|
||||
"""
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
"""
|
||||
Whether this object has been closed. A closed object can't
|
||||
be used anymore.
|
||||
"""
|
||||
return self._closed
|
||||
|
||||
def __enter__(self):
|
||||
assert hasattr(self, "close")
|
||||
if self._closed:
|
||||
raise RuntimeError("%s instance already closed" % (self.__class__,))
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
|
||||
def __del__(self, _is_shutting_down=_is_shutting_down):
|
||||
if not _is_shutting_down():
|
||||
if self.close is not None:
|
||||
self.close()
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self._ptr)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not hasattr(other, "_ptr"):
|
||||
return False
|
||||
return ctypes.addressof(self._ptr[0]) == \
|
||||
ctypes.addressof(other._ptr[0])
|
||||
|
||||
__nonzero__ = __bool__
|
||||
|
||||
# XXX useful?
|
||||
def __hash__(self):
|
||||
return hash(ctypes.cast(self._ptr, ctypes.c_void_p).value)
|
||||
@@ -0,0 +1,85 @@
|
||||
from ctypes import c_uint
|
||||
|
||||
from llvmlite.binding import ffi
|
||||
|
||||
|
||||
def initialize():
|
||||
"""
|
||||
Initialize the LLVM core (deprecated).
|
||||
|
||||
This function is deprecated and will raise an error when called.
|
||||
LLVM initialization is now handled automatically and no longer
|
||||
requires explicit initialization calls.
|
||||
|
||||
Raises:
|
||||
RuntimeError: Always raised as this function is no longer needed.
|
||||
"""
|
||||
raise RuntimeError(
|
||||
"llvmlite.binding.initialize() is deprecated and will be removed. "
|
||||
"LLVM initialization is now handled automatically. "
|
||||
"Please remove calls to this function from your code and check for "
|
||||
"other behavioral changes that may have occurred due to LLVM updates."
|
||||
)
|
||||
|
||||
|
||||
def initialize_all_targets():
|
||||
"""
|
||||
Initialize all targets. Necessary before targets can be looked up
|
||||
via the :class:`Target` class.
|
||||
"""
|
||||
ffi.lib.LLVMPY_InitializeAllTargetInfos()
|
||||
ffi.lib.LLVMPY_InitializeAllTargets()
|
||||
ffi.lib.LLVMPY_InitializeAllTargetMCs()
|
||||
|
||||
|
||||
def initialize_all_asmprinters():
|
||||
"""
|
||||
Initialize all code generators. Necessary before generating
|
||||
any assembly or machine code via the :meth:`TargetMachine.emit_object`
|
||||
and :meth:`TargetMachine.emit_assembly` methods.
|
||||
"""
|
||||
ffi.lib.LLVMPY_InitializeAllAsmPrinters()
|
||||
|
||||
|
||||
def initialize_native_target():
|
||||
"""
|
||||
Initialize the native (host) target. Necessary before doing any
|
||||
code generation.
|
||||
"""
|
||||
ffi.lib.LLVMPY_InitializeNativeTarget()
|
||||
|
||||
|
||||
def initialize_native_asmprinter():
|
||||
"""
|
||||
Initialize the native ASM printer.
|
||||
"""
|
||||
ffi.lib.LLVMPY_InitializeNativeAsmPrinter()
|
||||
|
||||
|
||||
def initialize_native_asmparser():
|
||||
"""
|
||||
Initialize the native ASM parser.
|
||||
"""
|
||||
ffi.lib.LLVMPY_InitializeNativeAsmParser()
|
||||
|
||||
|
||||
def shutdown():
|
||||
ffi.lib.LLVMPY_Shutdown()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Set function FFI
|
||||
|
||||
ffi.lib.LLVMPY_GetVersionInfo.restype = c_uint
|
||||
|
||||
|
||||
def _version_info():
|
||||
v = []
|
||||
x = ffi.lib.LLVMPY_GetVersionInfo()
|
||||
while x:
|
||||
v.append(x & 0xff)
|
||||
x >>= 8
|
||||
return tuple(reversed(v))
|
||||
|
||||
|
||||
llvm_version_info = _version_info()
|
||||
BIN
linedance-app/venv/lib/python3.12/site-packages/llvmlite/binding/libllvmlite.so
Executable file
BIN
linedance-app/venv/lib/python3.12/site-packages/llvmlite/binding/libllvmlite.so
Executable file
Binary file not shown.
@@ -0,0 +1,20 @@
|
||||
from ctypes import c_int, c_char_p, POINTER
|
||||
from llvmlite.binding import ffi
|
||||
|
||||
|
||||
def link_modules(dst, src):
|
||||
with ffi.OutputString() as outerr:
|
||||
err = ffi.lib.LLVMPY_LinkModules(dst, src, outerr)
|
||||
# The underlying module was destroyed
|
||||
src.detach()
|
||||
if err:
|
||||
raise RuntimeError(str(outerr))
|
||||
|
||||
|
||||
ffi.lib.LLVMPY_LinkModules.argtypes = [
|
||||
ffi.LLVMModuleRef,
|
||||
ffi.LLVMModuleRef,
|
||||
POINTER(c_char_p),
|
||||
]
|
||||
|
||||
ffi.lib.LLVMPY_LinkModules.restype = c_int
|
||||
@@ -0,0 +1,349 @@
|
||||
from ctypes import (c_char_p, byref, POINTER, c_bool, create_string_buffer,
|
||||
c_size_t, string_at)
|
||||
|
||||
from llvmlite.binding import ffi
|
||||
from llvmlite.binding.linker import link_modules
|
||||
from llvmlite.binding.common import _decode_string, _encode_string
|
||||
from llvmlite.binding.value import ValueRef, TypeRef
|
||||
from llvmlite.binding.context import get_global_context
|
||||
|
||||
|
||||
def parse_assembly(llvmir, context=None):
|
||||
"""
|
||||
Create Module from a LLVM IR string
|
||||
"""
|
||||
if context is None:
|
||||
context = get_global_context()
|
||||
llvmir = _encode_string(llvmir)
|
||||
strbuf = c_char_p(llvmir)
|
||||
with ffi.OutputString() as errmsg:
|
||||
mod = ModuleRef(
|
||||
ffi.lib.LLVMPY_ParseAssembly(context, strbuf, errmsg),
|
||||
context)
|
||||
if errmsg:
|
||||
mod.close()
|
||||
raise RuntimeError("LLVM IR parsing error\n{0}".format(errmsg))
|
||||
return mod
|
||||
|
||||
|
||||
def parse_bitcode(bitcode, context=None):
|
||||
"""
|
||||
Create Module from a LLVM *bitcode* (a bytes object).
|
||||
"""
|
||||
if context is None:
|
||||
context = get_global_context()
|
||||
buf = c_char_p(bitcode)
|
||||
bufsize = len(bitcode)
|
||||
with ffi.OutputString() as errmsg:
|
||||
mod = ModuleRef(ffi.lib.LLVMPY_ParseBitcode(
|
||||
context, buf, bufsize, errmsg), context)
|
||||
if errmsg:
|
||||
mod.close()
|
||||
raise RuntimeError(
|
||||
"LLVM bitcode parsing error\n{0}".format(errmsg))
|
||||
return mod
|
||||
|
||||
|
||||
class ModuleRef(ffi.ObjectRef):
|
||||
"""
|
||||
A reference to a LLVM module.
|
||||
"""
|
||||
|
||||
def __init__(self, module_ptr, context):
|
||||
super(ModuleRef, self).__init__(module_ptr)
|
||||
self._context = context
|
||||
|
||||
def __str__(self):
|
||||
with ffi.OutputString() as outstr:
|
||||
ffi.lib.LLVMPY_PrintModuleToString(self, outstr)
|
||||
return str(outstr)
|
||||
|
||||
def as_bitcode(self):
|
||||
"""
|
||||
Return the module's LLVM bitcode, as a bytes object.
|
||||
"""
|
||||
ptr = c_char_p(None)
|
||||
size = c_size_t(-1)
|
||||
ffi.lib.LLVMPY_WriteBitcodeToString(self, byref(ptr), byref(size))
|
||||
if not ptr:
|
||||
raise MemoryError
|
||||
try:
|
||||
assert size.value >= 0
|
||||
return string_at(ptr, size.value)
|
||||
finally:
|
||||
ffi.lib.LLVMPY_DisposeString(ptr)
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeModule(self)
|
||||
|
||||
def get_function(self, name):
|
||||
"""
|
||||
Get a ValueRef pointing to the function named *name*.
|
||||
NameError is raised if the symbol isn't found.
|
||||
"""
|
||||
p = ffi.lib.LLVMPY_GetNamedFunction(self, _encode_string(name))
|
||||
if not p:
|
||||
raise NameError(name)
|
||||
return ValueRef(p, 'function', dict(module=self))
|
||||
|
||||
def get_global_variable(self, name):
|
||||
"""
|
||||
Get a ValueRef pointing to the global variable named *name*.
|
||||
NameError is raised if the symbol isn't found.
|
||||
"""
|
||||
p = ffi.lib.LLVMPY_GetNamedGlobalVariable(self, _encode_string(name))
|
||||
if not p:
|
||||
raise NameError(name)
|
||||
return ValueRef(p, 'global', dict(module=self))
|
||||
|
||||
def get_struct_type(self, name):
|
||||
"""
|
||||
Get a TypeRef pointing to a structure type named *name*.
|
||||
NameError is raised if the struct type isn't found.
|
||||
"""
|
||||
p = ffi.lib.LLVMPY_GetNamedStructType(self, _encode_string(name))
|
||||
if not p:
|
||||
raise NameError(name)
|
||||
return TypeRef(p)
|
||||
|
||||
def verify(self):
|
||||
"""
|
||||
Verify the module IR's correctness. RuntimeError is raised on error.
|
||||
"""
|
||||
with ffi.OutputString() as outmsg:
|
||||
if ffi.lib.LLVMPY_VerifyModule(self, outmsg):
|
||||
raise RuntimeError(str(outmsg))
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""
|
||||
The module's identifier.
|
||||
"""
|
||||
return _decode_string(ffi.lib.LLVMPY_GetModuleName(self))
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
ffi.lib.LLVMPY_SetModuleName(self, _encode_string(value))
|
||||
|
||||
@property
|
||||
def source_file(self):
|
||||
"""
|
||||
The module's original source file name
|
||||
"""
|
||||
return _decode_string(ffi.lib.LLVMPY_GetModuleSourceFileName(self))
|
||||
|
||||
@property
|
||||
def data_layout(self):
|
||||
"""
|
||||
This module's data layout specification, as a string.
|
||||
"""
|
||||
# LLVMGetDataLayout() points inside a std::string managed by LLVM.
|
||||
with ffi.OutputString(owned=False) as outmsg:
|
||||
ffi.lib.LLVMPY_GetDataLayout(self, outmsg)
|
||||
return str(outmsg)
|
||||
|
||||
@data_layout.setter
|
||||
def data_layout(self, strrep):
|
||||
ffi.lib.LLVMPY_SetDataLayout(self,
|
||||
create_string_buffer(
|
||||
strrep.encode('utf8')))
|
||||
|
||||
@property
|
||||
def triple(self):
|
||||
"""
|
||||
This module's target "triple" specification, as a string.
|
||||
"""
|
||||
# LLVMGetTarget() points inside a std::string managed by LLVM.
|
||||
with ffi.OutputString(owned=False) as outmsg:
|
||||
ffi.lib.LLVMPY_GetTarget(self, outmsg)
|
||||
return str(outmsg)
|
||||
|
||||
@triple.setter
|
||||
def triple(self, strrep):
|
||||
ffi.lib.LLVMPY_SetTarget(self,
|
||||
create_string_buffer(
|
||||
strrep.encode('utf8')))
|
||||
|
||||
def link_in(self, other, preserve=False):
|
||||
"""
|
||||
Link the *other* module into this one. The *other* module will
|
||||
be destroyed unless *preserve* is true.
|
||||
"""
|
||||
if preserve:
|
||||
other = other.clone()
|
||||
link_modules(self, other)
|
||||
|
||||
@property
|
||||
def global_variables(self):
|
||||
"""
|
||||
Return an iterator over this module's global variables.
|
||||
The iterator will yield a ValueRef for each global variable.
|
||||
|
||||
Note that global variables don't include functions
|
||||
(a function is a "global value" but not a "global variable" in
|
||||
LLVM parlance)
|
||||
"""
|
||||
it = ffi.lib.LLVMPY_ModuleGlobalsIter(self)
|
||||
return _GlobalsIterator(it, dict(module=self))
|
||||
|
||||
@property
|
||||
def functions(self):
|
||||
"""
|
||||
Return an iterator over this module's functions.
|
||||
The iterator will yield a ValueRef for each function.
|
||||
"""
|
||||
it = ffi.lib.LLVMPY_ModuleFunctionsIter(self)
|
||||
return _FunctionsIterator(it, dict(module=self))
|
||||
|
||||
@property
|
||||
def struct_types(self):
|
||||
"""
|
||||
Return an iterator over the struct types defined in
|
||||
the module. The iterator will yield a TypeRef.
|
||||
"""
|
||||
it = ffi.lib.LLVMPY_ModuleTypesIter(self)
|
||||
return _TypesIterator(it, dict(module=self))
|
||||
|
||||
def clone(self):
|
||||
return ModuleRef(ffi.lib.LLVMPY_CloneModule(self), self._context)
|
||||
|
||||
|
||||
class _Iterator(ffi.ObjectRef):
|
||||
|
||||
kind = None
|
||||
|
||||
def __init__(self, ptr, parents):
|
||||
ffi.ObjectRef.__init__(self, ptr)
|
||||
self._parents = parents
|
||||
assert self.kind is not None
|
||||
|
||||
def __next__(self):
|
||||
vp = self._next()
|
||||
if vp:
|
||||
return ValueRef(vp, self.kind, self._parents)
|
||||
else:
|
||||
raise StopIteration
|
||||
|
||||
next = __next__
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
|
||||
class _GlobalsIterator(_Iterator):
|
||||
|
||||
kind = 'global'
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeGlobalsIter(self)
|
||||
|
||||
def _next(self):
|
||||
return ffi.lib.LLVMPY_GlobalsIterNext(self)
|
||||
|
||||
|
||||
class _FunctionsIterator(_Iterator):
|
||||
|
||||
kind = 'function'
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeFunctionsIter(self)
|
||||
|
||||
def _next(self):
|
||||
return ffi.lib.LLVMPY_FunctionsIterNext(self)
|
||||
|
||||
|
||||
class _TypesIterator(_Iterator):
|
||||
|
||||
kind = 'type'
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeTypesIter(self)
|
||||
|
||||
def __next__(self):
|
||||
vp = self._next()
|
||||
if vp:
|
||||
return TypeRef(vp)
|
||||
else:
|
||||
raise StopIteration
|
||||
|
||||
def _next(self):
|
||||
return ffi.lib.LLVMPY_TypesIterNext(self)
|
||||
|
||||
next = __next__
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Set function FFI
|
||||
|
||||
ffi.lib.LLVMPY_ParseAssembly.argtypes = [ffi.LLVMContextRef,
|
||||
c_char_p,
|
||||
POINTER(c_char_p)]
|
||||
ffi.lib.LLVMPY_ParseAssembly.restype = ffi.LLVMModuleRef
|
||||
|
||||
ffi.lib.LLVMPY_ParseBitcode.argtypes = [ffi.LLVMContextRef,
|
||||
c_char_p, c_size_t,
|
||||
POINTER(c_char_p)]
|
||||
ffi.lib.LLVMPY_ParseBitcode.restype = ffi.LLVMModuleRef
|
||||
|
||||
ffi.lib.LLVMPY_DisposeModule.argtypes = [ffi.LLVMModuleRef]
|
||||
|
||||
ffi.lib.LLVMPY_PrintModuleToString.argtypes = [ffi.LLVMModuleRef,
|
||||
POINTER(c_char_p)]
|
||||
ffi.lib.LLVMPY_WriteBitcodeToString.argtypes = [ffi.LLVMModuleRef,
|
||||
POINTER(c_char_p),
|
||||
POINTER(c_size_t)]
|
||||
|
||||
ffi.lib.LLVMPY_GetNamedFunction.argtypes = [ffi.LLVMModuleRef,
|
||||
c_char_p]
|
||||
ffi.lib.LLVMPY_GetNamedFunction.restype = ffi.LLVMValueRef
|
||||
|
||||
ffi.lib.LLVMPY_VerifyModule.argtypes = [ffi.LLVMModuleRef,
|
||||
POINTER(c_char_p)]
|
||||
ffi.lib.LLVMPY_VerifyModule.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_GetDataLayout.argtypes = [ffi.LLVMModuleRef, POINTER(c_char_p)]
|
||||
ffi.lib.LLVMPY_SetDataLayout.argtypes = [ffi.LLVMModuleRef, c_char_p]
|
||||
|
||||
ffi.lib.LLVMPY_GetTarget.argtypes = [ffi.LLVMModuleRef, POINTER(c_char_p)]
|
||||
ffi.lib.LLVMPY_SetTarget.argtypes = [ffi.LLVMModuleRef, c_char_p]
|
||||
|
||||
ffi.lib.LLVMPY_GetNamedGlobalVariable.argtypes = [ffi.LLVMModuleRef, c_char_p]
|
||||
ffi.lib.LLVMPY_GetNamedGlobalVariable.restype = ffi.LLVMValueRef
|
||||
|
||||
ffi.lib.LLVMPY_GetNamedStructType.argtypes = [ffi.LLVMModuleRef, c_char_p]
|
||||
ffi.lib.LLVMPY_GetNamedStructType.restype = ffi.LLVMTypeRef
|
||||
|
||||
ffi.lib.LLVMPY_ModuleGlobalsIter.argtypes = [ffi.LLVMModuleRef]
|
||||
ffi.lib.LLVMPY_ModuleGlobalsIter.restype = ffi.LLVMGlobalsIterator
|
||||
|
||||
ffi.lib.LLVMPY_DisposeGlobalsIter.argtypes = [ffi.LLVMGlobalsIterator]
|
||||
|
||||
ffi.lib.LLVMPY_GlobalsIterNext.argtypes = [ffi.LLVMGlobalsIterator]
|
||||
ffi.lib.LLVMPY_GlobalsIterNext.restype = ffi.LLVMValueRef
|
||||
|
||||
ffi.lib.LLVMPY_ModuleFunctionsIter.argtypes = [ffi.LLVMModuleRef]
|
||||
ffi.lib.LLVMPY_ModuleFunctionsIter.restype = ffi.LLVMFunctionsIterator
|
||||
|
||||
ffi.lib.LLVMPY_ModuleTypesIter.argtypes = [ffi.LLVMModuleRef]
|
||||
ffi.lib.LLVMPY_ModuleTypesIter.restype = ffi.LLVMTypesIterator
|
||||
|
||||
ffi.lib.LLVMPY_DisposeFunctionsIter.argtypes = [ffi.LLVMFunctionsIterator]
|
||||
|
||||
ffi.lib.LLVMPY_DisposeTypesIter.argtypes = [ffi.LLVMTypesIterator]
|
||||
|
||||
ffi.lib.LLVMPY_FunctionsIterNext.argtypes = [ffi.LLVMFunctionsIterator]
|
||||
ffi.lib.LLVMPY_FunctionsIterNext.restype = ffi.LLVMValueRef
|
||||
|
||||
ffi.lib.LLVMPY_TypesIterNext.argtypes = [ffi.LLVMTypesIterator]
|
||||
ffi.lib.LLVMPY_TypesIterNext.restype = ffi.LLVMTypeRef
|
||||
|
||||
ffi.lib.LLVMPY_CloneModule.argtypes = [ffi.LLVMModuleRef]
|
||||
ffi.lib.LLVMPY_CloneModule.restype = ffi.LLVMModuleRef
|
||||
|
||||
ffi.lib.LLVMPY_GetModuleName.argtypes = [ffi.LLVMModuleRef]
|
||||
ffi.lib.LLVMPY_GetModuleName.restype = c_char_p
|
||||
|
||||
ffi.lib.LLVMPY_SetModuleName.argtypes = [ffi.LLVMModuleRef, c_char_p]
|
||||
|
||||
ffi.lib.LLVMPY_GetModuleSourceFileName.argtypes = [ffi.LLVMModuleRef]
|
||||
ffi.lib.LLVMPY_GetModuleSourceFileName.restype = c_char_p
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
from llvmlite.binding import ffi
|
||||
from ctypes import (c_bool, c_char_p, c_char, c_size_t, string_at, c_uint64,
|
||||
POINTER)
|
||||
|
||||
|
||||
class SectionIteratorRef(ffi.ObjectRef):
|
||||
def name(self):
|
||||
return ffi.lib.LLVMPY_GetSectionName(self)
|
||||
|
||||
def is_text(self):
|
||||
return ffi.lib.LLVMPY_IsSectionText(self)
|
||||
|
||||
def size(self):
|
||||
return ffi.lib.LLVMPY_GetSectionSize(self)
|
||||
|
||||
def address(self):
|
||||
return ffi.lib.LLVMPY_GetSectionAddress(self)
|
||||
|
||||
def data(self):
|
||||
return string_at(ffi.lib.LLVMPY_GetSectionContents(self), self.size())
|
||||
|
||||
def is_end(self, object_file):
|
||||
return ffi.lib.LLVMPY_IsSectionIteratorAtEnd(object_file, self)
|
||||
|
||||
def next(self):
|
||||
ffi.lib.LLVMPY_MoveToNextSection(self)
|
||||
|
||||
def _dispose(self):
|
||||
ffi.lib.LLVMPY_DisposeSectionIterator(self)
|
||||
|
||||
|
||||
class ObjectFileRef(ffi.ObjectRef):
|
||||
@classmethod
|
||||
def from_data(cls, data):
|
||||
return cls(ffi.lib.LLVMPY_CreateObjectFile(data, len(data)))
|
||||
|
||||
@classmethod
|
||||
def from_path(cls, path):
|
||||
with open(path, 'rb') as f:
|
||||
data = f.read()
|
||||
return cls(ffi.lib.LLVMPY_CreateObjectFile(data, len(data)))
|
||||
|
||||
def sections(self):
|
||||
it = SectionIteratorRef(ffi.lib.LLVMPY_GetSections(self))
|
||||
while not it.is_end(self):
|
||||
yield it
|
||||
it.next()
|
||||
|
||||
def _dispose(self):
|
||||
ffi.lib.LLVMPY_DisposeObjectFile(self)
|
||||
|
||||
|
||||
ffi.lib.LLVMPY_CreateObjectFile.argtypes = [c_char_p, c_size_t]
|
||||
ffi.lib.LLVMPY_CreateObjectFile.restype = ffi.LLVMObjectFileRef
|
||||
|
||||
ffi.lib.LLVMPY_DisposeObjectFile.argtypes = [ffi.LLVMObjectFileRef]
|
||||
|
||||
ffi.lib.LLVMPY_GetSections.argtypes = [ffi.LLVMObjectFileRef]
|
||||
ffi.lib.LLVMPY_GetSections.restype = ffi.LLVMSectionIteratorRef
|
||||
|
||||
ffi.lib.LLVMPY_DisposeSectionIterator.argtypes = [ffi.LLVMSectionIteratorRef]
|
||||
|
||||
ffi.lib.LLVMPY_MoveToNextSection.argtypes = [ffi.LLVMSectionIteratorRef]
|
||||
|
||||
ffi.lib.LLVMPY_IsSectionIteratorAtEnd.argtypes = [
|
||||
ffi.LLVMObjectFileRef, ffi.LLVMSectionIteratorRef]
|
||||
ffi.lib.LLVMPY_IsSectionIteratorAtEnd.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_GetSectionName.argtypes = [ffi.LLVMSectionIteratorRef]
|
||||
ffi.lib.LLVMPY_GetSectionName.restype = c_char_p
|
||||
|
||||
ffi.lib.LLVMPY_GetSectionSize.argtypes = [ffi.LLVMSectionIteratorRef]
|
||||
ffi.lib.LLVMPY_GetSectionSize.restype = c_uint64
|
||||
|
||||
ffi.lib.LLVMPY_GetSectionAddress.argtypes = [ffi.LLVMSectionIteratorRef]
|
||||
ffi.lib.LLVMPY_GetSectionAddress.restype = c_uint64
|
||||
|
||||
ffi.lib.LLVMPY_GetSectionContents.argtypes = [ffi.LLVMSectionIteratorRef]
|
||||
ffi.lib.LLVMPY_GetSectionContents.restype = POINTER(c_char)
|
||||
|
||||
ffi.lib.LLVMPY_IsSectionText.argtypes = [ffi.LLVMSectionIteratorRef]
|
||||
ffi.lib.LLVMPY_IsSectionText.restype = c_bool
|
||||
@@ -0,0 +1,17 @@
|
||||
from llvmlite.binding import ffi
|
||||
from llvmlite.binding.common import _encode_string
|
||||
from ctypes import c_char_p
|
||||
|
||||
|
||||
def set_option(name, option):
|
||||
"""
|
||||
Set the given LLVM "command-line" option.
|
||||
|
||||
For example set_option("test", "-debug-pass=Structure") would display
|
||||
all optimization passes when generating code.
|
||||
"""
|
||||
ffi.lib.LLVMPY_SetCommandLine(_encode_string(name),
|
||||
_encode_string(option))
|
||||
|
||||
|
||||
ffi.lib.LLVMPY_SetCommandLine.argtypes = [c_char_p, c_char_p]
|
||||
@@ -0,0 +1,342 @@
|
||||
import ctypes
|
||||
from ctypes import POINTER, c_bool, c_char_p, c_uint8, c_uint64, c_size_t
|
||||
|
||||
from llvmlite.binding import ffi, targets
|
||||
|
||||
|
||||
class _LinkElement(ctypes.Structure):
|
||||
_fields_ = [("element_kind", c_uint8),
|
||||
("value", c_char_p),
|
||||
("value_len", c_size_t)]
|
||||
|
||||
|
||||
class _SymbolAddress(ctypes.Structure):
|
||||
_fields_ = [("name", c_char_p), ("address", c_uint64)]
|
||||
|
||||
|
||||
class JITLibraryBuilder:
|
||||
"""
|
||||
Create a library for linking by OrcJIT
|
||||
|
||||
OrcJIT operates like a linker: a number of compilation units and
|
||||
dependencies are collected together and linked into a single dynamic library
|
||||
that can export functions to other libraries or to be consumed directly as
|
||||
entry points into JITted code. The native OrcJIT has a lot of memory
|
||||
management complications so this API is designed to work well with Python's
|
||||
garbage collection.
|
||||
|
||||
The creation of a new library is a bit like a linker command line where
|
||||
compilation units, mostly as LLVM IR, and previously constructed libraries
|
||||
are linked together, then loaded into memory, and the addresses of exported
|
||||
symbols are extracted. Any static initializers are run and the exported
|
||||
addresses and a resource tracker is produced. As long as the resource
|
||||
tracker is referenced somewhere in Python, the exported addresses will be
|
||||
valid. Once the resource tracker is garbage collected, the static
|
||||
destructors will run and library will be unloaded from memory.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.__entries = []
|
||||
self.__exports = set()
|
||||
self.__imports = {}
|
||||
|
||||
def add_ir(self, llvmir):
|
||||
"""
|
||||
Adds a compilation unit to the library using LLVM IR as the input
|
||||
format.
|
||||
|
||||
This takes a string or an object that can be converted to a string,
|
||||
including IRBuilder, that contains LLVM IR.
|
||||
"""
|
||||
self.__entries.append((0, str(llvmir).encode('utf-8')))
|
||||
return self
|
||||
|
||||
def add_native_assembly(self, asm):
|
||||
"""
|
||||
Adds a compilation unit to the library using native assembly as the
|
||||
input format.
|
||||
|
||||
This takes a string or an object that can be converted to a string that
|
||||
contains native assembly, which will be
|
||||
parsed by LLVM.
|
||||
"""
|
||||
self.__entries.append((1, str(asm).encode('utf-8')))
|
||||
return self
|
||||
|
||||
def add_object_img(self, data):
|
||||
"""
|
||||
Adds a compilation unit to the library using pre-compiled object code.
|
||||
|
||||
This takes the bytes of the contents of an object artifact which will be
|
||||
loaded by LLVM.
|
||||
"""
|
||||
self.__entries.append((2, bytes(data)))
|
||||
return self
|
||||
|
||||
def add_object_file(self, file_path):
|
||||
"""
|
||||
Adds a compilation unit to the library using pre-compiled object file.
|
||||
|
||||
This takes a string or path-like object that references an object file
|
||||
which will be loaded by LLVM.
|
||||
"""
|
||||
with open(file_path, "rb") as f:
|
||||
self.__entries.append((2, f.read()))
|
||||
return self
|
||||
|
||||
def add_jit_library(self, name):
|
||||
"""
|
||||
Adds an existing JIT library as prerequisite.
|
||||
|
||||
The name of the library must match the one provided in a previous link
|
||||
command.
|
||||
"""
|
||||
self.__entries.append((3, str(name).encode('utf-8')))
|
||||
return self
|
||||
|
||||
def add_current_process(self):
|
||||
"""
|
||||
Allows the JITted library to access symbols in the current binary.
|
||||
|
||||
That is, it allows exporting the current binary's symbols, including
|
||||
loaded libraries, as imports to the JITted
|
||||
library.
|
||||
"""
|
||||
self.__entries.append((3, b''))
|
||||
return self
|
||||
|
||||
def import_symbol(self, name, address):
|
||||
"""
|
||||
Register the *address* of global symbol *name*. This will make
|
||||
it usable (e.g. callable) from LLVM-compiled functions.
|
||||
"""
|
||||
self.__imports[str(name)] = c_uint64(address)
|
||||
return self
|
||||
|
||||
def export_symbol(self, name):
|
||||
"""
|
||||
During linking, extract the address of a symbol that was defined in one
|
||||
of the compilation units.
|
||||
|
||||
This allows getting symbols, functions or global variables, out of the
|
||||
JIT linked library. The addresses will be
|
||||
available when the link method is called.
|
||||
"""
|
||||
self.__exports.add(str(name))
|
||||
return self
|
||||
|
||||
def link(self, lljit, library_name):
|
||||
"""
|
||||
Link all the current compilation units into a JITted library and extract
|
||||
the address of exported symbols.
|
||||
|
||||
An instance of the OrcJIT instance must be provided and this will be the
|
||||
scope that is used to find other JITted libraries that are dependencies
|
||||
and also be the place where this library will be defined.
|
||||
|
||||
After linking, the method will return a resource tracker that keeps the
|
||||
library alive. This tracker also knows the addresses of any exported
|
||||
symbols that were requested.
|
||||
|
||||
The addresses will be valid as long as the resource tracker is
|
||||
referenced.
|
||||
|
||||
When the resource tracker is destroyed, the library will be cleaned up,
|
||||
however, the name of the library cannot be reused.
|
||||
"""
|
||||
assert not lljit.closed, "Cannot add to closed JIT"
|
||||
encoded_library_name = str(library_name).encode('utf-8')
|
||||
assert len(encoded_library_name) > 0, "Library cannot be empty"
|
||||
elements = (_LinkElement * len(self.__entries))()
|
||||
for idx, (kind, value) in enumerate(self.__entries):
|
||||
elements[idx].element_kind = c_uint8(kind)
|
||||
elements[idx].value = c_char_p(value)
|
||||
elements[idx].value_len = c_size_t(len(value))
|
||||
exports = (_SymbolAddress * len(self.__exports))()
|
||||
for idx, name in enumerate(self.__exports):
|
||||
exports[idx].name = name.encode('utf-8')
|
||||
|
||||
imports = (_SymbolAddress * len(self.__imports))()
|
||||
for idx, (name, addr) in enumerate(self.__imports.items()):
|
||||
imports[idx].name = name.encode('utf-8')
|
||||
imports[idx].address = addr
|
||||
|
||||
with ffi.OutputString() as outerr:
|
||||
tracker = lljit._capi.LLVMPY_LLJIT_Link(
|
||||
lljit._ptr,
|
||||
encoded_library_name,
|
||||
elements,
|
||||
len(self.__entries),
|
||||
imports,
|
||||
len(self.__imports),
|
||||
exports,
|
||||
len(self.__exports),
|
||||
outerr)
|
||||
if not tracker:
|
||||
raise RuntimeError(str(outerr))
|
||||
return ResourceTracker(tracker,
|
||||
library_name,
|
||||
{name: exports[idx].address
|
||||
for idx, name in enumerate(self.__exports)})
|
||||
|
||||
|
||||
class ResourceTracker(ffi.ObjectRef):
|
||||
"""
|
||||
A resource tracker is created for each loaded JIT library and keeps the
|
||||
module alive.
|
||||
|
||||
OrcJIT supports unloading libraries that are no longer used. This resource
|
||||
tracker should be stored in any object that reference functions or constants
|
||||
for a JITted library. When all references to the resource tracker are
|
||||
dropped, this will trigger LLVM to unload the library and destroy any
|
||||
functions.
|
||||
|
||||
Failure to keep resource trackers while calling a function or accessing a
|
||||
symbol can result in crashes or memory corruption.
|
||||
|
||||
LLVM internally tracks references between different libraries, so only
|
||||
"leaf" libraries need to be tracked.
|
||||
"""
|
||||
def __init__(self, ptr, name, addresses):
|
||||
self.__addresses = addresses
|
||||
self.__name = name
|
||||
ffi.ObjectRef.__init__(self, ptr)
|
||||
|
||||
def __getitem__(self, item):
|
||||
"""
|
||||
Get the address of an exported symbol as an integer
|
||||
"""
|
||||
return self.__addresses[item]
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.__name
|
||||
|
||||
def _dispose(self):
|
||||
with ffi.OutputString() as outerr:
|
||||
if self._capi.LLVMPY_LLJIT_Dylib_Tracker_Dispose(self, outerr):
|
||||
raise RuntimeError(str(outerr))
|
||||
|
||||
|
||||
class LLJIT(ffi.ObjectRef):
|
||||
"""
|
||||
A OrcJIT-based LLVM JIT engine that can compile and run LLVM IR as a
|
||||
collection of JITted dynamic libraries
|
||||
|
||||
The C++ OrcJIT API has a lot of memory ownership patterns that do not work
|
||||
with Python. This API attempts to provide ones that are safe at the expense
|
||||
of some features. Each LLJIT instance is a collection of JIT-compiled
|
||||
libraries. In the C++ API, there is a "main" library; this API does not
|
||||
provide access to the main library. Use the JITLibraryBuilder to create a
|
||||
new named library instead.
|
||||
"""
|
||||
def __init__(self, ptr):
|
||||
self._td = None
|
||||
ffi.ObjectRef.__init__(self, ptr)
|
||||
|
||||
def lookup(self, dylib, fn):
|
||||
"""
|
||||
Find a function in this dynamic library and construct a new tracking
|
||||
object for it
|
||||
|
||||
If the library or function do not exist, an exception will occur.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dylib : str or None
|
||||
the name of the library containing the symbol
|
||||
fn : str
|
||||
the name of the function to get
|
||||
"""
|
||||
assert not self.closed, "Cannot lookup in closed JIT"
|
||||
address = ctypes.c_uint64()
|
||||
with ffi.OutputString() as outerr:
|
||||
tracker = ffi.lib.LLVMPY_LLJITLookup(self,
|
||||
dylib.encode("utf-8"),
|
||||
fn.encode("utf-8"),
|
||||
ctypes.byref(address),
|
||||
outerr)
|
||||
if not tracker:
|
||||
raise RuntimeError(str(outerr))
|
||||
|
||||
return ResourceTracker(tracker, dylib, {fn: address.value})
|
||||
|
||||
@property
|
||||
def target_data(self):
|
||||
"""
|
||||
The TargetData for this LLJIT instance.
|
||||
"""
|
||||
if self._td is not None:
|
||||
return self._td
|
||||
ptr = ffi.lib.LLVMPY_LLJITGetDataLayout(self)
|
||||
self._td = targets.TargetData(ptr)
|
||||
self._td._owned = True
|
||||
return self._td
|
||||
|
||||
def _dispose(self):
|
||||
if self._td is not None:
|
||||
self._td.detach()
|
||||
self._capi.LLVMPY_LLJITDispose(self)
|
||||
|
||||
|
||||
def create_lljit_compiler(target_machine=None, *,
|
||||
use_jit_link=False,
|
||||
suppress_errors=False):
|
||||
"""
|
||||
Create an LLJIT instance
|
||||
"""
|
||||
with ffi.OutputString() as outerr:
|
||||
lljit = ffi.lib.LLVMPY_CreateLLJITCompiler(target_machine,
|
||||
suppress_errors,
|
||||
use_jit_link,
|
||||
outerr)
|
||||
if not lljit:
|
||||
raise RuntimeError(str(outerr))
|
||||
|
||||
return LLJIT(lljit)
|
||||
|
||||
|
||||
ffi.lib.LLVMPY_LLJITLookup.argtypes = [
|
||||
ffi.LLVMOrcLLJITRef,
|
||||
c_char_p,
|
||||
c_char_p,
|
||||
POINTER(c_uint64),
|
||||
POINTER(c_char_p),
|
||||
]
|
||||
ffi.lib.LLVMPY_LLJITLookup.restype = ffi.LLVMOrcDylibTrackerRef
|
||||
|
||||
ffi.lib.LLVMPY_LLJITGetDataLayout.argtypes = [
|
||||
ffi.LLVMOrcLLJITRef,
|
||||
]
|
||||
ffi.lib.LLVMPY_LLJITGetDataLayout.restype = ffi.LLVMTargetDataRef
|
||||
|
||||
ffi.lib.LLVMPY_CreateLLJITCompiler.argtypes = [
|
||||
ffi.LLVMTargetMachineRef,
|
||||
c_bool,
|
||||
c_bool,
|
||||
POINTER(c_char_p),
|
||||
]
|
||||
ffi.lib.LLVMPY_CreateLLJITCompiler.restype = ffi.LLVMOrcLLJITRef
|
||||
|
||||
ffi.lib.LLVMPY_LLJITDispose.argtypes = [
|
||||
ffi.LLVMOrcLLJITRef,
|
||||
]
|
||||
|
||||
|
||||
ffi.lib.LLVMPY_LLJIT_Link.argtypes = [
|
||||
ffi.LLVMOrcLLJITRef,
|
||||
c_char_p,
|
||||
POINTER(_LinkElement),
|
||||
c_size_t,
|
||||
POINTER(_SymbolAddress),
|
||||
c_size_t,
|
||||
POINTER(_SymbolAddress),
|
||||
c_size_t,
|
||||
POINTER(c_char_p)
|
||||
]
|
||||
ffi.lib.LLVMPY_LLJIT_Link.restype = ffi.LLVMOrcDylibTrackerRef
|
||||
|
||||
ffi.lib.LLVMPY_LLJIT_Dylib_Tracker_Dispose.argtypes = [
|
||||
ffi.LLVMOrcDylibTrackerRef,
|
||||
POINTER(c_char_p)
|
||||
]
|
||||
ffi.lib.LLVMPY_LLJIT_Dylib_Tracker_Dispose.restype = c_bool
|
||||
@@ -0,0 +1,462 @@
|
||||
import os
|
||||
from ctypes import (POINTER, c_char_p, c_longlong, c_int, c_size_t,
|
||||
c_void_p, string_at)
|
||||
|
||||
from llvmlite.binding import ffi
|
||||
from llvmlite.binding.initfini import llvm_version_info
|
||||
from llvmlite.binding.common import _decode_string, _encode_string
|
||||
from collections import namedtuple
|
||||
|
||||
# import for backward compatible API, `has_svml` is now in config module.
|
||||
from llvmlite.binding.config import _has_svml as has_svml # noqa: F401
|
||||
|
||||
|
||||
Triple = namedtuple('Triple', ['Arch', 'SubArch', 'Vendor',
|
||||
'OS', 'Env', 'ObjectFormat'])
|
||||
|
||||
|
||||
def get_process_triple():
|
||||
"""
|
||||
Return a target triple suitable for generating code for the current process.
|
||||
An example when the default triple from ``get_default_triple()`` is not be
|
||||
suitable is when LLVM is compiled for 32-bit but the process is executing
|
||||
in 64-bit mode.
|
||||
"""
|
||||
with ffi.OutputString() as out:
|
||||
ffi.lib.LLVMPY_GetProcessTriple(out)
|
||||
return str(out)
|
||||
|
||||
|
||||
def get_triple_parts(triple: str):
|
||||
"""
|
||||
Return a tuple of the parts of the given triple.
|
||||
"""
|
||||
with ffi.OutputString() as arch, \
|
||||
ffi.OutputString() as vendor, \
|
||||
ffi.OutputString() as os, ffi.OutputString() as env:
|
||||
ffi.lib.LLVMPY_GetTripleParts(triple.encode('utf8'),
|
||||
arch, vendor, os, env)
|
||||
arch = str(arch)
|
||||
subarch = ''
|
||||
for _str in triple.split('-'):
|
||||
if _str.startswith(arch):
|
||||
subarch = _str[len(arch):]
|
||||
break
|
||||
return Triple(arch, subarch, str(vendor), str(os),
|
||||
str(env), get_object_format(triple))
|
||||
|
||||
|
||||
class FeatureMap(dict):
|
||||
"""
|
||||
Maps feature name to a boolean indicating the availability of the feature.
|
||||
Extends ``dict`` to add `.flatten()` method.
|
||||
"""
|
||||
|
||||
def flatten(self, sort=True):
|
||||
"""
|
||||
Args
|
||||
----
|
||||
sort: bool
|
||||
Optional. If True, the features are sorted by name; otherwise,
|
||||
the ordering is unstable between python session due to hash
|
||||
randomization. Defaults to True.
|
||||
|
||||
Returns a string suitable for use as the ``features`` argument to
|
||||
``Target.create_target_machine()``.
|
||||
|
||||
"""
|
||||
iterator = sorted(self.items()) if sort else iter(self.items())
|
||||
flag_map = {True: '+', False: '-'}
|
||||
return ','.join('{0}{1}'.format(flag_map[v], k)
|
||||
for k, v in iterator)
|
||||
|
||||
|
||||
def get_host_cpu_features():
|
||||
"""
|
||||
Returns a dictionary-like object indicating the CPU features for current
|
||||
architecture and whether they are enabled for this CPU. The key-value pairs
|
||||
are the feature name as string and a boolean indicating whether the feature
|
||||
is available. The returned value is an instance of ``FeatureMap`` class,
|
||||
which adds a new method ``.flatten()`` for returning a string suitable for
|
||||
use as the "features" argument to ``Target.create_target_machine()``.
|
||||
|
||||
If LLVM has not implemented this feature or it fails to get the information,
|
||||
this function will raise a RuntimeError exception.
|
||||
"""
|
||||
with ffi.OutputString() as out:
|
||||
outdict = FeatureMap()
|
||||
if not ffi.lib.LLVMPY_GetHostCPUFeatures(out):
|
||||
return outdict
|
||||
flag_map = {'+': True, '-': False}
|
||||
content = str(out)
|
||||
if content: # protect against empty string
|
||||
for feat in content.split(','):
|
||||
if feat: # protect against empty feature
|
||||
outdict[feat[1:]] = flag_map[feat[0]]
|
||||
return outdict
|
||||
|
||||
|
||||
def get_default_triple():
|
||||
"""
|
||||
Return the default target triple LLVM is configured to produce code for.
|
||||
"""
|
||||
with ffi.OutputString() as out:
|
||||
ffi.lib.LLVMPY_GetDefaultTargetTriple(out)
|
||||
return str(out)
|
||||
|
||||
|
||||
def get_host_cpu_name():
|
||||
"""
|
||||
Get the name of the host's CPU, suitable for using with
|
||||
:meth:`Target.create_target_machine()`.
|
||||
"""
|
||||
with ffi.OutputString() as out:
|
||||
ffi.lib.LLVMPY_GetHostCPUName(out)
|
||||
return str(out)
|
||||
|
||||
|
||||
# Adapted from https://github.com/llvm/llvm-project/blob/release/15.x/llvm/include/llvm/ADT/Triple.h#L269 # noqa
|
||||
llvm_version_major = llvm_version_info[0]
|
||||
|
||||
|
||||
_object_formats = {
|
||||
0: "Unknown",
|
||||
1: "COFF",
|
||||
2: "DXContainer",
|
||||
3: "ELF",
|
||||
4: "GOFF",
|
||||
5: "MachO",
|
||||
6: "SPIRV",
|
||||
7: "Wasm",
|
||||
8: "XCOFF",
|
||||
}
|
||||
|
||||
|
||||
def get_object_format(triple=None):
|
||||
"""
|
||||
Get the object format for the given *triple* string (or the default
|
||||
triple if omitted).
|
||||
A string is returned
|
||||
"""
|
||||
if triple is None:
|
||||
triple = get_default_triple()
|
||||
res = ffi.lib.LLVMPY_GetTripleObjectFormat(_encode_string(triple))
|
||||
return _object_formats[res]
|
||||
|
||||
|
||||
def create_target_data(layout):
|
||||
"""
|
||||
Create a TargetData instance for the given *layout* string.
|
||||
"""
|
||||
return TargetData(ffi.lib.LLVMPY_CreateTargetData(_encode_string(layout)))
|
||||
|
||||
|
||||
class TargetData(ffi.ObjectRef):
|
||||
"""
|
||||
A TargetData provides structured access to a data layout.
|
||||
Use :func:`create_target_data` to create instances.
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
if self._closed:
|
||||
return "<dead TargetData>"
|
||||
with ffi.OutputString() as out:
|
||||
ffi.lib.LLVMPY_CopyStringRepOfTargetData(self, out)
|
||||
return str(out)
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeTargetData(self)
|
||||
|
||||
def get_abi_size(self, ty):
|
||||
"""
|
||||
Get ABI size of LLVM type *ty*.
|
||||
"""
|
||||
return ffi.lib.LLVMPY_ABISizeOfType(self, ty)
|
||||
|
||||
def get_element_offset(self, ty, position):
|
||||
"""
|
||||
Get byte offset of type's ty element at the given position
|
||||
"""
|
||||
|
||||
offset = ffi.lib.LLVMPY_OffsetOfElement(self, ty, position)
|
||||
if offset == -1:
|
||||
raise ValueError("Could not determined offset of {}th "
|
||||
"element of the type '{}'. Is it a struct"
|
||||
"type?".format(position, str(ty)))
|
||||
return offset
|
||||
|
||||
def get_abi_alignment(self, ty):
|
||||
"""
|
||||
Get minimum ABI alignment of LLVM type *ty*.
|
||||
"""
|
||||
return ffi.lib.LLVMPY_ABIAlignmentOfType(self, ty)
|
||||
|
||||
|
||||
RELOC = frozenset(['default', 'static', 'pic', 'dynamicnopic'])
|
||||
CODEMODEL = frozenset(['default', 'jitdefault', 'small', 'kernel', 'medium',
|
||||
'large'])
|
||||
|
||||
|
||||
class Target(ffi.ObjectRef):
|
||||
_triple = ''
|
||||
|
||||
# No _dispose() method since LLVMGetTargetFromTriple() returns a
|
||||
# persistent object.
|
||||
|
||||
@classmethod
|
||||
def from_default_triple(cls):
|
||||
"""
|
||||
Create a Target instance for the default triple.
|
||||
"""
|
||||
triple = get_default_triple()
|
||||
return cls.from_triple(triple)
|
||||
|
||||
@classmethod
|
||||
def from_triple(cls, triple):
|
||||
"""
|
||||
Create a Target instance for the given triple (a string).
|
||||
"""
|
||||
with ffi.OutputString() as outerr:
|
||||
target = ffi.lib.LLVMPY_GetTargetFromTriple(triple.encode('utf8'),
|
||||
outerr)
|
||||
if not target:
|
||||
raise RuntimeError(str(outerr))
|
||||
target = cls(target)
|
||||
target._triple = triple
|
||||
return target
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
s = ffi.lib.LLVMPY_GetTargetName(self)
|
||||
return _decode_string(s)
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
s = ffi.lib.LLVMPY_GetTargetDescription(self)
|
||||
return _decode_string(s)
|
||||
|
||||
@property
|
||||
def triple(self):
|
||||
return self._triple
|
||||
|
||||
def __str__(self):
|
||||
return "<Target {0} ({1})>".format(self.name, self.description)
|
||||
|
||||
def create_target_machine(self, cpu='', features='',
|
||||
opt=2, reloc='default', codemodel='jitdefault',
|
||||
printmc=False, jit=False, abiname=''):
|
||||
"""
|
||||
Create a new TargetMachine for this target and the given options.
|
||||
|
||||
Specifying codemodel='default' will result in the use of the "small"
|
||||
code model. Specifying codemodel='jitdefault' will result in the code
|
||||
model being picked based on platform bitness (32="small", 64="large").
|
||||
|
||||
The `printmc` option corresponds to llvm's `-print-machineinstrs`.
|
||||
|
||||
The `jit` option should be set when the target-machine is to be used
|
||||
in a JIT engine.
|
||||
|
||||
The `abiname` option specifies the ABI. RISC-V targets with hard-float
|
||||
needs to pass the ABI name to LLVM.
|
||||
"""
|
||||
assert 0 <= opt <= 3
|
||||
assert reloc in RELOC
|
||||
assert codemodel in CODEMODEL
|
||||
triple = self._triple
|
||||
# MCJIT under Windows only supports ELF objects, see
|
||||
# http://lists.llvm.org/pipermail/llvm-dev/2013-December/068341.html
|
||||
# Note we still want to produce regular COFF files in AOT mode.
|
||||
if os.name == 'nt' and codemodel == 'jitdefault':
|
||||
triple += '-elf'
|
||||
tm = ffi.lib.LLVMPY_CreateTargetMachine(self,
|
||||
_encode_string(triple),
|
||||
_encode_string(cpu),
|
||||
_encode_string(features),
|
||||
opt,
|
||||
_encode_string(reloc),
|
||||
_encode_string(codemodel),
|
||||
int(printmc),
|
||||
int(jit),
|
||||
_encode_string(abiname),
|
||||
)
|
||||
if tm:
|
||||
return TargetMachine(tm)
|
||||
else:
|
||||
raise RuntimeError("Cannot create target machine")
|
||||
|
||||
|
||||
class TargetMachine(ffi.ObjectRef):
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeTargetMachine(self)
|
||||
|
||||
def add_analysis_passes(self, pm):
|
||||
"""
|
||||
Register analysis passes for this target machine with a pass manager.
|
||||
"""
|
||||
ffi.lib.LLVMPY_AddAnalysisPasses(self, pm)
|
||||
|
||||
def set_asm_verbosity(self, verbose):
|
||||
"""
|
||||
Set whether this target machine will emit assembly with human-readable
|
||||
comments describing control flow, debug information, and so on.
|
||||
"""
|
||||
ffi.lib.LLVMPY_SetTargetMachineAsmVerbosity(self, verbose)
|
||||
|
||||
def emit_object(self, module):
|
||||
"""
|
||||
Represent the module as a code object, suitable for use with
|
||||
the platform's linker. Returns a byte string.
|
||||
"""
|
||||
return self._emit_to_memory(module, use_object=True)
|
||||
|
||||
def emit_assembly(self, module):
|
||||
"""
|
||||
Return the raw assembler of the module, as a string.
|
||||
|
||||
llvm.initialize_native_asmprinter() must have been called first.
|
||||
"""
|
||||
return _decode_string(self._emit_to_memory(module, use_object=False))
|
||||
|
||||
def _emit_to_memory(self, module, use_object=False):
|
||||
"""Returns bytes of object code of the module.
|
||||
|
||||
Args
|
||||
----
|
||||
use_object : bool
|
||||
Emit object code or (if False) emit assembly code.
|
||||
"""
|
||||
with ffi.OutputString() as outerr:
|
||||
mb = ffi.lib.LLVMPY_TargetMachineEmitToMemory(self, module,
|
||||
int(use_object),
|
||||
outerr)
|
||||
if not mb:
|
||||
raise RuntimeError(str(outerr))
|
||||
|
||||
bufptr = ffi.lib.LLVMPY_GetBufferStart(mb)
|
||||
bufsz = ffi.lib.LLVMPY_GetBufferSize(mb)
|
||||
try:
|
||||
return string_at(bufptr, bufsz)
|
||||
finally:
|
||||
ffi.lib.LLVMPY_DisposeMemoryBuffer(mb)
|
||||
|
||||
@property
|
||||
def target_data(self):
|
||||
return TargetData(ffi.lib.LLVMPY_CreateTargetMachineData(self))
|
||||
|
||||
@property
|
||||
def triple(self):
|
||||
with ffi.OutputString() as out:
|
||||
ffi.lib.LLVMPY_GetTargetMachineTriple(self, out)
|
||||
return str(out)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# FFI
|
||||
|
||||
ffi.lib.LLVMPY_GetProcessTriple.argtypes = [POINTER(c_char_p)]
|
||||
ffi.lib.LLVMPY_GetTripleParts.argtypes = [c_char_p, POINTER(c_char_p),
|
||||
POINTER(c_char_p), POINTER(c_char_p),
|
||||
POINTER(c_char_p)]
|
||||
|
||||
ffi.lib.LLVMPY_GetHostCPUFeatures.argtypes = [POINTER(c_char_p)]
|
||||
ffi.lib.LLVMPY_GetHostCPUFeatures.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_GetDefaultTargetTriple.argtypes = [POINTER(c_char_p)]
|
||||
|
||||
ffi.lib.LLVMPY_GetHostCPUName.argtypes = [POINTER(c_char_p)]
|
||||
|
||||
ffi.lib.LLVMPY_GetTripleObjectFormat.argtypes = [c_char_p]
|
||||
ffi.lib.LLVMPY_GetTripleObjectFormat.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_CreateTargetData.argtypes = [c_char_p]
|
||||
ffi.lib.LLVMPY_CreateTargetData.restype = ffi.LLVMTargetDataRef
|
||||
|
||||
ffi.lib.LLVMPY_CopyStringRepOfTargetData.argtypes = [
|
||||
ffi.LLVMTargetDataRef,
|
||||
POINTER(c_char_p),
|
||||
]
|
||||
|
||||
ffi.lib.LLVMPY_DisposeTargetData.argtypes = [
|
||||
ffi.LLVMTargetDataRef,
|
||||
]
|
||||
|
||||
ffi.lib.LLVMPY_ABISizeOfType.argtypes = [ffi.LLVMTargetDataRef,
|
||||
ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_ABISizeOfType.restype = c_longlong
|
||||
|
||||
ffi.lib.LLVMPY_OffsetOfElement.argtypes = [ffi.LLVMTargetDataRef,
|
||||
ffi.LLVMTypeRef,
|
||||
c_int]
|
||||
ffi.lib.LLVMPY_OffsetOfElement.restype = c_longlong
|
||||
|
||||
ffi.lib.LLVMPY_ABIAlignmentOfType.argtypes = [ffi.LLVMTargetDataRef,
|
||||
ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_ABIAlignmentOfType.restype = c_longlong
|
||||
|
||||
ffi.lib.LLVMPY_GetTargetFromTriple.argtypes = [c_char_p, POINTER(c_char_p)]
|
||||
ffi.lib.LLVMPY_GetTargetFromTriple.restype = ffi.LLVMTargetRef
|
||||
|
||||
ffi.lib.LLVMPY_GetTargetName.argtypes = [ffi.LLVMTargetRef]
|
||||
ffi.lib.LLVMPY_GetTargetName.restype = c_char_p
|
||||
|
||||
ffi.lib.LLVMPY_GetTargetDescription.argtypes = [ffi.LLVMTargetRef]
|
||||
ffi.lib.LLVMPY_GetTargetDescription.restype = c_char_p
|
||||
|
||||
ffi.lib.LLVMPY_CreateTargetMachine.argtypes = [
|
||||
ffi.LLVMTargetRef,
|
||||
# Triple
|
||||
c_char_p,
|
||||
# CPU
|
||||
c_char_p,
|
||||
# Features
|
||||
c_char_p,
|
||||
# OptLevel
|
||||
c_int,
|
||||
# Reloc
|
||||
c_char_p,
|
||||
# CodeModel
|
||||
c_char_p,
|
||||
# PrintMC
|
||||
c_int,
|
||||
# JIT
|
||||
c_int,
|
||||
# ABIName
|
||||
c_char_p,
|
||||
]
|
||||
ffi.lib.LLVMPY_CreateTargetMachine.restype = ffi.LLVMTargetMachineRef
|
||||
|
||||
ffi.lib.LLVMPY_DisposeTargetMachine.argtypes = [ffi.LLVMTargetMachineRef]
|
||||
|
||||
ffi.lib.LLVMPY_GetTargetMachineTriple.argtypes = [ffi.LLVMTargetMachineRef,
|
||||
POINTER(c_char_p)]
|
||||
|
||||
ffi.lib.LLVMPY_SetTargetMachineAsmVerbosity.argtypes = [
|
||||
ffi.LLVMTargetMachineRef, c_int]
|
||||
|
||||
ffi.lib.LLVMPY_AddAnalysisPasses.argtypes = [
|
||||
ffi.LLVMTargetMachineRef,
|
||||
ffi.LLVMPassManagerRef,
|
||||
]
|
||||
|
||||
ffi.lib.LLVMPY_TargetMachineEmitToMemory.argtypes = [
|
||||
ffi.LLVMTargetMachineRef,
|
||||
ffi.LLVMModuleRef,
|
||||
c_int,
|
||||
POINTER(c_char_p),
|
||||
]
|
||||
ffi.lib.LLVMPY_TargetMachineEmitToMemory.restype = ffi.LLVMMemoryBufferRef
|
||||
|
||||
ffi.lib.LLVMPY_GetBufferStart.argtypes = [ffi.LLVMMemoryBufferRef]
|
||||
ffi.lib.LLVMPY_GetBufferStart.restype = c_void_p
|
||||
|
||||
ffi.lib.LLVMPY_GetBufferSize.argtypes = [ffi.LLVMMemoryBufferRef]
|
||||
ffi.lib.LLVMPY_GetBufferSize.restype = c_size_t
|
||||
|
||||
ffi.lib.LLVMPY_DisposeMemoryBuffer.argtypes = [ffi.LLVMMemoryBufferRef]
|
||||
|
||||
ffi.lib.LLVMPY_CreateTargetMachineData.argtypes = [
|
||||
ffi.LLVMTargetMachineRef,
|
||||
]
|
||||
ffi.lib.LLVMPY_CreateTargetMachineData.restype = ffi.LLVMTargetDataRef
|
||||
@@ -0,0 +1,267 @@
|
||||
from ctypes import c_int, c_bool, c_void_p, c_uint64, c_uint, POINTER
|
||||
import enum
|
||||
|
||||
from llvmlite import ir
|
||||
from llvmlite.binding import ffi
|
||||
|
||||
|
||||
class TypeKind(enum.IntEnum):
|
||||
# The LLVMTypeKind enum from llvm-c/Core.h
|
||||
|
||||
void = 0
|
||||
half = 1
|
||||
float = 2
|
||||
double = 3
|
||||
x86_fp80 = 4
|
||||
fp128 = 5
|
||||
ppc_fp128 = 6
|
||||
label = 7
|
||||
integer = 8
|
||||
function = 9
|
||||
struct = 10
|
||||
array = 11
|
||||
pointer = 12
|
||||
vector = 13
|
||||
metadata = 14
|
||||
x86_mmx = 15
|
||||
token = 16
|
||||
scalable_vector = 17
|
||||
bfloat = 18
|
||||
x86_amx = 19
|
||||
|
||||
|
||||
_TypeKindToIRType = {
|
||||
# All TypeKind here must have a TypeRef.as_ir() implementation
|
||||
TypeKind.void: ir.VoidType,
|
||||
TypeKind.half: ir.HalfType,
|
||||
TypeKind.float: ir.FloatType,
|
||||
TypeKind.double: ir.DoubleType,
|
||||
TypeKind.integer: ir.IntType,
|
||||
TypeKind.function: ir.FunctionType,
|
||||
TypeKind.pointer: ir.PointerType,
|
||||
TypeKind.array: ir.ArrayType,
|
||||
TypeKind.vector: ir.VectorType,
|
||||
TypeKind.struct: ir.LiteralStructType,
|
||||
}
|
||||
|
||||
|
||||
class TypeRef(ffi.ObjectRef):
|
||||
"""A weak reference to a LLVM type
|
||||
"""
|
||||
@property
|
||||
def name(self):
|
||||
"""
|
||||
Get type name
|
||||
"""
|
||||
return ffi.ret_string(ffi.lib.LLVMPY_GetTypeName(self))
|
||||
|
||||
@property
|
||||
def is_struct(self):
|
||||
"""
|
||||
Returns true if the type is a struct type.
|
||||
"""
|
||||
return ffi.lib.LLVMPY_TypeIsStruct(self)
|
||||
|
||||
@property
|
||||
def is_pointer(self):
|
||||
"""
|
||||
Returns true if the type is a pointer type.
|
||||
"""
|
||||
return ffi.lib.LLVMPY_TypeIsPointer(self)
|
||||
|
||||
@property
|
||||
def is_array(self):
|
||||
"""
|
||||
Returns true if the type is an array type.
|
||||
"""
|
||||
return ffi.lib.LLVMPY_TypeIsArray(self)
|
||||
|
||||
@property
|
||||
def is_vector(self):
|
||||
"""
|
||||
Returns true if the type is a vector type.
|
||||
"""
|
||||
return ffi.lib.LLVMPY_TypeIsVector(self)
|
||||
|
||||
@property
|
||||
def is_function(self):
|
||||
"""
|
||||
Returns true if the type is a function type.
|
||||
"""
|
||||
return ffi.lib.LLVMPY_TypeIsFunction(self)
|
||||
|
||||
@property
|
||||
def is_function_vararg(self):
|
||||
"""
|
||||
Returns true if a function type accepts a variable number of arguments.
|
||||
When the type is not a function, raises exception.
|
||||
"""
|
||||
if self.type_kind != TypeKind.function:
|
||||
raise ValueError("Type {} is not a function".format(self))
|
||||
return ffi.lib.LLVMPY_IsFunctionVararg(self)
|
||||
|
||||
@property
|
||||
def elements(self):
|
||||
"""
|
||||
Returns iterator over enclosing types
|
||||
"""
|
||||
if self.is_pointer:
|
||||
raise ValueError("Type {} doesn't contain elements.".format(self))
|
||||
return _TypeListIterator(ffi.lib.LLVMPY_ElementIter(self))
|
||||
|
||||
@property
|
||||
def element_count(self):
|
||||
"""
|
||||
Returns the number of elements in an array or a vector. For scalable
|
||||
vectors, returns minimum number of elements. When the type is neither
|
||||
an array nor a vector, raises exception.
|
||||
"""
|
||||
if not self.is_array and not self.is_vector:
|
||||
raise ValueError("Type {} is not an array nor vector".format(self))
|
||||
return ffi.lib.LLVMPY_GetTypeElementCount(self)
|
||||
|
||||
@property
|
||||
def type_width(self):
|
||||
"""
|
||||
Return the basic size of this type if it is a primitive type. These are
|
||||
fixed by LLVM and are not target-dependent.
|
||||
This will return zero if the type does not have a size or is not a
|
||||
primitive type.
|
||||
|
||||
If this is a scalable vector type, the scalable property will be set and
|
||||
the runtime size will be a positive integer multiple of the base size.
|
||||
|
||||
Note that this may not reflect the size of memory allocated for an
|
||||
instance of the type or the number of bytes that are written when an
|
||||
instance of the type is stored to memory.
|
||||
"""
|
||||
return ffi.lib.LLVMPY_GetTypeBitWidth(self)
|
||||
|
||||
@property
|
||||
def type_kind(self):
|
||||
"""
|
||||
Returns the LLVMTypeKind enumeration of this type.
|
||||
"""
|
||||
return TypeKind(ffi.lib.LLVMPY_GetTypeKind(self))
|
||||
|
||||
@property
|
||||
def is_packed_struct(self):
|
||||
return ffi.lib.LLVMPY_IsPackedStruct(self)
|
||||
|
||||
@property
|
||||
def is_literal_struct(self):
|
||||
return ffi.lib.LLVMPY_IsLiteralStruct(self)
|
||||
|
||||
@property
|
||||
def is_opaque_struct(self):
|
||||
return ffi.lib.LLVMPY_IsOpaqueStruct(self)
|
||||
|
||||
def get_function_parameters(self) -> tuple["TypeRef"]:
|
||||
nparams = ffi.lib.LLVMPY_CountParamTypes(self)
|
||||
if nparams > 0:
|
||||
out_buffer = (ffi.LLVMTypeRef * nparams)(None)
|
||||
ffi.lib.LLVMPY_GetParamTypes(self, out_buffer)
|
||||
return tuple(map(TypeRef, out_buffer))
|
||||
else:
|
||||
return ()
|
||||
|
||||
def get_function_return(self) -> "TypeRef":
|
||||
return TypeRef(ffi.lib.LLVMPY_GetReturnType(self))
|
||||
|
||||
def as_ir(self, ir_ctx: ir.Context) -> ir.Type:
|
||||
"""Convert into a ``llvmlite.ir.Type``.
|
||||
"""
|
||||
try:
|
||||
cls = _TypeKindToIRType[self.type_kind]
|
||||
except KeyError:
|
||||
msg = f"as_ir() unsupported for TypeRef of {self.type_kind}"
|
||||
raise TypeError(msg)
|
||||
else:
|
||||
return cls.from_llvm(self, ir_ctx)
|
||||
|
||||
def __str__(self):
|
||||
return ffi.ret_string(ffi.lib.LLVMPY_PrintType(self))
|
||||
|
||||
|
||||
class _TypeIterator(ffi.ObjectRef):
|
||||
|
||||
def __next__(self):
|
||||
vp = self._next()
|
||||
if vp:
|
||||
return TypeRef(vp)
|
||||
else:
|
||||
raise StopIteration
|
||||
|
||||
next = __next__
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
|
||||
class _TypeListIterator(_TypeIterator):
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeElementIter(self)
|
||||
|
||||
def _next(self):
|
||||
return ffi.lib.LLVMPY_ElementIterNext(self)
|
||||
|
||||
|
||||
# FFI
|
||||
|
||||
ffi.lib.LLVMPY_PrintType.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_PrintType.restype = c_void_p
|
||||
|
||||
ffi.lib.LLVMPY_TypeIsPointer.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_TypeIsPointer.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_TypeIsArray.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_TypeIsArray.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_TypeIsVector.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_TypeIsVector.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_TypeIsStruct.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_TypeIsStruct.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_TypeIsFunction.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_TypeIsFunction.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_IsPackedStruct.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_IsPackedStruct.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_IsOpaqueStruct.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_IsOpaqueStruct.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_IsLiteralStruct.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_IsLiteralStruct.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_GetReturnType.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_GetReturnType.restype = ffi.LLVMTypeRef
|
||||
|
||||
ffi.lib.LLVMPY_CountParamTypes.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_CountParamTypes.restype = c_uint
|
||||
|
||||
ffi.lib.LLVMPY_GetParamTypes.argtypes = [ffi.LLVMTypeRef,
|
||||
POINTER(ffi.LLVMTypeRef)]
|
||||
ffi.lib.LLVMPY_GetParamTypes.restype = None
|
||||
|
||||
ffi.lib.LLVMPY_IsFunctionVararg.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_IsFunctionVararg.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_GetTypeKind.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_GetTypeKind.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_GetTypeElementCount.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_GetTypeElementCount.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_GetTypeBitWidth.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_GetTypeBitWidth.restype = c_uint64
|
||||
|
||||
ffi.lib.LLVMPY_ElementIter.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_ElementIter.restype = ffi.LLVMElementIterator
|
||||
|
||||
ffi.lib.LLVMPY_ElementIterNext.argtypes = [ffi.LLVMElementIterator]
|
||||
ffi.lib.LLVMPY_ElementIterNext.restype = ffi.LLVMTypeRef
|
||||
|
||||
ffi.lib.LLVMPY_DisposeElementIter.argtypes = [ffi.LLVMElementIterator]
|
||||
@@ -0,0 +1,632 @@
|
||||
from ctypes import (POINTER, byref, cast, c_char_p, c_double, c_int, c_size_t,
|
||||
c_uint, c_uint64, c_bool, c_void_p)
|
||||
import enum
|
||||
|
||||
from llvmlite.binding import ffi
|
||||
from llvmlite.binding.common import _decode_string, _encode_string
|
||||
from llvmlite.binding.typeref import TypeRef
|
||||
|
||||
|
||||
class Linkage(enum.IntEnum):
|
||||
# The LLVMLinkage enum from llvm-c/Core.h
|
||||
|
||||
external = 0
|
||||
available_externally = 1
|
||||
linkonce_any = 2
|
||||
linkonce_odr = 3
|
||||
linkonce_odr_autohide = 4
|
||||
weak_any = 5
|
||||
weak_odr = 6
|
||||
appending = 7
|
||||
internal = 8
|
||||
private = 9
|
||||
dllimport = 10
|
||||
dllexport = 11
|
||||
external_weak = 12
|
||||
ghost = 13
|
||||
common = 14
|
||||
linker_private = 15
|
||||
linker_private_weak = 16
|
||||
|
||||
|
||||
class Visibility(enum.IntEnum):
|
||||
# The LLVMVisibility enum from llvm-c/Core.h
|
||||
|
||||
default = 0
|
||||
hidden = 1
|
||||
protected = 2
|
||||
|
||||
|
||||
class StorageClass(enum.IntEnum):
|
||||
# The LLVMDLLStorageClass enum from llvm-c/Core.h
|
||||
|
||||
default = 0
|
||||
dllimport = 1
|
||||
dllexport = 2
|
||||
|
||||
|
||||
class ValueKind(enum.IntEnum):
|
||||
# The LLVMValueKind enum from llvm-c/Core.h
|
||||
|
||||
argument = 0
|
||||
basic_block = 1
|
||||
memory_use = 2
|
||||
memory_def = 3
|
||||
memory_phi = 4
|
||||
|
||||
function = 5
|
||||
global_alias = 6
|
||||
global_ifunc = 7
|
||||
global_variable = 8
|
||||
block_address = 9
|
||||
constant_expr = 10
|
||||
constant_array = 11
|
||||
constant_struct = 12
|
||||
constant_vector = 13
|
||||
|
||||
undef_value = 14
|
||||
constant_aggregate_zero = 15
|
||||
constant_data_array = 16
|
||||
constant_data_vector = 17
|
||||
constant_int = 18
|
||||
constant_fp = 19
|
||||
constant_pointer_null = 20
|
||||
constant_token_none = 21
|
||||
|
||||
metadata_as_value = 22
|
||||
inline_asm = 23
|
||||
|
||||
instruction = 24
|
||||
poison_value = 25
|
||||
|
||||
|
||||
class ValueRef(ffi.ObjectRef):
|
||||
"""A weak reference to a LLVM value.
|
||||
"""
|
||||
|
||||
def __init__(self, ptr, kind, parents):
|
||||
self._kind = kind
|
||||
self._parents = parents
|
||||
ffi.ObjectRef.__init__(self, ptr)
|
||||
|
||||
def __str__(self):
|
||||
with ffi.OutputString() as outstr:
|
||||
ffi.lib.LLVMPY_PrintValueToString(self, outstr)
|
||||
return str(outstr)
|
||||
|
||||
@property
|
||||
def module(self):
|
||||
"""
|
||||
The module this function or global variable value was obtained from.
|
||||
"""
|
||||
return self._parents.get('module')
|
||||
|
||||
@property
|
||||
def function(self):
|
||||
"""
|
||||
The function this argument or basic block value was obtained from.
|
||||
"""
|
||||
return self._parents.get('function')
|
||||
|
||||
@property
|
||||
def block(self):
|
||||
"""
|
||||
The block this instruction value was obtained from.
|
||||
"""
|
||||
return self._parents.get('block')
|
||||
|
||||
@property
|
||||
def instruction(self):
|
||||
"""
|
||||
The instruction this operand value was obtained from.
|
||||
"""
|
||||
return self._parents.get('instruction')
|
||||
|
||||
@property
|
||||
def is_global(self):
|
||||
return self._kind == 'global'
|
||||
|
||||
@property
|
||||
def is_function(self):
|
||||
return self._kind == 'function'
|
||||
|
||||
@property
|
||||
def is_block(self):
|
||||
return self._kind == 'block'
|
||||
|
||||
@property
|
||||
def is_argument(self):
|
||||
return self._kind == 'argument'
|
||||
|
||||
@property
|
||||
def is_instruction(self):
|
||||
return self._kind == 'instruction'
|
||||
|
||||
@property
|
||||
def is_operand(self):
|
||||
return self._kind == 'operand'
|
||||
|
||||
@property
|
||||
def is_constant(self):
|
||||
return bool(ffi.lib.LLVMPY_IsConstant(self))
|
||||
|
||||
@property
|
||||
def value_kind(self):
|
||||
return ValueKind(ffi.lib.LLVMPY_GetValueKind(self))
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return _decode_string(ffi.lib.LLVMPY_GetValueName(self))
|
||||
|
||||
@name.setter
|
||||
def name(self, val):
|
||||
ffi.lib.LLVMPY_SetValueName(self, _encode_string(val))
|
||||
|
||||
@property
|
||||
def linkage(self):
|
||||
return Linkage(ffi.lib.LLVMPY_GetLinkage(self))
|
||||
|
||||
@linkage.setter
|
||||
def linkage(self, value):
|
||||
if not isinstance(value, Linkage):
|
||||
value = Linkage[value]
|
||||
ffi.lib.LLVMPY_SetLinkage(self, value)
|
||||
|
||||
@property
|
||||
def visibility(self):
|
||||
return Visibility(ffi.lib.LLVMPY_GetVisibility(self))
|
||||
|
||||
@visibility.setter
|
||||
def visibility(self, value):
|
||||
if not isinstance(value, Visibility):
|
||||
value = Visibility[value]
|
||||
ffi.lib.LLVMPY_SetVisibility(self, value)
|
||||
|
||||
@property
|
||||
def storage_class(self):
|
||||
return StorageClass(ffi.lib.LLVMPY_GetDLLStorageClass(self))
|
||||
|
||||
@storage_class.setter
|
||||
def storage_class(self, value):
|
||||
if not isinstance(value, StorageClass):
|
||||
value = StorageClass[value]
|
||||
ffi.lib.LLVMPY_SetDLLStorageClass(self, value)
|
||||
|
||||
def add_function_attribute(self, attr):
|
||||
"""Only works on function value
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
attr : str
|
||||
attribute name
|
||||
"""
|
||||
if not self.is_function:
|
||||
raise ValueError('expected function value, got %s' % (self._kind,))
|
||||
attrname = str(attr)
|
||||
attrval = ffi.lib.LLVMPY_GetEnumAttributeKindForName(
|
||||
_encode_string(attrname), len(attrname))
|
||||
if attrval == 0:
|
||||
raise ValueError('no such attribute {!r}'.format(attrname))
|
||||
ffi.lib.LLVMPY_AddFunctionAttr(self, attrval)
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""
|
||||
This value's LLVM type.
|
||||
"""
|
||||
# XXX what does this return?
|
||||
return TypeRef(ffi.lib.LLVMPY_TypeOf(self))
|
||||
|
||||
@property
|
||||
def global_value_type(self):
|
||||
"""
|
||||
Uses ``LLVMGlobalGetValueType()``.
|
||||
Needed for opaque pointers in globals.
|
||||
> For globals, use getValueType().
|
||||
See https://llvm.org/docs/OpaquePointers.html#migration-instructions
|
||||
"""
|
||||
assert self.is_global or self.is_function
|
||||
return TypeRef(ffi.lib.LLVMPY_GlobalGetValueType(self))
|
||||
|
||||
@property
|
||||
def is_declaration(self):
|
||||
"""
|
||||
Whether this value (presumably global) is defined in the current
|
||||
module.
|
||||
"""
|
||||
if not (self.is_global or self.is_function):
|
||||
raise ValueError('expected global or function value, got %s'
|
||||
% (self._kind,))
|
||||
return ffi.lib.LLVMPY_IsDeclaration(self)
|
||||
|
||||
@property
|
||||
def attributes(self):
|
||||
"""
|
||||
Return an iterator over this value's attributes.
|
||||
The iterator will yield a string for each attribute.
|
||||
"""
|
||||
itr = iter(())
|
||||
if self.is_function:
|
||||
it = ffi.lib.LLVMPY_FunctionAttributesIter(self)
|
||||
itr = _AttributeListIterator(it)
|
||||
elif self.is_instruction:
|
||||
if self.opcode == 'call':
|
||||
it = ffi.lib.LLVMPY_CallInstAttributesIter(self)
|
||||
itr = _AttributeListIterator(it)
|
||||
elif self.opcode == 'invoke':
|
||||
it = ffi.lib.LLVMPY_InvokeInstAttributesIter(self)
|
||||
itr = _AttributeListIterator(it)
|
||||
elif self.is_global:
|
||||
it = ffi.lib.LLVMPY_GlobalAttributesIter(self)
|
||||
itr = _AttributeSetIterator(it)
|
||||
elif self.is_argument:
|
||||
it = ffi.lib.LLVMPY_ArgumentAttributesIter(self)
|
||||
itr = _AttributeSetIterator(it)
|
||||
return itr
|
||||
|
||||
@property
|
||||
def blocks(self):
|
||||
"""
|
||||
Return an iterator over this function's blocks.
|
||||
The iterator will yield a ValueRef for each block.
|
||||
"""
|
||||
if not self.is_function:
|
||||
raise ValueError('expected function value, got %s' % (self._kind,))
|
||||
it = ffi.lib.LLVMPY_FunctionBlocksIter(self)
|
||||
parents = self._parents.copy()
|
||||
parents.update(function=self)
|
||||
return _BlocksIterator(it, parents)
|
||||
|
||||
@property
|
||||
def arguments(self):
|
||||
"""
|
||||
Return an iterator over this function's arguments.
|
||||
The iterator will yield a ValueRef for each argument.
|
||||
"""
|
||||
if not self.is_function:
|
||||
raise ValueError('expected function value, got %s' % (self._kind,))
|
||||
it = ffi.lib.LLVMPY_FunctionArgumentsIter(self)
|
||||
parents = self._parents.copy()
|
||||
parents.update(function=self)
|
||||
return _ArgumentsIterator(it, parents)
|
||||
|
||||
@property
|
||||
def instructions(self):
|
||||
"""
|
||||
Return an iterator over this block's instructions.
|
||||
The iterator will yield a ValueRef for each instruction.
|
||||
"""
|
||||
if not self.is_block:
|
||||
raise ValueError('expected block value, got %s' % (self._kind,))
|
||||
it = ffi.lib.LLVMPY_BlockInstructionsIter(self)
|
||||
parents = self._parents.copy()
|
||||
parents.update(block=self)
|
||||
return _InstructionsIterator(it, parents)
|
||||
|
||||
@property
|
||||
def operands(self):
|
||||
"""
|
||||
Return an iterator over this instruction's operands.
|
||||
The iterator will yield a ValueRef for each operand.
|
||||
"""
|
||||
if not self.is_instruction:
|
||||
raise ValueError('expected instruction value, got %s'
|
||||
% (self._kind,))
|
||||
it = ffi.lib.LLVMPY_InstructionOperandsIter(self)
|
||||
parents = self._parents.copy()
|
||||
parents.update(instruction=self)
|
||||
return _OperandsIterator(it, parents)
|
||||
|
||||
@property
|
||||
def opcode(self):
|
||||
if not self.is_instruction:
|
||||
raise ValueError('expected instruction value, got %s'
|
||||
% (self._kind,))
|
||||
return ffi.ret_string(ffi.lib.LLVMPY_GetOpcodeName(self))
|
||||
|
||||
@property
|
||||
def incoming_blocks(self):
|
||||
"""
|
||||
Return an iterator over this phi instruction's incoming blocks.
|
||||
The iterator will yield a ValueRef for each block.
|
||||
"""
|
||||
if not self.is_instruction or self.opcode != 'phi':
|
||||
raise ValueError('expected phi instruction value, got %s'
|
||||
% (self._kind,))
|
||||
it = ffi.lib.LLVMPY_PhiIncomingBlocksIter(self)
|
||||
parents = self._parents.copy()
|
||||
parents.update(instruction=self)
|
||||
return _IncomingBlocksIterator(it, parents)
|
||||
|
||||
def get_constant_value(self, signed_int=False, round_fp=False):
|
||||
"""
|
||||
Return the constant value, either as a literal (when supported)
|
||||
or as a string.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
signed_int : bool
|
||||
if True and the constant is an integer, returns a signed version
|
||||
round_fp : bool
|
||||
if True and the constant is a floating point value, rounds the
|
||||
result upon accuracy loss (e.g., when querying an fp128 value).
|
||||
By default, raises an exception on accuracy loss
|
||||
"""
|
||||
if not self.is_constant:
|
||||
raise ValueError('expected constant value, got %s'
|
||||
% (self._kind,))
|
||||
|
||||
if self.value_kind == ValueKind.constant_int:
|
||||
# Python integers are also arbitrary-precision
|
||||
little_endian = c_bool(False)
|
||||
words = ffi.lib.LLVMPY_GetConstantIntNumWords(self)
|
||||
ptr = ffi.lib.LLVMPY_GetConstantIntRawValue(
|
||||
self, byref(little_endian))
|
||||
asbytes = bytes(cast(ptr, POINTER(c_uint64 * words)).contents)
|
||||
return int.from_bytes(
|
||||
asbytes,
|
||||
('little' if little_endian.value else 'big'),
|
||||
signed=signed_int,
|
||||
)
|
||||
elif self.value_kind == ValueKind.constant_fp:
|
||||
# Convert floating-point values to double-precision (Python float)
|
||||
accuracy_loss = c_bool(False)
|
||||
value = ffi.lib.LLVMPY_GetConstantFPValue(self,
|
||||
byref(accuracy_loss))
|
||||
if accuracy_loss.value and not round_fp:
|
||||
raise ValueError(
|
||||
'Accuracy loss encountered in conversion of constant '
|
||||
f'value {str(self)}')
|
||||
|
||||
return value
|
||||
|
||||
# Otherwise, return the IR string
|
||||
return str(self)
|
||||
|
||||
|
||||
class _ValueIterator(ffi.ObjectRef):
|
||||
|
||||
kind = None # derived classes must specify the Value kind value
|
||||
# as class attribute
|
||||
|
||||
def __init__(self, ptr, parents):
|
||||
ffi.ObjectRef.__init__(self, ptr)
|
||||
# Keep parent objects (module, function, etc) alive
|
||||
self._parents = parents
|
||||
if self.kind is None:
|
||||
raise NotImplementedError('%s must specify kind attribute'
|
||||
% (type(self).__name__,))
|
||||
|
||||
def __next__(self):
|
||||
vp = self._next()
|
||||
if vp:
|
||||
return ValueRef(vp, self.kind, self._parents)
|
||||
else:
|
||||
raise StopIteration
|
||||
|
||||
next = __next__
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
|
||||
class _AttributeIterator(ffi.ObjectRef):
|
||||
|
||||
def __next__(self):
|
||||
vp = self._next()
|
||||
if vp:
|
||||
return vp
|
||||
else:
|
||||
raise StopIteration
|
||||
|
||||
next = __next__
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
|
||||
class _AttributeListIterator(_AttributeIterator):
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeAttributeListIter(self)
|
||||
|
||||
def _next(self):
|
||||
return ffi.ret_bytes(ffi.lib.LLVMPY_AttributeListIterNext(self))
|
||||
|
||||
|
||||
class _AttributeSetIterator(_AttributeIterator):
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeAttributeSetIter(self)
|
||||
|
||||
def _next(self):
|
||||
return ffi.ret_bytes(ffi.lib.LLVMPY_AttributeSetIterNext(self))
|
||||
|
||||
|
||||
class _BlocksIterator(_ValueIterator):
|
||||
|
||||
kind = 'block'
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeBlocksIter(self)
|
||||
|
||||
def _next(self):
|
||||
return ffi.lib.LLVMPY_BlocksIterNext(self)
|
||||
|
||||
|
||||
class _ArgumentsIterator(_ValueIterator):
|
||||
|
||||
kind = 'argument'
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeArgumentsIter(self)
|
||||
|
||||
def _next(self):
|
||||
return ffi.lib.LLVMPY_ArgumentsIterNext(self)
|
||||
|
||||
|
||||
class _InstructionsIterator(_ValueIterator):
|
||||
|
||||
kind = 'instruction'
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeInstructionsIter(self)
|
||||
|
||||
def _next(self):
|
||||
return ffi.lib.LLVMPY_InstructionsIterNext(self)
|
||||
|
||||
|
||||
class _OperandsIterator(_ValueIterator):
|
||||
|
||||
kind = 'operand'
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeOperandsIter(self)
|
||||
|
||||
def _next(self):
|
||||
return ffi.lib.LLVMPY_OperandsIterNext(self)
|
||||
|
||||
|
||||
class _IncomingBlocksIterator(_ValueIterator):
|
||||
|
||||
kind = 'block'
|
||||
|
||||
def _dispose(self):
|
||||
self._capi.LLVMPY_DisposeIncomingBlocksIter(self)
|
||||
|
||||
def _next(self):
|
||||
return ffi.lib.LLVMPY_IncomingBlocksIterNext(self)
|
||||
|
||||
|
||||
# FFI
|
||||
|
||||
ffi.lib.LLVMPY_PrintValueToString.argtypes = [
|
||||
ffi.LLVMValueRef,
|
||||
POINTER(c_char_p)
|
||||
]
|
||||
|
||||
ffi.lib.LLVMPY_GetGlobalParent.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_GetGlobalParent.restype = ffi.LLVMModuleRef
|
||||
|
||||
ffi.lib.LLVMPY_GetValueName.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_GetValueName.restype = c_char_p
|
||||
|
||||
ffi.lib.LLVMPY_SetValueName.argtypes = [ffi.LLVMValueRef, c_char_p]
|
||||
|
||||
ffi.lib.LLVMPY_TypeOf.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_TypeOf.restype = ffi.LLVMTypeRef
|
||||
|
||||
ffi.lib.LLVMPY_GlobalGetValueType.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_GlobalGetValueType.restype = ffi.LLVMTypeRef
|
||||
|
||||
ffi.lib.LLVMPY_GetTypeName.argtypes = [ffi.LLVMTypeRef]
|
||||
ffi.lib.LLVMPY_GetTypeName.restype = c_void_p
|
||||
|
||||
ffi.lib.LLVMPY_GetLinkage.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_GetLinkage.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_SetLinkage.argtypes = [ffi.LLVMValueRef, c_int]
|
||||
|
||||
ffi.lib.LLVMPY_GetVisibility.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_GetVisibility.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_SetVisibility.argtypes = [ffi.LLVMValueRef, c_int]
|
||||
|
||||
ffi.lib.LLVMPY_GetDLLStorageClass.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_GetDLLStorageClass.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_SetDLLStorageClass.argtypes = [ffi.LLVMValueRef, c_int]
|
||||
|
||||
ffi.lib.LLVMPY_GetEnumAttributeKindForName.argtypes = [c_char_p, c_size_t]
|
||||
ffi.lib.LLVMPY_GetEnumAttributeKindForName.restype = c_uint
|
||||
|
||||
ffi.lib.LLVMPY_AddFunctionAttr.argtypes = [ffi.LLVMValueRef, c_uint]
|
||||
|
||||
ffi.lib.LLVMPY_IsDeclaration.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_IsDeclaration.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_FunctionAttributesIter.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_FunctionAttributesIter.restype = ffi.LLVMAttributeListIterator
|
||||
|
||||
ffi.lib.LLVMPY_CallInstAttributesIter.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_CallInstAttributesIter.restype = ffi.LLVMAttributeListIterator
|
||||
|
||||
ffi.lib.LLVMPY_InvokeInstAttributesIter.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_InvokeInstAttributesIter.restype = ffi.LLVMAttributeListIterator
|
||||
|
||||
ffi.lib.LLVMPY_GlobalAttributesIter.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_GlobalAttributesIter.restype = ffi.LLVMAttributeSetIterator
|
||||
|
||||
ffi.lib.LLVMPY_ArgumentAttributesIter.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_ArgumentAttributesIter.restype = ffi.LLVMAttributeSetIterator
|
||||
|
||||
ffi.lib.LLVMPY_FunctionBlocksIter.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_FunctionBlocksIter.restype = ffi.LLVMBlocksIterator
|
||||
|
||||
ffi.lib.LLVMPY_FunctionArgumentsIter.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_FunctionArgumentsIter.restype = ffi.LLVMArgumentsIterator
|
||||
|
||||
ffi.lib.LLVMPY_BlockInstructionsIter.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_BlockInstructionsIter.restype = ffi.LLVMInstructionsIterator
|
||||
|
||||
ffi.lib.LLVMPY_InstructionOperandsIter.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_InstructionOperandsIter.restype = ffi.LLVMOperandsIterator
|
||||
|
||||
ffi.lib.LLVMPY_PhiIncomingBlocksIter.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_PhiIncomingBlocksIter.restype = ffi.LLVMIncomingBlocksIterator
|
||||
|
||||
ffi.lib.LLVMPY_DisposeAttributeListIter.argtypes = [
|
||||
ffi.LLVMAttributeListIterator]
|
||||
|
||||
ffi.lib.LLVMPY_DisposeAttributeSetIter.argtypes = [ffi.LLVMAttributeSetIterator]
|
||||
|
||||
ffi.lib.LLVMPY_DisposeBlocksIter.argtypes = [ffi.LLVMBlocksIterator]
|
||||
|
||||
ffi.lib.LLVMPY_DisposeInstructionsIter.argtypes = [ffi.LLVMInstructionsIterator]
|
||||
|
||||
ffi.lib.LLVMPY_DisposeOperandsIter.argtypes = [ffi.LLVMOperandsIterator]
|
||||
|
||||
ffi.lib.LLVMPY_DisposeIncomingBlocksIter.argtypes = [
|
||||
ffi.LLVMIncomingBlocksIterator]
|
||||
|
||||
ffi.lib.LLVMPY_AttributeListIterNext.argtypes = [ffi.LLVMAttributeListIterator]
|
||||
ffi.lib.LLVMPY_AttributeListIterNext.restype = c_void_p
|
||||
|
||||
ffi.lib.LLVMPY_AttributeSetIterNext.argtypes = [ffi.LLVMAttributeSetIterator]
|
||||
ffi.lib.LLVMPY_AttributeSetIterNext.restype = c_void_p
|
||||
|
||||
ffi.lib.LLVMPY_BlocksIterNext.argtypes = [ffi.LLVMBlocksIterator]
|
||||
ffi.lib.LLVMPY_BlocksIterNext.restype = ffi.LLVMValueRef
|
||||
|
||||
ffi.lib.LLVMPY_ArgumentsIterNext.argtypes = [ffi.LLVMArgumentsIterator]
|
||||
ffi.lib.LLVMPY_ArgumentsIterNext.restype = ffi.LLVMValueRef
|
||||
|
||||
ffi.lib.LLVMPY_InstructionsIterNext.argtypes = [ffi.LLVMInstructionsIterator]
|
||||
ffi.lib.LLVMPY_InstructionsIterNext.restype = ffi.LLVMValueRef
|
||||
|
||||
ffi.lib.LLVMPY_OperandsIterNext.argtypes = [ffi.LLVMOperandsIterator]
|
||||
ffi.lib.LLVMPY_OperandsIterNext.restype = ffi.LLVMValueRef
|
||||
|
||||
ffi.lib.LLVMPY_IncomingBlocksIterNext.argtypes = [
|
||||
ffi.LLVMIncomingBlocksIterator]
|
||||
ffi.lib.LLVMPY_IncomingBlocksIterNext.restype = ffi.LLVMValueRef
|
||||
|
||||
ffi.lib.LLVMPY_GetOpcodeName.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_GetOpcodeName.restype = c_void_p
|
||||
|
||||
ffi.lib.LLVMPY_IsConstant.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_IsConstant.restype = c_bool
|
||||
|
||||
ffi.lib.LLVMPY_GetValueKind.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_GetValueKind.restype = c_int
|
||||
|
||||
ffi.lib.LLVMPY_GetConstantIntRawValue.argtypes = [ffi.LLVMValueRef,
|
||||
POINTER(c_bool)]
|
||||
ffi.lib.LLVMPY_GetConstantIntRawValue.restype = POINTER(c_uint64)
|
||||
|
||||
ffi.lib.LLVMPY_GetConstantIntNumWords.argtypes = [ffi.LLVMValueRef]
|
||||
ffi.lib.LLVMPY_GetConstantIntNumWords.restype = c_uint
|
||||
|
||||
ffi.lib.LLVMPY_GetConstantFPValue.argtypes = [ffi.LLVMValueRef,
|
||||
POINTER(c_bool)]
|
||||
ffi.lib.LLVMPY_GetConstantFPValue.restype = c_double
|
||||
Reference in New Issue
Block a user