1 /* 2 * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 /* 26 * @test 27 * @enablePreview 28 * @requires sun.arch.data.model == "64" 29 * @compile platform/PlatformLayouts.java 30 * @modules java.base/jdk.internal.foreign 31 * java.base/jdk.internal.foreign.abi 32 * java.base/jdk.internal.foreign.abi.aarch64 33 * @build CallArrangerTestBase 34 * @run testng TestLinuxAArch64CallArranger 35 */ 36 37 import java.lang.foreign.FunctionDescriptor; 38 import java.lang.foreign.MemoryLayout; 39 import java.lang.foreign.StructLayout; 40 import java.lang.foreign.MemorySegment; 41 import jdk.internal.foreign.abi.Binding; 42 import jdk.internal.foreign.abi.CallingSequence; 43 import jdk.internal.foreign.abi.LinkerOptions; 44 import jdk.internal.foreign.abi.StubLocations; 45 import jdk.internal.foreign.abi.VMStorage; 46 import jdk.internal.foreign.abi.aarch64.CallArranger; 47 import org.testng.annotations.DataProvider; 48 import org.testng.annotations.Test; 49 50 import java.lang.invoke.MethodType; 51 52 import static java.lang.foreign.Linker.Option.firstVariadicArg; 53 import static java.lang.foreign.ValueLayout.ADDRESS; 54 import static jdk.internal.foreign.abi.Binding.*; 55 import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*; 56 import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*; 57 import static platform.PlatformLayouts.AArch64.*; 58 59 import static org.testng.Assert.assertEquals; 60 import static org.testng.Assert.assertFalse; 61 import static org.testng.Assert.assertTrue; 62 63 public class TestLinuxAArch64CallArranger extends CallArrangerTestBase { 64 65 private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER); 66 private static final VMStorage RETURN_BUFFER_STORAGE = StubLocations.RETURN_BUFFER.storage(StorageType.PLACEHOLDER); 67 68 @Test 69 public void testEmpty() { 70 MethodType mt = MethodType.methodType(void.class); 71 FunctionDescriptor fd = FunctionDescriptor.ofVoid(); 72 CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); 73 74 assertFalse(bindings.isInMemoryReturn()); 75 CallingSequence callingSequence = bindings.callingSequence(); 76 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 77 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 78 79 checkArgumentBindings(callingSequence, new Binding[][]{ 80 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) } 81 }); 82 83 checkReturnBindings(callingSequence, new Binding[]{}); 84 } 85 86 @Test 87 public void testInteger() { 88 MethodType mt = MethodType.methodType(void.class, 89 int.class, int.class, int.class, int.class, 90 int.class, int.class, int.class, int.class, 91 int.class, int.class); 92 FunctionDescriptor fd = FunctionDescriptor.ofVoid( 93 C_INT, C_INT, C_INT, C_INT, 94 C_INT, C_INT, C_INT, C_INT, 95 C_INT, C_INT); 96 CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); 97 98 assertFalse(bindings.isInMemoryReturn()); 99 CallingSequence callingSequence = bindings.callingSequence(); 100 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 101 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 102 103 checkArgumentBindings(callingSequence, new Binding[][]{ 104 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 105 { vmStore(r0, int.class) }, 106 { vmStore(r1, int.class) }, 107 { vmStore(r2, int.class) }, 108 { vmStore(r3, int.class) }, 109 { vmStore(r4, int.class) }, 110 { vmStore(r5, int.class) }, 111 { vmStore(r6, int.class) }, 112 { vmStore(r7, int.class) }, 113 { vmStore(stackStorage((short) 4, 0), int.class) }, 114 { vmStore(stackStorage((short) 4, 8), int.class) }, 115 }); 116 117 checkReturnBindings(callingSequence, new Binding[]{}); 118 } 119 120 @Test 121 public void testTwoIntTwoFloat() { 122 MethodType mt = MethodType.methodType(void.class, 123 int.class, int.class, float.class, float.class); 124 FunctionDescriptor fd = FunctionDescriptor.ofVoid( 125 C_INT, C_INT, C_FLOAT, C_FLOAT); 126 CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); 127 128 assertFalse(bindings.isInMemoryReturn()); 129 CallingSequence callingSequence = bindings.callingSequence(); 130 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 131 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 132 133 checkArgumentBindings(callingSequence, new Binding[][]{ 134 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 135 { vmStore(r0, int.class) }, 136 { vmStore(r1, int.class) }, 137 { vmStore(v0, float.class) }, 138 { vmStore(v1, float.class) }, 139 }); 140 141 checkReturnBindings(callingSequence, new Binding[]{}); 142 } 143 144 @Test(dataProvider = "structs") 145 public void testStruct(MemoryLayout struct, Binding[] expectedBindings) { 146 MethodType mt = MethodType.methodType(void.class, MemorySegment.class); 147 FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct); 148 CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); 149 150 assertFalse(bindings.isInMemoryReturn()); 151 CallingSequence callingSequence = bindings.callingSequence(); 152 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 153 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 154 155 checkArgumentBindings(callingSequence, new Binding[][]{ 156 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 157 expectedBindings 158 }); 159 160 checkReturnBindings(callingSequence, new Binding[]{}); 161 } 162 163 @DataProvider 164 public static Object[][] structs() { 165 MemoryLayout struct2 = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT); 166 return new Object[][]{ 167 // struct s { int32_t a, b; double c; }; 168 { MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE), new Binding[] { 169 dup(), 170 // s.a & s.b 171 bufferLoad(0, long.class), vmStore(r0, long.class), 172 // s.c --> note AArch64 passes this in an *integer* register 173 bufferLoad(8, long.class), vmStore(r1, long.class), 174 }}, 175 // struct s { int32_t a, b; double c; int32_t d }; 176 { struct2, new Binding[] { 177 copy(struct2), 178 unboxAddress(), 179 vmStore(r0, long.class) 180 }}, 181 // struct s { int32_t a[2]; float b[2] }; 182 { MemoryLayout.structLayout(C_INT, C_INT, C_FLOAT, C_FLOAT), new Binding[] { 183 dup(), 184 // s.a[0] & s.a[1] 185 bufferLoad(0, long.class), vmStore(r0, long.class), 186 // s.b[0] & s.b[1] 187 bufferLoad(8, long.class), vmStore(r1, long.class), 188 }}, 189 // struct s { float a; /* padding */ double b }; 190 { MemoryLayout.structLayout(C_FLOAT, MemoryLayout.paddingLayout(4), C_DOUBLE), 191 new Binding[] { 192 dup(), 193 // s.a 194 bufferLoad(0, long.class), vmStore(r0, long.class), 195 // s.b 196 bufferLoad(8, long.class), vmStore(r1, long.class), 197 }}, 198 }; 199 } 200 201 @Test 202 public void testMultipleStructs() { 203 MemoryLayout struct1 = MemoryLayout.structLayout(C_INT, C_INT, C_DOUBLE, C_INT); 204 MemoryLayout struct2 = MemoryLayout.structLayout(C_LONG, C_LONG, C_LONG); 205 206 MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, int.class); 207 FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct1, struct2, C_INT); 208 CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); 209 210 assertFalse(bindings.isInMemoryReturn()); 211 CallingSequence callingSequence = bindings.callingSequence(); 212 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 213 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 214 215 checkArgumentBindings(callingSequence, new Binding[][]{ 216 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 217 { 218 copy(struct1), 219 unboxAddress(), 220 vmStore(r0, long.class) 221 }, 222 { 223 copy(struct2), 224 unboxAddress(), 225 vmStore(r1, long.class) 226 }, 227 { vmStore(r2, int.class) } 228 }); 229 230 checkReturnBindings(callingSequence, new Binding[]{}); 231 } 232 233 @Test 234 public void testReturnStruct1() { 235 MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG, C_FLOAT); 236 237 MethodType mt = MethodType.methodType(MemorySegment.class); 238 FunctionDescriptor fd = FunctionDescriptor.of(struct); 239 CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); 240 241 assertTrue(bindings.isInMemoryReturn()); 242 CallingSequence callingSequence = bindings.callingSequence(); 243 assertEquals(callingSequence.callerMethodType(), MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class)); 244 assertEquals(callingSequence.functionDesc(), FunctionDescriptor.ofVoid(ADDRESS, C_POINTER)); 245 246 checkArgumentBindings(callingSequence, new Binding[][]{ 247 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 248 { 249 unboxAddress(), 250 vmStore(r8, long.class) 251 } 252 }); 253 254 checkReturnBindings(callingSequence, new Binding[]{}); 255 } 256 257 @Test 258 public void testReturnStruct2() { 259 MemoryLayout struct = MemoryLayout.structLayout(C_LONG, C_LONG); 260 261 MethodType mt = MethodType.methodType(MemorySegment.class); 262 FunctionDescriptor fd = FunctionDescriptor.of(struct); 263 CallArranger.Bindings bindings = CallArranger.LINUX.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 }); 274 275 checkReturnBindings(callingSequence, new Binding[]{ 276 allocate(struct), 277 dup(), 278 vmLoad(r0, long.class), 279 bufferStore(0, long.class), 280 dup(), 281 vmLoad(r1, long.class), 282 bufferStore(8, long.class), 283 }); 284 } 285 286 @Test 287 public void testStructHFA1() { 288 MemoryLayout hfa = MemoryLayout.structLayout(C_FLOAT, C_FLOAT); 289 290 MethodType mt = MethodType.methodType(MemorySegment.class, float.class, int.class, MemorySegment.class); 291 FunctionDescriptor fd = FunctionDescriptor.of(hfa, C_FLOAT, C_INT, hfa); 292 CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); 293 294 assertFalse(bindings.isInMemoryReturn()); 295 CallingSequence callingSequence = bindings.callingSequence(); 296 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class)); 297 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS, ADDRESS)); 298 299 checkArgumentBindings(callingSequence, new Binding[][]{ 300 { unboxAddress(), vmStore(RETURN_BUFFER_STORAGE, long.class) }, 301 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 302 { vmStore(v0, float.class) }, 303 { vmStore(r0, int.class) }, 304 { 305 dup(), 306 bufferLoad(0, float.class), 307 vmStore(v1, float.class), 308 bufferLoad(4, float.class), 309 vmStore(v2, float.class) 310 } 311 }); 312 313 checkReturnBindings(callingSequence, new Binding[]{ 314 allocate(hfa), 315 dup(), 316 vmLoad(v0, float.class), 317 bufferStore(0, float.class), 318 dup(), 319 vmLoad(v1, float.class), 320 bufferStore(4, float.class), 321 }); 322 } 323 324 @Test 325 public void testStructHFA3() { 326 MemoryLayout struct = MemoryLayout.structLayout(C_FLOAT, C_FLOAT, C_FLOAT); 327 328 MethodType mt = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class, MemorySegment.class); 329 FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, struct, struct); 330 CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); 331 332 assertFalse(bindings.isInMemoryReturn()); 333 CallingSequence callingSequence = bindings.callingSequence(); 334 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class)); 335 assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS)); 336 337 checkArgumentBindings(callingSequence, new Binding[][]{ 338 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 339 { 340 dup(), 341 bufferLoad(0, float.class), 342 vmStore(v0, float.class), 343 dup(), 344 bufferLoad(4, float.class), 345 vmStore(v1, float.class), 346 bufferLoad(8, float.class), 347 vmStore(v2, float.class) 348 }, 349 { 350 dup(), 351 bufferLoad(0, float.class), 352 vmStore(v3, float.class), 353 dup(), 354 bufferLoad(4, float.class), 355 vmStore(v4, float.class), 356 bufferLoad(8, float.class), 357 vmStore(v5, float.class) 358 }, 359 { 360 dup(), 361 bufferLoad(0, long.class), 362 vmStore(stackStorage((short) 8, 0), long.class), 363 bufferLoad(8, int.class), 364 vmStore(stackStorage((short) 4, 8), int.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 CallArranger.Bindings bindings = CallArranger.LINUX.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(r0, long.class) }, 394 { copy(struct), unboxAddress(), vmStore(r1, long.class) }, 395 { vmStore(r2, int.class) }, 396 { vmStore(r3, int.class) }, 397 { vmStore(r4, int.class) }, 398 { vmStore(r5, int.class) }, 399 { vmStore(r6, int.class) }, 400 { vmStore(r7, int.class) }, 401 { copy(struct), unboxAddress(), vmStore(stackStorage((short) 8, 0), long.class) }, 402 { vmStore(stackStorage((short) 4, 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 CallArranger.Bindings bindings = CallArranger.LINUX.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(r0, int.class) }, 424 { vmStore(r1, int.class) }, 425 { vmStore(v0, float.class) }, 426 }); 427 428 checkReturnBindings(callingSequence, new Binding[]{}); 429 } 430 431 @Test 432 public void testFloatArrayStruct() { 433 // should be classified as HFA 434 StructLayout S10 = MemoryLayout.structLayout( 435 MemoryLayout.sequenceLayout(4, C_DOUBLE) 436 ); 437 MethodType mt = MethodType.methodType(MemorySegment.class, MemorySegment.class); 438 FunctionDescriptor fd = FunctionDescriptor.of(S10, S10); 439 FunctionDescriptor fdExpected = FunctionDescriptor.of(S10, ADDRESS, ADDRESS, S10); // uses return buffer 440 CallArranger.Bindings bindings = CallArranger.LINUX.getBindings(mt, fd, false); 441 442 assertFalse(bindings.isInMemoryReturn()); 443 CallingSequence callingSequence = bindings.callingSequence(); 444 assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class, MemorySegment.class)); 445 assertEquals(callingSequence.functionDesc(), fdExpected); 446 447 // This is identical to the non-variadic calling sequence 448 checkArgumentBindings(callingSequence, new Binding[][]{ 449 { unboxAddress(), vmStore(RETURN_BUFFER_STORAGE, long.class) }, 450 { unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) }, 451 { dup(), 452 bufferLoad(0, double.class), 453 vmStore(v0, double.class), 454 dup(), 455 bufferLoad(8, double.class), 456 vmStore(v1, double.class), 457 dup(), 458 bufferLoad(16, double.class), 459 vmStore(v2, double.class), 460 bufferLoad(24, double.class), 461 vmStore(v3, double.class) }, 462 }); 463 464 checkReturnBindings(callingSequence, new Binding[]{ 465 allocate(S10), 466 dup(), 467 vmLoad(v0, double.class), 468 bufferStore(0, double.class), 469 dup(), 470 vmLoad(v1, double.class), 471 bufferStore(8, double.class), 472 dup(), 473 vmLoad(v2, double.class), 474 bufferStore(16, double.class), 475 dup(), 476 vmLoad(v3, double.class), 477 bufferStore(24, double.class), 478 }); 479 } 480 }