1 /*
  2  * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
  3  * Copyright (c) 2023, Institute of Software, Chinese Academy of Sciences.
  4  * All rights reserved.
  5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  6  *
  7  * This code is free software; you can redistribute it and/or modify it
  8  * under the terms of the GNU General Public License version 2 only, as
  9  * published by the Free Software Foundation.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  *
 25  */
 26 
 27 /*
 28  * @test
 29  * @requires sun.arch.data.model == "64"
 30  * @compile platform/PlatformLayouts.java
 31  * @modules java.base/jdk.internal.foreign
 32  *          java.base/jdk.internal.foreign.abi
 33  *          java.base/jdk.internal.foreign.abi.riscv64
 34  *          java.base/jdk.internal.foreign.abi.riscv64.linux
 35  * @build CallArrangerTestBase
 36  * @run testng TestRISCV64CallArranger
 37  */
 38 
 39 import java.lang.foreign.FunctionDescriptor;
 40 import java.lang.foreign.MemoryLayout;
 41 import java.lang.foreign.MemorySegment;
 42 import jdk.internal.foreign.abi.Binding;
 43 import jdk.internal.foreign.abi.CallingSequence;
 44 import jdk.internal.foreign.abi.LinkerOptions;
 45 import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64CallArranger;
 46 import jdk.internal.foreign.abi.StubLocations;
 47 import jdk.internal.foreign.abi.VMStorage;
 48 import org.testng.annotations.DataProvider;
 49 import org.testng.annotations.Test;
 50 
 51 import java.lang.foreign.ValueLayout;
 52 import java.lang.invoke.MethodType;
 53 
 54 import static java.lang.foreign.Linker.Option.firstVariadicArg;
 55 import static java.lang.foreign.ValueLayout.ADDRESS;
 56 import static jdk.internal.foreign.abi.Binding.*;
 57 import static jdk.internal.foreign.abi.riscv64.RISCV64Architecture.*;
 58 import static jdk.internal.foreign.abi.riscv64.RISCV64Architecture.Regs.*;
 59 import static platform.PlatformLayouts.RISCV64.*;
 60 
 61 import static org.testng.Assert.assertEquals;
 62 import static org.testng.Assert.assertFalse;
 63 import static org.testng.Assert.assertTrue;
 64 
 65 public class TestRISCV64CallArranger extends CallArrangerTestBase {
 66 
 67     private static final short STACK_SLOT_SIZE = 8;
 68     private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
 69     private static final VMStorage RETURN_BUFFER_STORAGE = StubLocations.RETURN_BUFFER.storage(StorageType.PLACEHOLDER);
 70 
 71     @Test
 72     public void testEmpty() {
 73         MethodType mt = MethodType.methodType(void.class);
 74         FunctionDescriptor fd = FunctionDescriptor.ofVoid();
 75         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
 76 
 77         assertFalse(bindings.isInMemoryReturn());
 78         CallingSequence callingSequence = bindings.callingSequence();
 79         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
 80         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
 81 
 82         checkArgumentBindings(callingSequence, new Binding[][]{
 83             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }
 84         });
 85 
 86         checkReturnBindings(callingSequence, new Binding[]{});
 87     }
 88 
 89     @Test
 90     public void testInteger() {
 91         MethodType mt = MethodType.methodType(void.class,
 92             byte.class, short.class, int.class, int.class,
 93             int.class, int.class, long.class, int.class,
 94             int.class, byte.class);
 95         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
 96             C_CHAR, C_SHORT, C_INT, C_INT,
 97             C_INT, C_INT, C_LONG, C_INT,
 98             C_INT, C_CHAR);
 99         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
100 
101         assertFalse(bindings.isInMemoryReturn());
102         CallingSequence callingSequence = bindings.callingSequence();
103         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
104         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
105 
106         checkArgumentBindings(callingSequence, new Binding[][]{
107             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
108             { cast(byte.class, int.class), vmStore(x10, int.class) },
109             { cast(short.class, int.class), vmStore(x11, int.class) },
110             { vmStore(x12, int.class) },
111             { vmStore(x13, int.class) },
112             { vmStore(x14, int.class) },
113             { vmStore(x15, int.class) },
114             { vmStore(x16, long.class) },
115             { vmStore(x17, int.class) },
116             { vmStore(stackStorage(STACK_SLOT_SIZE, 0), int.class) },
117             { cast(byte.class, int.class), vmStore(stackStorage(STACK_SLOT_SIZE, 8), int.class) }
118         });
119 
120         checkReturnBindings(callingSequence, new Binding[]{});
121     }
122 
123     @Test
124     public void testTwoIntTwoFloat() {
125         MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class, float.class);
126         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT, C_FLOAT);
127         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
128 
129         assertFalse(bindings.isInMemoryReturn());
130         CallingSequence callingSequence = bindings.callingSequence();
131         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
132         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
133 
134         checkArgumentBindings(callingSequence, new Binding[][]{
135             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
136             { vmStore(x10, int.class) },
137             { vmStore(x11, int.class) },
138             { vmStore(f10, float.class) },
139             { vmStore(f11, float.class) }
140         });
141 
142         checkReturnBindings(callingSequence, new Binding[]{});
143     }
144 
145     @Test(dataProvider = "structs")
146     public void testStruct(MemoryLayout struct, Binding[] expectedBindings) {
147         MethodType mt = MethodType.methodType(void.class, MemorySegment.class);
148         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct);
149         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
150 
151         assertFalse(bindings.isInMemoryReturn());
152         CallingSequence callingSequence = bindings.callingSequence();
153         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
154         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
155 
156         checkArgumentBindings(callingSequence, new Binding[][]{
157             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
158             expectedBindings
159         });
160 
161         checkReturnBindings(callingSequence, new Binding[]{});
162     }
163 
164     @DataProvider
165     public static Object[][] structs() {
166         MemoryLayout struct1 = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT);
167         return new Object[][]{
168             // struct s { void* a; double c; };
169             {
170                 MemoryLayout.structLayout(C_POINTER, C_DOUBLE),
171                 new Binding[]{
172                     dup(),
173                     bufferLoad(0, long.class), vmStore(x10, long.class),
174                     bufferLoad(8, long.class), vmStore(x11, long.class)
175                 }
176             },
177             // struct s { int32_t a, b; double c; };
178             { MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE),
179                 new Binding[]{
180                     dup(),
181                     // s.a & s.b
182                     bufferLoad(0, long.class), vmStore(x10, long.class),
183                     // s.c
184                     bufferLoad(8, long.class), vmStore(x11, long.class)
185                 }
186             },
187             // struct s { int32_t a, b; double c; int32_t d; };
188             { struct1,
189                 new Binding[]{
190                     copy(struct1),
191                     unboxAddress(),
192                     vmStore(x10, long.class)
193                 }
194             },
195             // struct s { int32_t a[1]; float b[1]; };
196             { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(1, C_INT),
197                 MemoryLayout.sequenceLayout(1, C_FLOAT)),
198                 new Binding[]{
199                     dup(),
200                     // s.a[0]
201                     bufferLoad(0, int.class), vmStore(x10, int.class),
202                     // s.b[0]
203                     bufferLoad(4, float.class), vmStore(f10, float.class)
204                 }
205             },
206             // struct s { float a; /* padding */ double b };
207             { MemoryLayout.structLayout(C_FLOAT, MemoryLayout.paddingLayout(4), C_DOUBLE),
208                 new Binding[]{
209                     dup(),
210                     // s.a
211                     bufferLoad(0, float.class), vmStore(f10, float.class),
212                     // s.b
213                     bufferLoad(8, double.class), vmStore(f11, double.class),
214                 }
215             }
216         };
217     }
218 
219     @Test
220     public void testStructFA1() {
221         MemoryLayout fa = MemoryLayout.structLayout(C_FLOAT, C_FLOAT);
222 
223         MethodType mt = MethodType.methodType(MemorySegment.class, float.class, int.class, MemorySegment.class);
224         FunctionDescriptor fd = FunctionDescriptor.of(fa, C_FLOAT, C_INT, fa);
225         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
226 
227         assertFalse(bindings.isInMemoryReturn());
228         CallingSequence callingSequence = bindings.callingSequence();
229         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class));
230         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS, ADDRESS));
231 
232         checkArgumentBindings(callingSequence, new Binding[][]{
233             { unboxAddress(), vmStore(RETURN_BUFFER_STORAGE, long.class) },
234             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
235             { vmStore(f10, float.class) },
236             { vmStore(x10, int.class) },
237             {
238                 dup(),
239                 bufferLoad(0, float.class),
240                 vmStore(f11, float.class),
241                 bufferLoad(4, float.class),
242                 vmStore(f12, float.class)
243             }
244         });
245 
246         checkReturnBindings(callingSequence, new Binding[]{
247             allocate(fa),
248             dup(),
249             vmLoad(f10, float.class),
250             bufferStore(0, float.class),
251             dup(),
252             vmLoad(f11, float.class),
253             bufferStore(4, float.class)
254         });
255     }
256 
257     @Test
258     public void testStructFA2() {
259         MemoryLayout fa = MemoryLayout.structLayout(C_FLOAT, MemoryLayout.paddingLayout(4), C_DOUBLE);
260 
261         MethodType mt = MethodType.methodType(MemorySegment.class, float.class, int.class, MemorySegment.class);
262         FunctionDescriptor fd = FunctionDescriptor.of(fa, C_FLOAT, C_INT, fa);
263         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
264 
265         assertFalse(bindings.isInMemoryReturn());
266         CallingSequence callingSequence = bindings.callingSequence();
267         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class));
268         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS, ADDRESS));
269 
270         checkArgumentBindings(callingSequence, new Binding[][]{
271             { unboxAddress(), vmStore(RETURN_BUFFER_STORAGE, long.class) },
272             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
273             { vmStore(f10, float.class) },
274             { vmStore(x10, int.class) },
275             {
276                 dup(),
277                 bufferLoad(0, float.class),
278                 vmStore(f11, float.class),
279                 bufferLoad(8, double.class),
280                 vmStore(f12, double.class)
281             }
282         });
283 
284         checkReturnBindings(callingSequence, new Binding[]{
285             allocate(fa),
286             dup(),
287             vmLoad(f10, float.class),
288             bufferStore(0, float.class),
289             dup(),
290             vmLoad(f11, double.class),
291             bufferStore(8, double.class)
292         });
293     }
294 
295     @Test
296     void spillFloatingPointStruct() {
297         MemoryLayout struct = MemoryLayout.structLayout(C_FLOAT, C_FLOAT);
298         // void f(float, float, float, float, float, float, float, struct)
299         MethodType mt = MethodType.methodType(void.class, float.class, float.class,
300             float.class, float.class, float.class,
301             float.class, float.class, MemorySegment.class);
302         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT,
303             C_FLOAT, C_FLOAT, C_FLOAT, struct);
304         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
305 
306         assertFalse(bindings.isInMemoryReturn());
307         CallingSequence callingSequence = bindings.callingSequence();
308         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
309         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
310 
311         checkArgumentBindings(callingSequence, new Binding[][]{
312             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
313             { vmStore(f10, float.class) },
314             { vmStore(f11, float.class) },
315             { vmStore(f12, float.class) },
316             { vmStore(f13, float.class) },
317             { vmStore(f14, float.class) },
318             { vmStore(f15, float.class) },
319             { vmStore(f16, float.class) },
320             {
321                 bufferLoad(0, long.class),
322                 vmStore(x10, long.class)
323             }
324         });
325 
326         checkReturnBindings(callingSequence, new Binding[]{});
327     }
328 
329     @Test
330     public void testStructBoth() {
331         MemoryLayout struct = MemoryLayout.structLayout(C_INT, C_FLOAT);
332 
333         MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, MemorySegment.class);
334         FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, struct, struct);
335         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
336 
337         assertFalse(bindings.isInMemoryReturn());
338         CallingSequence callingSequence = bindings.callingSequence();
339         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
340         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
341 
342         checkArgumentBindings(callingSequence, new Binding[][]{
343             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
344             {
345                 dup(),
346                 bufferLoad(0, int.class),
347                 vmStore(x10, int.class),
348                 bufferLoad(4, float.class),
349                 vmStore(f10, float.class)
350             },
351             {
352                 dup(),
353                 bufferLoad(0, int.class),
354                 vmStore(x11, int.class),
355                 bufferLoad(4, float.class),
356                 vmStore(f11, float.class)
357             },
358             {
359                 dup(),
360                 bufferLoad(0, int.class),
361                 vmStore(x12, int.class),
362                 bufferLoad(4, float.class),
363                 vmStore(f12, float.class)
364             }
365         });
366 
367         checkReturnBindings(callingSequence, new Binding[]{});
368     }
369 
370     @Test
371     public void testStructStackSpill() {
372         // A large (> 16 byte) struct argument that is spilled to the
373         // stack should be passed as a pointer to a copy and occupy one
374         // stack slot.
375 
376         MemoryLayout struct = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT);
377 
378         MethodType mt = MethodType.methodType(
379             void.class, MemorySegment.class, MemorySegment.class, int.class, int.class,
380             int.class, int.class, int.class, int.class, MemorySegment.class, int.class);
381         FunctionDescriptor fd = FunctionDescriptor.ofVoid(
382             struct, struct, C_INT, C_INT, C_INT, C_INT, C_INT, C_INT, struct, C_INT);
383         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
384 
385         assertFalse(bindings.isInMemoryReturn());
386         CallingSequence callingSequence = bindings.callingSequence();
387         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
388         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
389 
390         checkArgumentBindings(callingSequence, new Binding[][]{
391             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
392             { copy(struct), unboxAddress(), vmStore(x10, long.class) },
393             { copy(struct), unboxAddress(), vmStore(x11, long.class) },
394             { vmStore(x12, int.class) },
395             { vmStore(x13, int.class) },
396             { vmStore(x14, int.class) },
397             { vmStore(x15, int.class) },
398             { vmStore(x16, int.class) },
399             { vmStore(x17, int.class) },
400             { copy(struct), unboxAddress(), vmStore(stackStorage(STACK_SLOT_SIZE, 0), long.class) },
401             { vmStore(stackStorage(STACK_SLOT_SIZE, 8), int.class) }
402         });
403 
404         checkReturnBindings(callingSequence, new Binding[]{});
405     }
406 
407     @Test
408     public void testVarArgsInRegs() {
409         MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
410         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT);
411         FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT);
412         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
413 
414         assertFalse(bindings.isInMemoryReturn());
415         CallingSequence callingSequence = bindings.callingSequence();
416         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
417         assertEquals(callingSequence.functionDesc(), fdExpected);
418 
419         // This is identical to the non-variadic calling sequence
420         checkArgumentBindings(callingSequence, new Binding[][]{
421             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
422             { vmStore(x10, int.class) },
423             { vmStore(x11, int.class) },
424             { vmStore(x12, float.class) }
425         });
426 
427         checkReturnBindings(callingSequence, new Binding[]{});
428     }
429 
430     @Test
431     public void testVarArgsLong() {
432         MethodType mt = MethodType.methodType(void.class, int.class, int.class, int.class, double.class,
433             double.class, long.class, long.class, int.class,
434             double.class, double.class, long.class);
435         FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_INT, C_DOUBLE, C_DOUBLE,
436             C_LONG, C_LONG, C_INT, C_DOUBLE,
437             C_DOUBLE, C_LONG);
438         FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_INT, C_DOUBLE,
439             C_DOUBLE, C_LONG, C_LONG, C_INT,
440             C_DOUBLE, C_DOUBLE, C_LONG);
441         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
442 
443         assertFalse(bindings.isInMemoryReturn());
444         CallingSequence callingSequence = bindings.callingSequence();
445         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
446         assertEquals(callingSequence.functionDesc(), fdExpected);
447 
448         // This is identical to the non-variadic calling sequence
449         checkArgumentBindings(callingSequence, new Binding[][]{
450             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
451             { vmStore(x10, int.class) },
452             { vmStore(x11, int.class) },
453             { vmStore(x12, int.class) },
454             { vmStore(x13, double.class) },
455             { vmStore(x14, double.class) },
456             { vmStore(x15, long.class) },
457             { vmStore(x16, long.class) },
458             { vmStore(x17, int.class) },
459             { vmStore(stackStorage(STACK_SLOT_SIZE, 0), double.class) },
460             { vmStore(stackStorage(STACK_SLOT_SIZE, 8), double.class) },
461             { vmStore(stackStorage(STACK_SLOT_SIZE, 16), long.class) }
462         });
463 
464         checkReturnBindings(callingSequence, new Binding[]{});
465     }
466 
467     @Test
468     public void testReturnStruct1() {
469         MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG, C_FLOAT);
470 
471         MethodType mt = MethodType.methodType(MemorySegment.class, int.class, int.class, float.class);
472         FunctionDescriptor fd = FunctionDescriptor.of(struct, C_INT, C_INT, C_FLOAT);
473         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
474 
475         assertTrue(bindings.isInMemoryReturn());
476         CallingSequence callingSequence = bindings.callingSequence();
477         assertEquals(callingSequence.callerMethodType(),
478             MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class,
479                 int.class, int.class, float.class));
480         assertEquals(callingSequence.functionDesc(),
481             FunctionDescriptor.ofVoid(ADDRESS, C_POINTER, C_INT, C_INT, C_FLOAT));
482 
483         checkArgumentBindings(callingSequence, new Binding[][]{
484             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
485             { unboxAddress(), vmStore(x10, long.class) },
486             { vmStore(x11, int.class) },
487             { vmStore(x12, int.class) },
488             { vmStore(f10, float.class) }
489         });
490 
491         checkReturnBindings(callingSequence, new Binding[]{});
492     }
493 
494     @Test
495     public void testReturnStruct2() {
496         MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG);
497 
498         MethodType mt = MethodType.methodType(MemorySegment.class);
499         FunctionDescriptor fd = FunctionDescriptor.of(struct);
500         LinuxRISCV64CallArranger.Bindings bindings = LinuxRISCV64CallArranger.getBindings(mt, fd, false);
501 
502         assertFalse(bindings.isInMemoryReturn());
503         CallingSequence callingSequence = bindings.callingSequence();
504         assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class));
505         assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS, ADDRESS));
506 
507         checkArgumentBindings(callingSequence, new Binding[][]{
508             { unboxAddress(), vmStore(RETURN_BUFFER_STORAGE, long.class) },
509             { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }
510         });
511 
512         checkReturnBindings(callingSequence, new Binding[]{
513             allocate(struct),
514             dup(),
515             vmLoad(x10, long.class),
516             bufferStore(0, long.class),
517             dup(),
518             vmLoad(x11, long.class),
519             bufferStore(8, long.class)
520         });
521     }
522 }