package com.oracle.svm.hosted.phases;
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
import java.util.HashMap;
import java.util.Map;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.lir.LIR;
import org.graalvm.compiler.lir.LIRFrameState;
import org.graalvm.compiler.lir.LIRInstruction;
import org.graalvm.compiler.lir.framemap.FrameMap;
import org.graalvm.compiler.lir.gen.LIRGenerationResult;
import org.graalvm.compiler.lir.phases.PostAllocationOptimizationPhase;
import com.oracle.svm.core.ReservedRegisters;
import com.oracle.svm.core.graal.lir.DeoptEntryOp;
import com.oracle.svm.core.heap.SubstrateReferenceMap;
import com.oracle.svm.hosted.meta.HostedMethod;
import jdk.vm.ci.code.BytecodeFrame;
import jdk.vm.ci.code.StackLockValue;
import jdk.vm.ci.code.StackSlot;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.code.ValueUtil;
import jdk.vm.ci.meta.JavaValue;
public class VerifyDeoptFrameStatesLIRPhase extends PostAllocationOptimizationPhase {
@Override
protected void run(TargetDescription target, LIRGenerationResult lirGenRes, PostAllocationOptimizationContext context) {
new Instance().run(lirGenRes);
}
@Override
protected CharSequence createName() {
return "VerifyDeoptFrameStatesLIRPhase";
}
}
class Instance {
private Map<Integer, Object> allowedStackSlots;
public void run(LIRGenerationResult lirGenRes) {
LIR ir = lirGenRes.getLIR();
DebugContext debug = ir.getDebug();
FrameMap frameMap = lirGenRes.getFrameMap();
for (AbstractBlockBase<?> block : ir.linearScanOrder()) {
for (LIRInstruction op : ir.getLIRforBlock(block)) {
op.forEachState((instruction, state) -> doState(debug, frameMap, instruction, state));
}
}
}
private void doState(DebugContext debug, FrameMap frameMap, LIRInstruction op, LIRFrameState state) {
SubstrateReferenceMap refMap = (SubstrateReferenceMap) state.debugInfo().getReferenceMap();
boolean isDeoptEntry = ((HostedMethod) state.topFrame.getMethod()).compilationInfo.isDeoptEntry(state.topFrame.getBCI(), state.topFrame.duringCall, state.topFrame.rethrowException);
if (op instanceof DeoptEntryOp || (state.topFrame.duringCall && isDeoptEntry)) {
BytecodeFrame frame = state.topFrame;
Map<Integer, Object> allUsedRegisters = refMap.getDebugAllUsedRegisters();
Map<Integer, Object> allUsedStackSlots = refMap.getDebugAllUsedStackSlots();
if (allUsedRegisters != null && !allUsedRegisters.isEmpty()) {
throw shouldNotReachHere("Deoptimization target must not use any registers");
}
if (allUsedStackSlots != null) {
Map<Integer, Object> cleanedStackSlots = new HashMap<>(allUsedStackSlots);
do {
for (JavaValue v : frame.values) {
JavaValue value = v;
if (value instanceof StackLockValue) {
StackLockValue lock = (StackLockValue) value;
assert ValueUtil.isIllegal(lock.getSlot());
value = lock.getOwner();
}
if (value instanceof StackSlot) {
StackSlot stackSlot = (StackSlot) value;
int offset = stackSlot.getOffset(frameMap.totalFrameSize());
debug.log("remove slot %d: %s", offset, stackSlot);
cleanedStackSlots.remove(offset);
} else if (ValueUtil.isConstantJavaValue(value) || ValueUtil.isIllegalJavaValue(value)) {
} else if (ReservedRegisters.singleton().isAllowedInFrameState(value)) {
} else {
throw shouldNotReachHere("unknown value in deopt target: " + value);
}
}
frame = frame.caller();
} while (frame != null);
int firstBci = state.topFrame.getMethod().isSynchronized() ? BytecodeFrame.BEFORE_BCI : 0;
if (state.topFrame.getBCI() == firstBci && state.topFrame.caller() == null && state.topFrame.duringCall == false && state.topFrame.rethrowException == false) {
assert op instanceof DeoptEntryOp;
assert allowedStackSlots == null;
allowedStackSlots = new HashMap<>(cleanedStackSlots);
} else {
if (allowedStackSlots == null) {
allowedStackSlots = new HashMap<>();
}
for (Integer key : allowedStackSlots.keySet()) {
cleanedStackSlots.remove(key);
}
if (!cleanedStackSlots.isEmpty()) {
throw shouldNotReachHere("unknown values in stack slots: method " + state.topFrame.getMethod().toString() + ", op " + op.id() + " " + op + ": " + cleanedStackSlots);
}
}
}
}
}
}