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