Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbc68f4a

Browse files
authored
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 anyfloating point type values can be passed through registers. If the typeis double the maximum size of the struct is 32 bytes.On x86-64 Linux, it's maximum 16 bytes hence we need to differentiate.
1 parente7e1116 commitbc68f4a

File tree

4 files changed

+193
-20
lines changed

4 files changed

+193
-20
lines changed

‎Lib/test/test_ctypes/test_structures.py‎

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
importstruct
33
importsys
44
importunittest
5-
fromctypesimport (CDLL,Structure,Union,POINTER,sizeof,byref,alignment,
5+
fromctypesimport (CDLL,Array,Structure,Union,POINTER,sizeof,byref,alignment,
66
c_void_p,c_char,c_wchar,c_byte,c_ubyte,
77
c_uint8,c_uint16,c_uint32,
88
c_short,c_ushort,c_int,c_uint,
@@ -494,12 +494,59 @@ class Test3B(Test3A):
494494
('more_data',c_float*2),
495495
]
496496

497+
classTest3C1(Structure):
498+
_fields_= [
499+
("data",c_double*4)
500+
]
501+
502+
classDataType4(Array):
503+
_type_=c_double
504+
_length_=4
505+
506+
classTest3C2(Structure):
507+
_fields_= [
508+
("data",DataType4)
509+
]
510+
511+
classTest3C3(Structure):
512+
_fields_= [
513+
("x",c_double),
514+
("y",c_double),
515+
("z",c_double),
516+
("t",c_double)
517+
]
518+
519+
classTest3D1(Structure):
520+
_fields_= [
521+
("data",c_double*5)
522+
]
523+
524+
classDataType5(Array):
525+
_type_=c_double
526+
_length_=5
527+
528+
classTest3D2(Structure):
529+
_fields_= [
530+
("data",DataType5)
531+
]
532+
533+
classTest3D3(Structure):
534+
_fields_= [
535+
("x",c_double),
536+
("y",c_double),
537+
("z",c_double),
538+
("t",c_double),
539+
("u",c_double)
540+
]
541+
542+
# Load the shared library
543+
dll=CDLL(_ctypes_test.__file__)
544+
497545
s=Test2()
498546
expected=0
499547
foriinrange(16):
500548
s.data[i]=i
501549
expected+=i
502-
dll=CDLL(_ctypes_test.__file__)
503550
func=dll._testfunc_array_in_struct1
504551
func.restype=c_int
505552
func.argtypes= (Test2,)
@@ -540,6 +587,78 @@ class Test3B(Test3A):
540587
self.assertAlmostEqual(s.more_data[0],-3.0,places=6)
541588
self.assertAlmostEqual(s.more_data[1],-2.0,places=6)
542589

590+
# Tests for struct Test3C
591+
expected= (1.0,2.0,3.0,4.0)
592+
func=dll._testfunc_array_in_struct_set_defaults_3C
593+
func.restype=Test3C1
594+
result=func()
595+
# check the default values have been set properly
596+
self.assertEqual(
597+
(result.data[0],
598+
result.data[1],
599+
result.data[2],
600+
result.data[3]),
601+
expected
602+
)
603+
604+
func=dll._testfunc_array_in_struct_set_defaults_3C
605+
func.restype=Test3C2
606+
result=func()
607+
# check the default values have been set properly
608+
self.assertEqual(
609+
(result.data[0],
610+
result.data[1],
611+
result.data[2],
612+
result.data[3]),
613+
expected
614+
)
615+
616+
func=dll._testfunc_array_in_struct_set_defaults_3C
617+
func.restype=Test3C3
618+
result=func()
619+
# check the default values have been set properly
620+
self.assertEqual((result.x,result.y,result.z,result.t),expected)
621+
622+
# Tests for struct Test3D
623+
expected= (1.0,2.0,3.0,4.0,5.0)
624+
func=dll._testfunc_array_in_struct_set_defaults_3D
625+
func.restype=Test3D1
626+
result=func()
627+
# check the default values have been set properly
628+
self.assertEqual(
629+
(result.data[0],
630+
result.data[1],
631+
result.data[2],
632+
result.data[3],
633+
result.data[4]),
634+
expected
635+
)
636+
637+
func=dll._testfunc_array_in_struct_set_defaults_3D
638+
func.restype=Test3D2
639+
result=func()
640+
# check the default values have been set properly
641+
self.assertEqual(
642+
(result.data[0],
643+
result.data[1],
644+
result.data[2],
645+
result.data[3],
646+
result.data[4]),
647+
expected
648+
)
649+
650+
func=dll._testfunc_array_in_struct_set_defaults_3D
651+
func.restype=Test3D3
652+
result=func()
653+
# check the default values have been set properly
654+
self.assertEqual(
655+
(result.x,
656+
result.y,
657+
result.z,
658+
result.t,
659+
result.u),
660+
expected)
661+
543662
deftest_38368(self):
544663
classU(Union):
545664
_fields_= [
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix ctypes structs with array on Arm platform by setting ``MAX_STRUCT_SIZE`` to 32 in stgdict. Patch by Diego Russo.

‎Modules/_ctypes/_ctypes_test.c‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,42 @@ _testfunc_array_in_struct2a(Test3B in)
150150
returnresult;
151151
}
152152

153+
/*
154+
* See gh-110190. structs containing arrays of up to four floating point types
155+
* (max 32 bytes) are passed in registers on Arm.
156+
*/
157+
158+
typedefstruct {
159+
doubledata[4];
160+
}Test3C;
161+
162+
EXPORT(Test3C)
163+
_testfunc_array_in_struct_set_defaults_3C(void)
164+
{
165+
Test3Cs;
166+
s.data[0]=1.0;
167+
s.data[1]=2.0;
168+
s.data[2]=3.0;
169+
s.data[3]=4.0;
170+
returns;
171+
}
172+
173+
typedefstruct {
174+
doubledata[5];
175+
}Test3D;
176+
177+
EXPORT(Test3D)
178+
_testfunc_array_in_struct_set_defaults_3D(void)
179+
{
180+
Test3Ds;
181+
s.data[0]=1.0;
182+
s.data[1]=2.0;
183+
s.data[2]=3.0;
184+
s.data[3]=4.0;
185+
s.data[4]=5.0;
186+
returns;
187+
}
188+
153189
typedefunion {
154190
longa_long;
155191
struct {

‎Modules/_ctypes/stgdict.c‎

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -697,29 +697,43 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
697697
stgdict->align=total_align;
698698
stgdict->length=len;/* ADD ffi_ofs? */
699699

700-
#defineMAX_STRUCT_SIZE 16
700+
/*
701+
* On Arm platforms, structs with at most 4 elements of any floating point
702+
* type values can be passed through registers. If the type is double the
703+
* maximum size of the struct is 32 bytes.
704+
* By Arm platforms it is meant both 32 and 64-bit.
705+
*/
706+
#if defined(__aarch64__)|| defined(__arm__)
707+
# defineMAX_STRUCT_SIZE 32
708+
#else
709+
# defineMAX_STRUCT_SIZE 16
710+
#endif
701711

702712
if (arrays_seen&& (size <=MAX_STRUCT_SIZE)) {
703713
/*
704-
* See bpo-22273. Arrays are normally treated as pointers, which is
705-
* fine when an array name is being passed as parameter, but not when
706-
* passing structures by value that contain arrays. On 64-bit Linux,
707-
* small structures passed by value are passed in registers, and in
708-
* order to do this, libffi needs to know the true type of the array
709-
* members of structs. Treating them as pointers breaks things.
714+
* See bpo-22273 and gh-110190. Arrays are normally treated as
715+
* pointers, which is fine when an array name is being passed as
716+
* parameter, but not when passing structures by value that contain
717+
* arrays.
718+
* On x86_64 Linux and Arm platforms, small structures passed by
719+
* value are passed in registers, and in order to do this, libffi needs
720+
* to know the true type of the array members of structs. Treating them
721+
* as pointers breaks things.
710722
*
711-
* By small structures, we mean ones that are 16 bytes or less. In that
712-
* case, there can't be more than 16 elements after unrolling arrays,
713-
* as we (will) disallow bitfields. So we can collect the true ffi_type
714-
* values in a fixed-size local array on the stack and, if any arrays
715-
* were seen, replace the ffi_type_pointer.elements with a more
716-
* accurate set, to allow libffi to marshal them into registers
717-
* correctly. It means one more loop over the fields, but if we got
718-
* here, the structure is small, so there aren't too many of those.
723+
* By small structures, we mean ones that are 16 bytes or less on
724+
* x86-64 and 32 bytes or less on Arm. In that case, there can't be
725+
* more than 16 or 32 elements after unrolling arrays, as we (will)
726+
* disallow bitfields. So we can collect the true ffi_type values in
727+
* a fixed-size local array on the stack and, if any arrays were seen,
728+
* replace the ffi_type_pointer.elements with a more accurate set,
729+
* to allow libffi to marshal them into registers correctly.
730+
* It means one more loop over the fields, but if we got here,
731+
* the structure is small, so there aren't too many of those.
719732
*
720-
* Although the passing in registers is specific to 64-bit Linux, the
721-
* array-in-struct vs. pointer problem is general. But we restrict the
722-
* type transformation to small structs nonetheless.
733+
* Although the passing in registers is specific to x86_64 Linux
734+
* and Arm platforms, the array-in-struct vs. pointer problem is
735+
* general. But we restrict the type transformation to small structs
736+
* nonetheless.
723737
*
724738
* Note that although a union may be small in terms of memory usage, it
725739
* could contain many overlapping declarations of arrays, e.g.
@@ -745,6 +759,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
745759
* struct { uint_32 e1; uint_32 e2; ... uint_32 e_4; } f6;
746760
* }
747761
*
762+
* The same principle applies for a struct 32 bytes in size like in
763+
* the case of Arm platforms.
764+
*
748765
* So the struct/union needs setting up as follows: all non-array
749766
* elements copied across as is, and all array elements replaced with
750767
* an equivalent struct which has as many fields as the array has

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp