This commit is contained in:
2026-04-10 15:06:59 +02:00
parent 3031b7153b
commit e5a4711004
7806 changed files with 1918528 additions and 335 deletions

View File

@@ -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 *

View File

@@ -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]

View File

@@ -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]

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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]

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -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