1 /*
   2  * Copyright (c) 2011, 2015, 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 package org.graalvm.compiler.lir.amd64;
  24 
  25 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.ILLEGAL;
  26 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
  27 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK;
  28 import static org.graalvm.compiler.lir.LIRValueUtil.differentRegisters;
  29 import static jdk.vm.ci.code.ValueUtil.asRegister;
  30 import static jdk.vm.ci.code.ValueUtil.isRegister;
  31 
  32 import org.graalvm.compiler.asm.amd64.AMD64Assembler.ConditionFlag;
  33 import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler;
  34 import org.graalvm.compiler.core.common.LIRKind;
  35 import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
  36 import org.graalvm.compiler.lir.LIRFrameState;
  37 import org.graalvm.compiler.lir.LIRInstructionClass;
  38 import org.graalvm.compiler.lir.Opcode;
  39 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
  40 import org.graalvm.compiler.lir.gen.DiagnosticLIRGeneratorTool.ZapRegistersAfterInstruction;
  41 
  42 import jdk.vm.ci.amd64.AMD64;
  43 import jdk.vm.ci.amd64.AMD64Kind;
  44 import jdk.vm.ci.code.Register;
  45 import jdk.vm.ci.code.RegisterValue;
  46 import jdk.vm.ci.meta.AllocatableValue;
  47 import jdk.vm.ci.meta.InvokeTarget;
  48 import jdk.vm.ci.meta.ResolvedJavaMethod;
  49 import jdk.vm.ci.meta.Value;
  50 
  51 public class AMD64Call {
  52 
  53     public abstract static class CallOp extends AMD64LIRInstruction {
  54         public static final LIRInstructionClass<CallOp> TYPE = LIRInstructionClass.create(CallOp.class);
  55 
  56         @Def({REG, ILLEGAL}) protected Value result;
  57         @Use({REG, STACK}) protected Value[] parameters;
  58         @Temp({REG, STACK}) protected Value[] temps;
  59         @State protected LIRFrameState state;
  60 
  61         protected CallOp(LIRInstructionClass<? extends CallOp> c, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
  62             super(c);
  63             this.result = result;
  64             this.parameters = parameters;
  65             this.state = state;
  66             this.temps = addStackSlotsToTemporaries(parameters, temps);
  67             assert temps != null;
  68         }
  69 
  70         @Override
  71         public boolean destroysCallerSavedRegisters() {
  72             return true;
  73         }
  74     }
  75 
  76     public abstract static class MethodCallOp extends CallOp {
  77         public static final LIRInstructionClass<MethodCallOp> TYPE = LIRInstructionClass.create(MethodCallOp.class);
  78 
  79         protected final ResolvedJavaMethod callTarget;
  80 
  81         protected MethodCallOp(LIRInstructionClass<? extends MethodCallOp> c, ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
  82             super(c, result, parameters, temps, state);
  83             this.callTarget = callTarget;
  84         }
  85 
  86     }
  87 
  88     @Opcode("CALL_DIRECT")
  89     public static class DirectCallOp extends MethodCallOp {
  90         public static final LIRInstructionClass<DirectCallOp> TYPE = LIRInstructionClass.create(DirectCallOp.class);
  91 
  92         public DirectCallOp(ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
  93             this(TYPE, callTarget, result, parameters, temps, state);
  94         }
  95 
  96         protected DirectCallOp(LIRInstructionClass<? extends DirectCallOp> c, ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
  97             super(c, callTarget, result, parameters, temps, state);
  98         }
  99 
 100         @Override
 101         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 102             directCall(crb, masm, callTarget, null, true, state);
 103         }
 104     }
 105 
 106     @Opcode("CALL_INDIRECT")
 107     public static class IndirectCallOp extends MethodCallOp {
 108         public static final LIRInstructionClass<IndirectCallOp> TYPE = LIRInstructionClass.create(IndirectCallOp.class);
 109 
 110         @Use({REG}) protected Value targetAddress;
 111 
 112         public IndirectCallOp(ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, Value targetAddress, LIRFrameState state) {
 113             this(TYPE, callTarget, result, parameters, temps, targetAddress, state);
 114         }
 115 
 116         protected IndirectCallOp(LIRInstructionClass<? extends IndirectCallOp> c, ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, Value targetAddress,
 117                         LIRFrameState state) {
 118             super(c, callTarget, result, parameters, temps, state);
 119             this.targetAddress = targetAddress;
 120         }
 121 
 122         @Override
 123         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 124             indirectCall(crb, masm, asRegister(targetAddress), callTarget, state);
 125         }
 126 
 127         @Override
 128         public void verify() {
 129             super.verify();
 130             assert isRegister(targetAddress) : "The current register allocator cannot handle variables to be used at call sites, it must be in a fixed register for now";
 131         }
 132     }
 133 
 134     public abstract static class ForeignCallOp extends CallOp implements ZapRegistersAfterInstruction {
 135         public static final LIRInstructionClass<ForeignCallOp> TYPE = LIRInstructionClass.create(ForeignCallOp.class);
 136 
 137         protected final ForeignCallLinkage callTarget;
 138 
 139         public ForeignCallOp(LIRInstructionClass<? extends ForeignCallOp> c, ForeignCallLinkage callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
 140             super(c, result, parameters, temps, state);
 141             this.callTarget = callTarget;
 142         }
 143 
 144         @Override
 145         public boolean destroysCallerSavedRegisters() {
 146             return callTarget.destroysRegisters();
 147         }
 148     }
 149 
 150     @Opcode("NEAR_FOREIGN_CALL")
 151     public static final class DirectNearForeignCallOp extends ForeignCallOp {
 152         public static final LIRInstructionClass<DirectNearForeignCallOp> TYPE = LIRInstructionClass.create(DirectNearForeignCallOp.class);
 153 
 154         public DirectNearForeignCallOp(ForeignCallLinkage linkage, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
 155             super(TYPE, linkage, result, parameters, temps, state);
 156         }
 157 
 158         @Override
 159         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 160             directCall(crb, masm, callTarget, null, false, state);
 161         }
 162     }
 163 
 164     @Opcode("FAR_FOREIGN_CALL")
 165     public static final class DirectFarForeignCallOp extends ForeignCallOp {
 166         public static final LIRInstructionClass<DirectFarForeignCallOp> TYPE = LIRInstructionClass.create(DirectFarForeignCallOp.class);
 167 
 168         @Temp({REG}) protected AllocatableValue callTemp;
 169 
 170         public DirectFarForeignCallOp(ForeignCallLinkage callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
 171             super(TYPE, callTarget, result, parameters, temps, state);
 172             /*
 173              * The register allocator does not support virtual registers that are used at the call
 174              * site, so use a fixed register.
 175              */
 176             callTemp = AMD64.rax.asValue(LIRKind.value(AMD64Kind.QWORD));
 177             assert differentRegisters(parameters, callTemp);
 178         }
 179 
 180         @Override
 181         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 182             directCall(crb, masm, callTarget, ((RegisterValue) callTemp).getRegister(), false, state);
 183         }
 184     }
 185 
 186     public static void directCall(CompilationResultBuilder crb, AMD64MacroAssembler masm, InvokeTarget callTarget, Register scratch, boolean align, LIRFrameState info) {
 187         if (align) {
 188             emitAlignmentForDirectCall(crb, masm);
 189         }
 190         int before = masm.position();
 191         if (scratch != null) {
 192             // offset might not fit a 32-bit immediate, generate an
 193             // indirect call with a 64-bit immediate
 194             masm.movq(scratch, 0L);
 195             masm.call(scratch);
 196         } else {
 197             masm.call();
 198         }
 199         int after = masm.position();
 200         crb.recordDirectCall(before, after, callTarget, info);
 201         crb.recordExceptionHandlers(after, info);
 202         masm.ensureUniquePC();
 203     }
 204 
 205     protected static void emitAlignmentForDirectCall(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
 206         // make sure that the displacement word of the call ends up word aligned
 207         int offset = masm.position();
 208         offset += crb.target.arch.getMachineCodeCallDisplacementOffset();
 209         int modulus = crb.target.wordSize;
 210         if (offset % modulus != 0) {
 211             masm.nop(modulus - offset % modulus);
 212         }
 213     }
 214 
 215     public static void directJmp(CompilationResultBuilder crb, AMD64MacroAssembler masm, InvokeTarget target) {
 216         int before = masm.position();
 217         masm.jmp(0, true);
 218         int after = masm.position();
 219         crb.recordDirectCall(before, after, target, null);
 220         masm.ensureUniquePC();
 221     }
 222 
 223     public static void directConditionalJmp(CompilationResultBuilder crb, AMD64MacroAssembler masm, InvokeTarget target, ConditionFlag cond) {
 224         int before = masm.position();
 225         masm.jcc(cond, 0, true);
 226         int after = masm.position();
 227         crb.recordDirectCall(before, after, target, null);
 228         masm.ensureUniquePC();
 229     }
 230 
 231     public static void indirectCall(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register dst, InvokeTarget callTarget, LIRFrameState info) {
 232         int before = masm.position();
 233         masm.call(dst);
 234         int after = masm.position();
 235         crb.recordIndirectCall(before, after, callTarget, info);
 236         crb.recordExceptionHandlers(after, info);
 237         masm.ensureUniquePC();
 238     }
 239 }