Skip to content

Commit b521133

Browse files
dcharkescommit-bot@chromium.org
authored andcommitted
[vm/ffi] Structs by value compiler frontend (part 2)
Split off https://dart-review.googlesource.com/c/sdk/+/140290 to make that CL smaller. This CL adds support for passing struct arguments in the native calling convention calculation in `runtime/vm/compiler/ffi/native_calling_convention.cc`. The code in this CL is unit tested with expect files. The unit tests are designed to cover the majority of corner cases in the ABIs without resorting to very many unit tests. TEST=runtime/vm/compiler/ffi/native_calling_convention_test.cc The code in this CL has been end-to-end tested in the CL it is split off from. And will be end-to-end tested when that CL also lands. Issue: #36730 Change-Id: I5b3c3786122c5f856f33181f898fbd8db782cff3 Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try,vm-ffi-android-debug-arm64-try,vm-ffi-android-debug-arm-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-mac-debug-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/172763 Commit-Queue: Daco Harkes <[email protected]> Reviewed-by: Clement Skau <[email protected]>
1 parent 3b24f36 commit b521133

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1591
-116
lines changed

runtime/vm/compiler/backend/flow_graph_compiler.cc

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3255,8 +3255,10 @@ void FlowGraphCompiler::EmitNativeMove(
32553255
!destination.IsFpuRegisters()) {
32563256
// TODO(40209): If this is stack to stack, we could use FpuTMP.
32573257
// Test the impact on code size and speed.
3258-
EmitNativeMove(destination.Split(zone_, 0), source.Split(zone_, 0), temp);
3259-
EmitNativeMove(destination.Split(zone_, 1), source.Split(zone_, 1), temp);
3258+
EmitNativeMove(destination.Split(zone_, 2, 0), source.Split(zone_, 2, 0),
3259+
temp);
3260+
EmitNativeMove(destination.Split(zone_, 2, 1), source.Split(zone_, 2, 1),
3261+
temp);
32603262
return;
32613263
}
32623264

@@ -3331,7 +3333,7 @@ void FlowGraphCompiler::EmitMoveToNative(
33313333
for (intptr_t i : {0, 1}) {
33323334
const auto& src_split = compiler::ffi::NativeLocation::FromPairLocation(
33333335
zone_, src_loc, src_type, i);
3334-
EmitNativeMove(dst.Split(zone_, i), src_split, temp);
3336+
EmitNativeMove(dst.Split(zone_, 2, i), src_split, temp);
33353337
}
33363338
} else {
33373339
const auto& src =
@@ -3351,7 +3353,7 @@ void FlowGraphCompiler::EmitMoveFromNative(
33513353
for (intptr_t i : {0, 1}) {
33523354
const auto& dest_split = compiler::ffi::NativeLocation::FromPairLocation(
33533355
zone_, dst_loc, dst_type, i);
3354-
EmitNativeMove(dest_split, src.Split(zone_, i), temp);
3356+
EmitNativeMove(dest_split, src.Split(zone_, 2, i), temp);
33553357
}
33563358
} else {
33573359
const auto& dest =
@@ -3398,7 +3400,7 @@ void FlowGraphCompiler::EmitMoveConst(const compiler::ffi::NativeLocation& dst,
33983400
compiler::ffi::NativeLocation::FromLocation(zone_, intermediate,
33993401
src_type_split);
34003402
EmitMove(intermediate, src.AsPairLocation()->At(i), temp);
3401-
EmitNativeMove(dst.Split(zone_, i), intermediate_native, temp);
3403+
EmitNativeMove(dst.Split(zone_, 2, i), intermediate_native, temp);
34023404
}
34033405
} else {
34043406
const auto& intermediate_native =

runtime/vm/compiler/ffi/native_calling_convention.cc

Lines changed: 499 additions & 53 deletions
Large diffs are not rendered by default.

runtime/vm/compiler/ffi/native_calling_convention_test.cc

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,221 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_floatx10) {
8080
RunSignatureTest(Z, "floatx10", arguments, floatType);
8181
}
8282

83+
// Test with 3-byte struct.
84+
//
85+
// On ia32, result pointer is passed on stack and passed back in eax.
86+
//
87+
// On x64, is passed and returned in registers, except for on Windows where it
88+
// is passed on stack because of its size not being a power of two.
89+
//
90+
// See the *.expect in ./unit_tests for this behavior.
91+
UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct3bytesx10) {
92+
const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
93+
94+
auto& member_types = *new (Z) NativeTypes(Z, 3);
95+
member_types.Add(&int8type);
96+
member_types.Add(&int8type);
97+
member_types.Add(&int8type);
98+
const auto& struct_type =
99+
NativeCompoundType::FromNativeTypes(Z, member_types);
100+
101+
auto& arguments = *new (Z) NativeTypes(Z, 10);
102+
arguments.Add(&struct_type);
103+
arguments.Add(&struct_type);
104+
arguments.Add(&struct_type);
105+
arguments.Add(&struct_type);
106+
arguments.Add(&struct_type);
107+
arguments.Add(&struct_type);
108+
arguments.Add(&struct_type);
109+
arguments.Add(&struct_type);
110+
arguments.Add(&struct_type);
111+
arguments.Add(&struct_type);
112+
113+
RunSignatureTest(Z, "struct3bytesx10", arguments, struct_type);
114+
}
115+
116+
// Test with homogenous struct.
117+
//
118+
// On arm softfp, the return pointer is passed in the first int register, and
119+
// the first struct is passed in the next 3 registers and 1 stack slot.
120+
//
121+
// On arm hardfp, arm64, and x64 non-Windows the structs are passed in FPU
122+
// registers until exhausted, the rest is passed on the stack, and struct is
123+
// returned in FPU registers.
124+
//
125+
// On ia32 a return pointer and all arguments are passed on the stack.
126+
//
127+
// On x64 on Windows the structs are passed by pointer and pointer to the
128+
// return value is passed in.
129+
//
130+
// See the *.expect in ./unit_tests for this behavior.
131+
UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesHomogenousx10) {
132+
const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
133+
const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
134+
135+
// If passed in FPU registers, uses an even amount of them.
136+
auto& member_types = *new (Z) NativeTypes(Z, 4);
137+
member_types.Add(&float_type);
138+
member_types.Add(&float_type);
139+
member_types.Add(&float_type);
140+
member_types.Add(&float_type);
141+
const auto& struct_type =
142+
NativeCompoundType::FromNativeTypes(Z, member_types);
143+
144+
auto& arguments = *new (Z) NativeTypes(Z, 13);
145+
arguments.Add(&struct_type);
146+
arguments.Add(&float_type); // Claim a single FPU register.
147+
arguments.Add(&struct_type);
148+
arguments.Add(&struct_type);
149+
arguments.Add(&struct_type);
150+
arguments.Add(&struct_type);
151+
arguments.Add(&struct_type);
152+
arguments.Add(&struct_type);
153+
arguments.Add(&struct_type);
154+
arguments.Add(&struct_type);
155+
arguments.Add(&float_type); // Check float register back filling, if any.
156+
arguments.Add(&int8type); // Check integer register back filling, if any.
157+
arguments.Add(&struct_type); // Check stack alignment of struct.
158+
159+
RunSignatureTest(Z, "struct16bytesHomogenousx10", arguments, struct_type);
160+
}
161+
162+
// A fairly big struct.
163+
//
164+
// On arm, split up in 8-byte chunks. The first chunk goes into two registers,
165+
// the rest on the stack. Note that r1 goes unused and is not backfilled.
166+
//
167+
// On arm64 and Windows x64 passed by a pointer to copy.
168+
//
169+
// On ia32, wholly passed on stack.
170+
//
171+
// On non-Windows x64, wholly passed on stack, and the integer argument
172+
// backfills a still unoccupied integer register.
173+
//
174+
// See the *.expect in ./unit_tests for this behavior.
175+
UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct128bytesx1) {
176+
const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
177+
const auto& int64_type = *new (Z) NativePrimitiveType(kInt64);
178+
179+
auto& member_types = *new (Z) NativeTypes(Z, 16);
180+
member_types.Add(&int64_type);
181+
member_types.Add(&int64_type);
182+
member_types.Add(&int64_type);
183+
member_types.Add(&int64_type);
184+
member_types.Add(&int64_type);
185+
member_types.Add(&int64_type);
186+
member_types.Add(&int64_type);
187+
member_types.Add(&int64_type);
188+
member_types.Add(&int64_type);
189+
member_types.Add(&int64_type);
190+
member_types.Add(&int64_type);
191+
member_types.Add(&int64_type);
192+
member_types.Add(&int64_type);
193+
member_types.Add(&int64_type);
194+
member_types.Add(&int64_type);
195+
member_types.Add(&int64_type);
196+
const auto& struct_type =
197+
NativeCompoundType::FromNativeTypes(Z, member_types);
198+
199+
auto& arguments = *new (Z) NativeTypes(Z, 2);
200+
arguments.Add(&struct_type);
201+
arguments.Add(&int32_type); // Check integer register backfilling, if any.
202+
203+
RunSignatureTest(Z, "struct128bytesx1", arguments, struct_type);
204+
}
205+
206+
#if defined(TARGET_ARCH_X64)
207+
// On x64 non-Windows a struct can be spread over an FPU and int register.
208+
//
209+
// See the *.expect in ./unit_tests for this behavior.
210+
UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesMixedx10) {
211+
const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
212+
const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
213+
214+
auto& member_types = *new (Z) NativeTypes(Z, 4);
215+
member_types.Add(&float_type);
216+
member_types.Add(&float_type);
217+
member_types.Add(&int32_type);
218+
member_types.Add(&int32_type);
219+
const auto& struct_type =
220+
NativeCompoundType::FromNativeTypes(Z, member_types);
221+
222+
auto& arguments = *new (Z) NativeTypes(Z, 11);
223+
arguments.Add(&struct_type);
224+
arguments.Add(&struct_type);
225+
arguments.Add(&struct_type);
226+
arguments.Add(&struct_type);
227+
arguments.Add(&struct_type);
228+
arguments.Add(&struct_type);
229+
arguments.Add(&struct_type); // Integer registers exhausted, on stack.
230+
arguments.Add(&struct_type);
231+
arguments.Add(&struct_type);
232+
arguments.Add(&struct_type);
233+
arguments.Add(&float_type); // Use remaining FPU register.
234+
235+
RunSignatureTest(Z, "struct16bytesMixedx10", arguments, struct_type);
236+
}
237+
238+
// On x64 non-Windows a struct can be spread over an FPU and int register.
239+
//
240+
// See the *.expect in ./unit_tests for this behavior.
241+
UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesMixedx10_2) {
242+
const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
243+
const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
244+
245+
auto& member_types = *new (Z) NativeTypes(Z, 4);
246+
member_types.Add(&float_type);
247+
member_types.Add(&float_type);
248+
member_types.Add(&int32_type);
249+
member_types.Add(&int32_type);
250+
const auto& struct_type =
251+
NativeCompoundType::FromNativeTypes(Z, member_types);
252+
253+
auto& arguments = *new (Z) NativeTypes(Z, 15);
254+
arguments.Add(&float_type);
255+
arguments.Add(&float_type);
256+
arguments.Add(&float_type);
257+
arguments.Add(&float_type);
258+
arguments.Add(&struct_type);
259+
arguments.Add(&struct_type);
260+
arguments.Add(&struct_type);
261+
arguments.Add(&struct_type);
262+
arguments.Add(&struct_type); // FPU registers exhausted, on stack.
263+
arguments.Add(&struct_type);
264+
arguments.Add(&struct_type);
265+
arguments.Add(&struct_type);
266+
arguments.Add(&struct_type);
267+
arguments.Add(&struct_type);
268+
arguments.Add(&int32_type); // Use remaining integer register.
269+
270+
RunSignatureTest(Z, "struct16bytesMixedx10_2", arguments, struct_type);
271+
}
272+
#endif // defined(TARGET_ARCH_X64)
273+
274+
// On ia32 Windows a struct can be returned in registers, on non-Windows not.
275+
//
276+
// See the *.expect in ./unit_tests for this behavior.
277+
UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct8bytesx1) {
278+
const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
279+
280+
auto& member_types = *new (Z) NativeTypes(Z, 4);
281+
member_types.Add(&int8type);
282+
member_types.Add(&int8type);
283+
member_types.Add(&int8type);
284+
member_types.Add(&int8type);
285+
member_types.Add(&int8type);
286+
member_types.Add(&int8type);
287+
member_types.Add(&int8type);
288+
member_types.Add(&int8type);
289+
const auto& struct_type =
290+
NativeCompoundType::FromNativeTypes(Z, member_types);
291+
292+
auto& arguments = *new (Z) NativeTypes(Z, 1);
293+
arguments.Add(&struct_type);
294+
295+
RunSignatureTest(Z, "struct8bytesx1", arguments, struct_type);
296+
}
297+
83298
} // namespace ffi
84299
} // namespace compiler
85300
} // namespace dart

0 commit comments

Comments
 (0)