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 jdk.vm.ci.amd64.AMD64;
  27 import jdk.vm.ci.amd64.AMD64Kind;
  28 import jdk.vm.ci.code.CodeCacheProvider;
  29 import jdk.vm.ci.code.DebugInfo;
  30 import jdk.vm.ci.code.Register;
  31 import jdk.vm.ci.code.StackSlot;
  32 import jdk.vm.ci.code.site.ConstantReference;
  33 import jdk.vm.ci.code.site.DataSectionReference;
  34 import jdk.vm.ci.code.test.TestAssembler;
  35 import jdk.vm.ci.code.test.TestHotSpotVMConfig;
  36 import jdk.vm.ci.hotspot.HotSpotCallingConventionType;
  37 import jdk.vm.ci.hotspot.HotSpotConstant;
  38 import jdk.vm.ci.hotspot.HotSpotForeignCallTarget;
  39 import jdk.vm.ci.meta.JavaKind;
  40 import jdk.vm.ci.meta.VMConstant;
  41 
  42 public class AMD64TestAssembler extends TestAssembler {
  43 
  44     public AMD64TestAssembler(CodeCacheProvider codeCache, TestHotSpotVMConfig config) {
  45         super(codeCache, config, 16, 16, AMD64Kind.DWORD, AMD64.rax, AMD64.rcx, AMD64.rdi, AMD64.r8, AMD64.r9, AMD64.r10);
  46     }
  47 
  48     private void emitFatNop() {
  49         // 5 byte NOP:
  50         // NOP DWORD ptr [EAX + EAX*1 + 00H]
  51         code.emitByte(0x0F);
  52         code.emitByte(0x1F);
  53         code.emitByte(0x44);
  54         code.emitByte(0x00);
  55         code.emitByte(0x00);
  56     }
  57 
  58     @Override
  59     public void emitPrologue() {
  60         // WARNING: Initial instruction MUST be 5 bytes or longer so that
  61         // NativeJump::patch_verified_entry will be able to patch out the entry
  62         // code safely.
  63         emitFatNop();
  64         code.emitByte(0x50 | AMD64.rbp.encoding);  // PUSH rbp
  65         emitMove(true, AMD64.rbp, AMD64.rsp);      // MOV rbp, rsp
  66         setDeoptRescueSlot(newStackSlot(AMD64Kind.QWORD));
  67     }
  68 
  69     @Override
  70     public void emitEpilogue() {
  71         recordMark(config.MARKID_DEOPT_HANDLER_ENTRY);
  72         recordCall(new HotSpotForeignCallTarget(config.handleDeoptStub), 5, true, null);
  73         code.emitByte(0xE8); // CALL rel32
  74         code.emitInt(0xDEADDEAD);
  75     }
  76 
  77     @Override
  78     public void emitGrowStack(int size) {
  79         // SUB rsp, size
  80         code.emitByte(0x48);
  81         code.emitByte(0x81);
  82         code.emitByte(0xEC);
  83         code.emitInt(size);
  84     }
  85 
  86     @Override
  87     public Register emitIntArg0() {
  88         return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int).get(0);
  89     }
  90 
  91     @Override
  92     public Register emitIntArg1() {
  93         return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int).get(1);
  94     }
  95 
  96     private void emitREX(boolean w, int r, int x, int b) {
  97         int wrxb = (w ? 0x08 : 0) | ((r >> 3) << 2) | ((x >> 3) << 1) | (b >> 3);
  98         if (wrxb != 0) {
  99             code.emitByte(0x40 | wrxb);
 100         }
 101     }
 102 
 103     private void emitModRMReg(boolean w, int opcode, int r, int m) {
 104         emitREX(w, r, 0, m);
 105         code.emitByte((byte) opcode);
 106         code.emitByte((byte) 0xC0 | ((r & 0x7) << 3) | (m & 0x7));
 107     }
 108 
 109     private void emitModRMMemory(boolean w, int opcode, int r, int b, int offset) {
 110         emitREX(w, r, 0, b);
 111         code.emitByte((byte) opcode);
 112         code.emitByte((byte) 0x80 | ((r & 0x7) << 3) | (b & 0x7));
 113         code.emitInt(offset);
 114     }
 115 
 116     @Override
 117     public Register emitLoadInt(int c) {
 118         Register ret = newRegister();
 119         emitREX(false, 0, 0, ret.encoding);
 120         code.emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r32, imm32
 121         code.emitInt(c);
 122         return ret;
 123     }
 124 
 125     @Override
 126     public Register emitLoadLong(long c) {
 127         Register ret = newRegister();
 128         emitREX(true, 0, 0, ret.encoding);
 129         code.emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r64, imm64
 130         code.emitLong(c);
 131         return ret;
 132     }
 133 
 134     @Override
 135     public Register emitLoadFloat(float c) {
 136         DataSectionReference ref = new DataSectionReference();
 137         ref.setOffset(data.position());
 138         data.emitFloat(c);
 139 
 140         recordDataPatchInCode(ref);
 141         Register ret = AMD64.xmm0;
 142         emitREX(false, ret.encoding, 0, 0);
 143         code.emitByte(0xF3);
 144         code.emitByte(0x0F);
 145         code.emitByte(0x10);                               // MOVSS xmm1, xmm2/m32
 146         code.emitByte(0x05 | ((ret.encoding & 0x7) << 3)); // xmm, [rip+offset]
 147         code.emitInt(0xDEADDEAD);
 148         return ret;
 149     }
 150 
 151     @Override
 152     public Register emitLoadPointer(HotSpotConstant c) {
 153         recordDataPatchInCode(new ConstantReference((VMConstant) c));
 154         if (c.isCompressed()) {
 155             Register ret = newRegister();
 156             emitREX(false, 0, 0, ret.encoding);
 157             code.emitByte(0xB8 | (ret.encoding & 0x7)); // MOV r32, imm32
 158             code.emitInt(0xDEADDEAD);
 159             return ret;
 160         } else {
 161             return emitLoadLong(0xDEADDEADDEADDEADL);
 162         }
 163     }
 164 
 165     private Register emitLoadPointer(DataSectionReference ref, boolean narrow) {
 166         recordDataPatchInCode(ref);
 167         Register ret = newRegister();
 168         emitREX(!narrow, ret.encoding, 0, 0);
 169         code.emitByte(0x8B);                               // MOV r64,r/m64
 170         code.emitByte(0x05 | ((ret.encoding & 0x7) << 3)); // r64, [rip+offset]
 171         code.emitInt(0xDEADDEAD);
 172         return ret;
 173     }
 174 
 175     @Override
 176     public Register emitLoadPointer(DataSectionReference ref) {
 177         return emitLoadPointer(ref, false);
 178     }
 179 
 180     @Override
 181     public Register emitLoadNarrowPointer(DataSectionReference ref) {
 182         return emitLoadPointer(ref, true);
 183     }
 184 
 185     @Override
 186     public Register emitLoadPointer(Register b, int offset) {
 187         Register ret = newRegister();
 188         emitModRMMemory(true, 0x8B, ret.encoding, b.encoding, offset); // MOV r64,r/m64
 189         return ret;
 190     }
 191 
 192     @Override
 193     public StackSlot emitIntToStack(Register a) {
 194         StackSlot ret = newStackSlot(AMD64Kind.DWORD);
 195         // MOV r/m32,r32
 196         emitModRMMemory(false, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16);
 197         return ret;
 198     }
 199 
 200     @Override
 201     public StackSlot emitLongToStack(Register a) {
 202         StackSlot ret = newStackSlot(AMD64Kind.QWORD);
 203         // MOV r/m64,r64
 204         emitModRMMemory(true, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16);
 205         return ret;
 206     }
 207 
 208     @Override
 209     public StackSlot emitFloatToStack(Register a) {
 210         StackSlot ret = newStackSlot(AMD64Kind.SINGLE);
 211         emitREX(false, a.encoding, 0, 0);
 212         code.emitByte(0xF3);
 213         code.emitByte(0x0F);
 214         code.emitByte(0x11);                               // MOVSS xmm2/m32, xmm1
 215         code.emitByte(0x85 | ((a.encoding & 0x7) << 3));   // [rbp+offset]
 216         code.emitInt(ret.getRawOffset() + 16);
 217         return ret;
 218     }
 219 
 220     @Override
 221     public StackSlot emitPointerToStack(Register a) {
 222         StackSlot ret = newStackSlot(AMD64Kind.QWORD);
 223         // MOV r/m64,r64
 224         emitModRMMemory(true, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16);
 225         return ret;
 226     }
 227 
 228     @Override
 229     public StackSlot emitNarrowPointerToStack(Register a) {
 230         StackSlot ret = newStackSlot(AMD64Kind.DWORD);
 231         // MOV r/m32,r32
 232         emitModRMMemory(false, 0x89, a.encoding, AMD64.rbp.encoding, ret.getRawOffset() + 16);
 233         return ret;
 234     }
 235 
 236     @Override
 237     public Register emitUncompressPointer(Register compressed, long base, int shift) {
 238         if (shift > 0) {
 239             emitModRMReg(true, 0xC1, 4, compressed.encoding);
 240             code.emitByte(shift);
 241         }
 242         if (base == 0) {
 243             return compressed;
 244         } else {
 245             Register tmp = emitLoadLong(base);
 246             emitModRMReg(true, 0x03, tmp.encoding, compressed.encoding);
 247             return tmp;
 248         }
 249     }
 250 
 251     @Override
 252     public Register emitIntAdd(Register a, Register b) {
 253         emitModRMReg(false, 0x03, a.encoding, b.encoding);
 254         return a;
 255     }
 256 
 257     private void emitMove(boolean w, Register to, Register from) {
 258         if (to != from) {
 259             emitModRMReg(w, 0x8B, to.encoding, from.encoding);
 260         }
 261     }
 262 
 263     @Override
 264     public void emitIntRet(Register a) {
 265         emitMove(false, AMD64.rax, a);             // MOV eax, ...
 266         emitMove(true, AMD64.rsp, AMD64.rbp);      // MOV rsp, rbp
 267         code.emitByte(0x58 | AMD64.rbp.encoding);  // POP rbp
 268         code.emitByte(0xC3);                       // RET
 269     }
 270 
 271     @Override
 272     public void emitPointerRet(Register a) {
 273         emitMove(true, AMD64.rax, a);              // MOV rax, ...
 274         emitMove(true, AMD64.rsp, AMD64.rbp);      // MOV rsp, rbp
 275         code.emitByte(0x58 | AMD64.rbp.encoding);  // POP rbp
 276         code.emitByte(0xC3);                       // RET
 277     }
 278 
 279     @Override
 280     public void emitTrap(DebugInfo info) {
 281         recordImplicitException(info);
 282         // MOV rax, [0]
 283         code.emitByte(0x8B);
 284         code.emitByte(0x04);
 285         code.emitByte(0x25);
 286         code.emitInt(0);
 287     }
 288 }