1 /* 2 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2020, Arm Limited. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 package jdk.vm.ci.code.test.aarch64; 26 27 import jdk.vm.ci.aarch64.AArch64; 28 import jdk.vm.ci.aarch64.AArch64Kind; 29 import jdk.vm.ci.code.CallingConvention; 30 import jdk.vm.ci.code.CodeCacheProvider; 31 import jdk.vm.ci.code.DebugInfo; 32 import jdk.vm.ci.code.Register; 33 import jdk.vm.ci.code.RegisterValue; 34 import jdk.vm.ci.code.StackSlot; 35 import jdk.vm.ci.code.site.ConstantReference; 36 import jdk.vm.ci.code.site.DataSectionReference; 37 import jdk.vm.ci.code.test.TestAssembler; 38 import jdk.vm.ci.code.test.TestHotSpotVMConfig; 39 import jdk.vm.ci.hotspot.HotSpotCallingConventionType; 40 import jdk.vm.ci.hotspot.HotSpotConstant; 41 import jdk.vm.ci.hotspot.HotSpotForeignCallTarget; 42 import jdk.vm.ci.meta.AllocatableValue; 43 import jdk.vm.ci.meta.JavaKind; 44 import jdk.vm.ci.meta.VMConstant; 45 46 public class AArch64TestAssembler extends TestAssembler { 47 48 private static final Register scratchRegister = AArch64.rscratch1; 49 private static final Register doubleScratch = AArch64.v9; 50 AArch64TestAssembler(CodeCacheProvider codeCache, TestHotSpotVMConfig config)51 public AArch64TestAssembler(CodeCacheProvider codeCache, TestHotSpotVMConfig config) { 52 super(codeCache, config, 53 16 /* initialFrameSize */, 16 /* stackAlignment */, 54 AArch64Kind.DWORD /* narrowOopKind */, 55 /* registers */ 56 AArch64.r0, AArch64.r1, AArch64.r2, AArch64.r3, 57 AArch64.r4, AArch64.r5, AArch64.r6, AArch64.r7); 58 } 59 f(int val, int msb, int lsb)60 private static int f(int val, int msb, int lsb) { 61 int nbits = msb - lsb + 1; 62 assert val >= 0; 63 assert val < (1 << nbits); 64 assert msb >= lsb; 65 return val << lsb; 66 } 67 68 private static int f(Register r, int msb, int lsb) { 69 assert msb - lsb == 4; 70 return f(r.encoding, msb, lsb); 71 } 72 73 private void emitNop() { 74 code.emitInt(0xd503201f); 75 } 76 77 private void emitAdd(Register Rd, Register Rn, Register Rm) { 78 // ADD (shifted register) 79 code.emitInt(f(0b10001011000, 31, 21) 80 | f(Rm, 20, 16) 81 | f(0, 15, 10) 82 | f(Rn, 9, 5) 83 | f(Rd, 4, 0)); 84 } 85 86 private void emitAdd(Register Rd, Register Rn, int imm12) { 87 // ADD (immediate) 88 code.emitInt(f(0b1001000100, 31, 22) 89 | f(imm12, 21, 10) 90 | f(Rn, 9, 5) 91 | f(Rd, 4, 0)); 92 } 93 94 private void emitSub(Register Rd, Register Rn, int imm12) { 95 // SUB (immediate) 96 code.emitInt(f(0b1101000100, 31, 22) 97 | f(imm12, 21, 10) 98 | f(Rn, 9, 5) 99 | f(Rd, 4, 0)); 100 } 101 102 private void emitSub(Register Rd, Register Rn, Register Rm) { 103 // SUB (extended register) 104 code.emitInt(f(0b11001011001, 31, 21) 105 | f(Rm, 20, 16) 106 | f(0b011000, 15, 10) 107 | f(Rn, 9, 5) 108 | f(Rd, 4, 0)); 109 } 110 111 private void emitMov(Register Rd, Register Rm) { 112 // MOV (register) 113 code.emitInt(f(0b10101010000, 31, 21) 114 | f(Rm, 20, 16) 115 | f(0, 15, 10) 116 | f(AArch64.zr, 9, 5) 117 | f(Rd, 4, 0)); 118 } 119 120 private void emitMovz(Register Rd, int imm16, int shift) { 121 // MOVZ 122 int hw = 0; 123 switch (shift) { 124 case 0: hw = 0; break; 125 case 16: hw = 1; break; 126 case 32: hw = 2; break; 127 case 48: hw = 3; break; 128 default: throw new IllegalArgumentException(); 129 } 130 code.emitInt(f(0b110100101, 31, 23) 131 | f(hw, 22, 21) 132 | f(imm16, 20, 5) 133 | f(Rd, 4, 0)); 134 } 135 136 private void emitMovk(Register Rd, int imm16, int shift) { 137 // MOVK 138 int hw = 0; 139 switch (shift) { 140 case 0: hw = 0; break; 141 case 16: hw = 1; break; 142 case 32: hw = 2; break; 143 case 48: hw = 3; break; 144 default: throw new IllegalArgumentException(); 145 } 146 code.emitInt(f(0b111100101, 31, 23) 147 | f(hw, 22, 21) 148 | f(imm16, 20, 5) 149 | f(Rd, 4, 0)); 150 } 151 152 private void emitShiftLeft(Register Rd, Register Rn, int shift) { 153 // LSL (immediate) 154 code.emitInt(f(0b1101001101, 31, 22) 155 | f(-shift & 0b111111, 21, 16) 156 | f(63 - shift, 15, 10) 157 | f(Rn, 9, 5) 158 | f(Rd, 4, 0)); 159 } 160 161 private void emitLoadRegister(Register Rt, AArch64Kind kind, int offset) { 162 // LDR (literal) 163 int opc = 0; 164 switch (kind) { 165 case DWORD: opc = 0; break; 166 case QWORD: opc = 1; break; 167 default: throw new IllegalArgumentException(); 168 } 169 code.emitInt(f(opc, 31, 30) 170 | f(0b011000, 29, 24) 171 | f(offset, 23, 5) 172 | f(Rt, 4, 0)); 173 } 174 175 private void emitLoadRegister(Register Rt, AArch64Kind kind, Register Rn, int offset) { 176 // LDR (immediate) 177 assert offset >= 0; 178 int size = 0; 179 switch (kind) { 180 case DWORD: size = 0b10; break; 181 case QWORD: size = 0b11; break; 182 default: throw new IllegalArgumentException(); 183 } 184 code.emitInt(f(size, 31, 30) 185 | f(0b11100101, 29, 22) 186 | f(offset >> size, 21, 10) 187 | f(Rn, 9, 5) 188 | f(Rt, 4, 0)); 189 } 190 191 private void emitStoreRegister(Register Rt, AArch64Kind kind, Register Rn, int offset) { 192 // STR (immediate) 193 assert offset >= 0; 194 int size = 0, fp = 0; 195 switch (kind) { 196 case DWORD: size = 0b10; fp = 0; break; 197 case QWORD: size = 0b11; fp = 0; break; 198 case SINGLE: size = 0b10; fp = 1; break; 199 case DOUBLE: size = 0b11; fp = 1; break; 200 default: throw new IllegalArgumentException(); 201 } 202 code.emitInt(f(size, 31, 30) 203 | f(0b111, 29, 27) 204 | f(fp, 26, 26) 205 | f(0b0100, 25, 22) 206 | f(offset >> size, 21, 10) 207 | f(Rn, 9, 5) 208 | f(Rt, 4, 0)); 209 } 210 emitBlr(Register Rn)211 private void emitBlr(Register Rn) { 212 // BLR 213 code.emitInt(f(0b1101011000111111000000, 31, 10) 214 | f(Rn, 9, 5) 215 | f(0, 4, 0)); 216 } 217 emitFmov(Register Rd, AArch64Kind kind, Register Rn)218 private void emitFmov(Register Rd, AArch64Kind kind, Register Rn) { 219 // FMOV (general) 220 int ftype = 0, sf = 0; 221 switch (kind) { 222 case SINGLE: sf = 0; ftype = 0b00; break; 223 case DOUBLE: sf = 1; ftype = 0b01; break; 224 default: throw new IllegalArgumentException(); 225 } 226 code.emitInt(f(sf, 31, 31) 227 | f(0b0011110, 30, 24) 228 | f(ftype, 23, 22) 229 | f(0b100111, 21, 16) 230 | f(0, 15, 10) 231 | f(Rn, 9, 5) 232 | f(Rd, 4, 0)); 233 } 234 235 @Override emitGrowStack(int size)236 public void emitGrowStack(int size) { 237 assert size % 16 == 0; 238 if (size > -4096 && size < 0) { 239 emitAdd(AArch64.sp, AArch64.sp, -size); 240 } else if (size == 0) { 241 // No-op 242 } else if (size < 4096) { 243 emitSub(AArch64.sp, AArch64.sp, size); 244 } else if (size < 65535) { 245 emitMovz(scratchRegister, size & 0xffff, 0); 246 emitMovk(scratchRegister, (size >> 16) & 0xffff, 16); 247 emitSub(AArch64.sp, AArch64.sp, scratchRegister); 248 } else { 249 throw new IllegalArgumentException(); 250 } 251 } 252 253 @Override emitPrologue()254 public void emitPrologue() { 255 // Must be patchable by NativeJump::patch_verified_entry 256 emitNop(); 257 code.emitInt(0xa9be7bfd); // stp x29, x30, [sp, #-32]! 258 code.emitInt(0x910003fd); // mov x29, sp 259 260 setDeoptRescueSlot(newStackSlot(AArch64Kind.QWORD)); 261 } 262 263 @Override emitEpilogue()264 public void emitEpilogue() { 265 recordMark(config.MARKID_DEOPT_HANDLER_ENTRY); 266 recordCall(new HotSpotForeignCallTarget(config.handleDeoptStub), 5, true, null); 267 code.emitInt(0x94000000); // bl <imm26> 268 } 269 270 @Override emitCallPrologue(CallingConvention cc, Object... prim)271 public void emitCallPrologue(CallingConvention cc, Object... prim) { 272 emitGrowStack(cc.getStackSize()); 273 frameSize += cc.getStackSize(); 274 AllocatableValue[] args = cc.getArguments(); 275 for (int i = 0; i < args.length; i++) { 276 emitLoad(args[i], prim[i]); 277 } 278 } 279 280 @Override emitCallEpilogue(CallingConvention cc)281 public void emitCallEpilogue(CallingConvention cc) { 282 emitGrowStack(-cc.getStackSize()); 283 frameSize -= cc.getStackSize(); 284 } 285 286 @Override emitCall(long addr)287 public void emitCall(long addr) { 288 emitLoadLong(scratchRegister, addr); 289 emitBlr(scratchRegister); 290 } 291 292 @Override emitLoad(AllocatableValue av, Object prim)293 public void emitLoad(AllocatableValue av, Object prim) { 294 if (av instanceof RegisterValue) { 295 Register reg = ((RegisterValue) av).getRegister(); 296 if (prim instanceof Float) { 297 emitLoadFloat(reg, (Float) prim); 298 } else if (prim instanceof Double) { 299 emitLoadDouble(reg, (Double) prim); 300 } else if (prim instanceof Integer) { 301 emitLoadInt(reg, (Integer) prim); 302 } else if (prim instanceof Long) { 303 emitLoadLong(reg, (Long) prim); 304 } 305 } else if (av instanceof StackSlot) { 306 StackSlot slot = (StackSlot) av; 307 if (prim instanceof Float) { 308 emitFloatToStack(slot, emitLoadFloat(doubleScratch, (Float) prim)); 309 } else if (prim instanceof Double) { 310 emitDoubleToStack(slot, emitLoadDouble(doubleScratch, (Double) prim)); 311 } else if (prim instanceof Integer) { 312 emitIntToStack(slot, emitLoadInt(scratchRegister, (Integer) prim)); 313 } else if (prim instanceof Long) { 314 emitLongToStack(slot, emitLoadLong(scratchRegister, (Long) prim)); 315 } else { 316 assert false : "Unimplemented"; 317 } 318 } else { 319 throw new IllegalArgumentException("Unknown value " + av); 320 } 321 } 322 323 @Override emitLoadPointer(HotSpotConstant c)324 public Register emitLoadPointer(HotSpotConstant c) { 325 recordDataPatchInCode(new ConstantReference((VMConstant) c)); 326 327 Register ret = newRegister(); 328 if (c.isCompressed()) { 329 // Set upper 16 bits first. See MacroAssembler::patch_oop(). 330 emitMovz(ret, 0xdead, 16); 331 emitMovk(ret, 0xdead, 0); 332 } else { 333 // 48-bit VA 334 emitMovz(ret, 0xdead, 0); 335 emitMovk(ret, 0xdead, 16); 336 emitMovk(ret, 0xdead, 32); 337 } 338 return ret; 339 } 340 341 @Override emitLoadPointer(Register b, int offset)342 public Register emitLoadPointer(Register b, int offset) { 343 Register ret = newRegister(); 344 emitLoadRegister(ret, AArch64Kind.QWORD, b, offset); 345 return ret; 346 } 347 348 @Override emitLoadNarrowPointer(DataSectionReference ref)349 public Register emitLoadNarrowPointer(DataSectionReference ref) { 350 recordDataPatchInCode(ref); 351 352 Register ret = newRegister(); 353 emitLoadRegister(ret, AArch64Kind.DWORD, 0xdead); 354 return ret; 355 } 356 357 @Override emitLoadPointer(DataSectionReference ref)358 public Register emitLoadPointer(DataSectionReference ref) { 359 recordDataPatchInCode(ref); 360 361 Register ret = newRegister(); 362 emitLoadRegister(ret, AArch64Kind.QWORD, 0xdead); 363 return ret; 364 } 365 emitLoadDouble(Register reg, double c)366 private Register emitLoadDouble(Register reg, double c) { 367 DataSectionReference ref = new DataSectionReference(); 368 ref.setOffset(data.position()); 369 data.emitDouble(c); 370 371 recordDataPatchInCode(ref); 372 emitLoadRegister(scratchRegister, AArch64Kind.QWORD, 0xdead); 373 emitFmov(reg, AArch64Kind.DOUBLE, scratchRegister); 374 return reg; 375 } 376 emitLoadFloat(Register reg, float c)377 private Register emitLoadFloat(Register reg, float c) { 378 DataSectionReference ref = new DataSectionReference(); 379 ref.setOffset(data.position()); 380 data.emitFloat(c); 381 382 recordDataPatchInCode(ref); 383 emitLoadRegister(scratchRegister, AArch64Kind.DWORD, 0xdead); 384 emitFmov(reg, AArch64Kind.SINGLE, scratchRegister); 385 return reg; 386 } 387 388 @Override emitLoadFloat(float c)389 public Register emitLoadFloat(float c) { 390 Register ret = AArch64.v0; 391 return emitLoadFloat(ret, c); 392 } 393 emitLoadLong(Register reg, long c)394 private Register emitLoadLong(Register reg, long c) { 395 emitMovz(reg, (int)(c & 0xffff), 0); 396 emitMovk(reg, (int)((c >> 16) & 0xffff), 16); 397 emitMovk(reg, (int)((c >> 32) & 0xffff), 32); 398 emitMovk(reg, (int)((c >> 48) & 0xffff), 48); 399 return reg; 400 } 401 402 @Override emitLoadLong(long c)403 public Register emitLoadLong(long c) { 404 Register ret = newRegister(); 405 return emitLoadLong(ret, c); 406 } 407 emitLoadInt(Register reg, int c)408 private Register emitLoadInt(Register reg, int c) { 409 emitMovz(reg, (int)(c & 0xffff), 0); 410 emitMovk(reg, (int)((c >> 16) & 0xffff), 16); 411 return reg; 412 } 413 414 @Override emitLoadInt(int c)415 public Register emitLoadInt(int c) { 416 Register ret = newRegister(); 417 return emitLoadInt(ret, c); 418 } 419 420 @Override emitIntArg0()421 public Register emitIntArg0() { 422 return codeCache.getRegisterConfig() 423 .getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int) 424 .get(0); 425 } 426 427 @Override emitIntArg1()428 public Register emitIntArg1() { 429 return codeCache.getRegisterConfig() 430 .getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int) 431 .get(1); 432 } 433 434 @Override emitIntAdd(Register a, Register b)435 public Register emitIntAdd(Register a, Register b) { 436 emitAdd(a, a, b); 437 return a; 438 } 439 440 @Override emitTrap(DebugInfo info)441 public void emitTrap(DebugInfo info) { 442 // Dereference null pointer 443 emitMovz(scratchRegister, 0, 0); 444 recordImplicitException(info); 445 emitLoadRegister(AArch64.zr, AArch64Kind.QWORD, scratchRegister, 0); 446 } 447 448 @Override emitIntRet(Register a)449 public void emitIntRet(Register a) { 450 emitMov(AArch64.r0, a); 451 code.emitInt(0x910003bf); // mov sp, x29 452 code.emitInt(0xa8c27bfd); // ldp x29, x30, [sp], #32 453 code.emitInt(0xd65f03c0); // ret 454 } 455 456 @Override emitFloatRet(Register a)457 public void emitFloatRet(Register a) { 458 assert a == AArch64.v0 : "Unimplemented move " + a; 459 code.emitInt(0x910003bf); // mov sp, x29 460 code.emitInt(0xa8c27bfd); // ldp x29, x30, [sp], #32 461 code.emitInt(0xd65f03c0); // ret 462 } 463 464 @Override emitPointerRet(Register a)465 public void emitPointerRet(Register a) { 466 emitIntRet(a); 467 } 468 469 @Override emitPointerToStack(Register a)470 public StackSlot emitPointerToStack(Register a) { 471 return emitLongToStack(a); 472 } 473 474 @Override emitNarrowPointerToStack(Register a)475 public StackSlot emitNarrowPointerToStack(Register a) { 476 return emitIntToStack(a); 477 } 478 479 @Override emitUncompressPointer(Register compressed, long base, int shift)480 public Register emitUncompressPointer(Register compressed, long base, int shift) { 481 if (shift > 0) { 482 emitShiftLeft(compressed, compressed, shift); 483 } 484 485 if (base != 0) { 486 emitLoadLong(scratchRegister, base); 487 emitAdd(compressed, compressed, scratchRegister); 488 } 489 490 return compressed; 491 } 492 emitDoubleToStack(StackSlot slot, Register a)493 private StackSlot emitDoubleToStack(StackSlot slot, Register a) { 494 emitStoreRegister(a, AArch64Kind.DOUBLE, AArch64.sp, slot.getOffset(frameSize)); 495 return slot; 496 } 497 498 @Override emitDoubleToStack(Register a)499 public StackSlot emitDoubleToStack(Register a) { 500 StackSlot ret = newStackSlot(AArch64Kind.DOUBLE); 501 return emitDoubleToStack(ret, a); 502 } 503 emitFloatToStack(StackSlot slot, Register a)504 private StackSlot emitFloatToStack(StackSlot slot, Register a) { 505 emitStoreRegister(a, AArch64Kind.SINGLE, AArch64.sp, slot.getOffset(frameSize)); 506 return slot; 507 } 508 509 @Override emitFloatToStack(Register a)510 public StackSlot emitFloatToStack(Register a) { 511 StackSlot ret = newStackSlot(AArch64Kind.SINGLE); 512 return emitFloatToStack(ret, a); 513 } 514 emitIntToStack(StackSlot slot, Register a)515 private StackSlot emitIntToStack(StackSlot slot, Register a) { 516 emitStoreRegister(a, AArch64Kind.DWORD, AArch64.sp, slot.getOffset(frameSize)); 517 return slot; 518 } 519 520 @Override emitIntToStack(Register a)521 public StackSlot emitIntToStack(Register a) { 522 StackSlot ret = newStackSlot(AArch64Kind.DWORD); 523 return emitIntToStack(ret, a); 524 } 525 emitLongToStack(StackSlot slot, Register a)526 private StackSlot emitLongToStack(StackSlot slot, Register a) { 527 emitStoreRegister(a, AArch64Kind.QWORD, AArch64.sp, slot.getOffset(frameSize)); 528 return slot; 529 } 530 531 @Override emitLongToStack(Register a)532 public StackSlot emitLongToStack(Register a) { 533 StackSlot ret = newStackSlot(AArch64Kind.QWORD); 534 return emitLongToStack(ret, a); 535 } 536 537 } 538