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