package org.graalvm.compiler.hotspot.stubs;
import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_VMCONFIG;
import static org.graalvm.compiler.hotspot.HotSpotBackend.UNPACK_FRAMES;
import static org.graalvm.compiler.hotspot.HotSpotBackend.Options.PreferGraalStubs;
import static org.graalvm.compiler.hotspot.nodes.DeoptimizationFetchUnrollInfoCallNode.fetchUnrollInfo;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.pageSize;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.registerAsWord;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.wordSize;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.writeRegisterAsWord;
import static org.graalvm.compiler.hotspot.stubs.UncommonTrapStub.STACK_BANG_LOCATION;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.compiler.api.replacements.Fold.InjectedParameter;
import org.graalvm.compiler.api.replacements.Snippet;
import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
import org.graalvm.compiler.asm.NumUtil;
import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
import org.graalvm.compiler.graph.Node.NodeIntrinsic;
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
import org.graalvm.compiler.hotspot.nodes.EnterUnpackFramesStackFrameNode;
import org.graalvm.compiler.hotspot.nodes.LeaveCurrentStackFrameNode;
import org.graalvm.compiler.hotspot.nodes.LeaveDeoptimizedStackFrameNode;
import org.graalvm.compiler.hotspot.nodes.LeaveUnpackFramesStackFrameNode;
import org.graalvm.compiler.hotspot.nodes.PushInterpreterFrameNode;
import org.graalvm.compiler.hotspot.nodes.SaveAllRegistersNode;
import org.graalvm.compiler.hotspot.nodes.StubForeignCallNode;
import org.graalvm.compiler.word.Word;
import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.TargetDescription;
public class DeoptimizationStub extends SnippetStub {
private final TargetDescription target;
public DeoptimizationStub(HotSpotProviders providers, TargetDescription target, HotSpotForeignCallLinkage linkage) {
super(DeoptimizationStub.class, "deoptimizationHandler", providers, linkage);
this.target = target;
assert PreferGraalStubs.getValue();
}
@Override
public boolean preservesRegisters() {
return false;
}
@Override
protected Object getConstantParameterValue(int index, String name) {
switch (index) {
case 0:
return providers.getRegisters().getThreadRegister();
case 1:
return providers.getRegisters().getStackPointerRegister();
default:
throw GraalError.shouldNotReachHere("unknown parameter " + name + " at index " + index);
}
}
@Snippet
private static void deoptimizationHandler(@ConstantParameter Register threadRegister, @ConstantParameter Register stackPointerRegister) {
final Word thread = registerAsWord(threadRegister);
final long registerSaver = SaveAllRegistersNode.saveAllRegisters();
final Word unrollBlock = fetchUnrollInfo(registerSaver, deoptimizationUnpackDeopt(INJECTED_VMCONFIG));
deoptimizationCommon(stackPointerRegister, thread, registerSaver, unrollBlock);
}
static void deoptimizationCommon(Register stackPointerRegister, final Word thread, final long registerSaver, final Word unrollBlock) {
LeaveCurrentStackFrameNode.leaveCurrentStackFrame(registerSaver);
final Word initialInfo = unrollBlock.readWord(deoptimizationUnrollBlockInitialInfoOffset(INJECTED_VMCONFIG));
final int sizeOfDeoptimizedFrame = unrollBlock.readInt(deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset(INJECTED_VMCONFIG));
LeaveDeoptimizedStackFrameNode.leaveDeoptimizedStackFrame(sizeOfDeoptimizedFrame, initialInfo);
final int totalFrameSizes = unrollBlock.readInt(deoptimizationUnrollBlockTotalFrameSizesOffset(INJECTED_VMCONFIG));
final int bangPages = NumUtil.roundUp(totalFrameSizes, pageSize()) / pageSize() + stackShadowPages(INJECTED_VMCONFIG);
Word stackPointer = readRegister(stackPointerRegister);
for (int i = 1; i < bangPages; i++) {
stackPointer.writeInt((-i * pageSize()) + stackBias(INJECTED_VMCONFIG), 0, STACK_BANG_LOCATION);
}
final int numberOfFrames = unrollBlock.readInt(deoptimizationUnrollBlockNumberOfFramesOffset(INJECTED_VMCONFIG));
final Word frameSizes = unrollBlock.readWord(deoptimizationUnrollBlockFrameSizesOffset(INJECTED_VMCONFIG));
final Word framePcs = unrollBlock.readWord(deoptimizationUnrollBlockFramePcsOffset(INJECTED_VMCONFIG));
Word senderSp = readRegister(stackPointerRegister);
final int callerAdjustment = unrollBlock.readInt(deoptimizationUnrollBlockCallerAdjustmentOffset(INJECTED_VMCONFIG));
writeRegister(stackPointerRegister, readRegister(stackPointerRegister).subtract(callerAdjustment));
for (int i = 0; i < numberOfFrames; i++) {
final Word frameSize = frameSizes.readWord(i * wordSize());
final Word framePc = framePcs.readWord(i * wordSize());
PushInterpreterFrameNode.pushInterpreterFrame(frameSize, framePc, senderSp, initialInfo);
senderSp = readRegister(stackPointerRegister);
}
final Word framePc = framePcs.readWord(numberOfFrames * wordSize());
final Word senderFp = initialInfo;
EnterUnpackFramesStackFrameNode.enterUnpackFramesStackFrame(framePc, senderSp, senderFp, registerSaver);
final int mode = unrollBlock.readInt(deoptimizationUnrollBlockUnpackKindOffset(INJECTED_VMCONFIG));
unpackFrames(UNPACK_FRAMES, thread, mode);
LeaveUnpackFramesStackFrameNode.leaveUnpackFramesStackFrame(registerSaver);
}
private static Word readRegister(Register register) {
return registerAsWord(register, false, false);
}
private static void writeRegister(Register register, Word value) {
writeRegisterAsWord(register, value);
}
@Fold
static int stackShadowPages(@InjectedParameter GraalHotSpotVMConfig config) {
return config.useStackBanging ? config.stackShadowPages : 0;
}
@Deprecated
@Fold
static int stackBias(@InjectedParameter GraalHotSpotVMConfig config) {
return config.stackBias;
}
@Fold
static int deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset(@InjectedParameter GraalHotSpotVMConfig config) {
return config.deoptimizationUnrollBlockSizeOfDeoptimizedFrameOffset;
}
@Fold
static int deoptimizationUnrollBlockCallerAdjustmentOffset(@InjectedParameter GraalHotSpotVMConfig config) {
return config.deoptimizationUnrollBlockCallerAdjustmentOffset;
}
@Fold
static int deoptimizationUnrollBlockNumberOfFramesOffset(@InjectedParameter GraalHotSpotVMConfig config) {
return config.deoptimizationUnrollBlockNumberOfFramesOffset;
}
@Fold
static int deoptimizationUnrollBlockTotalFrameSizesOffset(@InjectedParameter GraalHotSpotVMConfig config) {
return config.deoptimizationUnrollBlockTotalFrameSizesOffset;
}
@Fold
static int deoptimizationUnrollBlockUnpackKindOffset(@InjectedParameter GraalHotSpotVMConfig config) {
return config.deoptimizationUnrollBlockUnpackKindOffset;
}
@Fold
static int deoptimizationUnrollBlockFrameSizesOffset(@InjectedParameter GraalHotSpotVMConfig config) {
return config.deoptimizationUnrollBlockFrameSizesOffset;
}
@Fold
static int deoptimizationUnrollBlockFramePcsOffset(@InjectedParameter GraalHotSpotVMConfig config) {
return config.deoptimizationUnrollBlockFramePcsOffset;
}
@Fold
static int deoptimizationUnrollBlockInitialInfoOffset(@InjectedParameter GraalHotSpotVMConfig config) {
return config.deoptimizationUnrollBlockInitialInfoOffset;
}
@Fold
static int deoptimizationUnpackDeopt(@InjectedParameter GraalHotSpotVMConfig config) {
return config.deoptimizationUnpackDeopt;
}
@Fold
static int deoptimizationUnpackUncommonTrap(@InjectedParameter GraalHotSpotVMConfig config) {
return config.deoptimizationUnpackUncommonTrap;
}
@NodeIntrinsic(value = StubForeignCallNode.class, setStampFromReturnType = true)
public static native int unpackFrames(@ConstantNodeParameter ForeignCallDescriptor unpackFrames, Word thread, int mode);
}