diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 75fec8cd3..d41d0329f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -23,6 +23,11 @@ jobs: CONDA_ENV: cienv LLVM: '16' + opaque_pointers: + PYTHON: '3.12' + CONDA_ENV: cienv + OPAQUE_POINTERS: yes + - template: buildscripts/azure/azure-linux-macos.yml parameters: name: Linux @@ -72,6 +77,11 @@ jobs: CONDA_ENV: cienv LLVM: '16' + opaque_pointers: + PYTHON: '3.12' + CONDA_ENV: cienv + OPAQUE_POINTERS: yes + - template: buildscripts/azure/azure-windows.yml parameters: name: Windows diff --git a/buildscripts/azure/azure-windows.yml b/buildscripts/azure/azure-windows.yml index 5f00cf9c3..7119132c8 100644 --- a/buildscripts/azure/azure-windows.yml +++ b/buildscripts/azure/azure-windows.yml @@ -27,6 +27,11 @@ jobs: CONDA_ENV: cienv LLVM: '16' + opaque_pointers: + PYTHON: '3.12' + CONDA_ENV: cienv + OPAQUE_POINTERS: yes + steps: - powershell: | diff --git a/buildscripts/incremental/test.cmd b/buildscripts/incremental/test.cmd index 488961a5c..7e3ab5c0a 100644 --- a/buildscripts/incremental/test.cmd +++ b/buildscripts/incremental/test.cmd @@ -1,4 +1,11 @@ call activate %CONDA_ENV% +if "%OPAQUE_POINTERS%"=="yes" ( + set LLVMLITE_ENABLE_OPAQUE_POINTERS=1 + echo "Testing with opaque pointers enabled" +) else ( + echo "Testing with opaque pointers disabled" +) + python runtests.py -v diff --git a/buildscripts/incremental/test.sh b/buildscripts/incremental/test.sh index bf2a41aa1..70f3d3906 100755 --- a/buildscripts/incremental/test.sh +++ b/buildscripts/incremental/test.sh @@ -9,10 +9,18 @@ set -v -e python --version +if [ "$OPAQUE_POINTERS" == "yes" ]; then + export LLVMLITE_ENABLE_OPAQUE_POINTERS=1 + echo "Testing with opaque pointers enabled" +else + echo "Testing with opaque pointers disabled" +fi + if [ "$WHEEL" == "yes" ]; then cd dist python -m llvmlite.tests -v else python runtests.py -v fi + if [ "$RUN_COVERAGE" == "yes" ]; then coverage run runtests.py; fi diff --git a/docs/source/user-guide/binding/target-information.rst b/docs/source/user-guide/binding/target-information.rst index 96376a072..68fd6dce9 100644 --- a/docs/source/user-guide/binding/target-information.rst +++ b/docs/source/user-guide/binding/target-information.rst @@ -84,6 +84,11 @@ Classes Get the ABI-mandated size of a :class:`TypeRef` object. Returns an integer. + * .. method:: get_abi_alignment(type) + + Similar to :meth:`get_abi_size`, but returns the ABI-mandated alignment + rather that the ABI size. + * .. method:: get_pointee_abi_size(type) Similar to :meth:`get_abi_size`, but assumes that *type* is diff --git a/docs/source/user-guide/deprecation.rst b/docs/source/user-guide/deprecation.rst index c18aaa1f2..cb0c36f2c 100644 --- a/docs/source/user-guide/deprecation.rst +++ b/docs/source/user-guide/deprecation.rst @@ -9,6 +9,127 @@ APIs that have become undesirable/obsolete. Any information about the schedule for their deprecation and reasoning behind the changes, along with examples, is provided. + +.. _deprecation-of-typed-pointers: + +Deprecation of Typed Pointers +============================= + +The use of Typed Pointers is deprecated, and :ref:`Opaque Pointers +` will be the default (and eventually required) in a future +llvmlite version. + +Reason for deprecation +---------------------- + +llvmlite aims to move forward to newer LLVM versions, which will necessitate +switching to `Opaque Pointers `_: + +- In LLVM 15, Opaque Pointers are the default. +- In LLVM 16, Typed Pointers are only supported on a best-effort basis (and + therefore may have bugs that go unfixed). +- In LLVM 17, support for Typed Pointers is removed. + +Although Opaque Pointers are already the default in LLVM 15, llvmlite still uses +Typed Pointers by default with LLVM 15. + +Examples(s) of the impact +------------------------- + +Code that uses llvmlite to work with pointers or to parse assembly that uses +pointers will break if not modified to use opaque pointers. + +Schedule +-------- + +- In llvmlite 0.44, Opaque Pointers will be the default pointer kind. +- In llvmlite 0.45, support for Typed Pointers will be removed. + +Recommendations +--------------- + +Code using llvmlite should be updated as follows when switching to Opaque +Pointers. + +IR layer +~~~~~~~~ + +Modify uses of ``.type.pointee`` of instructions to use ``.allocated_type`` +instead. For example: + +.. code:: python + + # Allocating an integer on the stack and storing a value to it + stackint = builder.alloca(ir.IntType(32)) + builder.store(ir.Constant(stackint.type.pointee, 123), stackint) + +becomes: + +.. code:: python + + # Allocating an integer on the stack and storing a value to it + stackint = builder.alloca(ir.IntType(32)) + builder.store(ir.Constant(stackint.allocated_type, 123), stackint) + + +Replace the use of ``.as_pointer()`` of types with the ``PointerType`` class. +For example: + +.. code:: python + + # Declaring a function of type i32(i32*, i32) + fnty = ir.FunctionType(ir.IntType(32), [ir.IntType(32).as_pointer(), + ir.IntType(32)]) + + +becomes: + +.. code:: python + + # Declaring a function of type i32(ptr, i32) + fnty = ir.FunctionType(ir.IntType(32), [ir.PointerType(), + ir.IntType(32)]) + + +Modify calls to ``ir.load``, ``ir.load_atomic``, and ``ir.gep`` instructions to +pass in pointer types. For example: + +.. code:: python + + ptr = builder.gep(func.args[0], [index]) + value = builder.load(ptr) + +becomes: + +.. code:: python + + ptr = builder.gep(func.args[0], [index], source_etype=ll.IntType(32)) + value = builder.load(ptr, typ=ll.IntType(32)) + + +Binding layer +~~~~~~~~~~~~~ + +When working with :class:`TargetData ` instances: + +- Replace calls to :meth:`get_pointee_abi_size() + ` with calls to + :meth:`get_abi_size() `. +- Replace calls to :meth:`get_pointee_abi_alignment() + ` with calls to + :meth:`get_abi_alignment() `. + +When working with global variables and functions (which will be :class:`ValueRef +` instances): + +- Replace any use of ``valueref.type`` with ``valueref.global_value_type`` for + any ``valueref`` that is a global variable or function. + +When passing assembly to :func:`llvmlite.binding.parse_assembly`: + +- Ensure that any IR passed to ``parse_assembly()`` uses Opaque Pointers. + + Deprecation of `llvmlite.llvmpy` module ======================================= The `llvmlite.llvmpy` module was originally created for compatibility with diff --git a/docs/source/user-guide/ir/types.rst b/docs/source/user-guide/ir/types.rst index 52d04d251..d7a96b1d4 100644 --- a/docs/source/user-guide/ir/types.rst +++ b/docs/source/user-guide/ir/types.rst @@ -58,22 +58,6 @@ instantiated, a type should be considered immutable. Atomic types ============= -.. class:: PointerType(pointee, addrspace=0) - - The type of pointers to another type. - - Pointer types expose the following attributes: - - * .. attribute:: addrspace - - The pointer's address space number. This optional integer - allows you to choose a non-default address space---the - meaning is platform dependent. - - * .. attribute:: pointee - - The type pointed to. - .. class:: IntType(bits) @@ -105,6 +89,70 @@ Atomic types function without a return value. +.. _pointer-types: + +Pointer Types +============= + +llvmlite presently supports both *Typed Pointers* and *Opaque Pointers*. Typed +Pointers are currently the default; Opaque Pointers will become the default in +future, and support for Typed Pointers will be eventually be removed. + +.. note:: + Further details of the migration to Opaque Pointers are outlined in the + section on :ref:`deprecation-of-typed-pointers`. + +When Typed Pointers are enabled, the pointer type is represented using: + +.. class:: PointerType(pointee, addrspace=0) + + The type of pointers to another type. + + Pointer types expose the following attributes: + + * .. attribute:: addrspace + + The pointer's address space number. This optional integer + allows you to choose a non-default address space---the + meaning is platform dependent. + + * .. attribute:: pointee + + The type pointed to. + +Opaque pointers can be enabled by setting the environment variable: + +.. code:: bash + + LLVMLITE_ENABLE_OPAQUE_POINTERS=1 + +or by setting the ``opaque_pointers_enabled`` attribute after importing +llvmlite, but prior to using any of its functionality. For example: + +.. code:: python + + import llvmlite + llvmlite.opaque_pointers_enabled = True + + # ... continue using llvmlite ... + +When Opaque Pointers are enabled, the pointer type is represented using: + +.. class:: PointerType(addrspace=0) + :no-index: + + The type of pointers. + + Pointer types expose the following attribute: + + * .. attribute:: addrspace + :no-index: + + The pointer's address space number. This optional integer + allows you to choose a non-default address space---the + meaning is platform dependent. + + .. _aggregate-types: Aggregate types diff --git a/examples/opaque_pointers/llvmir.py b/examples/opaque_pointers/llvmir.py new file mode 100644 index 000000000..d25f0f2a5 --- /dev/null +++ b/examples/opaque_pointers/llvmir.py @@ -0,0 +1,38 @@ +import llvmlite +llvmlite.opaque_pointers_enabled = True + +import llvmlite.ir as ll + +fntype = ll.FunctionType(ll.IntType(32), [ll.IntType(32), ll.IntType(32)]) + +module = ll.Module() + +func = ll.Function(module, fntype, name='foo') +bb_entry = func.append_basic_block() + +builder = ll.IRBuilder() +builder.position_at_end(bb_entry) + +stackint = builder.alloca(ll.IntType(32)) +# Instead of stackint.type.pointee we can access stackint.allocated_type +# directly. +builder.store(ll.Constant(stackint.allocated_type, 123), stackint) +myint = builder.load(stackint) + +addinstr = builder.add(func.args[0], func.args[1]) +mulinstr = builder.mul(addinstr, ll.Constant(ll.IntType(32), 123)) +pred = builder.icmp_signed('<', addinstr, mulinstr) +builder.ret(mulinstr) + +bb_block = func.append_basic_block() +builder.position_at_end(bb_block) + +bb_exit = func.append_basic_block() + +pred = builder.trunc(addinstr, ll.IntType(1)) +builder.cbranch(pred, bb_block, bb_exit) + +builder.position_at_end(bb_exit) +builder.ret(myint) + +print(module) diff --git a/examples/opaque_pointers/sum.py b/examples/opaque_pointers/sum.py new file mode 100644 index 000000000..f1c525c94 --- /dev/null +++ b/examples/opaque_pointers/sum.py @@ -0,0 +1,115 @@ +from __future__ import print_function + +from ctypes import CFUNCTYPE, c_int, POINTER +import sys +try: + from time import perf_counter as time +except ImportError: + from time import time + +import numpy as np + +try: + import faulthandler; faulthandler.enable() +except ImportError: + pass + +import llvmlite +llvmlite.opaque_pointers_enabled = True + +import llvmlite.ir as ll +import llvmlite.binding as llvm + + +llvm.initialize() +llvm.initialize_native_target() +llvm.initialize_native_asmprinter() + + +t1 = time() + +# Pointers are opaque, so we should define them as such here. +fnty = ll.FunctionType(ll.IntType(32), [ll.PointerType(), ll.IntType(32)]) +module = ll.Module() + +func = ll.Function(module, fnty, name="sum") + +bb_entry = func.append_basic_block() +bb_loop = func.append_basic_block() +bb_exit = func.append_basic_block() + +builder = ll.IRBuilder() +builder.position_at_end(bb_entry) + +builder.branch(bb_loop) +builder.position_at_end(bb_loop) + +index = builder.phi(ll.IntType(32)) +index.add_incoming(ll.Constant(index.type, 0), bb_entry) +accum = builder.phi(ll.IntType(32)) +accum.add_incoming(ll.Constant(accum.type, 0), bb_entry) + +# These GEP and load need an excplicit type. +ptr = builder.gep(func.args[0], [index], source_etype=ll.IntType(32)) +value = builder.load(ptr, typ=ll.IntType(32)) + +added = builder.add(accum, value) +accum.add_incoming(added, bb_loop) + +indexp1 = builder.add(index, ll.Constant(index.type, 1)) +index.add_incoming(indexp1, bb_loop) + +cond = builder.icmp_unsigned('<', indexp1, func.args[1]) +builder.cbranch(cond, bb_loop, bb_exit) + +builder.position_at_end(bb_exit) +builder.ret(added) + +strmod = str(module) + +t2 = time() + +print("-- generate IR:", t2-t1) + +t3 = time() + +llmod = llvm.parse_assembly(strmod) + +t4 = time() + +print("-- parse assembly:", t4-t3) + +print(llmod) + +pmb = llvm.create_pass_manager_builder() +pmb.opt_level = 2 +pm = llvm.create_module_pass_manager() +pmb.populate(pm) + +t5 = time() + +pm.run(llmod) + +t6 = time() + +print("-- optimize:", t6-t5) + +t7 = time() + +target_machine = llvm.Target.from_default_triple().create_target_machine() + +with llvm.create_mcjit_compiler(llmod, target_machine) as ee: + ee.finalize_object() + cfptr = ee.get_function_address("sum") + + t8 = time() + print("-- JIT compile:", t8 - t7) + + print(target_machine.emit_assembly(llmod)) + + cfunc = CFUNCTYPE(c_int, POINTER(c_int), c_int)(cfptr) + A = np.arange(10, dtype=np.int32) + res = cfunc(A.ctypes.data_as(POINTER(c_int)), A.size) + + print(res, A.sum()) + diff --git a/ffi/core.cpp b/ffi/core.cpp index 92fc09d87..9746b7cdf 100644 --- a/ffi/core.cpp +++ b/ffi/core.cpp @@ -20,17 +20,19 @@ LLVMPY_CreateByteString(const char *buf, size_t len) { API_EXPORT(void) LLVMPY_DisposeString(const char *msg) { free(const_cast(msg)); } +// FIXME: Remove `enableOpaquePointers' once typed pointers are removed. API_EXPORT(LLVMContextRef) -LLVMPY_GetGlobalContext() { +LLVMPY_GetGlobalContext(bool enableOpaquePointers) { auto context = LLVMGetGlobalContext(); - LLVMContextSetOpaquePointers(context, false); + LLVMContextSetOpaquePointers(context, enableOpaquePointers); return context; } +// FIXME: Remove `enableOpaquePointers' once typed pointers are removed. API_EXPORT(LLVMContextRef) -LLVMPY_ContextCreate() { +LLVMPY_ContextCreate(bool enableOpaquePointers) { LLVMContextRef context = LLVMContextCreate(); - LLVMContextSetOpaquePointers(context, false); + LLVMContextSetOpaquePointers(context, enableOpaquePointers); return context; } diff --git a/ffi/core.h b/ffi/core.h index 2d54d4453..73d799fc6 100644 --- a/ffi/core.h +++ b/ffi/core.h @@ -31,11 +31,13 @@ LLVMPY_CreateByteString(const char *buf, size_t len); API_EXPORT(void) LLVMPY_DisposeString(const char *msg); +// FIXME: Remove `enableOpaquePointers' once typed pointers are removed. API_EXPORT(LLVMContextRef) -LLVMPY_GetGlobalContext(); +LLVMPY_GetGlobalContext(bool enableOpaquePointers); +// FIXME: Remove `enableOpaquePointers' once typed pointers are removed. API_EXPORT(LLVMContextRef) -LLVMPY_ContextCreate(); +LLVMPY_ContextCreate(bool enableOpaquePointers); } /* end extern "C" */ diff --git a/ffi/targets.cpp b/ffi/targets.cpp index da7ba521c..6f75726c6 100644 --- a/ffi/targets.cpp +++ b/ffi/targets.cpp @@ -116,6 +116,12 @@ LLVMPY_OffsetOfElement(LLVMTargetDataRef TD, LLVMTypeRef Ty, int Element) { return (long long)LLVMOffsetOfElement(TD, Ty, Element); } +API_EXPORT(long long) +LLVMPY_ABIAlignmentOfType(LLVMTargetDataRef TD, LLVMTypeRef Ty) { + return (long long)LLVMABIAlignmentOfType(TD, Ty); +} + +// FIXME: Remove me once typed pointers are no longer supported. API_EXPORT(long long) LLVMPY_ABISizeOfElementType(LLVMTargetDataRef TD, LLVMTypeRef Ty) { llvm::Type *tp = llvm::unwrap(Ty); @@ -125,6 +131,7 @@ LLVMPY_ABISizeOfElementType(LLVMTargetDataRef TD, LLVMTypeRef Ty) { return (long long)LLVMABISizeOfType(TD, llvm::wrap(tp)); } +// FIXME: Remove me once typed pointers are no longer supported. API_EXPORT(long long) LLVMPY_ABIAlignmentOfElementType(LLVMTargetDataRef TD, LLVMTypeRef Ty) { llvm::Type *tp = llvm::unwrap(Ty); diff --git a/ffi/type.cpp b/ffi/type.cpp index d7669474a..235719913 100644 --- a/ffi/type.cpp +++ b/ffi/type.cpp @@ -147,6 +147,7 @@ LLVMPY_GetTypeBitWidth(LLVMTypeRef type) { return size.getFixedSize(); } +// FIXME: Remove me once typed pointers support is removed. API_EXPORT(LLVMTypeRef) LLVMPY_GetElementType(LLVMTypeRef type) { llvm::Type *unwrapped = llvm::unwrap(type); diff --git a/llvmlite/__init__.py b/llvmlite/__init__.py index e44e9d156..f830f73f9 100644 --- a/llvmlite/__init__.py +++ b/llvmlite/__init__.py @@ -1,3 +1,10 @@ from ._version import get_versions __version__ = get_versions()['version'] del get_versions + +# FIXME: Remove me once typed pointers are no longer supported. +def _opaque_pointers_enabled(): + import os + return os.environ.get('LLVMLITE_ENABLE_OPAQUE_POINTERS', '0') == '1' +opaque_pointers_enabled = _opaque_pointers_enabled() +del _opaque_pointers_enabled diff --git a/llvmlite/binding/context.py b/llvmlite/binding/context.py index 7dffb82a7..192ebd837 100644 --- a/llvmlite/binding/context.py +++ b/llvmlite/binding/context.py @@ -1,12 +1,18 @@ from llvmlite.binding import ffi +# FIXME: Remove me once typed pointers are no longer supported. +from llvmlite import opaque_pointers_enabled +from ctypes import c_bool + def create_context(): - return ContextRef(ffi.lib.LLVMPY_ContextCreate()) + return ContextRef( + ffi.lib.LLVMPY_ContextCreate(opaque_pointers_enabled)) def get_global_context(): - return GlobalContextRef(ffi.lib.LLVMPY_GetGlobalContext()) + return GlobalContextRef( + ffi.lib.LLVMPY_GetGlobalContext(opaque_pointers_enabled)) class ContextRef(ffi.ObjectRef): @@ -22,8 +28,12 @@ def _dispose(self): pass +# FIXME: Remove argtypes once typed pointers are no longer supported. +ffi.lib.LLVMPY_GetGlobalContext.argtypes = [c_bool] ffi.lib.LLVMPY_GetGlobalContext.restype = ffi.LLVMContextRef +# FIXME: Remove argtypes once typed pointers are no longer supported. +ffi.lib.LLVMPY_ContextCreate.argtypes = [c_bool] ffi.lib.LLVMPY_ContextCreate.restype = ffi.LLVMContextRef ffi.lib.LLVMPY_ContextDispose.argtypes = [ffi.LLVMContextRef] diff --git a/llvmlite/binding/targets.py b/llvmlite/binding/targets.py index 364079f24..8f2d7c317 100644 --- a/llvmlite/binding/targets.py +++ b/llvmlite/binding/targets.py @@ -7,6 +7,10 @@ from llvmlite.binding.common import _decode_string, _encode_string from collections import namedtuple +# FIXME: Remove `opaque_pointers_enabled` once typed pointers are no longer +# supported. +from llvmlite import opaque_pointers_enabled + Triple = namedtuple('Triple', ['Arch', 'SubArch', 'Vendor', 'OS', 'Env', 'ObjectFormat']) @@ -192,10 +196,19 @@ def get_element_offset(self, ty, position): "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) + def get_pointee_abi_size(self, ty): """ Get ABI size of pointee type of LLVM pointer type *ty*. """ + if opaque_pointers_enabled: + raise RuntimeError("Cannot get pointee type in opaque pointer " + "mode.") size = ffi.lib.LLVMPY_ABISizeOfElementType(self, ty) if size == -1: raise RuntimeError("Not a pointer type: %s" % (ty,)) @@ -205,6 +218,9 @@ def get_pointee_abi_alignment(self, ty): """ Get minimum ABI alignment of pointee type of LLVM pointer type *ty*. """ + if opaque_pointers_enabled: + raise RuntimeError("Cannot get pointee type in opaque pointer " + "mode.") size = ffi.lib.LLVMPY_ABIAlignmentOfElementType(self, ty) if size == -1: raise RuntimeError("Not a pointer type: %s" % (ty,)) @@ -420,10 +436,16 @@ def has_svml(): 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 + +# FIXME: Remove me once typed pointers are no longer supported. ffi.lib.LLVMPY_ABISizeOfElementType.argtypes = [ffi.LLVMTargetDataRef, ffi.LLVMTypeRef] ffi.lib.LLVMPY_ABISizeOfElementType.restype = c_longlong +# FIXME: Remove me once typed pointers are no longer supported. ffi.lib.LLVMPY_ABIAlignmentOfElementType.argtypes = [ffi.LLVMTargetDataRef, ffi.LLVMTypeRef] ffi.lib.LLVMPY_ABIAlignmentOfElementType.restype = c_longlong diff --git a/llvmlite/binding/typeref.py b/llvmlite/binding/typeref.py index afb658d45..7f93599db 100644 --- a/llvmlite/binding/typeref.py +++ b/llvmlite/binding/typeref.py @@ -4,6 +4,9 @@ from llvmlite import ir from llvmlite.binding import ffi +# FIXME: Remove `opaque_pointers_enabled' when TP's are removed. +from llvmlite import opaque_pointers_enabled + class TypeKind(enum.IntEnum): # The LLVMTypeKind enum from llvm-c/Core.h @@ -105,8 +108,11 @@ def elements(self): """ Returns iterator over enclosing types """ + if self.is_pointer and opaque_pointers_enabled: + raise ValueError("Type {} doesn't contain elements.".format(self)) return _TypeListIterator(ffi.lib.LLVMPY_ElementIter(self)) + # FIXME: Remove me once typed pointers support is removed. @property def element_type(self): """ @@ -220,6 +226,7 @@ def _next(self): ffi.lib.LLVMPY_PrintType.argtypes = [ffi.LLVMTypeRef] ffi.lib.LLVMPY_PrintType.restype = c_void_p +# FIXME: Remove me once typed pointers support is removed. ffi.lib.LLVMPY_GetElementType.argtypes = [ffi.LLVMTypeRef] ffi.lib.LLVMPY_GetElementType.restype = ffi.LLVMTypeRef diff --git a/llvmlite/ir/builder.py b/llvmlite/ir/builder.py index f62476ca8..bb9701ed8 100644 --- a/llvmlite/ir/builder.py +++ b/llvmlite/ir/builder.py @@ -753,7 +753,7 @@ def alloca(self, typ, size=None, name=''): self._insert(al) return al - def load(self, ptr, name='', align=None): + def load(self, ptr, name='', align=None, typ=None): """ Load value from pointer, with optional guaranteed alignment: name = *ptr @@ -761,7 +761,7 @@ def load(self, ptr, name='', align=None): if not isinstance(ptr.type, types.PointerType): msg = "cannot load from value of type %s (%r): not a pointer" raise TypeError(msg % (ptr.type, str(ptr))) - ld = instructions.LoadInstr(self.block, ptr, name) + ld = instructions.LoadInstr(self.block, ptr, name, typ=typ) ld.align = align self._insert(ld) return ld @@ -782,7 +782,7 @@ def store(self, value, ptr, align=None): self._insert(st) return st - def load_atomic(self, ptr, ordering, align, name=''): + def load_atomic(self, ptr, ordering, align, name='', typ=None): """ Load value from pointer, with optional guaranteed alignment: name = *ptr @@ -791,7 +791,7 @@ def load_atomic(self, ptr, ordering, align, name=''): msg = "cannot load from value of type %s (%r): not a pointer" raise TypeError(msg % (ptr.type, str(ptr))) ld = instructions.LoadAtomicInstr( - self.block, ptr, ordering, align, name) + self.block, ptr, ordering, align, name, typ=typ) self._insert(ld) return ld @@ -919,13 +919,14 @@ def invoke(self, fn, args, normal_to, unwind_to, # GEP APIs - def gep(self, ptr, indices, inbounds=False, name=''): + def gep(self, ptr, indices, inbounds=False, name='', source_etype=None): """ Compute effective address (getelementptr): name = getelementptr ptr, """ instr = instructions.GEPInstr(self.block, ptr, indices, - inbounds=inbounds, name=name) + inbounds=inbounds, name=name, + source_etype=source_etype) self._insert(instr) return instr diff --git a/llvmlite/ir/instructions.py b/llvmlite/ir/instructions.py index c6d488aae..58039ab50 100644 --- a/llvmlite/ir/instructions.py +++ b/llvmlite/ir/instructions.py @@ -431,9 +431,17 @@ def descr(self, buf): class LoadInstr(Instruction): - def __init__(self, parent, ptr, name=''): - super(LoadInstr, self).__init__(parent, ptr.type.pointee, "load", - [ptr], name=name) + def __init__(self, parent, ptr, name='', typ=None): + if typ is None: + if isinstance(ptr, AllocaInstr): + typ = ptr.allocated_type + # For compatibility with typed pointers. Eventually this should + # probably be removed (when typed pointers are fully removed). + elif not ptr.type.is_opaque: + typ = ptr.type.pointee + else: + raise ValueError("Load lacks type.") + super(LoadInstr, self).__init__(parent, typ, "load", [ptr], name=name) self.align = None def descr(self, buf): @@ -443,7 +451,7 @@ def descr(self, buf): else: align = '' buf.append("load {0}, {1} {2}{3}{4}\n".format( - val.type.pointee, + self.type, val.type, val.get_reference(), align, @@ -473,16 +481,25 @@ def descr(self, buf): class LoadAtomicInstr(Instruction): - def __init__(self, parent, ptr, ordering, align, name=''): - super(LoadAtomicInstr, self).__init__(parent, ptr.type.pointee, - "load atomic", [ptr], name=name) + def __init__(self, parent, ptr, ordering, align, name='', typ=None): + if typ is None: + if isinstance(ptr, AllocaInstr): + typ = ptr.allocated_type + # For compatibility with typed pointers. Eventually this should + # probably be removed (when typed pointers are fully removed). + elif not ptr.type.is_opaque: + typ = ptr.type.pointee + else: + raise ValueError("Load atomic lacks type.") + super(LoadAtomicInstr, self).__init__(parent, typ, "load atomic", + [ptr], name=name) self.ordering = ordering self.align = align def descr(self, buf): [val] = self.operands buf.append("load atomic {0}, {1} {2} {3}, align {4}{5}\n".format( - val.type.pointee, + self.type, val.type, val.get_reference(), self.ordering, @@ -516,10 +533,11 @@ def __init__(self, parent, typ, count, name): operands = [count] if count else () super(AllocaInstr, self).__init__(parent, typ.as_pointer(), "alloca", operands, name) + self.allocated_type = typ self.align = None def descr(self, buf): - buf.append("{0} {1}".format(self.opname, self.type.pointee)) + buf.append("{0} {1}".format(self.opname, self.allocated_type)) if self.operands: op, = self.operands buf.append(", {0} {1}".format(op.type, op.get_reference())) @@ -530,22 +548,31 @@ def descr(self, buf): class GEPInstr(Instruction): - def __init__(self, parent, ptr, indices, inbounds, name): - typ = ptr.type - lasttyp = None - lastaddrspace = 0 - for i in indices: - lasttyp, typ = typ, typ.gep(i) - # inherit the addrspace from the last seen pointer - if isinstance(lasttyp, types.PointerType): - lastaddrspace = lasttyp.addrspace - - if (not isinstance(typ, types.PointerType) and - isinstance(lasttyp, types.PointerType)): - typ = lasttyp + def __init__(self, parent, ptr, indices, inbounds, name, + source_etype=None): + if source_etype is not None: + typ = ptr.type + self.source_etype = source_etype + # For compatibility with typed pointers. Eventually this should + # probably be removed (when typed pointers are fully removed). + elif not ptr.type.is_opaque: + typ = ptr.type + lasttyp = None + lastaddrspace = 0 + for i in indices: + lasttyp, typ = typ, typ.gep(i) + # inherit the addrspace from the last seen pointer + if isinstance(lasttyp, types.PointerType): + lastaddrspace = lasttyp.addrspace + + if (not isinstance(typ, types.PointerType) and + isinstance(lasttyp, types.PointerType)): + typ = lasttyp + else: + typ = typ.as_pointer(lastaddrspace) + self.source_etype = ptr.type.pointee else: - typ = typ.as_pointer(lastaddrspace) - + raise ValueError("GEP lacks type.") super(GEPInstr, self).__init__(parent, typ, "getelementptr", [ptr] + list(indices), name=name) self.pointer = ptr @@ -558,7 +585,7 @@ def descr(self, buf): op = "getelementptr inbounds" if self.inbounds else "getelementptr" buf.append("{0} {1}, {2} {3}, {4} {5}\n".format( op, - self.pointer.type.pointee, + self.source_etype, self.pointer.type, self.pointer.get_reference(), ', '.join(indices), diff --git a/llvmlite/ir/types.py b/llvmlite/ir/types.py index 906bca4bf..41ae04ec3 100644 --- a/llvmlite/ir/types.py +++ b/llvmlite/ir/types.py @@ -6,6 +6,9 @@ from llvmlite.ir._utils import _StrCaching +# FIXME: Remove me once typed pointers are no longer supported. +from llvmlite import opaque_pointers_enabled + def _wrapname(x): return '"{0}"'.format(x.replace('\\', '\\5c').replace('"', '\\22')) @@ -30,7 +33,7 @@ def as_pointer(self, addrspace=0): def __ne__(self, other): return not (self == other) - def _get_ll_pointer_type(self, target_data, context=None): + def _get_ll_global_value_type(self, target_data, context=None): """ Convert this type object to an LLVM type. """ @@ -43,22 +46,22 @@ def _get_ll_pointer_type(self, target_data, context=None): m = Module(context=context) foo = GlobalVariable(m, self, name="foo") with parse_assembly(str(m)) as llmod: - return llmod.get_global_variable(foo.name).type + return llmod.get_global_variable(foo.name).global_value_type def get_abi_size(self, target_data, context=None): """ Get the ABI size of this type according to data layout *target_data*. """ - llty = self._get_ll_pointer_type(target_data, context) - return target_data.get_pointee_abi_size(llty) + llty = self._get_ll_global_value_type(target_data, context) + return target_data.get_abi_size(llty) def get_abi_alignment(self, target_data, context=None): """ Get the minimum ABI alignment of this type according to data layout *target_data*. """ - llty = self._get_ll_pointer_type(target_data, context) - return target_data.get_pointee_abi_alignment(llty) + llty = self._get_ll_global_value_type(target_data, context) + return target_data.get_abi_alignment(llty) def format_constant(self, value): """ @@ -109,30 +112,67 @@ def _to_string(self): class PointerType(Type): """ The type of all pointer values. + By default (without specialisation) represents an opaque pointer. """ + is_opaque = True is_pointer = True null = 'null' - def __init__(self, pointee, addrspace=0): - assert not isinstance(pointee, VoidType) - self.pointee = pointee + # Factory to create typed or opaque pointers based on `pointee'. + def __new__(cls, pointee=None, addrspace=0): + if cls is PointerType and pointee is not None: + return super().__new__(_TypedPointerType) + return super(PointerType, cls).__new__(cls) + + def __init__(self, addrspace=0): self.addrspace = addrspace def _to_string(self): if self.addrspace != 0: - return "{0} addrspace({1})*".format(self.pointee, self.addrspace) + return "ptr addrspace({0})".format(self.addrspace) else: - return "{0}*".format(self.pointee) + return "ptr" + + def __hash__(self): + return hash(PointerType) + + @classmethod + def from_llvm(cls, typeref, ir_ctx): + """ + Create from a llvmlite.binding.TypeRef + """ + if not opaque_pointers_enabled: + return _TypedPointerType.from_llvm(typeref, ir_ctx) + return cls() + + +class _TypedPointerType(PointerType): + """ + The type of typed pointer values. To be removed eventually. + """ + + def __init__(self, pointee, addrspace=0): + super(_TypedPointerType, self).__init__(addrspace) + assert pointee is not None + assert not isinstance(pointee, VoidType) + self.pointee = pointee + self.is_opaque = False + + def _to_string(self): + if not opaque_pointers_enabled: + return "{0}*".format(self.pointee) if self.addrspace == 0 else \ + "{0} addrspace({1})*".format(self.pointee, self.addrspace) + return super(_TypedPointerType, self)._to_string() + # This implements ``isOpaqueOrPointeeTypeEquals''. def __eq__(self, other): - if isinstance(other, PointerType): + if isinstance(other, _TypedPointerType): return (self.pointee, self.addrspace) == (other.pointee, other.addrspace) - else: - return False + return isinstance(other, PointerType) def __hash__(self): - return hash(PointerType) + return hash(_TypedPointerType) def gep(self, i): """ @@ -151,6 +191,7 @@ def from_llvm(cls, typeref, ir_ctx): """ Create from a llvmlite.binding.TypeRef """ + assert not opaque_pointers_enabled # opaque pointer will change this [pointee] = typeref.elements # addrspace is not handled diff --git a/llvmlite/tests/test_binding.py b/llvmlite/tests/test_binding.py index 265682f2d..4935328b4 100644 --- a/llvmlite/tests/test_binding.py +++ b/llvmlite/tests/test_binding.py @@ -18,6 +18,9 @@ from llvmlite.binding import ffi from llvmlite.tests import TestCase +# FIXME: Remove me once typed pointers are no longer supported. +from llvmlite import opaque_pointers_enabled + # arvm7l needs extra ABI symbols to link successfully if platform.machine() == 'armv7l': llvm.load_library_permanently('libgcc_s.so.1') @@ -1216,14 +1219,14 @@ def test_target_data_abi_enquiries(self): for g in (gv_i32, gv_i8, gv_struct): self.assertEqual(td.get_abi_size(g.type), pointer_size) - self.assertEqual(td.get_pointee_abi_size(gv_i32.type), 4) - self.assertEqual(td.get_pointee_abi_alignment(gv_i32.type), 4) + self.assertEqual(td.get_abi_size(gv_i32.global_value_type), 4) + self.assertEqual(td.get_abi_alignment(gv_i32.global_value_type), 4) - self.assertEqual(td.get_pointee_abi_size(gv_i8.type), 1) - self.assertIn(td.get_pointee_abi_alignment(gv_i8.type), (1, 2, 4)) + self.assertEqual(td.get_abi_size(gv_i8.global_value_type), 1) + self.assertIn(td.get_abi_alignment(gv_i8.global_value_type), (1, 2, 4)) - self.assertEqual(td.get_pointee_abi_size(gv_struct.type), 24) - self.assertIn(td.get_pointee_abi_alignment(gv_struct.type), (4, 8)) + self.assertEqual(td.get_abi_size(gv_struct.global_value_type), 24) + self.assertIn(td.get_abi_alignment(gv_struct.global_value_type), (4, 8)) def test_object_cache_notify(self): notifies = [] @@ -1607,28 +1610,32 @@ def test_type_name(self): self.assertEqual(tp.name, "") st = mod.get_global_variable("glob_struct") self.assertIsNotNone(re.match(r"struct\.glob_type(\.[\d]+)?", - st.type.element_type.name)) + st.global_value_type.name)) def test_type_printing_variable(self): mod = self.module() glob = mod.get_global_variable("glob") - tp = glob.type - self.assertEqual(str(tp), 'i32*') + tp = glob.global_value_type + self.assertEqual(str(tp), 'i32') def test_type_printing_function(self): mod = self.module() fn = mod.get_function("sum") - self.assertEqual(str(fn.type), "i32 (i32, i32)*") + self.assertEqual(str(fn.global_value_type), "i32 (i32, i32)") def test_type_printing_struct(self): mod = self.module() st = mod.get_global_variable("glob_struct") self.assertTrue(st.type.is_pointer) - self.assertIsNotNone(re.match(r'%struct\.glob_type(\.[\d]+)?\*', - str(st.type))) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertIsNotNone(re.match(r'ptr', str(st.type))) + else: + self.assertIsNotNone(re.match(r'%struct\.glob_type(\.[\d]+)?\*', + str(st.type))) self.assertIsNotNone(re.match( r"%struct\.glob_type(\.[\d]+)? = type { i64, \[2 x i64\] }", - str(st.type.element_type))) + str(st.global_value_type))) def test_close(self): glob = self.glob() @@ -1814,7 +1821,11 @@ def test_constant_as_string(self): inst = list(list(func.blocks)[0].instructions)[0] arg = list(inst.operands)[0] self.assertTrue(arg.is_constant) - self.assertEqual(arg.get_constant_value(), 'i64* null') + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(arg.get_constant_value(), 'ptr null') + else: + self.assertEqual(arg.get_constant_value(), 'i64* null') def test_incoming_phi_blocks(self): mod = self.module(asm_phi_blocks) @@ -1843,7 +1854,7 @@ class TestTypeRef(BaseTest): def test_str(self): mod = self.module() glob = mod.get_global_variable("glob") - self.assertEqual(str(glob.type), "i32*") + self.assertEqual(str(glob.global_value_type), "i32") glob_struct_type = mod.get_struct_type("struct.glob_type") self.assertEqual(str(glob_struct_type), "%struct.glob_type = type { i64, [2 x i64] }") @@ -1863,7 +1874,7 @@ def test_type_kind(self): self.assertEqual(glob_struct.type.type_kind, llvm.TypeKind.pointer) self.assertTrue(glob_struct.type.is_pointer) - stype = next(iter(glob_struct.type.elements)) + stype = glob_struct.global_value_type self.assertEqual(stype.type_kind, llvm.TypeKind.struct) self.assertTrue(stype.is_struct) @@ -1879,7 +1890,7 @@ def test_type_kind(self): funcptr = mod.get_function("sum").type self.assertEqual(funcptr.type_kind, llvm.TypeKind.pointer) - functype, = funcptr.elements + functype = mod.get_function("sum").global_value_type self.assertEqual(functype.type_kind, llvm.TypeKind.function) def test_element_count(self): @@ -1907,20 +1918,24 @@ def test_vararg_function(self): # Variadic function mod = self.module(asm_vararg_declare) func = mod.get_function('vararg') - decltype = func.type.element_type + decltype = func.global_value_type self.assertTrue(decltype.is_function_vararg) mod = self.module(asm_sum_declare) func = mod.get_function('sum') - decltype = func.type.element_type + decltype = func.global_value_type self.assertFalse(decltype.is_function_vararg) # test that the function pointer type cannot use is_function_vararg self.assertTrue(func.type.is_pointer) with self.assertRaises(ValueError) as raises: func.type.is_function_vararg - self.assertIn("Type i32 (i32, i32)* is not a function", - str(raises.exception)) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertIn("Type ptr is not a function", str(raises.exception)) + else: + self.assertIn("Type i32 (i32, i32)* is not a function", + str(raises.exception)) def test_function_typeref_as_ir(self): mod = self.module() @@ -2190,10 +2205,10 @@ def test_get_pointee_abi_size(self): td = self.target_data() glob = self.glob() - self.assertEqual(td.get_pointee_abi_size(glob.type), 4) + self.assertEqual(td.get_abi_size(glob.global_value_type), 4) glob = self.glob("glob_struct") - self.assertEqual(td.get_pointee_abi_size(glob.type), 24) + self.assertEqual(td.get_abi_size(glob.global_value_type), 24) def test_get_struct_element_offset(self): td = self.target_data() @@ -2202,7 +2217,7 @@ def test_get_struct_element_offset(self): with self.assertRaises(ValueError): td.get_element_offset(glob.type, 0) - struct_type = glob.type.element_type + struct_type = glob.global_value_type self.assertEqual(td.get_element_offset(struct_type, 0), 0) self.assertEqual(td.get_element_offset(struct_type, 1), 8) diff --git a/llvmlite/tests/test_ir.py b/llvmlite/tests/test_ir.py index 284129679..1531e9e2c 100644 --- a/llvmlite/tests/test_ir.py +++ b/llvmlite/tests/test_ir.py @@ -13,6 +13,9 @@ from llvmlite import ir from llvmlite import binding as llvm +# FIXME: Remove me once typed pointers are no longer supported. +from llvmlite import opaque_pointers_enabled + int1 = ir.IntType(1) int8 = ir.IntType(8) @@ -120,7 +123,11 @@ def check_func_body(self, func, asm): class TestFunction(TestBase): - proto = """i32 @"my_func"(i32 %".1", i32 %".2", double %".3", i32* %".4")""" + # FIXME: Remove `else' once TP are no longer supported. + proto = \ + """i32 @"my_func"(i32 %".1", i32 %".2", double %".3", ptr %".4")""" \ + if opaque_pointers_enabled else \ + """i32 @"my_func"(i32 %".1", i32 %".2", double %".3", i32* %".4")""" def test_declare(self): # A simple declaration @@ -139,11 +146,19 @@ def test_declare_attributes(self): pers = ir.Function(self.module(), tp_pers, '__gxx_personality_v0') func.attributes.personality = pers asm = self.descr(func).strip() - self.assertEqual(asm, - ("declare %s alwaysinline convergent optsize " - "alignstack(16) " - "personality i8 (...)* @\"__gxx_personality_v0\"") % - self.proto) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(asm, + ("declare %s alwaysinline convergent optsize " + "alignstack(16) " + "personality ptr @\"__gxx_personality_v0\"") % + self.proto) + else: + self.assertEqual(asm, + ("declare %s alwaysinline convergent optsize " + "alignstack(16) personality " + "i8 (...)* @\"__gxx_personality_v0\"") % + self.proto) # Check pickling self.assert_pickle_correctly(func) @@ -157,9 +172,15 @@ def test_function_attributes(self): func.args[3].add_attribute("nonnull") func.return_value.add_attribute("noalias") asm = self.descr(func).strip() - self.assertEqual(asm, - """declare noalias i32 @"my_func"(i32 zeroext %".1", i32 dereferenceable(5) dereferenceable_or_null(10) %".2", double %".3", i32* nonnull align 4 %".4")""" # noqa E501 - ) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(asm, + """declare noalias i32 @"my_func"(i32 zeroext %".1", i32 dereferenceable(5) dereferenceable_or_null(10) %".2", double %".3", ptr nonnull align 4 %".4")""" # noqa E501 + ) + else: + self.assertEqual(asm, + """declare noalias i32 @"my_func"(i32 zeroext %".1", i32 dereferenceable(5) dereferenceable_or_null(10) %".2", double %".3", i32* nonnull align 4 %".4")""" # noqa E501 + ) # Check pickling self.assert_pickle_correctly(func) @@ -263,10 +284,17 @@ def test_declare_intrinsics(self): assume = module.declare_intrinsic('llvm.assume') self.check_descr(self.descr(powi).strip(), """\ declare double @"llvm.powi.f64"(double %".1", i32 %".2")""") - self.check_descr(self.descr(memset).strip(), """\ - declare void @"llvm.memset.p0i8.i32"(i8* %".1", i8 %".2", i32 %".3", i1 %".4")""") # noqa E501 - self.check_descr(self.descr(memcpy).strip(), """\ - declare void @"llvm.memcpy.p0i8.p0i8.i32"(i8* %".1", i8* %".2", i32 %".3", i1 %".4")""") # noqa E501 + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_descr(self.descr(memset).strip(), """\ + declare void @"llvm.memset.p0i8.i32"(ptr %".1", i8 %".2", i32 %".3", i1 %".4")""") # noqa E501 + self.check_descr(self.descr(memcpy).strip(), """\ + declare void @"llvm.memcpy.p0i8.p0i8.i32"(ptr %".1", ptr %".2", i32 %".3", i1 %".4")""") # noqa E501 + else: + self.check_descr(self.descr(memset).strip(), """\ + declare void @"llvm.memset.p0i8.i32"(i8* %".1", i8 %".2", i32 %".3", i1 %".4")""") # noqa E501 + self.check_descr(self.descr(memcpy).strip(), """\ + declare void @"llvm.memcpy.p0i8.p0i8.i32"(i8* %".1", i8* %".2", i32 %".3", i1 %".4")""") # noqa E501 self.check_descr(self.descr(assume).strip(), """\ declare void @"llvm.assume"(i1 %".1")""") @@ -374,7 +402,11 @@ def test_metadata_null(self): # A null metadata (typed) value mod = self.module() mod.add_metadata([int32.as_pointer()(None)]) - self.assert_ir_line("!0 = !{ i32* null }", mod) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assert_ir_line("!0 = !{ ptr null }", mod) + else: + self.assert_ir_line("!0 = !{ i32* null }", mod) self.assert_valid_ir(mod) # A null metadata (untyped) value mod = self.module() @@ -591,8 +623,14 @@ def test_globals_access(self): with self.assertRaises(KeyError): mod.get_global('kkk') # Globals should have a useful repr() - self.assertEqual(repr(globdouble), - "") + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(repr(globdouble), + "") + else: + self.assertEqual( + repr(globdouble), + "") def test_functions_global_values_access(self): """ @@ -997,21 +1035,39 @@ def test_mem_ops(self): # Mismatching pointer type with self.assertRaises(TypeError) as cm: builder.store(b, e) - self.assertEqual(str(cm.exception), - "cannot store i32 to double*: mismatching types") - self.check_block(block, """\ - my_block: - %"c" = alloca i32 - %"d" = alloca i32, i32 42 - %"e" = alloca double, i32 %".1", align 8 - store double %".3", double* %"e" - store i32 %".2", i32* %"c" - %"g" = load i32, i32* %"c" - store i32 %".2", i32* %"c", align 1 - %"i" = load i32, i32* %"c", align 1 - store atomic i32 %".2", i32* %"c" seq_cst, align 4 - %"k" = load atomic i32, i32* %"c" seq_cst, align 4 - """) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(cm.exception), + "cannot store i32 to ptr: mismatching types") + self.check_block(block, """\ + my_block: + %"c" = alloca i32 + %"d" = alloca i32, i32 42 + %"e" = alloca double, i32 %".1", align 8 + store double %".3", ptr %"e" + store i32 %".2", ptr %"c" + %"g" = load i32, ptr %"c" + store i32 %".2", ptr %"c", align 1 + %"i" = load i32, ptr %"c", align 1 + store atomic i32 %".2", ptr %"c" seq_cst, align 4 + %"k" = load atomic i32, ptr %"c" seq_cst, align 4 + """) + else: + self.assertEqual(str(cm.exception), + "cannot store i32 to double*: mismatching types") + self.check_block(block, """\ + my_block: + %"c" = alloca i32 + %"d" = alloca i32, i32 42 + %"e" = alloca double, i32 %".1", align 8 + store double %".3", double* %"e" + store i32 %".2", i32* %"c" + %"g" = load i32, i32* %"c" + store i32 %".2", i32* %"c", align 1 + %"i" = load i32, i32* %"c", align 1 + store atomic i32 %".2", i32* %"c" seq_cst, align 4 + %"k" = load atomic i32, i32* %"c" seq_cst, align 4 + """) def test_gep(self): block = self.block(name='my_block') @@ -1020,11 +1076,19 @@ def test_gep(self): c = builder.alloca(ir.PointerType(int32), name='c') d = builder.gep(c, [ir.Constant(int32, 5), a], name='d') self.assertEqual(d.type, ir.PointerType(int32)) - self.check_block(block, """\ - my_block: - %"c" = alloca i32* - %"d" = getelementptr i32*, i32** %"c", i32 5, i32 %".1" - """) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + %"c" = alloca ptr + %"d" = getelementptr ptr, ptr %"c", i32 5, i32 %".1" + """) + else: + self.check_block(block, """\ + my_block: + %"c" = alloca i32* + %"d" = getelementptr i32*, i32** %"c", i32 5, i32 %".1" + """) # XXX test with more complex types def test_gep_castinstr(self): @@ -1038,11 +1102,19 @@ def test_gep_castinstr(self): d = builder.bitcast(a, ls.as_pointer(), name='d') e = builder.gep(d, [ir.Constant(int32, x) for x in [0, 3]], name='e') self.assertEqual(e.type, ir.PointerType(int8ptr)) - self.check_block(block, """\ - my_block: - %"d" = bitcast i32 %".1" to {i64, i8*, i8*, i8*, i64}* - %"e" = getelementptr {i64, i8*, i8*, i8*, i64}, {i64, i8*, i8*, i8*, i64}* %"d", i32 0, i32 3 - """) # noqa E501 + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + %"d" = bitcast i32 %".1" to ptr + %"e" = getelementptr {i64, ptr, ptr, ptr, i64}, ptr %"d", i32 0, i32 3 + """) # noqa E501 + else: + self.check_block(block, """\ + my_block: + %"d" = bitcast i32 %".1" to {i64, i8*, i8*, i8*, i64}* + %"e" = getelementptr {i64, i8*, i8*, i8*, i64}, {i64, i8*, i8*, i8*, i64}* %"d", i32 0, i32 3 + """) # noqa E501 def test_gep_castinstr_addrspace(self): # similar to: @@ -1057,11 +1129,19 @@ def test_gep_castinstr_addrspace(self): e = builder.gep(d, [ir.Constant(int32, x) for x in [0, 3]], name='e') self.assertEqual(e.type.addrspace, addrspace) self.assertEqual(e.type, ir.PointerType(int8ptr, addrspace=addrspace)) - self.check_block(block, """\ - my_block: - %"d" = bitcast i32 %".1" to {i64, i8*, i8*, i8*, i64} addrspace(4)* - %"e" = getelementptr {i64, i8*, i8*, i8*, i64}, {i64, i8*, i8*, i8*, i64} addrspace(4)* %"d", i32 0, i32 3 - """) # noqa E501 + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + %"d" = bitcast i32 %".1" to ptr addrspace(4) + %"e" = getelementptr {i64, ptr, ptr, ptr, i64}, ptr addrspace(4) %"d", i32 0, i32 3 + """) # noqa E501 + else: + self.check_block(block, """\ + my_block: + %"d" = bitcast i32 %".1" to {i64, i8*, i8*, i8*, i64} addrspace(4)* + %"e" = getelementptr {i64, i8*, i8*, i8*, i64}, {i64, i8*, i8*, i8*, i64} addrspace(4)* %"d", i32 0, i32 3 + """) # noqa E501 def test_gep_addrspace(self): block = self.block(name='my_block') @@ -1069,18 +1149,31 @@ def test_gep_addrspace(self): a, b = builder.function.args[:2] addrspace = 4 c = builder.alloca(ir.PointerType(int32, addrspace=addrspace), name='c') - self.assertEqual(str(c.type), 'i32 addrspace(4)**') + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(c.type), 'ptr') + else: + self.assertEqual(str(c.type), 'i32 addrspace(4)**') self.assertEqual(c.type.pointee.addrspace, addrspace) d = builder.gep(c, [ir.Constant(int32, 5), a], name='d') self.assertEqual(d.type.addrspace, addrspace) e = builder.gep(d, [ir.Constant(int32, 10)], name='e') self.assertEqual(e.type.addrspace, addrspace) - self.check_block(block, """\ - my_block: - %"c" = alloca i32 addrspace(4)* - %"d" = getelementptr i32 addrspace(4)*, i32 addrspace(4)** %"c", i32 5, i32 %".1" - %"e" = getelementptr i32, i32 addrspace(4)* %"d", i32 10 - """) # noqa E501 + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + %"c" = alloca ptr addrspace(4) + %"d" = getelementptr ptr addrspace(4), ptr %"c", i32 5, i32 %".1" + %"e" = getelementptr i32, ptr addrspace(4) %"d", i32 10 + """) # noqa E501 + else: + self.check_block(block, """\ + my_block: + %"c" = alloca i32 addrspace(4)* + %"d" = getelementptr i32 addrspace(4)*, i32 addrspace(4)** %"c", i32 5, i32 %".1" + %"e" = getelementptr i32, i32 addrspace(4)* %"d", i32 10 + """) # noqa E501 def test_extract_insert_value(self): block = self.block(name='my_block') @@ -1128,20 +1221,37 @@ def test_extract_insert_value(self): # Replacement value has the wrong type builder.insert_value(c_inner, a, 1) - self.check_block(block, """\ - my_block: - %"c" = extractvalue {i32, i1} {i32 4, i1 true}, 0 - %"d" = insertvalue {i32, i1} {i32 4, i1 true}, i32 %".1", 0 - %"e" = insertvalue {i32, i1} %"d", i1 false, 1 - %"ptr" = alloca {i8, {i32, i1}} - %"j" = load {i8, {i32, i1}}, {i8, {i32, i1}}* %"ptr" - %"k" = extractvalue {i8, {i32, i1}} %"j", 0 - %"l" = extractvalue {i8, {i32, i1}} %"j", 1 - %"m" = extractvalue {i8, {i32, i1}} %"j", 1, 0 - %"n" = extractvalue {i8, {i32, i1}} %"j", 1, 1 - %"o" = insertvalue {i8, {i32, i1}} %"j", {i32, i1} %"l", 1 - %"p" = insertvalue {i8, {i32, i1}} %"j", i32 %".1", 1, 0 - """) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + %"c" = extractvalue {i32, i1} {i32 4, i1 true}, 0 + %"d" = insertvalue {i32, i1} {i32 4, i1 true}, i32 %".1", 0 + %"e" = insertvalue {i32, i1} %"d", i1 false, 1 + %"ptr" = alloca {i8, {i32, i1}} + %"j" = load {i8, {i32, i1}}, ptr %"ptr" + %"k" = extractvalue {i8, {i32, i1}} %"j", 0 + %"l" = extractvalue {i8, {i32, i1}} %"j", 1 + %"m" = extractvalue {i8, {i32, i1}} %"j", 1, 0 + %"n" = extractvalue {i8, {i32, i1}} %"j", 1, 1 + %"o" = insertvalue {i8, {i32, i1}} %"j", {i32, i1} %"l", 1 + %"p" = insertvalue {i8, {i32, i1}} %"j", i32 %".1", 1, 0 + """) + else: + self.check_block(block, """\ + my_block: + %"c" = extractvalue {i32, i1} {i32 4, i1 true}, 0 + %"d" = insertvalue {i32, i1} {i32 4, i1 true}, i32 %".1", 0 + %"e" = insertvalue {i32, i1} %"d", i1 false, 1 + %"ptr" = alloca {i8, {i32, i1}} + %"j" = load {i8, {i32, i1}}, {i8, {i32, i1}}* %"ptr" + %"k" = extractvalue {i8, {i32, i1}} %"j", 0 + %"l" = extractvalue {i8, {i32, i1}} %"j", 1 + %"m" = extractvalue {i8, {i32, i1}} %"j", 1, 0 + %"n" = extractvalue {i8, {i32, i1}} %"j", 1, 1 + %"o" = insertvalue {i8, {i32, i1}} %"j", {i32, i1} %"l", 1 + %"p" = insertvalue {i8, {i32, i1}} %"j", i32 %".1", 1, 0 + """) def test_cast_ops(self): block = self.block(name='my_block') @@ -1160,21 +1270,39 @@ def test_cast_ops(self): j = builder.inttoptr(i, ir.PointerType(int8), 'j') # noqa F841 k = builder.bitcast(a, flt, "k") # noqa F841 self.assertFalse(block.is_terminated) - self.check_block(block, """\ - my_block: - %"c" = trunc i32 %".1" to i8 - %"d" = zext i8 %"c" to i32 - %"e" = sext i8 %"c" to i32 - %"fb" = fptrunc double %".3" to float - %"fc" = fpext float %"fb" to double - %"g" = fptoui double %".3" to i32 - %"h" = fptosi double %".3" to i8 - %"fd" = uitofp i32 %"g" to float - %"fe" = sitofp i8 %"h" to double - %"i" = ptrtoint i32* %".4" to i32 - %"j" = inttoptr i32 %"i" to i8* - %"k" = bitcast i32 %".1" to float - """) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + %"c" = trunc i32 %".1" to i8 + %"d" = zext i8 %"c" to i32 + %"e" = sext i8 %"c" to i32 + %"fb" = fptrunc double %".3" to float + %"fc" = fpext float %"fb" to double + %"g" = fptoui double %".3" to i32 + %"h" = fptosi double %".3" to i8 + %"fd" = uitofp i32 %"g" to float + %"fe" = sitofp i8 %"h" to double + %"i" = ptrtoint ptr %".4" to i32 + %"j" = inttoptr i32 %"i" to ptr + %"k" = bitcast i32 %".1" to float + """) + else: + self.check_block(block, """\ + my_block: + %"c" = trunc i32 %".1" to i8 + %"d" = zext i8 %"c" to i32 + %"e" = sext i8 %"c" to i32 + %"fb" = fptrunc double %".3" to float + %"fc" = fpext float %"fb" to double + %"g" = fptoui double %".3" to i32 + %"h" = fptosi double %".3" to i8 + %"fd" = uitofp i32 %"g" to float + %"fe" = sitofp i8 %"h" to double + %"i" = ptrtoint i32* %".4" to i32 + %"j" = inttoptr i32 %"i" to i8* + %"k" = bitcast i32 %".1" to float + """) def test_atomicrmw(self): block = self.block(name='my_block') @@ -1183,11 +1311,19 @@ def test_atomicrmw(self): c = builder.alloca(int32, name='c') d = builder.atomic_rmw('add', c, a, 'monotonic', 'd') self.assertEqual(d.type, int32) - self.check_block(block, """\ - my_block: - %"c" = alloca i32 - %"d" = atomicrmw add i32* %"c", i32 %".1" monotonic - """) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + %"c" = alloca i32 + %"d" = atomicrmw add ptr %"c", i32 %".1" monotonic + """) + else: + self.check_block(block, """\ + my_block: + %"c" = alloca i32 + %"d" = atomicrmw add i32* %"c", i32 %".1" monotonic + """) def test_branch(self): block = self.block(name='my_block') @@ -1238,10 +1374,17 @@ def test_branch_indirect(self): indirectbr.add_destination(bb_1) indirectbr.add_destination(bb_2) self.assertTrue(block.is_terminated) - self.check_block(block, """\ - my_block: - indirectbr i8* blockaddress(@"my_func", %"b_1"), [label %"b_1", label %"b_2"] - """) # noqa E501 + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + indirectbr ptr blockaddress(@"my_func", %"b_1"), [label %"b_1", label %"b_2"] + """) # noqa E501 + else: + self.check_block(block, """\ + my_block: + indirectbr i8* blockaddress(@"my_func", %"b_1"), [label %"b_1", label %"b_2"] + """) # noqa E501 def test_returns(self): def check(block, expected_ir): @@ -1354,11 +1497,19 @@ def test_call_metadata(self): a = builder.alloca(int32, name="a") b = builder.module.add_metadata(()) builder.call(dbg_declare, (a, b, b)) - self.check_block(block, """\ - my_block: - %"a" = alloca i32 - call void @"llvm.dbg.declare"(metadata i32* %"a", metadata !0, metadata !0) - """) # noqa E501 + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + %"a" = alloca i32 + call void @"llvm.dbg.declare"(metadata ptr %"a", metadata !0, metadata !0) + """) # noqa E501 + else: + self.check_block(block, """\ + my_block: + %"a" = alloca i32 + call void @"llvm.dbg.declare"(metadata i32* %"a", metadata !0, metadata !0) + """) # noqa E501 def test_call_attributes(self): block = self.block(name='my_block') @@ -1377,12 +1528,21 @@ def test_call_attributes(self): 2: 'noalias' } ) - self.check_block_regex(block, """\ - my_block: - %"retval" = alloca i32 - %"other" = alloca i32 - call void @"fun"\\(i32\\* noalias sret(\\(i32\\))? %"retval", i32 42, i32\\* noalias %"other"\\) - """) # noqa E501 + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block_regex(block, """\ + my_block: + %"retval" = alloca i32 + %"other" = alloca i32 + call void @"fun"\\(ptr noalias sret(\\(i32\\))? %"retval", i32 42, ptr noalias %"other"\\) + """) # noqa E501 + else: + self.check_block_regex(block, """\ + my_block: + %"retval" = alloca i32 + %"other" = alloca i32 + call void @"fun"\\(i32\\* noalias sret(\\(i32\\))? %"retval", i32 42, i32\\* noalias %"other"\\) + """) # noqa E501 def test_call_tail(self): block = self.block(name='my_block') @@ -1460,13 +1620,23 @@ def test_invoke_attributes(self): 2: 'noalias' } ) - self.check_block_regex(block, """\ - my_block: - %"retval" = alloca i32 - %"other" = alloca i32 - invoke fast fastcc void @"fun"\\(i32\\* noalias sret(\\(i32\\))? %"retval", i32 42, i32\\* noalias %"other"\\) noinline - to label %"normal" unwind label %"unwind" - """) # noqa E501 + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block_regex(block, """\ + my_block: + %"retval" = alloca i32 + %"other" = alloca i32 + invoke fast fastcc void @"fun"\\(ptr noalias sret(\\(i32\\))? %"retval", i32 42, ptr noalias %"other"\\) noinline + to label %"normal" unwind label %"unwind" + """) # noqa E501 + else: + self.check_block_regex(block, """\ + my_block: + %"retval" = alloca i32 + %"other" = alloca i32 + invoke fast fastcc void @"fun"\\(i32\\* noalias sret(\\(i32\\))? %"retval", i32 42, i32\\* noalias %"other"\\) noinline + to label %"normal" unwind label %"unwind" + """) # noqa E501 def test_landingpad(self): block = self.block(name='my_block') @@ -1480,13 +1650,23 @@ def test_landingpad(self): lp.add_clause(ir.FilterClause(ir.Constant(ir.ArrayType( int_typeinfo.type, 1), [int_typeinfo]))) builder.resume(lp) - self.check_block(block, """\ - my_block: - %"lp" = landingpad {i32, i8*} - catch i8** @"_ZTIi" - filter [1 x i8**] [i8** @"_ZTIi"] - resume {i32, i8*} %"lp" - """) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + %"lp" = landingpad {i32, ptr} + catch ptr @"_ZTIi" + filter [1 x ptr] [ptr @"_ZTIi"] + resume {i32, ptr} %"lp" + """) + else: + self.check_block(block, """\ + my_block: + %"lp" = landingpad {i32, i8*} + catch i8** @"_ZTIi" + filter [1 x i8**] [i8** @"_ZTIi"] + resume {i32, i8*} %"lp" + """) def test_assume(self): block = self.block(name='my_block') @@ -2158,10 +2338,17 @@ def test_metadata(self): builder = ir.IRBuilder(block) builder.debug_metadata = builder.module.add_metadata([]) builder.alloca(ir.PointerType(int32), name='c') - self.check_block(block, """\ - my_block: - %"c" = alloca i32*, !dbg !0 - """) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.check_block(block, """\ + my_block: + %"c" = alloca ptr, !dbg !0 + """) + else: + self.check_block(block, """\ + my_block: + %"c" = alloca i32*, !dbg !0 + """) class TestTypes(TestBase): @@ -2236,18 +2423,36 @@ def test_str(self): 'i1 (float, ...)') self.assertEqual(str(ir.FunctionType(int1, (flt, dbl), var_arg=True)), 'i1 (float, double, ...)') - self.assertEqual(str(ir.PointerType(int32)), 'i32*') - self.assertEqual(str(ir.PointerType(ir.PointerType(int32))), 'i32**') + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(ir.PointerType(int32)), 'ptr') + self.assertEqual(str(ir.PointerType(ir.PointerType(int32))), 'ptr') + else: + self.assertEqual(str(ir.PointerType(int32)), 'i32*') + self.assertEqual(str(ir.PointerType(ir.PointerType(int32))), + 'i32**') self.assertEqual(str(ir.ArrayType(int1, 5)), '[5 x i1]') - self.assertEqual(str(ir.ArrayType(ir.PointerType(int1), 5)), - '[5 x i1*]') - self.assertEqual(str(ir.PointerType(ir.ArrayType(int1, 5))), - '[5 x i1]*') + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(ir.ArrayType(ir.PointerType(int1), 5)), + '[5 x ptr]') + self.assertEqual(str(ir.PointerType(ir.ArrayType(int1, 5))), 'ptr') + else: + self.assertEqual(str(ir.ArrayType(ir.PointerType(int1), 5)), + '[5 x i1*]') + self.assertEqual(str(ir.PointerType(ir.ArrayType(int1, 5))), + '[5 x i1]*') self.assertEqual(str(ir.LiteralStructType((int1,))), '{i1}') self.assertEqual(str(ir.LiteralStructType((int1, flt))), '{i1, float}') - self.assertEqual(str(ir.LiteralStructType(( - ir.PointerType(int1), ir.LiteralStructType((int32, int8))))), - '{i1*, {i32, i8}}') + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(ir.LiteralStructType(( + ir.PointerType(int1), ir.LiteralStructType((int32, int8))))), + '{ptr, {i32, i8}}') + else: + self.assertEqual(str(ir.LiteralStructType(( + ir.PointerType(int1), ir.LiteralStructType((int32, int8))))), + '{i1*, {i32, i8}}') self.assertEqual(str(ir.LiteralStructType((int1,), packed=True)), '<{i1}>') self.assertEqual(str(ir.LiteralStructType((int1, flt), packed=True)), @@ -2524,8 +2729,13 @@ def test_gep(self): tp = ir.LiteralStructType((flt, int1)) gv = ir.GlobalVariable(m, tp, "myconstant") c = gv.gep([ir.Constant(int32, x) for x in (0, 1)]) - self.assertEqual(str(c), - 'getelementptr ({float, i1}, {float, i1}* @"myconstant", i32 0, i32 1)') # noqa E501 + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(c), + 'getelementptr ({float, i1}, ptr @"myconstant", i32 0, i32 1)') # noqa E501 + else: + self.assertEqual(str(c), + 'getelementptr ({float, i1}, {float, i1}* @"myconstant", i32 0, i32 1)') # noqa E501 self.assertEqual(c.type, ir.PointerType(int1)) const = ir.Constant(tp, None) @@ -2534,8 +2744,13 @@ def test_gep(self): const_ptr = ir.Constant(tp.as_pointer(), None) c2 = const_ptr.gep([ir.Constant(int32, 0)]) - self.assertEqual(str(c2), - 'getelementptr ({float, i1}, {float, i1}* null, i32 0)') # noqa E501 + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(c2), + 'getelementptr ({float, i1}, ptr null, i32 0)') # noqa E501 + else: + self.assertEqual(str(c2), + 'getelementptr ({float, i1}, {float, i1}* null, i32 0)') # noqa E501 self.assertEqual(c.type, ir.PointerType(int1)) def test_gep_addrspace_globalvar(self): @@ -2547,9 +2762,15 @@ def test_gep_addrspace_globalvar(self): self.assertEqual(gv.addrspace, addrspace) c = gv.gep([ir.Constant(int32, x) for x in (0, 1)]) self.assertEqual(c.type.addrspace, addrspace) - self.assertEqual(str(c), - ('getelementptr ({float, i1}, {float, i1} ' - 'addrspace(4)* @"myconstant", i32 0, i32 1)')) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(c), + ('getelementptr ({float, i1}, ptr ' + 'addrspace(4) @"myconstant", i32 0, i32 1)')) + else: + self.assertEqual(str(c), + ('getelementptr ({float, i1}, {float, i1} ' + 'addrspace(4)* @"myconstant", i32 0, i32 1)')) self.assertEqual(c.type, ir.PointerType(int1, addrspace=addrspace)) def test_trunc(self): @@ -2576,7 +2797,11 @@ def test_bitcast(self): m = self.module() gv = ir.GlobalVariable(m, int32, "myconstant") c = gv.bitcast(int64.as_pointer()) - self.assertEqual(str(c), 'bitcast (i32* @"myconstant" to i64*)') + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(c), 'bitcast (ptr @"myconstant" to ptr)') + else: + self.assertEqual(str(c), 'bitcast (i32* @"myconstant" to i64*)') def test_fptoui(self): c = ir.Constant(flt, 1).fptoui(int32) @@ -2601,19 +2826,33 @@ def test_ptrtoint_1(self): self.assertRaises(TypeError, one.ptrtoint, int64) self.assertRaises(TypeError, ptr.ptrtoint, flt) - self.assertEqual(str(c), 'ptrtoint (i64* null to i32)') + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(c), 'ptrtoint (ptr null to i32)') + else: + self.assertEqual(str(c), 'ptrtoint (i64* null to i32)') def test_ptrtoint_2(self): m = self.module() gv = ir.GlobalVariable(m, int32, "myconstant") c = gv.ptrtoint(int64) - self.assertEqual(str(c), 'ptrtoint (i32* @"myconstant" to i64)') - - self.assertRaisesRegex( - TypeError, - r"can only ptrtoint\(\) to integer type, not 'i64\*'", - gv.ptrtoint, - int64.as_pointer()) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(c), 'ptrtoint (ptr @"myconstant" to i64)') + + self.assertRaisesRegex( + TypeError, + r"can only ptrtoint\(\) to integer type, not 'ptr'", + gv.ptrtoint, + int64.as_pointer()) + else: + self.assertEqual(str(c), 'ptrtoint (i32* @"myconstant" to i64)') + + self.assertRaisesRegex( + TypeError, + r"can only ptrtoint\(\) to integer type, not 'i64\*'", + gv.ptrtoint, + int64.as_pointer()) c2 = ir.Constant(int32, 0) self.assertRaisesRegex( @@ -2629,7 +2868,11 @@ def test_inttoptr(self): self.assertRaises(TypeError, one.inttoptr, int64) self.assertRaises(TypeError, pi.inttoptr, int64.as_pointer()) - self.assertEqual(str(c), 'inttoptr (i32 1 to i64*)') + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertEqual(str(c), 'inttoptr (i32 1 to ptr)') + else: + self.assertEqual(str(c), 'inttoptr (i32 1 to i64*)') def test_neg(self): one = ir.Constant(int32, 1) diff --git a/llvmlite/tests/test_refprune.py b/llvmlite/tests/test_refprune.py index 0c4208a26..1e6f17921 100644 --- a/llvmlite/tests/test_refprune.py +++ b/llvmlite/tests/test_refprune.py @@ -3,6 +3,9 @@ from llvmlite import binding as llvm from llvmlite.tests import TestCase +# FIXME: Remove me once typed pointers are no longer supported. +from llvmlite import opaque_pointers_enabled + from . import refprune_proto as proto @@ -210,9 +213,22 @@ def test_per_bb_2(self): mod, stats = self.check(self.per_bb_ir_2) self.assertEqual(stats.basicblock, 4) # not pruned - self.assertIn("call void @NRT_incref(i8* %ptr)", str(mod)) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertIn("call void @NRT_incref(ptr %ptr)", str(mod)) + else: + self.assertIn("call void @NRT_incref(i8* %ptr)", str(mod)) + # FIXME: Remove `else' once TP are no longer supported. per_bb_ir_3 = r""" +define void @main(ptr %ptr, ptr %other) { + call void @NRT_incref(ptr %ptr) + call void @NRT_incref(ptr %ptr) + call void @NRT_decref(ptr %ptr) + call void @NRT_decref(ptr %other) + ret void +} +""" if opaque_pointers_enabled else r""" define void @main(i8* %ptr, i8* %other) { call void @NRT_incref(i8* %ptr) call void @NRT_incref(i8* %ptr) @@ -226,10 +242,25 @@ def test_per_bb_3(self): mod, stats = self.check(self.per_bb_ir_3) self.assertEqual(stats.basicblock, 2) # not pruned - self.assertIn("call void @NRT_decref(i8* %other)", str(mod)) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertIn("call void @NRT_decref(ptr %other)", str(mod)) + else: + self.assertIn("call void @NRT_decref(i8* %other)", str(mod)) + # FIXME: Remove `else' once TP are no longer supported. per_bb_ir_4 = r""" ; reordered +define void @main(ptr %ptr, ptr %other) { + call void @NRT_incref(ptr %ptr) + call void @NRT_decref(ptr %ptr) + call void @NRT_decref(ptr %ptr) + call void @NRT_decref(ptr %other) + call void @NRT_incref(ptr %ptr) + ret void +} +""" if opaque_pointers_enabled else r""" +; reordered define void @main(i8* %ptr, i8* %other) { call void @NRT_incref(i8* %ptr) call void @NRT_decref(i8* %ptr) @@ -244,7 +275,11 @@ def test_per_bb_4(self): mod, stats = self.check(self.per_bb_ir_4) self.assertEqual(stats.basicblock, 4) # not pruned - self.assertIn("call void @NRT_decref(i8* %other)", str(mod)) + # FIXME: Remove `else' once TP are no longer supported. + if opaque_pointers_enabled: + self.assertIn("call void @NRT_decref(ptr %other)", str(mod)) + else: + self.assertIn("call void @NRT_decref(i8* %other)", str(mod)) class TestDiamond(BaseTestByIR):