-
-
Notifications
You must be signed in to change notification settings - Fork 31k
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
ctypes array cannot be returned by value on ARM64 #110190
Comments
I looked into it. Long words in short: class Example1(ctypes.Structure):
_fields_ = [("coords", ctypes.c_double * 3)]
class Example2(ctypes.Structure):
_fields_ = [("coords", ctypes.c_void_p * 3)]
# Three ways to wrap the struct:
class Example3(ctypes.Structure):
_fields_ = [("x", ctypes.c_void_p), ("y", ctypes.c_void_p), ("z", ctypes.c_void_p)]
def run():
# Load the shared library
so_location = os.path.abspath("libctypesexample")
_lib = ctypes.cdll.LoadLibrary(so_location)
_lib.set_defaults.restype = Example1
o = _lib.set_defaults()
print(
f"Example1: ({float.hex(o.coords[0])}, {float.hex(o.coords[1])}, {float.hex(o.coords[2])})"
)
_lib.set_defaults.restype = Example2
o = _lib.set_defaults()
print(f"Example2: ({o.coords[0]:x}, {o.coords[1]:x}, {o.coords[2]})")
_lib.set_defaults.restype = Example3
o = _lib.set_defaults()
print(f"Example3: ({o.x:x}, {o.y:x}, {o.z})")
Notice the binary representation of Example1 has |
The special handling is here: cpython/Modules/_ctypes/stgdict.c Lines 700 to 710 in 21a6263
You seem to hit one special HVA calling convention of aarch64 which is currently not handled.
Details
Integral values are returned in x0. Floating-point values are returned in s0, d0, or v0, as appropriate. A type is considered to be an HFA or HVA if all of the following hold:
HVA values with four or fewer elements are returned in s0-s3, d0-d3, or v0-v3, as appropriate. Types returned by value are handled differently depending on whether they have certain properties, and whether the function is a non-static member function. Types which have all of these properties,
and are returned by non-member functions or static member functions, use the following return style:
All other types use this convention:
|
Now the cause is clear, |
Hello, I'm looking at this issue and working on a fix. |
Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate.
Hello, I've investigated this further and actually this was happening across Arm platforms including Linux aarch64 and aarch32 and not just on Darwin. I've raised the PR that fixes the issue providing information and context. Feel free to review it and any feedback is welcome. |
Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate.
…112604) Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate. (cherry picked from commit bc68f4a)
…112604) Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate. (cherry picked from commit bc68f4a)
…2767) Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate. (cherry picked from commit bc68f4a)
…2766) Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate. (cherry picked from commit bc68f4a)
This is now fixed in (to be released) 3.11.8, 3.12.1, and 3.13.0 alpha 3. |
While this is fixed, it uncovered that a similar failure is happening on PPC64LE. The new tests introduced by Diego here fail on our PPC64LE buildbots as well. See for instance: |
on PPC64LE (python#112818) (cherry picked from commit 9f67042)
…ngh-112604 on PPC64LE (pythonGH-112818) (cherry picked from commit 9f67042) Co-authored-by: Łukasz Langa <[email protected]>
…n PPC64LE (GH-112818) (#112829) (cherry picked from commit 9f67042) Co-authored-by: Łukasz Langa <[email protected]>
…ngh-112604 on PPC64LE (pythonGH-112818) (cherry picked from commit 9f67042) Co-authored-by: Łukasz Langa <[email protected]>
@kulikjak thanks for the update! |
Is there a link to the GCC bug? Thanks. |
Sure, here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114416 |
Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate.
…12959) Fix the same issue of PR python#112604 on PPC64LE platform Refactor tests to make easier to add more platfroms if needed.
I just merged the SPARC PR. Sorry for the delay. |
Thank you for the merge! Since the sparc |
…112604) (python#112766) Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate. (cherry picked from commit bc68f4a) Signed-off-by: Michał Górny <[email protected]>
…ngh-112604 on PPC64LE (pythonGH-112818) (python#112830) (cherry picked from commit 9f67042) Signed-off-by: Michał Górny <[email protected]>
…honGH-112959) (python#113167) Fix the same issue of PR python#112604 on PPC64LE platform Refactor tests to make easier to add more platfroms if needed. (cherry picked from commit 6644ca4) Change-Id: I1ada30808c0d593a43eca3fa7a628c26bc276310 Signed-off-by: Michał Górny <[email protected]>
…onGH-114753) (cherry picked from commit a06b606) Co-authored-by: Diego Russo <[email protected]> Signed-off-by: Michał Górny <[email protected]>
) Signed-off-by: Michał Górny <[email protected]>
…112604) (python#112766) Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate. (cherry picked from commit bc68f4a) Signed-off-by: Michał Górny <[email protected]>
…ngh-112604 on PPC64LE (pythonGH-112818) (python#112830) (cherry picked from commit 9f67042) Signed-off-by: Michał Górny <[email protected]>
…honGH-112959) (python#113167) Fix the same issue of PR python#112604 on PPC64LE platform Refactor tests to make easier to add more platfroms if needed. (cherry picked from commit 6644ca4) Change-Id: I1ada30808c0d593a43eca3fa7a628c26bc276310 Signed-off-by: Michał Górny <[email protected]>
…onGH-114753) (cherry picked from commit a06b606) Co-authored-by: Diego Russo <[email protected]> Signed-off-by: Michał Górny <[email protected]>
) Signed-off-by: Michał Górny <[email protected]>
…112604) (python#112766) Set MAX_STRUCT_SIZE to 32 in stgdict.c when on Arm platforms. This because on Arm platforms structs with at most 4 elements of any floating point type values can be passed through registers. If the type is double the maximum size of the struct is 32 bytes. On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate. (cherry picked from commit bc68f4a) Signed-off-by: Michał Górny <[email protected]>
…ngh-112604 on PPC64LE (pythonGH-112818) (python#112830) (cherry picked from commit 9f67042) Signed-off-by: Michał Górny <[email protected]>
…honGH-112959) (python#113167) Fix the same issue of PR python#112604 on PPC64LE platform Refactor tests to make easier to add more platfroms if needed. (cherry picked from commit 6644ca4) Change-Id: I1ada30808c0d593a43eca3fa7a628c26bc276310 Signed-off-by: Michał Górny <[email protected]>
…onGH-114753) (cherry picked from commit a06b606) Co-authored-by: Diego Russo <[email protected]> Signed-off-by: Michał Górny <[email protected]>
) Signed-off-by: Michał Górny <[email protected]>
Bug report
Bug description:
Dear,
I'm wrapping a C-library with the ctypes module on Darwin (ARM64). I have a function that simply initializes a C-struct and returns the struct by value. The C-struct contains one simple C-array type. When using ArrayTypes in Python, it seems the struct is not returned properly.
On Windows and Linux platforms the same code does work as expected.
The strange thing is that if I lay out the struct explicitly with singular types in Python (as I expect the layout of the struct is the same), it does work as expected on all tried platforms.
To reproduce, I first create a simple .c file with the struct and the initializing function. In Python, I give code that I use to compile (with gcc), and then three different ways to wrap the struct and function in Python using ctypes. I believe the three ways should be equivalent (and they are on Windows and Linux), but not on Darwin (ARM64).
I saved this file as
ctypesexample.c
I saved this file as
reproduce.py
I tried this code on Linux (debian) -- both in Docker and WSL. And this works perfectly. Equivalent code in Windows is also working fine. In the past, I have run similar code as above on many different versions of Python (3.5-3.11) on both platforms for years (and still) without problems. For example on WSL with the following platform info
uname_result(system='Linux', release='5.15.90.1-microsoft-standard-WSL2', version='#1 SMP Fri Jan 27 02:56:13 UTC 2023', machine='x86_64')
and Python:
Python 3.10.6 (main, Mar 10 2023, 10:55:28) [GCC 11.3.0] on linux
I get this correct output:
While on Darwin (ARM) with the following platform info:
system='Darwin', release='22.1.0', version='Darwin Kernel Version 22.1.0: Sun Oct 9 20:14:30 PDT 2022; root:xnu-8792.41.9~2/RELEASE_ARM64_T8103', machine='arm64')
and Python
Python 3.11.4 (main, Jun 12 2023, 11:39:45) [Clang 14.0.3 (clang-1403.0.22.14.1)] on darwin
I get the following output:
In summary, the struct doesn't seem to be properly returned as value using array types, though with a literal listing of the types, it seems there is no problem. So there is a workaround, but as we're using ctypes for automatic wrapping through reflection and the code works properly on Windows and Linux, it would be quite some work to implement the workaround.
I hope my bug report finds you without any errors, and many thanks for giving the opportunity to report.
Best regards
CPython versions tested on:
3.10, 3.11
Operating systems tested on:
Linux, macOS, Windows
Linked PRs
The text was updated successfully, but these errors were encountered: