summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorDiego Russo <diego.russo@arm.com>2023-12-05 15:07:50 (GMT)
committerGitHub <noreply@github.com>2023-12-05 15:07:50 (GMT)
commitbc68f4a4abcfbea60bb1db1ccadb07613561931c (patch)
tree33433b1b17d0871f69594a60da419ff2276fb7d1 /Modules
parente7e1116a781434763c309b55a31204a98237f7b4 (diff)
downloadcpython-bc68f4a4abcfbea60bb1db1ccadb07613561931c.zip
cpython-bc68f4a4abcfbea60bb1db1ccadb07613561931c.tar.gz
cpython-bc68f4a4abcfbea60bb1db1ccadb07613561931c.tar.bz2
gh-110190: Fix ctypes structs with array on Arm (#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.
Diffstat (limited to 'Modules')
-rw-r--r--Modules/_ctypes/_ctypes_test.c36
-rw-r--r--Modules/_ctypes/stgdict.c53
2 files changed, 71 insertions, 18 deletions
diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c
index d33e6fc..fc9fc13 100644
--- a/Modules/_ctypes/_ctypes_test.c
+++ b/Modules/_ctypes/_ctypes_test.c
@@ -150,6 +150,42 @@ _testfunc_array_in_struct2a(Test3B in)
return result;
}
+/*
+ * See gh-110190. structs containing arrays of up to four floating point types
+ * (max 32 bytes) are passed in registers on Arm.
+ */
+
+typedef struct {
+ double data[4];
+} Test3C;
+
+EXPORT(Test3C)
+_testfunc_array_in_struct_set_defaults_3C(void)
+{
+ Test3C s;
+ s.data[0] = 1.0;
+ s.data[1] = 2.0;
+ s.data[2] = 3.0;
+ s.data[3] = 4.0;
+ return s;
+}
+
+typedef struct {
+ double data[5];
+} Test3D;
+
+EXPORT(Test3D)
+_testfunc_array_in_struct_set_defaults_3D(void)
+{
+ Test3D s;
+ s.data[0] = 1.0;
+ s.data[1] = 2.0;
+ s.data[2] = 3.0;
+ s.data[3] = 4.0;
+ s.data[4] = 5.0;
+ return s;
+}
+
typedef union {
long a_long;
struct {
diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c
index 6fbcf77..04dd9ba 100644
--- a/Modules/_ctypes/stgdict.c
+++ b/Modules/_ctypes/stgdict.c
@@ -697,29 +697,43 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
stgdict->align = total_align;
stgdict->length = len; /* ADD ffi_ofs? */
-#define MAX_STRUCT_SIZE 16
+/*
+ * 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.
+ * By Arm platforms it is meant both 32 and 64-bit.
+*/
+#if defined(__aarch64__) || defined(__arm__)
+# define MAX_STRUCT_SIZE 32
+#else
+# define MAX_STRUCT_SIZE 16
+#endif
if (arrays_seen && (size <= MAX_STRUCT_SIZE)) {
/*
- * See bpo-22273. Arrays are normally treated as pointers, which is
- * fine when an array name is being passed as parameter, but not when
- * passing structures by value that contain arrays. On 64-bit Linux,
- * small structures passed by value are passed in registers, and in
- * order to do this, libffi needs to know the true type of the array
- * members of structs. Treating them as pointers breaks things.
+ * See bpo-22273 and gh-110190. Arrays are normally treated as
+ * pointers, which is fine when an array name is being passed as
+ * parameter, but not when passing structures by value that contain
+ * arrays.
+ * On x86_64 Linux and Arm platforms, small structures passed by
+ * value are passed in registers, and in order to do this, libffi needs
+ * to know the true type of the array members of structs. Treating them
+ * as pointers breaks things.
*
- * By small structures, we mean ones that are 16 bytes or less. In that
- * case, there can't be more than 16 elements after unrolling arrays,
- * as we (will) disallow bitfields. So we can collect the true ffi_type
- * values in a fixed-size local array on the stack and, if any arrays
- * were seen, replace the ffi_type_pointer.elements with a more
- * accurate set, to allow libffi to marshal them into registers
- * correctly. It means one more loop over the fields, but if we got
- * here, the structure is small, so there aren't too many of those.
+ * By small structures, we mean ones that are 16 bytes or less on
+ * x86-64 and 32 bytes or less on Arm. In that case, there can't be
+ * more than 16 or 32 elements after unrolling arrays, as we (will)
+ * disallow bitfields. So we can collect the true ffi_type values in
+ * a fixed-size local array on the stack and, if any arrays were seen,
+ * replace the ffi_type_pointer.elements with a more accurate set,
+ * to allow libffi to marshal them into registers correctly.
+ * It means one more loop over the fields, but if we got here,
+ * the structure is small, so there aren't too many of those.
*
- * Although the passing in registers is specific to 64-bit Linux, the
- * array-in-struct vs. pointer problem is general. But we restrict the
- * type transformation to small structs nonetheless.
+ * Although the passing in registers is specific to x86_64 Linux
+ * and Arm platforms, the array-in-struct vs. pointer problem is
+ * general. But we restrict the type transformation to small structs
+ * nonetheless.
*
* Note that although a union may be small in terms of memory usage, it
* could contain many overlapping declarations of arrays, e.g.
@@ -745,6 +759,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
* struct { uint_32 e1; uint_32 e2; ... uint_32 e_4; } f6;
* }
*
+ * The same principle applies for a struct 32 bytes in size like in
+ * the case of Arm platforms.
+ *
* So the struct/union needs setting up as follows: all non-array
* elements copied across as is, and all array elements replaced with
* an equivalent struct which has as many fields as the array has