src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File
*** old/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java	Mon Mar 20 17:38:49 2017
--- new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java	Mon Mar 20 17:38:49 2017

*** 22,80 **** --- 22,133 ---- */ package org.graalvm.compiler.hotspot.phases; import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Required; ! import org.graalvm.compiler.core.common.PermanentBailoutException; import org.graalvm.compiler.core.common.cfg.Loop; + import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.Debug; + import org.graalvm.compiler.debug.DebugCounter; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.iterators.NodeIterable; import org.graalvm.compiler.loop.LoopsData; import org.graalvm.compiler.loop.phases.LoopTransformations; import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.Verbosity; import org.graalvm.compiler.nodes.AbstractBeginNode; + import org.graalvm.compiler.nodes.AbstractLocalNode; import org.graalvm.compiler.nodes.EntryMarkerNode; import org.graalvm.compiler.nodes.EntryProxyNode; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FrameState; + import org.graalvm.compiler.nodes.LoopBeginNode; + import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.StartNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.cfg.Block; import org.graalvm.compiler.nodes.extended.OSRLocalNode; + import org.graalvm.compiler.nodes.extended.OSRLockNode; + import org.graalvm.compiler.nodes.extended.OSRMonitorEnterNode; import org.graalvm.compiler.nodes.extended.OSRStartNode; + import org.graalvm.compiler.nodes.java.AccessMonitorNode; + import org.graalvm.compiler.nodes.java.MonitorEnterNode; + import org.graalvm.compiler.nodes.java.MonitorExitNode; + import org.graalvm.compiler.nodes.java.MonitorIdNode; import org.graalvm.compiler.nodes.util.GraphUtil; + import org.graalvm.compiler.options.Option; + import org.graalvm.compiler.options.OptionKey; + import org.graalvm.compiler.options.OptionType; + import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.Phase; import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; import jdk.vm.ci.runtime.JVMCICompiler; public class OnStackReplacementPhase extends Phase { + public static class Options { + // @formatter:off + @Option(help = "Deoptimize OSR compiled code when the OSR entry loop is finished " + + "if there is no mature profile available for the rest of the method.", type = OptionType.Debug) + public static final OptionKey<Boolean> DeoptAfterOSR = new OptionKey<>(true); + @Option(help = "Support OSR compilations with locks. If DeoptAfterOSR is true we can per definition not have " + + "unbalaced enter/extis mappings. If DeoptAfterOSR is false insert artificial monitor enters after " + + "the OSRStart to have balanced enter/exits in the graph.", type = OptionType.Debug) + public static final OptionKey<Boolean> SupportOSRWithLocks = new OptionKey<>(true); + // @formatter:on + } + + private static final DebugCounter OsrWithLocksCount = Debug.counter("OSRWithLocks"); + + private static boolean supportOSRWithLocks(OptionValues options) { + return Options.SupportOSRWithLocks.getValue(options); + } + @Override protected void run(StructuredGraph graph) { if (graph.getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI) { // This happens during inlining in a OSR method, because the same phase plan will be // used. assert graph.getNodes(EntryMarkerNode.TYPE).isEmpty(); return; } ! Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement initial at bci %d", graph.getEntryBCI()); + EntryMarkerNode osr; int maxIterations = -1; int iterations = 0; + + final EntryMarkerNode originalOSRNode = getEntryMarker(graph); + final LoopBeginNode originalOSRLoop = osrLoop(originalOSRNode); + final boolean currentOSRWithLocks = osrWithLocks(originalOSRNode); + + if (originalOSRLoop == null) { + /* + * OSR with Locks: We do not have an OSR loop for the original OSR bci. Therefore we + * cannot decide where to deopt and which framestate will be used. In the worst case the + * framestate of the OSR entry would be used. + */ + throw new PermanentBailoutException("OSR compilation without OSR entry loop."); + } + + if (!supportOSRWithLocks(graph.getOptions()) && currentOSRWithLocks) { + throw new PermanentBailoutException("OSR with locks disabled."); + } + do { osr = getEntryMarker(graph); LoopsData loops = new LoopsData(graph); // Find the loop that contains the EntryMarker Loop<Block> l = loops.getCFG().getNodeToBlock().get(osr).getLoop(); if (l == null) { break; } + iterations++; if (maxIterations == -1) { maxIterations = l.getDepth(); } else if (iterations > maxIterations) { throw GraalError.shouldNotReachHere();
*** 102,131 **** --- 155,239 ---- osr.setNext(null); osrStart.setNext(next); graph.setStart(osrStart); osrStart.setStateAfter(osrState); for (int i = 0; i < osrState.localsSize(); i++) { ! ValueNode value = osrState.localAt(i); + Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement after setting OSR start"); ! final int localsSize = osrState.localsSize(); + final int locksSize = osrState.locksSize(); + + for (int i = 0; i < localsSize + locksSize; i++) { + ValueNode value = null; + if (i >= localsSize) { + value = osrState.lockAt(i - localsSize); + } else { + value = osrState.localAt(i); + } if (value instanceof EntryProxyNode) { EntryProxyNode proxy = (EntryProxyNode) value; /* * we need to drop the stamp since the types we see during OSR may be too precise * (if a branch was not parsed for example). */ ! proxy.replaceAndDelete(graph.addOrUnique(new OSRLocalNode(i, proxy.stamp().unrestricted()))); ! Stamp s = proxy.stamp().unrestricted(); + AbstractLocalNode osrLocal = null; + if (i >= localsSize) { + osrLocal = graph.addOrUnique(new OSRLockNode(i - localsSize, s)); + } else { + osrLocal = graph.addOrUnique(new OSRLocalNode(i, s)); + } + proxy.replaceAndDelete(osrLocal); } else { assert value == null || value instanceof OSRLocalNode; } } osr.replaceAtUsages(InputType.Guard, osrStart); assert osr.usages().isEmpty(); + osr.replaceAtUsages(InputType.Guard, osrStart); + Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement after replacing entry proxies"); GraphUtil.killCFG(start); + Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement result"); + new DeadCodeEliminationPhase(Required).apply(graph); + if (currentOSRWithLocks) { + OsrWithLocksCount.increment(); + for (int i = osrState.monitorIdCount() - 1; i >= 0; --i) { + MonitorIdNode id = osrState.monitorIdAt(i); + ValueNode lockedObject = osrState.lockAt(i); + OSRMonitorEnterNode osrMonitorEnter = graph.add(new OSRMonitorEnterNode(lockedObject, id)); + for (Node usage : id.usages()) { + if (usage instanceof AccessMonitorNode) { + AccessMonitorNode access = (AccessMonitorNode) usage; + access.setObject(lockedObject); + } + } + FixedNode oldNext = osrStart.next(); + oldNext.replaceAtPredecessor(null); + osrMonitorEnter.setNext(oldNext); + osrStart.setNext(osrMonitorEnter); + } + Debug.dump(Debug.INFO_LOG_LEVEL, graph, "After inserting OSR monitor enters"); + /* + * Ensure balanced monitorenter - monitorexit + * + * Ensure that there is no monitor exit without a monitor enter in the graph. If there + * is one this can only be done by bytecode as we have the monitor enter before the OSR + * loop but the exit in a path of the loop that must be under a condition, else it will + * throw an IllegalStateException anyway in the 2.iteration + */ + for (MonitorExitNode exit : graph.getNodes(MonitorExitNode.TYPE)) { + MonitorIdNode id = exit.getMonitorId(); + if (id.usages().filter(MonitorEnterNode.class).count() != 1) { + throw new PermanentBailoutException("Unbalanced monitor enter-exit in OSR compilation with locks. Object is locked before the loop but released inside the loop."); + } + } + } Debug.dump(Debug.INFO_LOG_LEVEL, graph, "OnStackReplacement result"); new DeadCodeEliminationPhase(Required).apply(graph); + /* + * There must not be any parameter nodes left after OSR compilation. + */ + assert graph.getNodes(ParameterNode.TYPE).count() == 0 : "OSR Compilation contains references to parameters."; } private static EntryMarkerNode getEntryMarker(StructuredGraph graph) { NodeIterable<EntryMarkerNode> osrNodes = graph.getNodes(EntryMarkerNode.TYPE); EntryMarkerNode osr = osrNodes.first();
*** 133,151 **** --- 241,270 ---- throw new PermanentBailoutException("No OnStackReplacementNode generated"); } if (osrNodes.count() > 1) { throw new GraalError("Multiple OnStackReplacementNodes generated"); } if (osr.stateAfter().locksSize() != 0) { throw new PermanentBailoutException("OSR with locks not supported"); } if (osr.stateAfter().stackSize() != 0) { throw new PermanentBailoutException("OSR with stack entries not supported: %s", osr.stateAfter().toString(Verbosity.Debugger)); } return osr; } + private static LoopBeginNode osrLoop(EntryMarkerNode osr) { + // Check that there is an OSR loop for the OSR begin + LoopsData loops = new LoopsData(osr.graph()); + Loop<Block> l = loops.getCFG().getNodeToBlock().get(osr).getLoop(); + if (l == null) { + return null; + } + return (LoopBeginNode) l.getHeader().getBeginNode(); + } + + private static boolean osrWithLocks(EntryMarkerNode osr) { + return osr.stateAfter().locksSize() != 0; + } + @Override public float codeSizeIncrease() { return 5.0f; } }

src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File