# HG changeset patch # User Doug Simon # Date 1348688862 -7200 # Node ID c9f45d2d96cfbaebc27ab4b4bd1fbf2ffb11ac01 # Parent 9214d9ff2fb93c03176b74b78325e287c3d6f448 fixed bug in monitor snippets; they are now used for general lowering of monitor operations added support for runtime checking balanced monitors in compiled code expanded debug tracing for monitor snippets diff -r 9214d9ff2fb9 -r c9f45d2d96cf graal/com.oracle.graal.compiler.phases/src/com/oracle/graal/compiler/GraalOptions.java --- a/graal/com.oracle.graal.compiler.phases/src/com/oracle/graal/compiler/GraalOptions.java Wed Sep 26 21:45:20 2012 +0200 +++ b/graal/com.oracle.graal.compiler.phases/src/com/oracle/graal/compiler/GraalOptions.java Wed Sep 26 21:47:42 2012 +0200 @@ -267,7 +267,7 @@ public static String HIRLowerInstanceOf = ""; public static String HIRLowerNewInstance = ""; public static String HIRLowerNewArray = ""; - public static String HIRLowerMonitors = "MonitorTest"; + public static String HIRLowerMonitors = ""; public static String HIRLowerNewMultiArray = ""; /** diff -r 9214d9ff2fb9 -r c9f45d2d96cf graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorCounterNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorCounterNode.java Wed Sep 26 21:47:42 2012 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012, 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. + * + * 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 com.oracle.graal.hotspot.nodes; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.gen.*; +import com.oracle.graal.compiler.target.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.snippets.*; + +/** + * Node that is used to maintain a stack based counter of how many locks + * are currently held. + */ +public final class MonitorCounterNode extends FloatingNode implements LIRGenLowerable { + + public MonitorCounterNode(Kind wordKind) { + super(StampFactory.forWord(wordKind, true)); + } + + @Override + public void generate(LIRGenerator gen) { + assert graph().getNodes().filter(MonitorCounterNode.class).count() == 1 : "monitor counters not canonicalized to single instance"; + StackSlot counter = gen.frameMap().allocateStackBlock(gen.target().wordSize, false); + Value result = gen.emitLea(counter); + gen.setResult(this, result); + } + + + @SuppressWarnings("unused") + @NodeIntrinsic + public static Word counter(@ConstantNodeParameter Kind wordKind) { + throw new UnsupportedOperationException(); + } + +} diff -r 9214d9ff2fb9 -r c9f45d2d96cf graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/MonitorSnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/MonitorSnippets.java Wed Sep 26 21:45:20 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/MonitorSnippets.java Wed Sep 26 21:47:42 2012 +0200 @@ -26,18 +26,21 @@ import static com.oracle.graal.hotspot.nodes.DirectCompareAndSwapNode.*; import static com.oracle.graal.hotspot.nodes.EndLockScopeNode.*; import static com.oracle.graal.hotspot.snippets.HotSpotSnippetUtils.*; -import static com.oracle.graal.snippets.SnippetTemplate.Arguments.*; import static com.oracle.graal.snippets.nodes.DirectObjectStoreNode.*; import java.util.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.util.*; import com.oracle.graal.graph.*; import com.oracle.graal.graph.Node.NodeIntrinsic; +import com.oracle.graal.graph.iterators.*; import com.oracle.graal.hotspot.nodes.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.snippets.*; import com.oracle.graal.snippets.Snippet.ConstantParameter; @@ -45,6 +48,7 @@ import com.oracle.graal.snippets.SnippetTemplate.AbstractTemplates; import com.oracle.graal.snippets.SnippetTemplate.Arguments; import com.oracle.graal.snippets.SnippetTemplate.Key; +import com.oracle.graal.snippets.nodes.*; /** * Snippets used for implementing the monitorenter and monitorexit instructions. @@ -56,32 +60,19 @@ public class MonitorSnippets implements SnippetsInterface { /** - * Monitor operations on objects whose type contains this substring will be logged. + * Monitor operations on objects whose type contains this substring will be traced. */ - private static final String LOG_TYPE = System.getProperty("graal.monitorsnippets.log"); - - private static void log(boolean enabled, String action, Object object) { - if (enabled) { - Log.print(action); - Log.print(' '); - Log.printlnObject(object); - } - } + private static final String TRACE_TYPE_FILTER = System.getProperty("graal.monitors.trace.typeFilter"); /** - * Leaving the breakpoint code in provides a good example of how to use - * {@link BreakpointNode} as an intrinsic. + * Monitor operations in methods whose fully qualified name contains this substring will be traced. */ - private static final boolean ENABLE_BREAKPOINT = false; + private static final String TRACE_METHOD_FILTER = System.getProperty("graal.monitors.trace.methodFilter"); - @SuppressWarnings("unused") - @NodeIntrinsic(BreakpointNode.class) - static void bkpt(Object object, Word mark, Word tmp, Word value) { - throw new GraalInternalError(""); - } + public static final boolean CHECK_BALANCED_MONITORS = Boolean.getBoolean("graal.monitors.checkBalance"); @Snippet - public static void monitorenter(@Parameter("object") Object object, @ConstantParameter("logEnabled") boolean logEnabled) { + public static void monitorenter(@Parameter("object") Object object, @ConstantParameter("trace") boolean trace) { verifyOop(object); // Load the mark word - this includes a null-check on object @@ -89,6 +80,12 @@ final Word lock = beginLockScope(false, wordKind()); + trace(trace, " object: 0x%016lx\n", Word.fromObject(object).toLong()); + trace(trace, " lock: 0x%016lx\n", lock.toLong()); + trace(trace, " mark: 0x%016lx\n", mark.toLong()); + + incCounter(); + if (useBiasedLocking()) { // See whether the lock is currently biased toward our thread and // whether the epoch is still valid. @@ -106,9 +103,12 @@ final Word prototypeMarkWord = loadWordFromObject(hub, prototypeMarkWordOffset()); final Word thread = thread(); final Word tmp = prototypeMarkWord.or(thread).xor(mark).and(~ageMaskInPlace()); + trace(trace, "prototypeMarkWord: 0x%016lx\n", prototypeMarkWord.toLong()); + trace(trace, " thread: 0x%016lx\n", thread.toLong()); + trace(trace, " tmp: 0x%016lx\n", tmp.toLong()); if (tmp == Word.zero()) { // Object is already biased to current thread -> done - log(logEnabled, "+lock{bias}", object); + trace(trace, "+lock{bias:existing}", object); return; } @@ -117,6 +117,10 @@ // figure out more details about the state of the mark word in order to // know what operations can be legally performed on the object's // mark word. + + // If the low three bits in the xor result aren't clear, that means + // the prototype header is no longer biasable and we have to revoke + // the bias on this object. if (tmp.and(biasedLockMaskInPlace()) == Word.zero()) { // Biasing is still enabled for object's type. See whether the // epoch of the current bias is still valid, meaning that the epoch @@ -127,7 +131,7 @@ // that the current epoch is invalid in order to do this because // otherwise the manipulations it performs on the mark word are // illegal. - if (tmp.and(epochMaskInPlace()) != Word.zero()) { + if (tmp.and(epochMaskInPlace()) == Word.zero()) { // The epoch of the current bias is still valid but we know nothing // about the owner; it might be set or it might be clear. Try to // acquire the bias of the object using an atomic operation. If this @@ -136,35 +140,37 @@ // don't accidentally blow away another thread's valid bias. Word unbiasedMark = mark.and(biasedLockMaskInPlace() | ageMaskInPlace() | epochMaskInPlace()); Word biasedMark = unbiasedMark.or(thread); + trace(trace, " unbiasedMark: 0x%016lx\n", unbiasedMark.toLong()); + trace(trace, " biasedMark: 0x%016lx\n", biasedMark.toLong()); if (compareAndSwap(object, markOffset(), unbiasedMark, biasedMark) == unbiasedMark) { // Object is now biased to current thread -> done - log(logEnabled, "+lock{bias}", object); + trace(trace, "+lock{bias:acquired}", object); return; } - // If the biasing toward our thread failed, this means that - // another thread succeeded in biasing it toward itself and we - // need to revoke that bias. The revocation will occur in the - // interpreter runtime in the slow case. - log(logEnabled, "+lock{stub:revoke}", object); + // If the biasing toward our thread failed, this means that another thread + // owns the bias and we need to revoke that bias. The revocation will occur + // in the interpreter runtime. + trace(trace, "+lock{stub:revoke}", object); MonitorEnterStubCall.call(object, lock); return; } else { // At this point we know the epoch has expired, meaning that the - // current "bias owner", if any, is actually invalid. Under these - // circumstances _only_, we are allowed to use the current mark word + // current bias owner, if any, is actually invalid. Under these + // circumstances _only_, are we allowed to use the current mark word // value as the comparison value when doing the CAS to acquire the // bias in the current epoch. In other words, we allow transfer of // the bias from one thread to another directly in this situation. Word biasedMark = prototypeMarkWord.or(thread); + trace(trace, " biasedMark: 0x%016lx\n", biasedMark.toLong()); if (compareAndSwap(object, markOffset(), mark, biasedMark) == mark) { // Object is now biased to current thread -> done - log(logEnabled, "+lock{bias}", object); + trace(trace, "+lock{bias:transfer}", object); return; } // If the biasing toward our thread failed, then another thread // succeeded in biasing it toward itself and we need to revoke that // bias. The revocation will occur in the runtime in the slow case. - log(logEnabled, "+lock{stub:epoch-expired}", object); + trace(trace, "+lock{stub:epoch-expired}", object); MonitorEnterStubCall.call(object, lock); return; } @@ -192,6 +198,7 @@ // Create the unlocked mark word pattern Word unlockedMark = mark.or(unlockedMask()); + trace(trace, " unlockedMark: 0x%016lx\n", unlockedMark.toLong()); // Copy this unlocked mark word into the lock slot on the stack storeWord(lock, lockDisplacedMarkOffset(), 0, unlockedMark); @@ -200,6 +207,7 @@ // (address of) the lock slot into the object's mark word. Word currentMark = compareAndSwap(object, markOffset(), unlockedMark, lock); if (currentMark != unlockedMark) { + trace(trace, " currentMark: 0x%016lx\n", currentMark.toLong()); // The mark word in the object header was not the same. // Either the object is locked by another thread or is already locked // by the current thread. The latter is true if the mark word @@ -219,21 +227,22 @@ final Word stackPointer = stackPointer(); if (currentMark.minus(stackPointer).and(alignedMask.minus(pageSize())) != Word.zero()) { // Most likely not a recursive lock, go into a slow runtime call - log(logEnabled, "+lock{stub:failed-cas}", object); + trace(trace, "+lock{stub:failed-cas}", object); MonitorEnterStubCall.call(object, lock); return; } else { // Recursively locked => write 0 to the lock slot storeWord(lock, lockDisplacedMarkOffset(), 0, Word.zero()); - log(logEnabled, "+lock{recursive}", object); + trace(trace, "+lock{recursive}", object); } } else { - log(logEnabled, "+lock{cas}", object); + trace(trace, "+lock{cas}", object); } } @Snippet public static void monitorenterEliminated() { + incCounter(); beginLockScope(true, wordKind()); } @@ -241,20 +250,22 @@ * Calls straight out to the monitorenter stub. */ @Snippet - public static void monitorenterStub(@Parameter("object") Object object, @ConstantParameter("checkNull") boolean checkNull, @ConstantParameter("logEnabled") boolean logEnabled) { + public static void monitorenterStub(@Parameter("object") Object object, @ConstantParameter("checkNull") boolean checkNull, @ConstantParameter("trace") boolean trace) { verifyOop(object); + incCounter(); if (checkNull && object == null) { DeoptimizeNode.deopt(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.NullCheckException); } // BeginLockScope nodes do not read from object so a use of object // cannot float about the null check above final Word lock = beginLockScope(false, wordKind()); - log(logEnabled, "+lock{stub}", object); + trace(trace, "+lock{stub}", object); MonitorEnterStubCall.call(object, lock); } @Snippet - public static void monitorexit(@Parameter("object") Object object, @ConstantParameter("logEnabled") boolean logEnabled) { + public static void monitorexit(@Parameter("object") Object object, @ConstantParameter("trace") boolean trace) { + trace(trace, " object: 0x%016lx\n", Word.fromObject(object).toLong()); if (useBiasedLocking()) { // Check for biased locking unlock case, which is a no-op // Note: we do not have to check the thread ID for two reasons. @@ -263,9 +274,11 @@ // lock, the object could not be rebiased toward another thread, so // the bias bit would be clear. final Word mark = loadWordFromObject(object, markOffset()); + trace(trace, " mark: 0x%016lx\n", mark.toLong()); if (mark.and(biasedLockMaskInPlace()).toLong() == biasedLockPattern()) { endLockScope(); - log(logEnabled, "-lock{bias}", object); + decCounter(); + trace(trace, "-lock{bias}", object); return; } } @@ -274,10 +287,11 @@ // Load displaced mark final Word displacedMark = loadWordFromWord(lock, lockDisplacedMarkOffset()); + trace(trace, " displacedMark: 0x%016lx\n", displacedMark.toLong()); if (displacedMark == Word.zero()) { // Recursive locking => done - log(logEnabled, "-lock{recursive}", object); + trace(trace, "-lock{recursive}", object); } else { verifyOop(object); // Test if object's mark word is pointing to the displaced mark word, and if so, restore @@ -286,29 +300,90 @@ if (DirectCompareAndSwapNode.compareAndSwap(object, markOffset(), lock, displacedMark) != lock) { // The object's mark word was not pointing to the displaced header, // we do unlocking via runtime call. - log(logEnabled, "-lock{stub}", object); + trace(trace, "-lock{stub}", object); MonitorExitStubCall.call(object); } else { - log(logEnabled, "-lock{cas}", object); + trace(trace, "-lock{cas}", object); } } endLockScope(); + decCounter(); } /** * Calls straight out to the monitorexit stub. */ @Snippet - public static void monitorexitStub(@Parameter("object") Object object, @ConstantParameter("logEnabled") boolean logEnabled) { + public static void monitorexitStub(@Parameter("object") Object object, @ConstantParameter("trace") boolean trace) { verifyOop(object); - log(logEnabled, "-lock{stub}", object); + trace(trace, "-lock{stub}", object); MonitorExitStubCall.call(object); endLockScope(); + decCounter(); } @Snippet public static void monitorexitEliminated() { endLockScope(); + decCounter(); + } + + private static void trace(boolean enabled, String action, Object object) { + if (enabled) { + Log.print(action); + Log.print(' '); + Log.printlnObject(object); + } + } + + private static void trace(boolean enabled, String format, long value) { + if (enabled) { + Log.printf(format, value); + } + } + + /** + * Leaving the breakpoint code in to provide an example of how to use the {@link BreakpointNode} intrinsic. + */ + private static final boolean ENABLE_BREAKPOINT = false; + + @SuppressWarnings("unused") + @NodeIntrinsic(BreakpointNode.class) + static void bkpt(Object object, Word mark, Word tmp, Word value) { + throw new GraalInternalError(""); + } + + private static void incCounter() { + if (CHECK_BALANCED_MONITORS) { + final Word counter = MonitorCounterNode.counter(wordKind()); + final int count = UnsafeLoadNode.load(counter, 0, 0, Kind.Int); + DirectObjectStoreNode.storeInt(counter, 0, 0, count + 1); + } + } + + private static void decCounter() { + if (CHECK_BALANCED_MONITORS) { + final Word counter = MonitorCounterNode.counter(wordKind()); + final int count = UnsafeLoadNode.load(counter, 0, 0, Kind.Int); + DirectObjectStoreNode.storeInt(counter, 0, 0, count - 1); + } + } + + @Snippet + private static void initCounter() { + final Word counter = MonitorCounterNode.counter(wordKind()); + DirectObjectStoreNode.storeInt(counter, 0, 0, 0); + } + + @Snippet + private static void checkCounter(String errMsg) { + final Word counter = MonitorCounterNode.counter(wordKind()); + final int count = UnsafeLoadNode.load(counter, 0, 0, Kind.Int); + if (count != 0) { + Log.print(errMsg); + Log.println(count); + DirectObjectStoreNode.storeInt(Word.zero(), 0, 0, count + 1); + } } public static class Templates extends AbstractTemplates { @@ -319,6 +394,8 @@ private final ResolvedJavaMethod monitorexitStub; private final ResolvedJavaMethod monitorenterEliminated; private final ResolvedJavaMethod monitorexitEliminated; + private final ResolvedJavaMethod initCounter; + private final ResolvedJavaMethod checkCounter; private final boolean useFastLocking; public Templates(CodeCacheProvider runtime, boolean useFastLocking) { @@ -329,36 +406,34 @@ monitorexitStub = snippet("monitorexitStub", Object.class, boolean.class); monitorenterEliminated = snippet("monitorenterEliminated"); monitorexitEliminated = snippet("monitorexitEliminated"); + initCounter = snippet("initCounter"); + checkCounter = snippet("checkCounter", String.class); this.useFastLocking = useFastLocking; } - static boolean isLoggingEnabledFor(ValueNode object) { - ResolvedJavaType type = object.objectStamp().type(); - if (LOG_TYPE == null) { - return false; - } else { - if (LOG_TYPE.length() == 0) { - return true; - } - if (type == null) { - return false; - } - return (type.name().contains(LOG_TYPE)); - } - } + public void lower(MonitorEnterNode monitorenterNode, @SuppressWarnings("unused") LoweringTool tool) { + StructuredGraph graph = (StructuredGraph) monitorenterNode.graph(); - public void lower(MonitorEnterNode monitorenterNode, @SuppressWarnings("unused") LoweringTool tool) { + checkBalancedMonitors(graph); + FrameState stateAfter = monitorenterNode.stateAfter(); - ResolvedJavaMethod method = monitorenterNode.eliminated() ? monitorenterEliminated : useFastLocking ? monitorenter : monitorenterStub; + boolean eliminated = monitorenterNode.eliminated(); + ResolvedJavaMethod method = eliminated ? monitorenterEliminated : useFastLocking ? monitorenter : monitorenterStub; boolean checkNull = !monitorenterNode.object().stamp().nonNull(); Key key = new Key(method); if (method == monitorenterStub) { key.add("checkNull", checkNull); } - if (!monitorenterNode.eliminated()) { - key.add("logEnabled", isLoggingEnabledFor(monitorenterNode.object())); + if (!eliminated) { + key.add("trace", isTracingEnabledForType(monitorenterNode.object()) || + isTracingEnabledForMethod(stateAfter.method()) || + isTracingEnabledForMethod(graph.method())); } - Arguments arguments = arguments("object", monitorenterNode.object()); + + Arguments arguments = new Arguments(); + if (!eliminated) { + arguments.add("object", monitorenterNode.object()); + } SnippetTemplate template = cache.get(key); Map nodes = template.instantiate(runtime, monitorenterNode, arguments); for (Node n : nodes.values()) { @@ -370,13 +445,20 @@ } public void lower(MonitorExitNode monitorexitNode, @SuppressWarnings("unused") LoweringTool tool) { + StructuredGraph graph = (StructuredGraph) monitorexitNode.graph(); FrameState stateAfter = monitorexitNode.stateAfter(); - ResolvedJavaMethod method = monitorexitNode.eliminated() ? monitorexitEliminated : useFastLocking ? monitorexit : monitorexitStub; + boolean eliminated = monitorexitNode.eliminated(); + ResolvedJavaMethod method = eliminated ? monitorexitEliminated : useFastLocking ? monitorexit : monitorexitStub; Key key = new Key(method); - if (!monitorexitNode.eliminated()) { - key.add("logEnabled", isLoggingEnabledFor(monitorexitNode.object())); + if (!eliminated) { + key.add("trace", isTracingEnabledForType(monitorexitNode.object()) || + isTracingEnabledForMethod(stateAfter.method()) || + isTracingEnabledForMethod(graph.method())); } - Arguments arguments = arguments("object", monitorexitNode.object()); + Arguments arguments = new Arguments(); + if (!eliminated) { + arguments.add("object", monitorexitNode.object()); + } SnippetTemplate template = cache.get(key); Map nodes = template.instantiate(runtime, monitorexitNode, arguments); for (Node n : nodes.values()) { @@ -386,5 +468,69 @@ } } } + + static boolean isTracingEnabledForType(ValueNode object) { + ResolvedJavaType type = object.objectStamp().type(); + if (TRACE_TYPE_FILTER == null) { + return false; + } else { + if (TRACE_TYPE_FILTER.length() == 0) { + return true; + } + if (type == null) { + return false; + } + return (type.name().contains(TRACE_TYPE_FILTER)); + } + } + + static boolean isTracingEnabledForMethod(ResolvedJavaMethod method) { + if (TRACE_METHOD_FILTER == null) { + return false; + } else { + if (TRACE_METHOD_FILTER.length() == 0) { + return true; + } + if (method == null) { + return false; + } + return (MetaUtil.format("%H.%n", method).contains(TRACE_METHOD_FILTER)); + } + } + + /** + * If balanced monitor checking is enabled then nodes are inserted at the start and + * all return points of the graph to initialize and check the monitor counter + * respectively. + */ + private void checkBalancedMonitors(StructuredGraph graph) { + if (CHECK_BALANCED_MONITORS) { + NodeIterable nodes = graph.getNodes().filter(MonitorCounterNode.class); + if (nodes.isEmpty()) { + // Only insert the nodes if this is the first monitorenter being lowered. + JavaType returnType = initCounter.signature().returnType(initCounter.holder()); + MethodCallTargetNode callTarget = graph.add(new MethodCallTargetNode(InvokeKind.Static, initCounter, new ValueNode[0], returnType)); + InvokeNode invoke = graph.add(new InvokeNode(callTarget, 0, -1)); + invoke.setStateAfter(graph.start().stateAfter()); + graph.addAfterFixed(graph.start(), invoke); + StructuredGraph inlineeGraph = (StructuredGraph) initCounter.compilerStorage().get(Graph.class); + InliningUtil.inline(invoke, inlineeGraph, false); + + List rets = graph.getNodes().filter(ReturnNode.class).snapshot(); + for (ReturnNode ret : rets) { + returnType = checkCounter.signature().returnType(checkCounter.holder()); + ConstantNode errMsg = ConstantNode.forObject("unbalanced monitors in " + MetaUtil.format("%H.%n(%p)", graph.method()), runtime, graph); + callTarget = graph.add(new MethodCallTargetNode(InvokeKind.Static, checkCounter, new ValueNode[] {errMsg}, returnType)); + invoke = graph.add(new InvokeNode(callTarget, 0, -1)); + List stack = Collections.emptyList(); + FrameState stateAfter = new FrameState(graph.method(), FrameState.AFTER_BCI, new ValueNode[0], stack, new ValueNode[0], false, false, null); + invoke.setStateAfter(graph.add(stateAfter)); + graph.addBeforeFixed(ret, invoke); + inlineeGraph = (StructuredGraph) checkCounter.compilerStorage().get(Graph.class); + InliningUtil.inline(invoke, inlineeGraph, false); + } + } + } + } } }