1/* 2 * Copyright (c) 2015, 2016, 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 24package jdk.vm.ci.code.test.amd64; 25 26import static jdk.vm.ci.amd64.AMD64.xmm0; 27 28import jdk.vm.ci.amd64.AMD64; 29import jdk.vm.ci.amd64.AMD64Kind; 30import jdk.vm.ci.code.CallingConvention; 31import jdk.vm.ci.code.CodeCacheProvider; 32import jdk.vm.ci.code.DebugInfo; 33import jdk.vm.ci.code.Register; 34import jdk.vm.ci.code.RegisterValue; 35import jdk.vm.ci.code.StackSlot; 36import jdk.vm.ci.code.site.ConstantReference; 37import jdk.vm.ci.code.site.DataSectionReference; 38import jdk.vm.ci.code.test.TestAssembler; 39import jdk.vm.ci.code.test.TestHotSpotVMConfig; 40import jdk.vm.ci.hotspot.HotSpotCallingConventionType; 41import jdk.vm.ci.hotspot.HotSpotConstant; 42import jdk.vm.ci.hotspot.HotSpotForeignCallTarget; 43import jdk.vm.ci.meta.AllocatableValue; 44import jdk.vm.ci.meta.JavaKind; 45import jdk.vm.ci.meta.VMConstant; 46 47public class AMD64TestAssembler extends TestAssembler { 48 49 private static final Register scratchRegister = AMD64.r12; 50 private static final Register doubleScratch = AMD64.xmm15; 51 52 public AMD64TestAssembler(CodeCacheProvider codeCache, TestHotSpotVMConfig config) { 53 super(codeCache, config, 16, 16, AMD64Kind.DWORD, AMD64.rax, AMD64.rcx, AMD64.rdi, AMD64.r8, AMD64.r9, AMD64.r10); 54 } 55 56 private void emitFatNop() { 57 // 5 byte NOP: 58 // NOP DWORD ptr [EAX + EAX*1 + 00H] 59 code.emitByte(0x0F); 60 code.emitByte(0x1F); 61 code.emitByte(0x44); 62 code.emitByte(0x00); 63 code.emitByte(0x00); 64 } 65 66 @Override 67 public void emitPrologue() { 68 // WARNING: Initial instruction MUST be 5 bytes or longer so that 69 // NativeJump::patch_verified_entry will be able to patch out the entry 70 // code safely. 71 emitFatNop(); 72 code.emitByte(0x50 | AMD64.rbp.encoding); // PUSH rbp 73 emitMove(true, AMD64.rbp, AMD64.rsp); // MOV rbp, rsp 74 setDeoptRescueSlot(newStackSlot(AMD64Kind.QWORD)); 75 } 76 77 @Override 78 public void emitEpilogue() { 79 recordMark(config.MARKID_DEOPT_HANDLER_ENTRY); 80 recordCall(new HotSpotForeignCallTarget(config.handleDeoptStub), 5, true, null); 81 code.emitByte(0xE8); // CALL rel32 82 code.emitInt(0xDEADDEAD); 83 } 84 85 @Override 86 public void emitGrowStack(int size) { 87 // SUB rsp, size 88 code.emitByte(0x48); 89 code.emitByte(0x81); 90 code.emitByte(0xEC); 91 code.emitInt(size); 92 } 93 94 @Override 95 public Register emitIntArg0() { 96 return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int).get(0); 97 } 98 99 @Override 100 public Register emitIntArg1() { 101 return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int).get(1); 102 } 103 104 private void emitREX(boolean w, int r, int x, int b) { 105 int wrxb = (w ? 0x08 : 0) | ((r >> 3) << 2) | ((x >> 3) << 1) | (b >> 3); 106 if (wrxb != 0) { 107 code.emitByte(0x40 | wrxb); 108 } 109 } 110 111 private void emitModRMReg(boolean w, int opcode, int r, int m) { 112 emitREX(w, r, 0, m); 113 code.emitByte((byte) opcode); 114 code.emitByte((byte) 0xC0 | ((r & 0x7) << 3) | (m & 0x7)); 115 } 116 117 private void emitModRMMemory(boolean w, int opcode, int r, int b, int offset) { 118 emitREX(w, r, 0, b); 119 code.emitByte((byte) opcode); 120 code.emitByte((byte) 0x80 | ((r & 0x7) << 3) | (b & 0x7)); 121 code.emitInt(offset); 122 } 123 124 @Override 125 public Register emitLoadInt(int c) { 126 Register ret = newRegister(); 127 return emitLoadInt(ret, c); 128 } 129 130 public Register emitLoadInt(Register ret, int c) { 131 emitREX(false, 0, 0, ret.encoding); 132 code.emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r32, imm32 133 code.emitInt(c); 134 return ret; 135 } 136 137 @Override 138 public Register emitLoadLong(long c) { 139 Register ret = newRegister(); 140 return emitLoadLong(ret, c); 141 } 142 143 public Register emitLoadLong(Register ret, long c) { 144 emitREX(true, 0, 0, ret.encoding); 145 code.emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r64, imm64 146 code.emitLong(c); 147 return ret; 148 } 149 150 @Override 151 public Register emitLoadFloat(float c) { 152 Register ret = AMD64.xmm0; 153 return emitLoadFloat(ret, c); 154 } 155 156 public Register emitLoadFloat(Register ret, float c) { 157 DataSectionReference ref = new DataSectionReference(); 158 ref.setOffset(data.position()); 159 data.emitFloat(c); 160 161 recordDataPatchInCode(ref); 162 emitREX(false, ret.encoding, 0, 0); 163 code.emitByte(0xF3); 164 code.emitByte(0x0F); 165 code.emitByte(0x10); // MOVSS xmm1, xmm2/m32 166 code.emitByte(0x05 | ((ret.encoding & 0x7) << 3)); // xmm, [rip+offset] 167 code.emitInt(0xDEADDEAD); 168 return ret; 169 } 170 171 public Register emitLoadDouble(double c) { 172 Register ret = AMD64.xmm0; 173 return emitLoadDouble(ret, c); 174 } 175 176 public Register emitLoadDouble(Register ret, double c) { 177 DataSectionReference ref = new DataSectionReference(); 178 ref.setOffset(data.position()); 179 data.emitDouble(c); 180 181 recordDataPatchInCode(ref); 182 emitREX(false, ret.encoding, 0, 0); 183 code.emitByte(0xF2); 184 code.emitByte(0x0F); 185 code.emitByte(0x10); // MOVSD xmm1, xmm2/m32 186 code.emitByte(0x05 | ((ret.encoding & 0x7) << 3)); // xmm, [rip+offset] 187 code.emitInt(0xDEADDEAD); 188 return ret; 189 } 190 191 @Override 192 public Register emitLoadPointer(HotSpotConstant c) { 193 recordDataPatchInCode(new ConstantReference((VMConstant) c)); 194 if (c.isCompressed()) { 195 Register ret = newRegister(); 196 emitREX(false, 0, 0, ret.encoding); 197 code.emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r32, imm32 198 code.emitInt(0xDEADDEAD); 199 return ret; 200 } else { 201 return emitLoadLong(0xDEADDEADDEADDEADL); 202 } 203 } 204 205 private Register emitLoadPointer(DataSectionReference ref, boolean narrow) { 206 recordDataPatchInCode(ref); 207 Register ret = newRegister(); 208 emitREX(!narrow, ret.encoding, 0, 0); 209 code.emitByte(0x8B); // MOV r64,r/m64 210 code.emitByte(0x05 | ((ret.encoding & 0x7) << 3)); // r64, [rip+offset] 211 code.emitInt(0xDEADDEAD); 212 return ret; 213 } 214 215 @Override 216 public Register emitLoadPointer(DataSectionReference ref) { 217 return emitLoadPointer(ref, false); 218 } 219 220 @Override 221 public Register emitLoadNarrowPointer(DataSectionReference ref) { 222 return emitLoadPointer(ref, true); 223 } 224 225 @Override 226 public Register emitLoadPointer(Register b, int offset) { 227 Register ret = newRegister(); 228 emitModRMMemory(true, 0x8B, ret.encoding, b.encoding, offset); // MOV r64,r/m64 229 return ret; 230 } 231 232 private int getAdjustedOffset(StackSlot ret) { 233 if (ret.getRawOffset() < 0) { 234 return ret.getRawOffset() + 16; 235 } else { 236 return -(frameSize - ret.getRawOffset()) + 16; 237 } 238 } 239 240 @Override 241 public StackSlot emitIntToStack(Register a) { 242 StackSlot ret = newStackSlot(AMD64Kind.DWORD); 243 return emitIntToStack(ret, a); 244 } 245 246 public StackSlot emitIntToStack(StackSlot ret, Register a) { 247 // MOV r/m32,r32 248 emitModRMMemory(false, 0x89, a.encoding, AMD64.rbp.encoding, getAdjustedOffset(ret)); 249 return ret; 250 } 251 252 @Override 253 public StackSlot emitLongToStack(Register a) { 254 StackSlot ret = newStackSlot(AMD64Kind.QWORD); 255 return emitLongToStack(ret, a); 256 } 257 258 public StackSlot emitLongToStack(StackSlot ret, Register a) { 259 // MOV r/m64,r64 260 emitModRMMemory(true, 0x89, a.encoding, AMD64.rbp.encoding, getAdjustedOffset(ret)); 261 return ret; 262 } 263 264 @Override 265 public StackSlot emitFloatToStack(Register a) { 266 StackSlot ret = newStackSlot(AMD64Kind.SINGLE); 267 return emitFloatToStack(ret, a); 268 } 269 270 public StackSlot emitFloatToStack(StackSlot ret, Register a) { 271 emitREX(false, a.encoding, 0, 0); 272 code.emitByte(0xF3); 273 code.emitByte(0x0F); 274 code.emitByte(0x11); // MOVSS xmm2/m32, xmm1 275 code.emitByte(0x85 | ((a.encoding & 0x7) << 3)); // [rbp+offset] 276 code.emitInt(getAdjustedOffset(ret)); 277 return ret; 278 } 279 280 @Override 281 public StackSlot emitDoubleToStack(Register a) { 282 StackSlot ret = newStackSlot(AMD64Kind.DOUBLE); 283 return emitDoubleToStack(ret, a); 284 } 285 286 public StackSlot emitDoubleToStack(StackSlot ret, Register a) { 287 emitREX(false, a.encoding, 0, 0); 288 code.emitByte(0xF2); 289 code.emitByte(0x0F); 290 code.emitByte(0x11); // MOVSD xmm2/m32, xmm1 291 code.emitByte(0x85 | ((a.encoding & 0x7) << 3)); // [rbp+offset] 292 code.emitInt(getAdjustedOffset(ret)); 293 return ret; 294 } 295 296 @Override 297 public StackSlot emitPointerToStack(Register a) { 298 StackSlot ret = newStackSlot(AMD64Kind.QWORD); 299 // MOV r/m64,r64 300 emitModRMMemory(true, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16); 301 return ret; 302 } 303 304 @Override 305 public StackSlot emitNarrowPointerToStack(Register a) { 306 StackSlot ret = newStackSlot(AMD64Kind.DWORD); 307 // MOV r/m32,r32 308 emitModRMMemory(false, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16); 309 return ret; 310 } 311 312 @Override 313 public Register emitUncompressPointer(Register compressed, long base, int shift) { 314 if (shift > 0) { 315 emitModRMReg(true, 0xC1, 4, compressed.encoding); 316 code.emitByte(shift); 317 } 318 if (base == 0) { 319 return compressed; 320 } else { 321 Register tmp = emitLoadLong(base); 322 emitModRMReg(true, 0x03, tmp.encoding, compressed.encoding); 323 return tmp; 324 } 325 } 326 327 @Override 328 public Register emitIntAdd(Register a, Register b) { 329 emitModRMReg(false, 0x03, a.encoding, b.encoding); 330 return a; 331 } 332 333 private void emitMove(boolean w, Register to, Register from) { 334 if (to != from) { 335 emitModRMReg(w, 0x8B, to.encoding, from.encoding); 336 } 337 } 338 339 @Override 340 public void emitIntRet(Register a) { 341 emitMove(false, AMD64.rax, a); // MOV eax, ... 342 emitMove(true, AMD64.rsp, AMD64.rbp); // MOV rsp, rbp 343 code.emitByte(0x58 | AMD64.rbp.encoding); // POP rbp 344 code.emitByte(0xC3); // RET 345 } 346 347 @Override 348 public void emitFloatRet(Register a) { 349 assert a == xmm0 : "Unimplemented move " + a; 350 emitMove(true, AMD64.rsp, AMD64.rbp); // MOV rsp, rbp 351 code.emitByte(0x58 | AMD64.rbp.encoding); // POP rbp 352 code.emitByte(0xC3); // RET 353 } 354 355 @Override 356 public void emitPointerRet(Register a) { 357 emitMove(true, AMD64.rax, a); // MOV rax, ... 358 emitMove(true, AMD64.rsp, AMD64.rbp); // MOV rsp, rbp 359 code.emitByte(0x58 | AMD64.rbp.encoding); // POP rbp 360 code.emitByte(0xC3); // RET 361 } 362 363 @Override 364 public void emitTrap(DebugInfo info) { 365 recordImplicitException(info); 366 // MOV rax, [0] 367 code.emitByte(0x8B); 368 code.emitByte(0x04); 369 code.emitByte(0x25); 370 code.emitInt(0); 371 } 372 373 @Override 374 public void emitLoad(AllocatableValue av, Object prim) { 375 if (av instanceof RegisterValue) { 376 Register reg = ((RegisterValue) av).getRegister(); 377 if (prim instanceof Float) { 378 emitLoadFloat(reg, (Float) prim); 379 } else if (prim instanceof Double) { 380 emitLoadDouble(reg, (Double) prim); 381 } else if (prim instanceof Integer) { 382 emitLoadInt(reg, (Integer) prim); 383 } else if (prim instanceof Long) { 384 emitLoadLong(reg, (Long) prim); 385 } 386 } else if (av instanceof StackSlot) { 387 StackSlot slot = (StackSlot) av; 388 if (prim instanceof Float) { 389 emitFloatToStack(slot, emitLoadFloat(doubleScratch, (Float) prim)); 390 } else if (prim instanceof Double) { 391 emitDoubleToStack(slot, emitLoadDouble(doubleScratch, (Double) prim)); 392 } else if (prim instanceof Integer) { 393 emitIntToStack(slot, emitLoadInt(scratchRegister, (Integer) prim)); 394 } else if (prim instanceof Long) { 395 emitLongToStack(slot, emitLoadLong(scratchRegister, (Long) prim)); 396 } else { 397 assert false : "Unimplemented"; 398 } 399 } else { 400 throw new IllegalArgumentException("Unknown value " + av); 401 } 402 } 403 404 @Override 405 public void emitCallPrologue(CallingConvention cc, Object... prim) { 406 emitGrowStack(cc.getStackSize()); 407 frameSize += cc.getStackSize(); 408 AllocatableValue[] args = cc.getArguments(); 409 // Do the emission in reverse, this avoids register collisons of xmm0 - which is used a 410 // scratch register when putting arguments on the stack. 411 for (int i = args.length - 1; i >= 0; i--) { 412 emitLoad(args[i], prim[i]); 413 } 414 } 415 416 @Override 417 public void emitCall(long addr) { 418 Register target = emitLoadLong(addr); 419 code.emitByte(0xFF); // CALL r/m64 420 int enc = target.encoding; 421 if (enc >= 8) { 422 code.emitByte(0x41); 423 enc -= 8; 424 } 425 code.emitByte(0xD0 | enc); 426 } 427 428 @Override 429 public void emitCallEpilogue(CallingConvention cc) { 430 emitGrowStack(-cc.getStackSize()); 431 frameSize -= cc.getStackSize(); 432 } 433} 434