Skip to content

Commit

Permalink
Merge pull request #388 from alendit/custom_context
Browse files Browse the repository at this point in the history
Allow creation of other contexts besides the global one
  • Loading branch information
sklam committed Sep 28, 2018
2 parents 571f5ea + 4fc9524 commit 80d8f86
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 19 deletions.
22 changes: 22 additions & 0 deletions docs/source/user-guide/binding/context.rst
@@ -0,0 +1,22 @@
Context
============

LLVMContext is an opaque context reference used to group modules into logical groups.
For example, the type names are unique within a context, the name collisions
are resolved by LLVM automatically.

LLVMContextRef
--------------

A wrapper around LLVMContext. Should not be instantiated directly, use the
following methods:

.. class:: LLVMContextRef

* .. function:: create_context():

Create a new LLVMContext instance.

* .. function:: get_global_context():

Get the reference to the global context.
1 change: 1 addition & 0 deletions docs/source/user-guide/binding/index.rst
Expand Up @@ -22,6 +22,7 @@ implement Numba_'s JIT compiler.
initialization-finalization
dynamic-libraries
target-information
context
modules
value-references
type-references
Expand Down
12 changes: 10 additions & 2 deletions docs/source/user-guide/binding/modules.rst
Expand Up @@ -24,21 +24,29 @@ Factory functions

You can create a module from the following factory functions:

* .. function:: parse_assembly(llvmir)
* .. function:: parse_assembly(llvmir, context=None)

Parse the given *llvmir*, a string containing some LLVM IR
code. If parsing is successful, a new :class:`ModuleRef`
instance is returned.

* context: an instance of :class:`LLVMContextRef`.

Defaults to the global context.

EXAMPLE: You can obtain *llvmir* by calling ``str()`` on an
:class:`llvmlite.ir.Module` object.

* .. function:: parse_bitcode(bitcode)
* .. function:: parse_bitcode(bitcode, context=None)

Parse the given *bitcode*, a bytestring containing the
LLVM bitcode of a module. If parsing is successful, a new
:class:`ModuleRef` instance is returned.

* context: an instance of :class:`LLVMContextRef`.

Defaults to the global context.

EXAMPLE: You can obtain the *bitcode* by calling
:meth:`ModuleRef.as_bitcode`.

Expand Down
10 changes: 10 additions & 0 deletions ffi/core.cpp
Expand Up @@ -29,6 +29,16 @@ LLVMPY_GetGlobalContext() {
return LLVMGetGlobalContext();
}

API_EXPORT(LLVMContextRef)
LLVMPY_ContextCreate() {
return LLVMContextCreate();
}

API_EXPORT(void)
LLVMPY_ContextDispose(LLVMContextRef context) {
return LLVMContextDispose(context);
}

API_EXPORT(void)
LLVMPY_SetCommandLine(const char *name, const char *option)
{
Expand Down
3 changes: 3 additions & 0 deletions ffi/core.h
Expand Up @@ -30,6 +30,9 @@ LLVMPY_DisposeString(const char *msg);
API_EXPORT(LLVMContextRef)
LLVMPY_GetGlobalContext();

API_EXPORT(LLVMContextRef)
LLVMPY_ContextCreate();

} /* end extern "C" */


Expand Down
1 change: 1 addition & 0 deletions llvmlite/binding/__init__.py
Expand Up @@ -15,3 +15,4 @@
from .value import *
from .analysis import *
from .object_file import *
from .context import *
30 changes: 30 additions & 0 deletions llvmlite/binding/context.py
@@ -0,0 +1,30 @@
from __future__ import print_function, absolute_import

from . 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]
6 changes: 6 additions & 0 deletions llvmlite/binding/ffi.py
Expand Up @@ -246,6 +246,12 @@ def __del__(self):
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?
Expand Down
29 changes: 18 additions & 11 deletions llvmlite/binding/module.py
Expand Up @@ -6,33 +6,38 @@
from .linker import link_modules
from .common import _decode_string, _encode_string
from .value import ValueRef, TypeRef
from .context import get_global_context


def parse_assembly(llvmir):
def parse_assembly(llvmir, context=None):
"""
Create Module from a LLVM IR string
"""
context = ffi.lib.LLVMPY_GetGlobalContext()
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))
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):
def parse_bitcode(bitcode, context=None):
"""
Create Module from a LLVM *bitcode* (a bytes object).
"""
context = ffi.lib.LLVMPY_GetGlobalContext()
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, buf, bufsize, errmsg), context)
if errmsg:
mod.close()
raise RuntimeError(
Expand All @@ -45,6 +50,10 @@ 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)
Expand Down Expand Up @@ -190,7 +199,7 @@ def struct_types(self):
return _TypesIterator(it, module=self)

def clone(self):
return ModuleRef(ffi.lib.LLVMPY_CloneModule(self))
return ModuleRef(ffi.lib.LLVMPY_CloneModule(self), self._context)


class _Iterator(ffi.ObjectRef):
Expand Down Expand Up @@ -243,11 +252,11 @@ def __next__(self):
else:
raise StopIteration

next = __next__

def _next(self):
return ffi.lib.LLVMPY_TypesIterNext(self)

next = __next__


# =============================================================================
# Set function FFI
Expand All @@ -262,8 +271,6 @@ def _next(self):
POINTER(c_char_p)]
ffi.lib.LLVMPY_ParseBitcode.restype = ffi.LLVMModuleRef

ffi.lib.LLVMPY_GetGlobalContext.restype = ffi.LLVMContextRef

ffi.lib.LLVMPY_DisposeModule.argtypes = [ffi.LLVMModuleRef]

ffi.lib.LLVMPY_PrintModuleToString.argtypes = [ffi.LLVMModuleRef,
Expand Down
20 changes: 14 additions & 6 deletions llvmlite/tests/test_binding.py
Expand Up @@ -174,9 +174,9 @@ def tearDown(self):
# This will probably put any existing garbage in gc.garbage again
del self.old_garbage

def module(self, asm=asm_sum):
def module(self, asm=asm_sum, context=None):
asm = asm.format(triple=llvm.get_default_triple())
mod = llvm.parse_assembly(asm)
mod = llvm.parse_assembly(asm, context)
return mod

def glob(self, name='glob', mod=None):
Expand Down Expand Up @@ -244,6 +244,11 @@ def test_parse_assembly_error(self):
self.assertIn("parsing error", s)
self.assertIn("invalid operand type", s)

def test_global_context(self):
gcontext1 = llvm.context.get_global_context()
gcontext2 = llvm.context.get_global_context()
assert gcontext1 == gcontext2

def test_dylib_symbols(self):
llvm.add_symbol("__xyzzy", 1234)
llvm.add_symbol("__xyzzy", 5678)
Expand Down Expand Up @@ -519,12 +524,15 @@ def test_parse_bitcode_error(self):
self.assertIn("Invalid bitcode signature", str(cm.exception))

def test_bitcode_roundtrip(self):
bc = self.module(asm=asm_mul).as_bitcode()
mod = llvm.parse_bitcode(bc)
# create a new context to avoid struct renaming
context1 = llvm.create_context()
bc = self.module(context=context1).as_bitcode()
context2 = llvm.create_context()
mod = llvm.parse_bitcode(bc, context2)
self.assertEqual(mod.as_bitcode(), bc)

mod.get_function("mul")
mod.get_global_variable("mul_glob")
mod.get_function("sum")
mod.get_global_variable("glob")

def test_cloning(self):
m = self.module()
Expand Down

0 comments on commit 80d8f86

Please sign in to comment.