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).get(0);
  94     }
  95 
  96     @Override
  97     public Register emitIntArg1() {
  98         return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int).get(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     @Override
 274     public StackSlot emitDoubleToStack(Register a) {
 275         StackSlot ret = newStackSlot(AMD64Kind.DOUBLE);
 276         return emitDoubleToStack(ret, a);
 277     }
 278 
 279     public StackSlot emitDoubleToStack(StackSlot ret, Register a) {
 280         emitREX(false, a.encoding, 0, 0);
 281         code.emitByte(0xF2);
 282         code.emitByte(0x0F);
 283         code.emitByte(0x11);                               // MOVSD xmm2/m32, xmm1
 284         code.emitByte(0x85 | ((a.encoding & 0x7) << 3));   // [rbp+offset]
 285         if (ret.getRawOffset() < 0) {
 286             code.emitInt(ret.getRawOffset() + 16);
 287         } else {
 288             code.emitInt(-(frameSize - ret.getRawOffset()) + 16);
 289         }
 290         return ret;
 291     }
 292 
 293     @Override
 294     public StackSlot emitPointerToStack(Register a) {
 295         StackSlot ret = newStackSlot(AMD64Kind.QWORD);
 296         // MOV r/m64,r64
 297         emitModRMMemory(true, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16);
 298         return ret;
 299     }
 300 
 301     @Override
 302     public StackSlot emitNarrowPointerToStack(Register a) {
 303         StackSlot ret = newStackSlot(AMD64Kind.DWORD);
 304         // MOV r/m32,r32
 305         emitModRMMemory(false, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16);
 306         return ret;
 307     }
 308 
 309     @Override
 310     public Register emitUncompressPointer(Register compressed, long base, int shift) {
 311         if (shift > 0) {
 312             emitModRMReg(true, 0xC1, 4, compressed.encoding);
 313             code.emitByte(shift);
 314         }
 315         if (base == 0) {
 316             return compressed;
 317         } else {
 318             Register tmp = emitLoadLong(base);
 319             emitModRMReg(true, 0x03, tmp.encoding, compressed.encoding);
 320             return tmp;
 321         }
 322     }
 323 
 324     @Override
 325     public Register emitIntAdd(Register a, Register b) {
 326         emitModRMReg(false, 0x03, a.encoding, b.encoding);
 327         return a;
 328     }
 329 
 330     private void emitMove(boolean w, Register to, Register from) {
 331         if (to != from) {
 332             emitModRMReg(w, 0x8B, to.encoding, from.encoding);
 333         }
 334     }
 335 
 336     @Override
 337     public void emitIntRet(Register a) {
 338         emitMove(false, AMD64.rax, a);             // MOV eax, ...
 339         emitMove(true, AMD64.rsp, AMD64.rbp);      // MOV rsp, rbp
 340         code.emitByte(0x58 | AMD64.rbp.encoding);  // POP rbp
 341         code.emitByte(0xC3);                       // RET
 342     }
 343 
 344     @Override
 345     public void emitFloatRet(Register a) {
 346         assert a == xmm0 : "Unimplemented move " + a;
 347         emitMove(true, AMD64.rsp, AMD64.rbp);      // MOV rsp, rbp
 348         code.emitByte(0x58 | AMD64.rbp.encoding);  // POP rbp
 349         code.emitByte(0xC3);                       // RET
 350     }
 351 
 352     @Override
 353     public void emitPointerRet(Register a) {
 354         emitMove(true, AMD64.rax, a);              // MOV rax, ...
 355         emitMove(true, AMD64.rsp, AMD64.rbp);      // MOV rsp, rbp
 356         code.emitByte(0x58 | AMD64.rbp.encoding);  // POP rbp
 357         code.emitByte(0xC3);                       // RET
 358     }
 359 
 360     @Override
 361     public void emitTrap(DebugInfo info) {
 362         recordImplicitException(info);
 363         // MOV rax, [0]
 364         code.emitByte(0x8B);
 365         code.emitByte(0x04);
 366         code.emitByte(0x25);
 367         code.emitInt(0);
 368     }
 369 
 370     @Override
 371     public void emitLoad(AllocatableValue av, Object prim) {
 372         if (av instanceof RegisterValue) {
 373             Register reg = ((RegisterValue) av).getRegister();
 374             if (prim instanceof Float) {
 375                 emitLoadFloat(reg, (Float) prim);
 376             } else if (prim instanceof Double) {
 377                 emitLoadDouble(reg, (Double) prim);
 378             } else if (prim instanceof Integer) {
 379                 emitLoadInt(reg, (Integer) prim);
 380             } else if (prim instanceof Long) {
 381                 emitLoadLong(reg, (Long) prim);
 382             }
 383         } else if (av instanceof StackSlot) {
 384             StackSlot slot = (StackSlot) av;
 385             if (prim instanceof Float) {
 386                 emitFloatToStack(slot, emitLoadFloat((Float) prim));
 387             } else if (prim instanceof Double) {
 388                 emitDoubleToStack(slot, emitLoadDouble((Double) prim));
 389             } else if (prim instanceof Integer) {
 390                 emitIntToStack(slot, emitLoadInt((Integer) prim));
 391             } else if (prim instanceof Long) {
 392                 emitLongToStack(slot, emitLoadLong((Long) prim));
 393             }
 394             assert false : "Unimplemented";
 395         } else {
 396             throw new IllegalArgumentException("Unknown value " + av);
 397         }
 398     }
 399 
 400     @Override
 401     public void emitCallPrologue(CallingConvention cc, Object... prim) {
 402         emitGrowStack(cc.getStackSize());
 403         frameSize += cc.getStackSize();
 404         AllocatableValue[] args = cc.getArguments();
 405         // Do the emission in reverse, this avoids register collisons of xmm0 - which is used a
 406         // scratch register when putting arguments on the stack.
 407         for (int i = args.length - 1; i >= 0; i--) {
 408             emitLoad(args[i], prim[i]);
 409         }
 410     }
 411 
 412     @Override
 413     public void emitCall(long addr) {
 414         Register target = emitLoadLong(addr);
 415         code.emitByte(0xFF); // CALL r/m64
 416         int enc = target.encoding;
 417         if (enc >= 8) {
 418             code.emitByte(0x41);
 419             enc -= 8;
 420         }
 421         code.emitByte(0xD0 | enc);
 422     }
 423 
 424     @Override
 425     public void emitCallEpilogue(CallingConvention cc) {
 426         emitGrowStack(-cc.getStackSize());
 427         frameSize -= cc.getStackSize();
 428     }
 429 }