Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segfault calling llvm function from ctypes under Python 3.10 #838

Closed
oscarbenjamin opened this issue May 17, 2022 · 3 comments
Closed

Segfault calling llvm function from ctypes under Python 3.10 #838

oscarbenjamin opened this issue May 17, 2022 · 3 comments
Labels
needtriage Needs to be triaged further

Comments

@oscarbenjamin
Copy link

This is derived from a SymPy issue (sympy/sympy#23509) after updating CI to use Python 3.10 for the tests that involved llvmlite. These tests still pass under Python 3.9 but fail with a segfault under Python 3.10.

I haven't used llvmlite directly before so I'm not 100% sure that the code in this demonstration is correct (maybe it's a bug on SymPy's side?). The example does seem to work fine under Python 3.9 though and the SymPy code for this was tested in CI for previous Python versions.

There is context in the SymPy issue but I have extracted a self-contained reproducer that doesn't use SymPy here:

# demo.py

import ctypes
import llvmlite.binding as llvm

llvm.initialize()
llvm.initialize_native_target()
llvm.initialize_native_asmprinter()

strmod = """\
; ModuleID = "mod1"
target triple = "unknown-unknown-unknown"
target datalayout = ""

define i32* @"jit_func1"(double %"a")
{
entry:
  %".3" = fadd double 0x3ff0000000000000, %"a"
  %".4" = call i32* @"PyFloat_FromDouble"(double %".3")
  ret i32* %".4"
}

declare i32* @"PyFloat_FromDouble"(double %".1")
"""

llmod = llvm.parse_assembly(strmod)

pmb = llvm.create_pass_manager_builder()
pmb.opt_level = 2
pass_manager = llvm.create_module_pass_manager()
pmb.populate(pass_manager)

pass_manager.run(llmod)

target_machine = llvm.Target.from_default_triple().create_target_machine()
exe_eng = llvm.create_mcjit_compiler(llmod, target_machine)
exe_eng.finalize_object()

fptr = exe_eng.get_function_address('jit_func1')

cfunc = ctypes.CFUNCTYPE(ctypes.py_object, ctypes.c_double)(fptr)

print(cfunc(4.0))

I can confirm on OSX (x86_64) that this script succeeds under Python 3.9.0:

$ python demo.py 
5.0

However under Python 3.10.0 this gives a segfault.

$ python demo.py 
Segmentation fault: 11

I'm using llvmlite 0.38.0 (installed with pip) in both cases.

I don't immediately have a Linux machine to test this with but the 3.10 CI failures suggest that the problem is the same on Linux.

@oscarbenjamin
Copy link
Author

I suppose another possibility would be that this is to do with a change in ctypes but I don't see any mention of ctypes in the Python 3.10 release notes:
https://docs.python.org/3/whatsnew/3.10.html

@esc esc added the needtriage Needs to be triaged further label May 17, 2022
@sklam
Copy link
Member

sklam commented May 24, 2022

i do wonder if this is a GIL problem---calling Python API without the GIL.

This line release the GIL

cfunc = ctypes.CFUNCTYPE(ctypes.py_object, ctypes.c_double)(fptr)

Try ctypes.PYFUNCTYPE instead

@oscarbenjamin
Copy link
Author

Try ctypes.PYFUNCTYPE instead

That seems to work in a simple example. I'll test further.

Yes, it makes sense that PyFloat_FromDouble needs the GIL now that you point that out. Maybe the actual fix is just not using PyFloat_FromDouble and doing cfunc = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_double)(fptr) (which seems to work).

Thanks for your help. Since this doesn't seem to be an issue with llvmlite I'll close this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needtriage Needs to be triaged further
Projects
None yet
Development

No branches or pull requests

3 participants