summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorDiego Russo <diego.russo@arm.com>2023-12-13 16:08:15 (GMT)
committerGitHub <noreply@github.com>2023-12-13 16:08:15 (GMT)
commit6644ca45cde9ca1b80513a90dacccfeea2d98620 (patch)
tree4029519873aff47a763094ea153a519bfeb27d59 /Modules
parent79dad03747fe17634136209f1bcaf346a8c10617 (diff)
downloadcpython-6644ca45cde9ca1b80513a90dacccfeea2d98620.zip
cpython-6644ca45cde9ca1b80513a90dacccfeea2d98620.tar.gz
cpython-6644ca45cde9ca1b80513a90dacccfeea2d98620.tar.bz2
gh-110190: Fix ctypes structs with array on PPCLE64 (GH-112959)
Fix the same issue of PR #112604 on PPC64LE platform Refactor tests to make easier to add more platfroms if needed.
Diffstat (limited to 'Modules')
-rw-r--r--Modules/_ctypes/_ctypes_test.c138
-rw-r--r--Modules/_ctypes/stgdict.c45
2 files changed, 124 insertions, 59 deletions
diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c
index 2681b9c..ecc6041 100644
--- a/Modules/_ctypes/_ctypes_test.c
+++ b/Modules/_ctypes/_ctypes_test.c
@@ -96,11 +96,10 @@ typedef struct {
} Test2;
EXPORT(int)
-_testfunc_array_in_struct1(Test2 in)
+_testfunc_array_in_struct2(Test2 in)
{
int result = 0;
-
- for (unsigned i = 0; i < 16; i++)
+ for (unsigned i = 0; i < sizeof(in.data)/sizeof(in.data[0]); i++)
result += in.data[i];
/* As the structure is passed by value, changes to it shouldn't be
* reflected in the caller.
@@ -109,22 +108,25 @@ _testfunc_array_in_struct1(Test2 in)
return result;
}
-typedef struct {
- double data[2];
-} Test3;
-
+/*
+ * Test3A struct test the MAX_STRUCT_SIZE 16 with single precision floats.
+ * These structs should be passed via registers on all platforms and they are
+ * used for within bounds tests.
+ */
typedef struct {
float data[2];
float more_data[2];
-} Test3B;
+} Test3A;
EXPORT(double)
-_testfunc_array_in_struct2(Test3 in)
+_testfunc_array_in_struct3A(Test3A in)
{
double result = 0;
- for (unsigned i = 0; i < 2; i++)
+ for (unsigned i = 0; i < sizeof(in.data)/sizeof(in.data[0]); i++)
result += in.data[i];
+ for (unsigned i = 0; i < sizeof(in.more_data)/sizeof(in.more_data[0]); i++)
+ result += in.more_data[i];
/* As the structure is passed by value, changes to it shouldn't be
* reflected in the caller.
*/
@@ -132,56 +134,116 @@ _testfunc_array_in_struct2(Test3 in)
return result;
}
+/* The structs Test3B..Test3E use the same functions hence using the MACRO
+ * to define their implementation.
+ */
+
+#define _TESTFUNC_ARRAY_IN_STRUCT_IMPL \
+ double result = 0; \
+ \
+ for (unsigned i = 0; i < sizeof(in.data)/sizeof(in.data[0]); i++) \
+ result += in.data[i]; \
+ /* As the structure is passed by value, changes to it shouldn't be \
+ * reflected in the caller. \
+ */ \
+ memset(in.data, 0, sizeof(in.data)); \
+ return result; \
+
+#define _TESTFUNC_ARRAY_IN_STRUCT_SET_DEFAULTS_IMPL \
+ for (unsigned i = 0; i < sizeof(s.data)/sizeof(s.data[0]); i++) \
+ s.data[i] = (double)i+1; \
+ return s; \
+
+
+/*
+ * Test3B struct test the MAX_STRUCT_SIZE 16 with double precision floats.
+ * These structs should be passed via registers on all platforms and they are
+ * used for within bounds tests.
+ */
+typedef struct {
+ double data[2];
+} Test3B;
+
EXPORT(double)
-_testfunc_array_in_struct2a(Test3B in)
+_testfunc_array_in_struct3B(Test3B in)
{
- double result = 0;
+ _TESTFUNC_ARRAY_IN_STRUCT_IMPL
+}
- for (unsigned i = 0; i < 2; i++)
- result += in.data[i];
- for (unsigned i = 0; i < 2; i++)
- result += in.more_data[i];
- /* As the structure is passed by value, changes to it shouldn't be
- * reflected in the caller.
- */
- memset(in.data, 0, sizeof(in.data));
- return result;
+EXPORT(Test3B)
+_testfunc_array_in_struct3B_set_defaults(void)
+{
+ Test3B s;
+ _TESTFUNC_ARRAY_IN_STRUCT_SET_DEFAULTS_IMPL
}
/*
- * See gh-110190. structs containing arrays of up to four floating point types
- * (max 32 bytes) are passed in registers on Arm.
+ * Test3C struct tests the MAX_STRUCT_SIZE 32. Structs containing arrays of up
+ * to four floating point types are passed in registers on Arm platforms.
+ * This struct is used for within bounds test on Arm platfroms and for an
+ * out-of-bounds tests for platfroms where MAX_STRUCT_SIZE is less than 32.
+ * See gh-110190.
*/
-
typedef struct {
double data[4];
} Test3C;
+EXPORT(double)
+_testfunc_array_in_struct3C(Test3C in)
+{
+ _TESTFUNC_ARRAY_IN_STRUCT_IMPL
+}
+
EXPORT(Test3C)
-_testfunc_array_in_struct_set_defaults_3C(void)
+_testfunc_array_in_struct3C_set_defaults(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;
+ _TESTFUNC_ARRAY_IN_STRUCT_SET_DEFAULTS_IMPL
}
+/*
+ * Test3D struct tests the MAX_STRUCT_SIZE 64. Structs containing arrays of up
+ * to eight floating point types are passed in registers on PPC64LE platforms.
+ * This struct is used for within bounds test on PPC64LE platfroms and for an
+ * out-of-bounds tests for platfroms where MAX_STRUCT_SIZE is less than 64.
+ * See gh-110190.
+ */
typedef struct {
- double data[5];
+ double data[8];
} Test3D;
+EXPORT(double)
+_testfunc_array_in_struct3D(Test3D in)
+{
+ _TESTFUNC_ARRAY_IN_STRUCT_IMPL
+}
+
EXPORT(Test3D)
-_testfunc_array_in_struct_set_defaults_3D(void)
+_testfunc_array_in_struct3D_set_defaults(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;
+ _TESTFUNC_ARRAY_IN_STRUCT_SET_DEFAULTS_IMPL
+}
+
+/*
+ * Test3E struct tests the out-of-bounds for all architectures.
+ * See gh-110190.
+ */
+typedef struct {
+ double data[9];
+} Test3E;
+
+EXPORT(double)
+_testfunc_array_in_struct3E(Test3E in)
+{
+ _TESTFUNC_ARRAY_IN_STRUCT_IMPL
+}
+
+EXPORT(Test3E)
+_testfunc_array_in_struct3E_set_defaults(void)
+{
+ Test3E s;
+ _TESTFUNC_ARRAY_IN_STRUCT_SET_DEFAULTS_IMPL
}
typedef union {
diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c
index 04dd9ba..dfdb96b 100644
--- a/Modules/_ctypes/stgdict.c
+++ b/Modules/_ctypes/stgdict.c
@@ -698,13 +698,12 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
stgdict->length = len; /* ADD ffi_ofs? */
/*
- * 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.
-*/
+ * The value of MAX_STRUCT_SIZE depends on the platform Python is running on.
+ */
#if defined(__aarch64__) || defined(__arm__)
# define MAX_STRUCT_SIZE 32
+#elif defined(__powerpc64__)
+# define MAX_STRUCT_SIZE 64
#else
# define MAX_STRUCT_SIZE 16
#endif
@@ -715,24 +714,29 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
* 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.
+ * 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.
+ *
+ * Small structures have different sizes depending on the platform
+ * where Python is running on:
+ *
+ * * x86-64: 16 bytes or less
+ * * Arm platforms (both 32 and 64 bit): 32 bytes or less
+ * * PowerPC 64 Little Endian: 64 bytes or less
*
- * 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.
+ * In that case, there can't be more than 16, 32 or 64 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 x86_64 Linux
- * and Arm platforms, the array-in-struct vs. pointer problem is
- * general. But we restrict the type transformation to small structs
+ * Although the passing in registers is specific to the above
+ * 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
@@ -759,8 +763,7 @@ 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.
+ * The same principle applies for a struct 32 or 64 bytes in size.
*
* So the struct/union needs setting up as follows: all non-array
* elements copied across as is, and all array elements replaced with