diff --git a/ffi/core.cpp b/ffi/core.cpp index 4afc7f7fd..e7273a89d 100644 --- a/ffi/core.cpp +++ b/ffi/core.cpp @@ -1,6 +1,7 @@ #include "core.h" #include "llvm-c/Support.h" +#include "llvm/IR/LLVMContext.h" extern "C" { @@ -21,10 +22,33 @@ API_EXPORT(void) LLVMPY_DisposeString(const char *msg) { free(const_cast(msg)); } API_EXPORT(LLVMContextRef) -LLVMPY_GetGlobalContext() { return LLVMGetGlobalContext(); } +LLVMPY_GetGlobalContext(bool enable_opaque_pointers) { + LLVMContextRef ctx = LLVMGetGlobalContext(); +#if LLVM_VERSION_MAJOR >= 14 + if (enable_opaque_pointers) + llvm::unwrap(ctx)->enableOpaquePointers(); +#endif + return ctx; +} API_EXPORT(LLVMContextRef) -LLVMPY_ContextCreate() { return LLVMContextCreate(); } +LLVMPY_ContextCreate(bool enable_opaque_pointers) { + LLVMContextRef ctx = LLVMContextCreate(); +#if LLVM_VERSION_MAJOR >= 14 + if (enable_opaque_pointers) + llvm::unwrap(ctx)->enableOpaquePointers(); +#endif + return ctx; +} + +API_EXPORT(bool) +LLVMPY_SupportsTypedPointers(LLVMContextRef ctx) { +#if LLVM_VERSION_MAJOR >= 13 + return llvm::unwrap(ctx)->supportsTypedPointers(); +#else + return true; +#endif +} API_EXPORT(void) LLVMPY_ContextDispose(LLVMContextRef context) { diff --git a/ffi/core.h b/ffi/core.h index 4888f8001..74894a2a1 100644 --- a/ffi/core.h +++ b/ffi/core.h @@ -27,10 +27,13 @@ API_EXPORT(void) LLVMPY_DisposeString(const char *msg); API_EXPORT(LLVMContextRef) -LLVMPY_GetGlobalContext(); +LLVMPY_GetGlobalContext(bool enable_opaque_pointers); API_EXPORT(LLVMContextRef) -LLVMPY_ContextCreate(); +LLVMPY_ContextCreate(bool enable_opaque_pointers); + +API_EXPORT(bool) +LLVMPY_SupportsTypedPointers(LLVMContextRef ctx); } /* end extern "C" */ diff --git a/ffi/targets.cpp b/ffi/targets.cpp index b96d22c9f..48ef43e76 100644 --- a/ffi/targets.cpp +++ b/ffi/targets.cpp @@ -104,6 +104,11 @@ LLVMPY_CopyStringRepOfTargetData(LLVMTargetDataRef TD, char **Out) { API_EXPORT(void) LLVMPY_DisposeTargetData(LLVMTargetDataRef TD) { LLVMDisposeTargetData(TD); } +API_EXPORT(long long) +LLVMPY_ABIAlignmentOfType(LLVMTargetDataRef TD, LLVMTypeRef Ty) { + return (long long)LLVMABIAlignmentOfType(TD, Ty); +} + API_EXPORT(long long) LLVMPY_ABISizeOfType(LLVMTargetDataRef TD, LLVMTypeRef Ty) { return (long long)LLVMABISizeOfType(TD, Ty); diff --git a/ffi/value.cpp b/ffi/value.cpp index 01871699d..d9d22229f 100644 --- a/ffi/value.cpp +++ b/ffi/value.cpp @@ -325,6 +325,16 @@ LLVMPY_SetValueName(LLVMValueRef Val, const char *Name) { API_EXPORT(LLVMModuleRef) LLVMPY_GetGlobalParent(LLVMValueRef Val) { return LLVMGetGlobalParent(Val); } +API_EXPORT(LLVMTypeRef) +LLVMPY_GlobalGetValueType(LLVMValueRef Val) { + llvm::Value *unwrapped = llvm::unwrap(Val); + llvm::GlobalValue *gv = llvm::dyn_cast(unwrapped); + if (!gv) { + return nullptr; + } + return llvm::wrap(gv->getValueType()); +} + API_EXPORT(LLVMTypeRef) LLVMPY_TypeOf(LLVMValueRef Val) { return LLVMTypeOf(Val); } diff --git a/llvmlite/binding/context.py b/llvmlite/binding/context.py index 7dffb82a7..b24f5fa55 100644 --- a/llvmlite/binding/context.py +++ b/llvmlite/binding/context.py @@ -1,18 +1,23 @@ +from ctypes import c_bool + from llvmlite.binding import ffi def create_context(): - return ContextRef(ffi.lib.LLVMPY_ContextCreate()) + return ContextRef(ffi.lib.LLVMPY_ContextCreate(True)) def get_global_context(): - return GlobalContextRef(ffi.lib.LLVMPY_GetGlobalContext()) + return GlobalContextRef(ffi.lib.LLVMPY_GetGlobalContext(True)) class ContextRef(ffi.ObjectRef): def __init__(self, context_ptr): super(ContextRef, self).__init__(context_ptr) + def supports_typed_pointers(self): + return ffi.lib.LLVMPY_SupportsTypedPointers(self) + def _dispose(self): ffi.lib.LLVMPY_ContextDispose(self) @@ -22,8 +27,13 @@ def _dispose(self): pass +ffi.lib.LLVMPY_GetGlobalContext.argtypes = [c_bool] ffi.lib.LLVMPY_GetGlobalContext.restype = ffi.LLVMContextRef +ffi.lib.LLVMPY_ContextCreate.argtypes = [c_bool] ffi.lib.LLVMPY_ContextCreate.restype = ffi.LLVMContextRef +ffi.lib.LLVMPY_SupportsTypedPointers.argtypes = [ffi.LLVMContextRef] +ffi.lib.LLVMPY_SupportsTypedPointers.restype = c_bool + ffi.lib.LLVMPY_ContextDispose.argtypes = [ffi.LLVMContextRef] diff --git a/llvmlite/binding/targets.py b/llvmlite/binding/targets.py index a7e6ffdc3..0e0d1d042 100644 --- a/llvmlite/binding/targets.py +++ b/llvmlite/binding/targets.py @@ -135,6 +135,12 @@ def get_abi_size(self, ty): """ return ffi.lib.LLVMPY_ABISizeOfType(self, ty) + def get_abi_alignment(self, ty): + """ + Get ABI size of LLVM type *ty*. + """ + return ffi.lib.LLVMPY_ABIAlignmentOfType(self, ty) + def get_element_offset(self, ty, position): """ Get byte offset of type's ty element at the given position @@ -363,6 +369,10 @@ def has_svml(): ffi.LLVMTargetDataRef, ] +ffi.lib.LLVMPY_ABIAlignmentOfType.argtypes = [ffi.LLVMTargetDataRef, + ffi.LLVMTypeRef] +ffi.lib.LLVMPY_ABIAlignmentOfType.restype = c_longlong + ffi.lib.LLVMPY_ABISizeOfType.argtypes = [ffi.LLVMTargetDataRef, ffi.LLVMTypeRef] ffi.lib.LLVMPY_ABISizeOfType.restype = c_longlong diff --git a/llvmlite/binding/value.py b/llvmlite/binding/value.py index 4e21b3ee5..e1bd3cdc1 100644 --- a/llvmlite/binding/value.py +++ b/llvmlite/binding/value.py @@ -195,6 +195,13 @@ def add_function_attribute(self, attr): raise ValueError('no such attribute {!r}'.format(attrname)) ffi.lib.LLVMPY_AddFunctionAttr(self, attrval) + @property + def global_value_type(self): + """ + This value's LLVM type, if it is a global value. + """ + return TypeRef(ffi.lib.LLVMPY_GlobalGetValueType(self)) + @property def type(self): """ @@ -418,6 +425,9 @@ def _next(self): ffi.lib.LLVMPY_SetValueName.argtypes = [ffi.LLVMValueRef, c_char_p] +ffi.lib.LLVMPY_GlobalGetValueType.argtypes = [ffi.LLVMValueRef] +ffi.lib.LLVMPY_GlobalGetValueType.restype = ffi.LLVMTypeRef + ffi.lib.LLVMPY_TypeOf.argtypes = [ffi.LLVMValueRef] ffi.lib.LLVMPY_TypeOf.restype = ffi.LLVMTypeRef diff --git a/llvmlite/ir/types.py b/llvmlite/ir/types.py index 00740c488..8e997976f 100644 --- a/llvmlite/ir/types.py +++ b/llvmlite/ir/types.py @@ -30,7 +30,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 +43,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): """ diff --git a/llvmlite/tests/test_binding.py b/llvmlite/tests/test_binding.py index c5c1e7a6d..6c4a129c9 100644 --- a/llvmlite/tests/test_binding.py +++ b/llvmlite/tests/test_binding.py @@ -969,14 +969,30 @@ 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) + if llvm.context.get_global_context().supports_typed_pointers(): + with self.subTest('pointee_types'): + 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_pointee_abi_size(gv_i8.type), 1) - self.assertIn(td.get_pointee_abi_alignment(gv_i8.type), (1, 2, 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_pointee_abi_size(gv_struct.type), 24) - self.assertIn(td.get_pointee_abi_alignment(gv_struct.type), (4, 8)) + self.assertEqual(td.get_pointee_abi_size(gv_struct.type), 24) + self.assertIn(td.get_pointee_abi_alignment( + gv_struct.type), (4, 8)) + + with self.subTest('global_value_types'): + 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_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_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 = [] @@ -1163,28 +1179,33 @@ 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*') + if llvm.context.get_global_context().supports_typed_pointers(): + self.assertEqual(str(tp), 'i32*') + else: + self.assertEqual(str(tp), 'ptr') def test_type_printing_function(self): mod = self.module() fn = mod.get_function("sum") - self.assertEqual(str(fn.type), "i32 (i32, i32)*") + self.assertRegex(str(fn.global_value_type), r"i32 \(i32, i32\)") + self.assertRegex(str(fn.type), r"(ptr|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))) - self.assertIsNotNone(re.match( - r"%struct\.glob_type(\.[\d]+)? = type { i64, \[2 x i64\] }", - str(st.type.element_type))) + self.assertRegex( + str(st.type), + r'(ptr|%struct\.glob_type(\.[\d]+)?\*)') + self.assertRegex( + str(st.global_value_type), + r"%struct\.glob_type(\.[\d]+)? = type { i64, \[2 x i64\] }") def test_close(self): glob = self.glob() @@ -1331,6 +1352,20 @@ def test_get_abi_size(self): glob = self.glob() self.assertEqual(td.get_abi_size(glob.type), 8) + def test_get_global_value_abi_size(self): + td = self.target_data() + + glob = self.glob() + self.assertIsNotNone(glob.global_value_type) + self.assertEqual(td.get_abi_size(glob.global_value_type), 4) + + glob = self.glob("glob_struct") + self.assertIsNotNone(glob.global_value_type) + self.assertEqual(td.get_abi_size(glob.global_value_type), 24) + + @unittest.skipUnless( + llvm.context.get_global_context().supports_typed_pointers(), + "No typed pointer support") def test_get_pointee_abi_size(self): td = self.target_data() @@ -1347,7 +1382,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_refprune.py b/llvmlite/tests/test_refprune.py index f2948d03f..f4cc0ee50 100644 --- a/llvmlite/tests/test_refprune.py +++ b/llvmlite/tests/test_refprune.py @@ -210,7 +210,7 @@ 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)) + self.assertRegex(str(mod), r"call void @NRT_incref\((ptr|i8\*) %ptr\)") per_bb_ir_3 = r""" define void @main(i8* %ptr, i8* %other) { @@ -226,7 +226,8 @@ 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)) + self.assertRegex( + str(mod), r"call void @NRT_decref\((ptr|i8\*) %other\)") per_bb_ir_4 = r""" ; reordered @@ -244,7 +245,8 @@ 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)) + self.assertRegex( + str(mod), r"call void @NRT_decref\((ptr|i8\*) %other\)") class TestDiamond(BaseTestByIR):