package org.graalvm.compiler.hotspot.amd64;
import static jdk.vm.ci.amd64.AMD64.rax;
import static jdk.vm.ci.amd64.AMD64.rip;
import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC;
import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
import static org.graalvm.compiler.core.common.NumUtil.isInt;
import org.graalvm.compiler.asm.amd64.AMD64Address;
import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler;
import org.graalvm.compiler.core.common.LIRKind;
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
import org.graalvm.compiler.hotspot.HotSpotMarkId;
import org.graalvm.compiler.lir.LIRFrameState;
import org.graalvm.compiler.lir.LIRInstructionClass;
import org.graalvm.compiler.lir.Opcode;
import org.graalvm.compiler.lir.amd64.AMD64LIRInstruction;
import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.RegisterValue;
import jdk.vm.ci.code.site.InfopointReason;
import jdk.vm.ci.meta.AllocatableValue;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.Value;
@Opcode("SAFEPOINT")
public final class AMD64HotSpotSafepointOp extends AMD64LIRInstruction {
public static final LIRInstructionClass<AMD64HotSpotSafepointOp> TYPE = LIRInstructionClass.create(AMD64HotSpotSafepointOp.class);
@State protected LIRFrameState state;
@Temp({OperandFlag.REG, OperandFlag.ILLEGAL}) private AllocatableValue temp;
private final GraalHotSpotVMConfig config;
private final Register thread;
public AMD64HotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, NodeLIRBuilderTool tool, Register thread) {
super(TYPE);
this.state = state;
this.config = config;
this.thread = thread;
if (config.useThreadLocalPolling || isPollingPageFar(config) || ImmutableCode.getValue(tool.getOptions())) {
temp = tool.getLIRGeneratorTool().newVariable(LIRKind.value(tool.getLIRGeneratorTool().target().arch.getWordKind()));
} else {
temp = Value.ILLEGAL;
}
}
@Override
public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm) {
emitCode(crb, asm, config, false, state, thread, temp instanceof RegisterValue ? ((RegisterValue) temp).getRegister() : null);
}
public static void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register thread, Register scratch) {
if (config.useThreadLocalPolling) {
emitThreadLocalPoll(crb, asm, config, atReturn, state, thread, scratch);
} else {
emitGlobalPoll(crb, asm, config, atReturn, state, scratch);
}
}
private static boolean isPollingPageFar(GraalHotSpotVMConfig config) {
final long pollingPageAddress = config.safepointPollingAddress;
return config.forceUnreachable || !isInt(pollingPageAddress - config.codeCacheLowBound) || !isInt(pollingPageAddress - config.codeCacheHighBound);
}
private static void emitGlobalPoll(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register scratch) {
assert !atReturn || state == null : "state is unneeded at return";
if (ImmutableCode.getValue(crb.getOptions())) {
JavaKind hostWordKind = JavaKind.Long;
int alignment = hostWordKind.getBitCount() / Byte.SIZE;
JavaConstant pollingPageAddress = JavaConstant.forIntegerKind(hostWordKind, config.safepointPollingAddress);
if (GeneratePIC.getValue(crb.getOptions())) {
asm.movq(scratch, asm.getPlaceholder(-1));
} else {
asm.movq(scratch, (AMD64Address) crb.recordDataReferenceInCode(pollingPageAddress, alignment));
}
final int pos = asm.position();
crb.recordMark(atReturn ? HotSpotMarkId.POLL_RETURN_FAR : HotSpotMarkId.POLL_FAR);
if (state != null) {
crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT);
}
asm.testl(rax, new AMD64Address(scratch));
} else if (isPollingPageFar(config)) {
asm.movq(scratch, config.safepointPollingAddress);
crb.recordMark(atReturn ? HotSpotMarkId.POLL_RETURN_FAR : HotSpotMarkId.POLL_FAR);
final int pos = asm.position();
if (state != null) {
crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT);
}
asm.testl(rax, new AMD64Address(scratch));
} else {
crb.recordMark(atReturn ? HotSpotMarkId.POLL_RETURN_NEAR : HotSpotMarkId.POLL_NEAR);
final int pos = asm.position();
if (state != null) {
crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT);
}
asm.testl(rax, new AMD64Address(rip, 0));
}
}
private static void emitThreadLocalPoll(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register thread,
Register scratch) {
assert !atReturn || state == null : "state is unneeded at return";
assert config.threadPollingPageOffset >= 0;
asm.movptr(scratch, new AMD64Address(thread, config.threadPollingPageOffset));
crb.recordMark(atReturn ? HotSpotMarkId.POLL_RETURN_FAR : HotSpotMarkId.POLL_FAR);
final int pos = asm.position();
if (state != null) {
crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT);
}
asm.testl(rax, new AMD64Address(scratch));
}
}