jdk.graal.compiler.lir.amd64.AMD64Call Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of compiler Show documentation
Show all versions of compiler Show documentation
The GraalVM compiler and the Graal-truffle optimizer.
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.graal.compiler.lir.amd64;
import static jdk.vm.ci.code.ValueUtil.asRegister;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import jdk.graal.compiler.asm.amd64.AMD64MacroAssembler;
import jdk.graal.compiler.code.CompilationResult.MarkId;
import jdk.graal.compiler.core.common.LIRKind;
import jdk.graal.compiler.core.common.spi.ForeignCallLinkage;
import jdk.graal.compiler.debug.Assertions;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.lir.LIRFrameState;
import jdk.graal.compiler.lir.LIRInstructionClass;
import jdk.graal.compiler.lir.LIRValueUtil;
import jdk.graal.compiler.lir.Opcode;
import jdk.graal.compiler.lir.asm.CompilationResultBuilder;
import jdk.graal.compiler.lir.gen.DiagnosticLIRGeneratorTool;
import jdk.vm.ci.amd64.AMD64;
import jdk.vm.ci.amd64.AMD64Kind;
import jdk.vm.ci.code.CallingConvention;
import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.RegisterValue;
import jdk.vm.ci.code.site.Call;
import jdk.vm.ci.meta.AllocatableValue;
import jdk.vm.ci.meta.InvokeTarget;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.Value;
public class AMD64Call {
public abstract static class CallOp extends AMD64LIRInstruction {
public static final LIRInstructionClass TYPE = LIRInstructionClass.create(CallOp.class);
@Def({OperandFlag.REG, OperandFlag.ILLEGAL}) protected Value result;
@Use({OperandFlag.REG, OperandFlag.STACK}) protected Value[] parameters;
@Temp({OperandFlag.REG, OperandFlag.STACK}) protected Value[] temps;
@State protected LIRFrameState state;
protected CallOp(LIRInstructionClass extends CallOp> c, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
super(c);
this.result = result;
this.parameters = parameters;
this.state = state;
this.temps = addStackSlotsToTemporaries(parameters, temps);
assert temps != null;
}
@Override
public boolean destroysCallerSavedRegisters() {
return true;
}
}
public abstract static class MethodCallOp extends CallOp {
public static final LIRInstructionClass TYPE = LIRInstructionClass.create(MethodCallOp.class);
protected final ResolvedJavaMethod callTarget;
protected MethodCallOp(LIRInstructionClass extends MethodCallOp> c, ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
super(c, result, parameters, temps, state);
this.callTarget = callTarget;
}
}
@Opcode("CALL_DIRECT")
public abstract static class DirectCallOp extends MethodCallOp {
public static final LIRInstructionClass TYPE = LIRInstructionClass.create(DirectCallOp.class);
protected DirectCallOp(LIRInstructionClass extends DirectCallOp> c, ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
super(c, callTarget, result, parameters, temps, state);
}
}
@Opcode("CALL_INDIRECT")
public static class IndirectCallOp extends MethodCallOp {
public static final LIRInstructionClass TYPE = LIRInstructionClass.create(IndirectCallOp.class);
@Use({OperandFlag.REG}) protected Value targetAddress;
public IndirectCallOp(ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, Value targetAddress, LIRFrameState state) {
this(TYPE, callTarget, result, parameters, temps, targetAddress, state);
}
protected IndirectCallOp(LIRInstructionClass extends IndirectCallOp> c, ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, Value targetAddress,
LIRFrameState state) {
super(c, callTarget, result, parameters, temps, state);
this.targetAddress = targetAddress;
}
@Override
public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
indirectCall(crb, masm, asRegister(targetAddress), callTarget, state);
}
@Override
public void verify() {
super.verify();
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";
}
}
public abstract static class ForeignCallOp extends CallOp implements DiagnosticLIRGeneratorTool.ZapRegistersAfterInstruction {
public static final LIRInstructionClass TYPE = LIRInstructionClass.create(ForeignCallOp.class);
protected final ForeignCallLinkage callTarget;
public ForeignCallOp(LIRInstructionClass extends ForeignCallOp> c, ForeignCallLinkage callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
super(c, result, parameters, temps, state);
this.callTarget = callTarget;
}
@Override
public boolean destroysCallerSavedRegisters() {
return callTarget.destroysRegisters();
}
@Override
public boolean needsClearUpperVectorRegisters() {
return callTarget.needsClearUpperVectorRegisters();
}
}
@Opcode("NEAR_FOREIGN_CALL")
public static final class DirectNearForeignCallOp extends ForeignCallOp {
public static final LIRInstructionClass TYPE = LIRInstructionClass.create(DirectNearForeignCallOp.class);
public DirectNearForeignCallOp(ForeignCallLinkage linkage, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
super(TYPE, linkage, result, parameters, temps, state);
}
@Override
public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
directCall(crb, masm, callTarget, null, false, state);
}
}
@Opcode("FAR_FOREIGN_CALL")
public static final class DirectFarForeignCallOp extends ForeignCallOp {
public static final LIRInstructionClass TYPE = LIRInstructionClass.create(DirectFarForeignCallOp.class);
@Temp({OperandFlag.REG}) protected AllocatableValue callTemp;
public DirectFarForeignCallOp(ForeignCallLinkage callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state) {
super(TYPE, callTarget, result, parameters, temps, state);
/*
* The register allocator does not support virtual registers that are used at the call
* site, so use a fixed register.
*/
callTemp = AMD64.rax.asValue(LIRKind.value(AMD64Kind.QWORD));
assert LIRValueUtil.differentRegisters(parameters, callTemp);
}
@Override
public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
directCall(crb, masm, callTarget, ((RegisterValue) callTemp).getRegister(), false, state);
}
}
/**
* @param scratch register to use for call target address when emitting a far call
* @param align indicates whether the displacement operand of an immediate call instruction must
* be 4-byte aligned
* @return the position of the emitted call instruction
*/
public static int directCall(CompilationResultBuilder crb, AMD64MacroAssembler masm, InvokeTarget callTarget, Register scratch, boolean align, LIRFrameState info) {
int callPosition;
AMD64MacroAssembler.PostCallAction afterCall = (before, after) -> {
Call call = crb.recordDirectCall(before, after, callTarget, info);
if (align) {
checkCallDisplacementAlignment(crb, before, call);
}
crb.recordExceptionHandlers(after, info);
if (masm.position() == after) {
masm.postCallNop(call);
}
};
if (scratch != null) {
if (align) {
throw new GraalError("register call has no immediate displacement operand to be aligned");
}
// offset might not fit a 32-bit immediate, generate an
// indirect call with a 64-bit immediate
// This is an implicit contract between the backend and the JVMCI code installer. The
// latter expects a mov instruction immediately preceding a call instruction. The jcc
// erratum padding should be inserted before the mov instruction.
callPosition = masm.directCall(afterCall, 0L, scratch, callTarget);
} else {
masm.alignBeforeCall(align, 0);
callPosition = masm.call(afterCall, callTarget);
}
return callPosition;
}
private static final int INLINE_CACHE_MOV_SIZE = 10;
/**
* Emits a direct call instruction, immediately preceded by a mov instruction. This is used for
* emitting HotSpot inline cache call. The runtime will patch the mov instruction to load the
* cached receiver type, which will be used in {@code [Entry Point]} code section for receiver
* type check.
*
* @param nonOopBits placeholder bit pattern for inline cache receiver type patching
*/
public static void directInlineCacheCall(CompilationResultBuilder crb, AMD64MacroAssembler masm, InvokeTarget callTarget, MarkId markId, long nonOopBits, LIRFrameState info) {
masm.alignBeforeCall(true, INLINE_CACHE_MOV_SIZE);
// The mark for an invocation that uses an inline cache must be placed at the
// instruction that loads the Klass from the inline cache.
crb.recordMark(markId);
int movPos = masm.position();
masm.movq(AMD64.rax, nonOopBits);
masm.call((before, after) -> {
assert movPos + INLINE_CACHE_MOV_SIZE == before : Assertions.errorMessage(movPos, before);
Call call = crb.recordDirectCall(before, after, callTarget, info);
checkCallDisplacementAlignment(crb, before, call);
crb.recordExceptionHandlers(after, info);
if (masm.position() == after) {
masm.postCallNop(call);
}
}, callTarget);
}
private static void checkCallDisplacementAlignment(CompilationResultBuilder crb, int before, Call call) throws GraalError {
int opcode = crb.asm.getByte(call.pcOffset);
if (!(opcode == 0xE8)) {
throw new GraalError("unexpected opcode 0x%x at offset %d: %s", opcode, before, call);
}
int callDisplacement = call.pcOffset + 1;
if (callDisplacement % 4 != 0) {
throw new GraalError("displacement for call at offset %d is not 4-byte aligned: %s", call.pcOffset, call);
}
}
public static void directJmp(CompilationResultBuilder crb, AMD64MacroAssembler masm, ForeignCallLinkage target, Register scratch) {
int before;
if (scratch != null) {
// offset might not fit a 32-bit immediate, generate an
// indirect call with a 64-bit immediate
before = masm.directJmp(0L, scratch);
} else {
before = masm.jmp(0, true);
}
crb.recordDirectCall(before, masm.position(), target, null);
}
public static int indirectCall(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register dst, InvokeTarget callTarget, LIRFrameState info) {
return indirectCall(crb, masm, dst, callTarget, info, false);
}
public static int indirectCall(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register dst, InvokeTarget callTarget, LIRFrameState info, CallingConvention.Type callingConventionType) {
return indirectCall(crb, masm, dst, callTarget, info, false, callingConventionType);
}
public static int indirectCall(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register dst, InvokeTarget callTarget, LIRFrameState info, boolean mitigateDecodingAsDirectCall) {
return indirectCall(crb, masm, dst, callTarget, info, mitigateDecodingAsDirectCall, null);
}
/**
* @return the position of the emitted call instruction
*/
public static int indirectCall(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register dst, InvokeTarget callTarget, LIRFrameState info, boolean mitigateDecodingAsDirectCall,
CallingConvention.Type callingConventionType) {
AMD64MacroAssembler.PostCallAction postCallAction = (before, after) -> {
Call call = crb.recordIndirectCall(before, after, callTarget, info);
crb.recordExceptionHandlers(after, info);
if (masm.position() == after) {
masm.postCallNop(call);
}
};
return masm.indirectCall(postCallAction, dst, mitigateDecodingAsDirectCall, callTarget, callingConventionType);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy