changeset 13817:d7ed39d0a6d9

Merge
author Tom Rodriguez <tom.rodriguez@oracle.com>
date Wed, 29 Jan 2014 15:02:19 -0800
parents d006a6332db8 (current diff) 460e453d6fec (diff)
children d2f520f46180
files graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/InlinableCallSite.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/InlinedCallSite.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SplitOutputStream.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLInlinableDirectDispatchNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLInlinedDirectDispatchNode.java
diffstat 48 files changed, 1040 insertions(+), 600 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.asm.ptx/src/com/oracle/graal/asm/ptx/PTXAssembler.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.asm.ptx/src/com/oracle/graal/asm/ptx/PTXAssembler.java	Wed Jan 29 15:02:19 2014 -0800
@@ -397,13 +397,17 @@
     }
 
     // Checkstyle: stop method name check
-    public final void bra(String tgt, int pred) {
+    /*
+     * Emit conditional branch to target 'tgt' guarded by predicate register 'pred' whose state is
+     * tested to be 'predCheck'.
+     */
+    public final void bra(String tgt, int pred, boolean predCheck) {
         assert pred >= 0;
 
         if (tgt.equals("?")) {
             Thread.dumpStack();
         }
-        emitString("@%p" + pred + " " + "bra" + " " + tgt + ";");
+        emitString("@" + (predCheck ? "%p" : "!%p") + pred + " " + "bra" + " " + tgt + ";");
     }
 
     public final void bra(String src) {
--- a/graal/com.oracle.graal.compiler.ptx.test/src/com/oracle/graal/compiler/ptx/test/ControlPTXTest.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.compiler.ptx.test/src/com/oracle/graal/compiler/ptx/test/ControlPTXTest.java	Wed Jan 29 15:02:19 2014 -0800
@@ -26,18 +26,26 @@
 
 public class ControlPTXTest extends PTXTest {
 
-    @Ignore("[CUDA] *** Error (status=702): Synchronize kernel")
     @Test
-    public void testControl() {
+    public void testControl1() {
         test("testLoop", 42);
         test("testSwitchDefault1I", 3);
         test("testSwitch1I", 2);
         test("testIfElse1I", 222);
         test("testIfElse2I", 19, 64);
+    }
 
-        test("testIntegerTestBranch2I", 0xff00, 0x00ff);
+    @Test
+    public void testControl2() {
         compileKernel("testStatic");
         compileKernel("testCall");
+    }
+
+    @Ignore("[CUDA] Check for malformed PTX kernel or incorrect PTX compilation options")
+    @Test
+    public void testControl3() {
+        // test("testIntegerTestBranch2I", 0xff00, 0x00ff);
+        compileKernel("testIntegerTestBranch2I");
         compileKernel("testLookupSwitch1I");
     }
 
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/deopt/SynchronizedMethodDeoptimizationTest.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/deopt/SynchronizedMethodDeoptimizationTest.java	Wed Jan 29 15:02:19 2014 -0800
@@ -22,52 +22,29 @@
  */
 package com.oracle.graal.compiler.test.deopt;
 
-import java.lang.reflect.*;
-
 import org.junit.*;
 
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.test.*;
-import com.oracle.graal.nodes.*;
+import com.oracle.graal.compiler.test.ea.EATestBase.TestClassObject;
 
 /**
  * In the following tests, we try to deoptimize out of synchronized methods.
  */
 public class SynchronizedMethodDeoptimizationTest extends GraalCompilerTest {
 
-    public static final int N = 15000;
+    public static final TestClassObject testObject = null;
 
     public static synchronized Object testMethodSynchronized(Object o) {
         if (o == null) {
-            return null;
+            // this branch will always deoptimize
+            return testObject.x;
         }
         return o;
     }
 
     @Test
     public void test1() {
-        Method method = getMethod("testMethodSynchronized");
-        String testString = "test";
-        for (int i = 0; i < N; ++i) {
-            Assert.assertEquals(testString, testMethodSynchronized(testString));
-        }
-        final StructuredGraph graph = parseProfiled(method);
-        final ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(method);
-        InstalledCode compiledMethod = getCode(javaMethod, graph);
-        try {
-            Object result = compiledMethod.executeVarargs(testString);
-            Assert.assertEquals(testString, result);
-        } catch (InvalidInstalledCodeException t) {
-            Assert.fail("method invalidated");
-        }
-
-        try {
-            Object result = compiledMethod.executeVarargs(new Object[]{null});
-            Assert.assertEquals(null, result);
-            Assert.assertFalse(compiledMethod.isValid());
-        } catch (InvalidInstalledCodeException t) {
-            Assert.fail("method invalidated");
-        }
+        test("testMethodSynchronized", "test");
+        test("testMethodSynchronized", (Object) null);
     }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/debug/BenchmarkCounters.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/debug/BenchmarkCounters.java	Wed Jan 29 15:02:19 2014 -0800
@@ -107,7 +107,7 @@
 
     public static int getIndex(DynamicCounterNode counter) {
         if (!enabled) {
-            throw new GraalInternalError("counter nodes shouldn't exist when counters are not enabled");
+            throw new GraalInternalError("counter nodes shouldn't exist when counters are not enabled: " + counter.getGroup() + ", " + counter.getName());
         }
         String name = counter.getName();
         String group = counter.getGroup();
@@ -332,12 +332,15 @@
             if (index >= config.graalCountersSize) {
                 throw new GraalInternalError("too many counters, reduce number of counters or increase GRAAL_COUNTERS_SIZE (current value: " + config.graalCountersSize + ")");
             }
-            ConstantLocationNode location = ConstantLocationNode.create(LocationIdentity.ANY_LOCATION, Kind.Long, config.graalCountersThreadOffset + Unsafe.ARRAY_LONG_INDEX_SCALE * index, graph);
-            ReadNode read = graph.add(new ReadNode(thread, location, StampFactory.forKind(Kind.Long), BarrierType.NONE, false));
+            ConstantLocationNode arrayLocation = ConstantLocationNode.create(LocationIdentity.ANY_LOCATION, Kind.Long, config.graalCountersThreadOffset, graph);
+            ReadNode readArray = graph.add(new ReadNode(thread, arrayLocation, StampFactory.forKind(wordKind), BarrierType.NONE, false));
+            ConstantLocationNode location = ConstantLocationNode.create(LocationIdentity.ANY_LOCATION, Kind.Long, Unsafe.ARRAY_LONG_INDEX_SCALE * index, graph);
+            ReadNode read = graph.add(new ReadNode(readArray, location, StampFactory.forKind(Kind.Long), BarrierType.NONE, false));
             IntegerAddNode add = graph.unique(new IntegerAddNode(Kind.Long, read, counter.getIncrement()));
-            WriteNode write = graph.add(new WriteNode(thread, add, location, BarrierType.NONE, false));
+            WriteNode write = graph.add(new WriteNode(readArray, add, location, BarrierType.NONE, false));
 
             graph.addBeforeFixed(counter, thread);
+            graph.addBeforeFixed(counter, readArray);
             graph.addBeforeFixed(counter, read);
             graph.addBeforeFixed(counter, write);
         }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoweringProvider.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoweringProvider.java	Wed Jan 29 15:02:19 2014 -0800
@@ -282,7 +282,7 @@
             write.setStateAfter(store.stateAfter());
             graph.replaceFixedWithFixed(store, write);
         } else if (n instanceof LoadHubNode) {
-            if (graph.getGuardsStage().ordinal() == StructuredGraph.GuardsStage.FIXED_DEOPTS.ordinal()) {
+            if (graph.getGuardsStage().ordinal() >= StructuredGraph.GuardsStage.FIXED_DEOPTS.ordinal()) {
                 LoadHubNode loadHub = (LoadHubNode) n;
                 assert loadHub.kind() == wordKind;
                 ValueNode object = loadHub.object();
@@ -443,11 +443,11 @@
                 newObjectSnippets.lower((DynamicNewArrayNode) n, registers, tool);
             }
         } else if (n instanceof MonitorEnterNode) {
-            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.FIXED_DEOPTS) {
+            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
                 monitorSnippets.lower((MonitorEnterNode) n, registers, tool);
             }
         } else if (n instanceof MonitorExitNode) {
-            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.FIXED_DEOPTS) {
+            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
                 monitorSnippets.lower((MonitorExitNode) n, tool);
             }
         } else if (n instanceof G1PreWriteBarrier) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/OnStackReplacementPhase.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/OnStackReplacementPhase.java	Wed Jan 29 15:02:19 2014 -0800
@@ -78,10 +78,7 @@
                 ProxyNode proxy = (ProxyNode) usage;
                 proxy.replaceAndDelete(proxy.value());
             }
-            FixedNode next = osr.next();
-            osr.setNext(null);
-            ((FixedWithNextNode) osr.predecessor()).setNext(next);
-            GraphUtil.killWithUnusedFloatingInputs(osr);
+            GraphUtil.removeFixedWithUnusedInputs(osr);
             Debug.dump(graph, "OnStackReplacement loop peeling result");
         } while (true);
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/InstanceOfSnippets.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/InstanceOfSnippets.java	Wed Jan 29 15:02:19 2014 -0800
@@ -230,7 +230,7 @@
                 Arguments args;
 
                 StructuredGraph graph = instanceOf.graph();
-                if (hintInfo.hintHitProbability >= hintHitProbabilityThresholdForDeoptimizingSnippet()) {
+                if (hintInfo.hintHitProbability >= hintHitProbabilityThresholdForDeoptimizingSnippet() && hintInfo.exact == null) {
                     Hints hints = createHints(hintInfo, providers.getMetaAccess(), false, graph);
                     args = new Arguments(instanceofWithProfile, graph.getGuardsStage(), tool.getLoweringStage());
                     args.add("object", object);
@@ -256,7 +256,7 @@
                 }
                 args.add("trueValue", replacer.trueValue);
                 args.add("falseValue", replacer.falseValue);
-                if (hintInfo.hintHitProbability >= hintHitProbabilityThresholdForDeoptimizingSnippet()) {
+                if (hintInfo.hintHitProbability >= hintHitProbabilityThresholdForDeoptimizingSnippet() && hintInfo.exact == null) {
                     args.addConst("nullSeen", hintInfo.profile.getNullSeen() != TriState.FALSE);
                 }
                 return args;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java	Wed Jan 29 15:02:19 2014 -0800
@@ -34,7 +34,6 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.Node.ConstantNodeParameter;
 import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.graph.iterators.*;
@@ -425,7 +424,6 @@
         public void lower(MonitorEnterNode monitorenterNode, HotSpotRegistersProvider registers, LoweringTool tool) {
             StructuredGraph graph = monitorenterNode.graph();
             checkBalancedMonitors(graph, tool);
-            FrameState stateAfter = monitorenterNode.stateAfter();
 
             Arguments args;
             if (useFastLocking) {
@@ -435,24 +433,15 @@
             }
             args.add("object", monitorenterNode.object());
             args.addConst("lockDepth", monitorenterNode.getMonitorId().getLockDepth());
-            boolean tracingEnabledForMethod = stateAfter != null && (isTracingEnabledForMethod(stateAfter.method()) || isTracingEnabledForMethod(graph.method()));
             args.addConst("threadRegister", registers.getThreadRegister());
             args.addConst("stackPointerRegister", registers.getStackPointerRegister());
-            args.addConst("trace", isTracingEnabledForType(monitorenterNode.object()) || tracingEnabledForMethod);
-
-            Map<Node, Node> nodes = template(args).instantiate(providers.getMetaAccess(), monitorenterNode, DEFAULT_REPLACER, args);
+            args.addConst("trace", isTracingEnabledForType(monitorenterNode.object()) || isTracingEnabledForMethod(graph.method()));
 
-            for (Node n : nodes.values()) {
-                if (n instanceof BeginLockScopeNode) {
-                    BeginLockScopeNode begin = (BeginLockScopeNode) n;
-                    begin.setStateAfter(stateAfter);
-                }
-            }
+            template(args).instantiate(providers.getMetaAccess(), monitorenterNode, DEFAULT_REPLACER, args);
         }
 
         public void lower(MonitorExitNode monitorexitNode, LoweringTool tool) {
             StructuredGraph graph = monitorexitNode.graph();
-            FrameState stateAfter = monitorexitNode.stateAfter();
 
             Arguments args;
             if (useFastLocking) {
@@ -462,16 +451,9 @@
             }
             args.add("object", monitorexitNode.object());
             args.addConst("lockDepth", monitorexitNode.getMonitorId().getLockDepth());
-            args.addConst("trace", isTracingEnabledForType(monitorexitNode.object()) || isTracingEnabledForMethod(stateAfter.method()) || isTracingEnabledForMethod(graph.method()));
-
-            Map<Node, Node> nodes = template(args).instantiate(providers.getMetaAccess(), monitorexitNode, DEFAULT_REPLACER, args);
+            args.addConst("trace", isTracingEnabledForType(monitorexitNode.object()) || isTracingEnabledForMethod(graph.method()));
 
-            for (Node n : nodes.values()) {
-                if (n instanceof EndLockScopeNode) {
-                    EndLockScopeNode end = (EndLockScopeNode) n;
-                    end.setStateAfter(stateAfter);
-                }
-            }
+            template(args).instantiate(providers.getMetaAccess(), monitorexitNode, DEFAULT_REPLACER, args);
         }
 
         static boolean isTracingEnabledForType(ValueNode object) {
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Wed Jan 29 15:02:19 2014 -0800
@@ -1067,15 +1067,6 @@
             }
         }
 
-        private ConstantNode genTypeOrDeopt(Representation representation, JavaType type, boolean initialized) {
-            if (initialized) {
-                return appendConstant(((ResolvedJavaType) type).getEncoding(representation));
-            } else {
-                handleUnresolvedExceptionType(representation, type);
-                return null;
-            }
-        }
-
         private void appendOptimizedStoreField(StoreFieldNode store) {
             append(store);
         }
@@ -1711,14 +1702,17 @@
                 ResolvedJavaType resolvedCatchType = (ResolvedJavaType) catchType;
                 for (ResolvedJavaType skippedType : graphBuilderConfig.getSkippedExceptionTypes()) {
                     if (skippedType.isAssignableFrom(resolvedCatchType)) {
-                        append(new DeoptimizeNode(InvalidateReprofile, UnreachedCode));
+                        Block nextBlock = block.successors.size() == 1 ? unwindBlock(block.deoptBci) : block.successors.get(1);
+                        ValueNode exception = frameState.stackAt(0);
+                        FixedNode trueSuccessor = currentGraph.add(new DeoptimizeNode(InvalidateReprofile, UnreachedCode));
+                        FixedNode nextDispatch = createTarget(nextBlock, frameState);
+                        append(new IfNode(currentGraph.unique(new InstanceOfNode((ResolvedJavaType) catchType, exception, null)), trueSuccessor, nextDispatch, 0));
                         return;
                     }
                 }
             }
 
-            ConstantNode typeInstruction = genTypeOrDeopt(Representation.ObjectHub, catchType, initialized);
-            if (typeInstruction != null) {
+            if (initialized) {
                 Block nextBlock = block.successors.size() == 1 ? unwindBlock(block.deoptBci) : block.successors.get(1);
                 ValueNode exception = frameState.stackAt(0);
                 CheckCastNode checkCast = currentGraph.add(new CheckCastNode((ResolvedJavaType) catchType, exception, null, false));
@@ -1730,6 +1724,8 @@
                 FixedNode nextDispatch = createTarget(nextBlock, frameState);
                 checkCast.setNext(catchSuccessor);
                 append(new IfNode(currentGraph.unique(new InstanceOfNode((ResolvedJavaType) catchType, exception, null)), checkCast, nextDispatch, 0.5));
+            } else {
+                handleUnresolvedExceptionType(Representation.ObjectHub, catchType);
             }
         }
 
--- a/graal/com.oracle.graal.lir.ptx/src/com/oracle/graal/lir/ptx/PTXControlFlow.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.lir.ptx/src/com/oracle/graal/lir/ptx/PTXControlFlow.java	Wed Jan 29 15:02:19 2014 -0800
@@ -86,9 +86,9 @@
         @Override
         public void emitCode(CompilationResultBuilder crb, PTXMacroAssembler masm) {
             if (crb.isSuccessorEdge(trueDestination)) {
-                masm.bra(masm.nameOf(falseDestination.label()), predRegNum);
+                masm.bra(masm.nameOf(falseDestination.label()), predRegNum, false);
             } else {
-                masm.bra(masm.nameOf(trueDestination.label()));
+                masm.bra(masm.nameOf(trueDestination.label()), predRegNum, true);
                 if (!crb.isSuccessorEdge(falseDestination)) {
                     masm.jmp(falseDestination.label());
                 }
@@ -238,7 +238,7 @@
                         default:
                             throw new GraalInternalError("switch only supported for int, long and object");
                     }
-                    masm.bra(masm.nameOf(target), predRegNum);
+                    masm.bra(masm.nameOf(target), predRegNum, true);
                 }
             };
             strategy.run(closure);
@@ -281,7 +281,7 @@
 
             // Jump to default target if index is not within the jump table
             if (defaultTarget != null) {
-                masm.bra(masm.nameOf(defaultTarget.label()), predRegNum);
+                masm.bra(masm.nameOf(defaultTarget.label()), predRegNum, true);
             }
 
             // address of jump table
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FixedGuardNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FixedGuardNode.java	Wed Jan 29 15:02:19 2014 -0800
@@ -51,7 +51,7 @@
                     tool.deleteBranch(next);
                 }
 
-                DeoptimizeNode deopt = graph().add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, getReason()));
+                DeoptimizeNode deopt = graph().add(new DeoptimizeNode(getAction(), getReason()));
                 deopt.setDeoptimizationState(getDeoptimizationState());
                 setNext(deopt);
             }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/LoopBeginNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/LoopBeginNode.java	Wed Jan 29 15:02:19 2014 -0800
@@ -31,6 +31,7 @@
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.util.*;
 
 public class LoopBeginNode extends MergeNode implements IterableNodeType, LIRLowerable {
 
@@ -184,7 +185,11 @@
     public void removeExits() {
         for (LoopExitNode loopexit : loopExits().snapshot()) {
             loopexit.removeProxies();
+            FrameState stateAfter = loopexit.stateAfter();
             graph().replaceFixedWithFixed(loopexit, graph().add(new BeginNode()));
+            if (stateAfter != null && stateAfter.isAlive() && stateAfter.usages().isEmpty()) {
+                GraphUtil.killWithUnusedFloatingInputs(stateAfter);
+            }
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessMonitorNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessMonitorNode.java	Wed Jan 29 15:02:19 2014 -0800
@@ -33,11 +33,34 @@
  * The Java bytecode specification allows non-balanced locking. Graal does not handle such cases and
  * throws a {@link BailoutException} instead during graph building.
  */
-public abstract class AccessMonitorNode extends AbstractMemoryCheckpoint implements MemoryCheckpoint {
+public abstract class AccessMonitorNode extends AbstractMemoryCheckpoint implements MemoryCheckpoint, DeoptimizingNode {
 
+    @Input private FrameState deoptState;
     @Input private ValueNode object;
     @Input private MonitorIdNode monitorId;
 
+    @Override
+    public boolean canDeoptimize() {
+        return true;
+    }
+
+    @Override
+    public FrameState getDeoptimizationState() {
+        return deoptState;
+    }
+
+    @Override
+    public void setDeoptimizationState(FrameState f) {
+        updateUsages(deoptState, f);
+        deoptState = f;
+    }
+
+    @Override
+    public FrameState getState() {
+        assert deoptState == null || stateAfter() == null;
+        return deoptState == null ? stateAfter() : deoptState;
+    }
+
     public ValueNode object() {
         return object;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorEnterNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorEnterNode.java	Wed Jan 29 15:02:19 2014 -0800
@@ -23,6 +23,7 @@
 package com.oracle.graal.nodes.java;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
@@ -30,7 +31,7 @@
 /**
  * The {@code MonitorEnterNode} represents the acquisition of a monitor.
  */
-public final class MonitorEnterNode extends AccessMonitorNode implements Virtualizable, Lowerable, MonitorEnter, MemoryCheckpoint.Single {
+public final class MonitorEnterNode extends AccessMonitorNode implements Virtualizable, Lowerable, IterableNodeType, MonitorEnter, MemoryCheckpoint.Single {
 
     /**
      * Creates a new MonitorEnterNode.
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorExitNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorExitNode.java	Wed Jan 29 15:02:19 2014 -0800
@@ -58,7 +58,7 @@
 
     @Override
     public void simplify(SimplifierTool tool) {
-        if (escapedReturnValue != null && stateAfter().bci != FrameState.AFTER_BCI) {
+        if (escapedReturnValue != null && stateAfter() != null && stateAfter().bci != FrameState.AFTER_BCI) {
             ValueNode returnValue = escapedReturnValue;
             setEscapedReturnValue(null);
             tool.removeIfUnused(returnValue);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Wed Jan 29 15:02:19 2014 -0800
@@ -137,6 +137,13 @@
         }
     }
 
+    public static void removeFixedWithUnusedInputs(FixedWithNextNode fixed) {
+        FixedNode next = fixed.next();
+        fixed.setNext(null);
+        fixed.replaceAtPredecessor(next);
+        killWithUnusedFloatingInputs(fixed);
+    }
+
     public static void checkRedundantPhi(PhiNode phiNode) {
         if (phiNode.isDeleted() || phiNode.valueCount() == 1) {
             return;
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConditionalEliminationPhase.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConditionalEliminationPhase.java	Wed Jan 29 15:02:19 2014 -0800
@@ -626,7 +626,7 @@
                         if (type != ObjectStamp.typeOrNull(receiver)) {
                             ResolvedJavaMethod method = type.resolveMethod(callTarget.targetMethod());
                             if (method != null) {
-                                if ((method.getModifiers() & Modifier.FINAL) != 0 || (type.getModifiers() & Modifier.FINAL) != 0) {
+                                if (Modifier.isFinal(method.getModifiers()) || Modifier.isFinal(type.getModifiers())) {
                                     callTarget.setInvokeKind(InvokeKind.Special);
                                     callTarget.setTargetMethod(method);
                                 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LockEliminationPhase.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LockEliminationPhase.java	Wed Jan 29 15:02:19 2014 -0800
@@ -36,19 +36,8 @@
             if (next instanceof MonitorEnterNode) {
                 MonitorEnterNode monitorEnterNode = (MonitorEnterNode) next;
                 if (monitorEnterNode.object() == node.object()) {
-                    FixedNode monitorEnterSuccessor = monitorEnterNode.next();
-                    monitorEnterNode.setNext(null);
-                    ((FixedWithNextNode) node.predecessor()).setNext(monitorEnterSuccessor);
-                    FrameState stateAfterFirst = node.stateAfter();
-                    FrameState stateAfterSecond = monitorEnterNode.stateAfter();
-                    node.safeDelete();
-                    monitorEnterNode.safeDelete();
-                    if (stateAfterFirst.usages().isEmpty()) {
-                        GraphUtil.killWithUnusedFloatingInputs(stateAfterFirst);
-                    }
-                    if (stateAfterSecond.usages().isEmpty()) {
-                        GraphUtil.killWithUnusedFloatingInputs(stateAfterSecond);
-                    }
+                    GraphUtil.removeFixedWithUnusedInputs(monitorEnterNode);
+                    GraphUtil.removeFixedWithUnusedInputs(node);
                 }
             }
         }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Wed Jan 29 15:02:19 2014 -0800
@@ -569,11 +569,7 @@
                     LoopTransformations.fullUnroll(loop, phaseContext, new CanonicalizerPhase(true));
                     new CanonicalizerPhase(true).applyIncremental(snippetCopy, phaseContext, mark);
                 }
-                FixedNode explodeLoopNext = explodeLoop.next();
-                explodeLoop.clearSuccessors();
-                explodeLoop.replaceAtPredecessor(explodeLoopNext);
-                explodeLoop.replaceAtUsages(null);
-                GraphUtil.killCFG(explodeLoop);
+                GraphUtil.removeFixedWithUnusedInputs(explodeLoop);
                 exploded = true;
             }
         } while (exploded);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ControlFlowExceptionPartialEvaluationTest.java	Wed Jan 29 15:02:19 2014 -0800
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2014, 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.truffle.test;
+
+import org.junit.*;
+
+import com.oracle.graal.truffle.test.nodes.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+@Ignore("Currently ignored due to problems with code coverage tools.")
+public class ControlFlowExceptionPartialEvaluationTest extends PartialEvaluationTest {
+
+    public static Object constant42() {
+        return 42;
+    }
+
+    @Test
+    public void catchControlFlowException() {
+        FrameDescriptor fd = new FrameDescriptor();
+        AbstractTestNode result = new CatchControlFlowExceptionTestNode(new ThrowControlFlowExceptionTestNode());
+        assertPartialEvalEquals("constant42", new RootTestNode(fd, "catchControlFlowException", result));
+    }
+
+    @Test
+    public void catchSlowPathAndControlFlowException() {
+        FrameDescriptor fd = new FrameDescriptor();
+        AbstractTestNode result = new CatchSlowPathAndControlFlowExceptionTestNode(new ThrowControlFlowExceptionTestNode());
+        assertPartialEvalEquals("constant42", new RootTestNode(fd, "catchSlowPathAndControlFlowException", result));
+    }
+
+    public static class ThrowControlFlowExceptionTestNode extends AbstractTestNode {
+
+        @Override
+        public int execute(VirtualFrame frame) {
+            throw new ControlFlowException();
+        }
+    }
+
+    public static class CatchControlFlowExceptionTestNode extends AbstractTestNode {
+
+        @Child private AbstractTestNode child;
+
+        public CatchControlFlowExceptionTestNode(AbstractTestNode child) {
+            this.child = adoptChild(child);
+        }
+
+        @Override
+        public int execute(VirtualFrame frame) {
+            try {
+                return child.execute(frame);
+            } catch (ControlFlowException e) {
+                return 42;
+            }
+        }
+    }
+
+    public static class CatchSlowPathAndControlFlowExceptionTestNode extends AbstractTestNode {
+
+        @Child private AbstractTestNode child;
+
+        public CatchSlowPathAndControlFlowExceptionTestNode(AbstractTestNode child) {
+            this.child = adoptChild(child);
+        }
+
+        @Override
+        public int execute(VirtualFrame frame) {
+            try {
+                return executeChild(frame);
+            } catch (SlowPathException spe) {
+                return -1;
+            } catch (ControlFlowException e) {
+                return 42;
+            }
+        }
+
+        @SuppressWarnings("unused")
+        private int executeChild(VirtualFrame frame) throws SlowPathException {
+            return child.execute(frame);
+        }
+    }
+}
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Wed Jan 29 15:02:19 2014 -0800
@@ -27,7 +27,6 @@
 import org.junit.*;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.runtime.*;
 import com.oracle.graal.compiler.test.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.Debug.Scope;
@@ -39,9 +38,7 @@
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.phases.util.*;
 import com.oracle.graal.printer.*;
-import com.oracle.graal.runtime.*;
 import com.oracle.graal.truffle.*;
 import com.oracle.graal.virtual.phases.ea.*;
 import com.oracle.truffle.api.*;
@@ -50,15 +47,12 @@
 public class PartialEvaluationTest extends GraalCompilerTest {
 
     private static final long UNROLL_LIMIT = 100;
-    private final PartialEvaluator partialEvaluator;
+    private final TruffleCompilerImpl truffleCompiler;
 
     public PartialEvaluationTest() {
         // Make sure Truffle runtime is initialized.
         Assert.assertTrue(Truffle.getRuntime() instanceof GraalTruffleRuntime);
-        Replacements truffleReplacements = ((GraalTruffleRuntime) Truffle.getRuntime()).getReplacements();
-        Providers providers = getProviders().copyWith(truffleReplacements);
-        TruffleCache truffleCache = new TruffleCache(providers, GraphBuilderConfiguration.getDefault(), TruffleCompilerImpl.Optimizations);
-        this.partialEvaluator = new PartialEvaluator(Graal.getRequiredCapability(RuntimeProvider.class), providers, truffleCache);
+        this.truffleCompiler = new TruffleCompilerImpl();
 
         DebugEnvironment.initialize(System.out);
     }
@@ -70,7 +64,7 @@
     protected InstalledCode assertPartialEvalEquals(String methodName, RootNode root, Arguments arguments) {
         Assumptions assumptions = new Assumptions(true);
         StructuredGraph actual = partialEval(root, arguments, assumptions, true);
-        InstalledCode result = new TruffleCompilerImpl().compileMethodHelper(actual, GraphBuilderConfiguration.getDefault(), assumptions, root.toString(), getSpeculationLog());
+        InstalledCode result = truffleCompiler.compileMethodHelper(actual, assumptions, root.toString(), getSpeculationLog());
         StructuredGraph expected = parseForComparison(methodName);
         removeFrameStates(actual);
         Assert.assertEquals(getCanonicalGraphString(expected, true), getCanonicalGraphString(actual, true));
@@ -102,7 +96,7 @@
 
         try (Scope s = Debug.scope("TruffleCompilation", new TruffleDebugJavaMethod(compilable))) {
 
-            StructuredGraph resultGraph = partialEvaluator.createGraph(compilable, assumptions);
+            StructuredGraph resultGraph = truffleCompiler.getPartialEvaluator().createGraph(compilable, assumptions);
             CanonicalizerPhase canonicalizer = new CanonicalizerPhase(canonicalizeReads);
             PhaseContext context = new PhaseContext(getProviders(), assumptions);
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Wed Jan 29 15:02:19 2014 -0800
@@ -56,7 +56,6 @@
         super(rootNode);
         this.compiler = compiler;
         this.compilationProfile = new CompilationProfile(compilationThreshold, invokeCounter, rootNode.toString());
-        this.getRootNode().setCallTarget(this);
 
         if (TruffleUseTimeForCompilationDecision.getValue()) {
             compilationPolicy = new TimedCompilationPolicy();
@@ -261,8 +260,8 @@
             }
 
             int notInlinedCallSiteCount = TruffleInliningImpl.getInlinableCallSites(callTarget).size();
-            int nodeCount = NodeUtil.countNodes(callTarget.getRootNode());
-            int inlinedCallSiteCount = NodeUtil.countNodes(callTarget.getRootNode(), InlinedCallSite.class);
+            int nodeCount = NodeUtil.countNodes(callTarget.getRootNode(), null, true);
+            int inlinedCallSiteCount = countInlinedNodes(callTarget.getRootNode());
             String comment = callTarget.installedCode == null ? " int" : "";
             comment += callTarget.compilationEnabled ? "" : " fail";
             OUT.printf("%-50s | %10d | %15d | %15d | %10d | %3d%s\n", callTarget.getRootNode(), callTarget.callCount, inlinedCallSiteCount, notInlinedCallSiteCount, nodeCount,
@@ -277,6 +276,18 @@
         OUT.printf("%-50s | %10d | %15d | %15d | %10d | %3d\n", "Total", totalCallCount, totalInlinedCallSiteCount, totalNotInlinedCallSiteCount, totalNodeCount, totalInvalidationCount);
     }
 
+    private static int countInlinedNodes(Node rootNode) {
+        List<CallNode> callers = NodeUtil.findAllNodeInstances(rootNode, CallNode.class);
+        int count = 0;
+        for (CallNode callNode : callers) {
+            if (callNode.isInlined()) {
+                count++;
+                count += countInlinedNodes(callNode.getInlinedRoot());
+            }
+        }
+        return count;
+    }
+
     private static void registerCallTarget(OptimizedCallTarget callTarget) {
         callTargets.put(callTarget, 0);
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Wed Jan 29 15:02:19 2014 -0800
@@ -68,16 +68,16 @@
     private final Providers providers;
     private final ResolvedJavaMethod executeHelperMethod;
     private final CanonicalizerPhase canonicalizer;
-    private final ResolvedJavaType[] skippedExceptionTypes;
+    private final GraphBuilderConfiguration config;
     private Set<Constant> constantReceivers;
     private final GraphCache cache;
     private final TruffleCache truffleCache;
 
-    public PartialEvaluator(RuntimeProvider runtime, Providers providers, TruffleCache truffleCache) {
+    public PartialEvaluator(RuntimeProvider runtime, Providers providers, TruffleCache truffleCache, GraphBuilderConfiguration config) {
         this.providers = providers;
         CustomCanonicalizer customCanonicalizer = new PartialEvaluatorCanonicalizer(providers.getMetaAccess(), providers.getConstantReflection());
         this.canonicalizer = new CanonicalizerPhase(!ImmutableCode.getValue(), customCanonicalizer);
-        this.skippedExceptionTypes = TruffleCompilerImpl.getSkippedExceptionTypes(providers.getMetaAccess());
+        this.config = config;
         this.cache = runtime.getGraphCache();
         this.truffleCache = truffleCache;
         try {
@@ -98,9 +98,6 @@
             constantReceivers = new HashSet<>();
         }
 
-        final GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault();
-        config.setSkippedExceptionTypes(skippedExceptionTypes);
-
         final StructuredGraph graph = new StructuredGraph(executeHelperMethod);
 
         try (Scope s = Debug.scope("createGraph", graph)) {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Wed Jan 29 15:02:19 2014 -0800
@@ -36,7 +36,6 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.runtime.*;
 import com.oracle.graal.compiler.*;
-import com.oracle.graal.compiler.CompilerThreadFactory.*;
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.Debug.Scope;
@@ -65,15 +64,16 @@
     private final Suites suites;
     private final PartialEvaluator partialEvaluator;
     private final Backend backend;
-    private final ResolvedJavaType[] skippedExceptionTypes;
+    private final GraphBuilderConfiguration config;
     private final RuntimeProvider runtime;
     private final TruffleCache truffleCache;
     private final ThreadPoolExecutor compileQueue;
 
-    private static final Class[] SKIPPED_EXCEPTION_CLASSES = new Class[]{SlowPathException.class, UnexpectedResultException.class, ArithmeticException.class};
+    private static final Class[] SKIPPED_EXCEPTION_CLASSES = new Class[]{UnexpectedResultException.class, SlowPathException.class, ArithmeticException.class};
 
     public static final OptimisticOptimizations Optimizations = OptimisticOptimizations.ALL.remove(OptimisticOptimizations.Optimization.UseExceptionProbability,
                     OptimisticOptimizations.Optimization.RemoveNeverExecutedCode, OptimisticOptimizations.Optimization.UseTypeCheckedInlining, OptimisticOptimizations.Optimization.UseTypeCheckHints);
+    private static final OptimisticOptimizations OptimizationsGraal = OptimisticOptimizations.ALL.remove(OptimisticOptimizations.Optimization.UseExceptionProbability);
 
     public TruffleCompilerImpl() {
         this.runtime = Graal.getRequiredCapability(RuntimeProvider.class);
@@ -81,10 +81,9 @@
         Replacements truffleReplacements = ((GraalTruffleRuntime) Truffle.getRuntime()).getReplacements();
         this.providers = backend.getProviders().copyWith(truffleReplacements);
         this.suites = backend.getSuites().getDefaultSuites();
-        this.skippedExceptionTypes = getSkippedExceptionTypes(providers.getMetaAccess());
 
         // Create compilation queue.
-        CompilerThreadFactory factory = new CompilerThreadFactory("TruffleCompilerThread", new DebugConfigAccess() {
+        CompilerThreadFactory factory = new CompilerThreadFactory("TruffleCompilerThread", new CompilerThreadFactory.DebugConfigAccess() {
             public GraalDebugConfig getDebugConfig() {
                 if (Debug.isEnabled()) {
                     GraalDebugConfig debugConfig = DebugEnvironment.initialize(TTY.out().out());
@@ -97,18 +96,21 @@
         });
         compileQueue = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory);
 
-        final GraphBuilderConfiguration config = GraphBuilderConfiguration.getEagerDefault();
-        config.setSkippedExceptionTypes(skippedExceptionTypes);
-        this.truffleCache = new TruffleCache(providers, config, TruffleCompilerImpl.Optimizations);
+        ResolvedJavaType[] skippedExceptionTypes = getSkippedExceptionTypes(providers.getMetaAccess());
+        GraphBuilderConfiguration eagerConfig = GraphBuilderConfiguration.getEagerDefault();
+        eagerConfig.setSkippedExceptionTypes(skippedExceptionTypes);
+        this.truffleCache = new TruffleCache(providers, eagerConfig, TruffleCompilerImpl.Optimizations);
 
-        this.partialEvaluator = new PartialEvaluator(runtime, providers, truffleCache);
+        this.config = GraphBuilderConfiguration.getDefault();
+        this.config.setSkippedExceptionTypes(skippedExceptionTypes);
+        this.partialEvaluator = new PartialEvaluator(runtime, providers, truffleCache, config);
 
         if (Debug.isEnabled()) {
             DebugEnvironment.initialize(System.out);
         }
     }
 
-    static ResolvedJavaType[] getSkippedExceptionTypes(MetaAccessProvider metaAccess) {
+    private static ResolvedJavaType[] getSkippedExceptionTypes(MetaAccessProvider metaAccess) {
         ResolvedJavaType[] skippedExceptionTypes = new ResolvedJavaType[SKIPPED_EXCEPTION_CLASSES.length];
         for (int i = 0; i < SKIPPED_EXCEPTION_CLASSES.length; i++) {
             skippedExceptionTypes[i] = metaAccess.lookupJavaType(SKIPPED_EXCEPTION_CLASSES[i]);
@@ -135,8 +137,6 @@
 
     private InstalledCode compileMethodImpl(final OptimizedCallTarget compilable) {
         final StructuredGraph graph;
-        final GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault();
-        config.setSkippedExceptionTypes(skippedExceptionTypes);
         GraphCache graphCache = runtime.getGraphCache();
         if (graphCache != null) {
             graphCache.removeStaleGraphs();
@@ -156,7 +156,7 @@
         }
         long timePartialEvaluationFinished = System.nanoTime();
         int nodeCountPartialEval = graph.getNodeCount();
-        InstalledCode compiledMethod = compileMethodHelper(graph, config, assumptions, compilable.toString(), compilable.getSpeculationLog());
+        InstalledCode compiledMethod = compileMethodHelper(graph, assumptions, compilable.toString(), compilable.getSpeculationLog());
         long timeCompilationFinished = System.nanoTime();
         int nodeCountLowered = graph.getNodeCount();
 
@@ -168,7 +168,7 @@
         }
 
         if (TraceTruffleCompilation.getValue()) {
-            int nodeCountTruffle = NodeUtil.countNodes(compilable.getRootNode());
+            int nodeCountTruffle = NodeUtil.countNodes(compilable.getRootNode(), null, true);
             byte[] code = compiledMethod.getCode();
             OUT.printf("[truffle] optimized %-50s %x |Nodes %7d |Time %5.0f(%4.0f+%-4.0f)ms |Nodes %5d/%5d |CodeSize %d\n", compilable.getRootNode(), compilable.hashCode(), nodeCountTruffle,
                             (timeCompilationFinished - timeCompilationStarted) / 1e6, (timePartialEvaluationFinished - timeCompilationStarted) / 1e6,
@@ -186,29 +186,34 @@
     private class InlineTreeVisitor implements NodeVisitor {
 
         public boolean visit(Node node) {
-            if (node instanceof InlinedCallSite) {
-                InlinedCallSite inlinedCallSite = (InlinedCallSite) node;
-                int indent = this.indent(node);
-                for (int i = 0; i < indent; ++i) {
-                    OUT.print("   ");
+            if (node instanceof CallNode) {
+                CallNode callNode = (CallNode) node;
+                if (callNode.isInlined()) {
+                    int indent = this.indent(node);
+                    for (int i = 0; i < indent; ++i) {
+                        OUT.print("   ");
+                    }
+                    OUT.println(callNode.getCallTarget());
+                    callNode.getInlinedRoot().accept(this);
                 }
-                OUT.println(inlinedCallSite.getCallTarget());
             }
             return true;
         }
 
         private int indent(Node n) {
             if (n instanceof RootNode) {
+                CallNode inlinedParent = ((RootNode) n).getParentInlinedCall();
+                if (inlinedParent != null) {
+                    return indent(inlinedParent) + 1;
+                }
                 return 0;
-            } else if (n instanceof InlinedCallSite) {
-                return indent(n.getParent()) + 1;
             } else {
                 return indent(n.getParent());
             }
         }
     }
 
-    public InstalledCode compileMethodHelper(StructuredGraph graph, GraphBuilderConfiguration config, Assumptions assumptions, String name, SpeculationLog speculationLog) {
+    public InstalledCode compileMethodHelper(StructuredGraph graph, Assumptions assumptions, String name, SpeculationLog speculationLog) {
         try (Scope s = Debug.scope("TruffleFinal")) {
             Debug.dump(graph, "After TruffleTier");
         } catch (Throwable e) {
@@ -220,8 +225,8 @@
             CodeCacheProvider codeCache = providers.getCodeCache();
             CallingConvention cc = getCallingConvention(codeCache, Type.JavaCallee, graph.method(), false);
             CompilationResult compilationResult = new CompilationResult(name);
-            result = compileGraph(graph, cc, graph.method(), providers, backend, codeCache.getTarget(), null, createGraphBuilderSuite(config), OptimisticOptimizations.ALL, getProfilingInfo(graph),
-                            speculationLog, suites, false, compilationResult, CompilationResultBuilderFactory.Default);
+            result = compileGraph(graph, cc, graph.method(), providers, backend, codeCache.getTarget(), null, createGraphBuilderSuite(), OptimizationsGraal, getProfilingInfo(graph), speculationLog,
+                            suites, false, compilationResult, CompilationResultBuilderFactory.Default);
         } catch (Throwable e) {
             throw Debug.handle(e);
         }
@@ -259,7 +264,7 @@
         return installedCode;
     }
 
-    private PhaseSuite<HighTierContext> createGraphBuilderSuite(GraphBuilderConfiguration config) {
+    private PhaseSuite<HighTierContext> createGraphBuilderSuite() {
         PhaseSuite<HighTierContext> suite = backend.getSuites().getDefaultGraphBuilderSuite().copy();
         ListIterator<BasePhase<? super HighTierContext>> iterator = suite.findPhase(GraphBuilderPhase.class);
         iterator.remove();
@@ -277,4 +282,8 @@
             }
         }
     }
+
+    public PartialEvaluator getPartialEvaluator() {
+        return partialEvaluator;
+    }
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningImpl.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningImpl.java	Wed Jan 29 15:02:19 2014 -0800
@@ -73,7 +73,7 @@
             if (!policy.isWorthInlining(inlinableCallSite)) {
                 break;
             }
-            if (inlinableCallSite.getCallSite().inline(target)) {
+            if (inlinableCallSite.getCallSite().inline()) {
                 if (TraceTruffleInlining.getValue()) {
                     printCallSiteInfo(policy, inlinableCallSite, "inlined");
                 }
@@ -84,7 +84,7 @@
 
         if (inlined) {
             for (InlinableCallSiteInfo callSite : inlinableCallSites) {
-                callSite.getCallSite().resetCallCount();
+                CallNode.internalResetCallCount(callSite.getCallSite());
             }
         } else {
             if (TraceTruffleInliningDetails.getValue()) {
@@ -114,7 +114,7 @@
         private final int callerInvocationCount;
 
         public InliningPolicy(OptimizedCallTarget caller) {
-            this.callerNodeCount = NodeUtil.countNodes(caller.getRootNode());
+            this.callerNodeCount = NodeUtil.countNodes(caller.getRootNode(), null, true);
             this.callerInvocationCount = caller.getCompilationProfile().getOriginalInvokeCounter();
         }
 
@@ -155,15 +155,17 @@
 
     private static final class InlinableCallSiteInfo {
 
-        private final InlinableCallSite callSite;
+        private final CallNode callSite;
         private final int callCount;
         private final int nodeCount;
         private final int recursiveDepth;
 
-        public InlinableCallSiteInfo(InlinableCallSite callSite) {
+        public InlinableCallSiteInfo(CallNode callSite) {
+            assert callSite.isInlinable();
             this.callSite = callSite;
-            this.callCount = callSite.getCallCount();
-            this.nodeCount = NodeUtil.countNodes(callSite.getInlineTree());
+            this.callCount = CallNode.internalGetCallCount(callSite);
+            RootCallTarget target = (RootCallTarget) callSite.getCallTarget();
+            this.nodeCount = target.getRootNode().getInlineNodeCount();
             this.recursiveDepth = calculateRecursiveDepth();
         }
 
@@ -173,21 +175,23 @@
 
         private int calculateRecursiveDepth() {
             int depth = 0;
-            Node parent = ((Node) callSite).getParent();
-            while (!(parent instanceof RootNode)) {
-                assert parent != null;
-                if (parent instanceof InlinedCallSite && ((InlinedCallSite) parent).getCallTarget() == callSite.getCallTarget()) {
-                    depth++;
+
+            Node parent = callSite.getParent();
+            while (parent != null) {
+                if (parent instanceof RootNode) {
+                    RootNode root = ((RootNode) parent);
+                    if (root.getCallTarget() == callSite.getCallTarget()) {
+                        depth++;
+                    }
+                    parent = root.getParentInlinedCall();
+                } else {
+                    parent = parent.getParent();
                 }
-                parent = parent.getParent();
-            }
-            if (((RootNode) parent).getCallTarget() == callSite.getCallTarget()) {
-                depth++;
             }
             return depth;
         }
 
-        public InlinableCallSite getCallSite() {
+        public CallNode getCallSite() {
             return callSite;
         }
 
@@ -206,8 +210,15 @@
 
             @Override
             public boolean visit(Node node) {
-                if (node instanceof InlinableCallSite) {
-                    inlinableCallSites.add(new InlinableCallSiteInfo((InlinableCallSite) node));
+                if (node instanceof CallNode) {
+                    CallNode callNode = (CallNode) node;
+                    if (!callNode.isInlined()) {
+                        if (callNode.isInlinable()) {
+                            inlinableCallSites.add(new InlinableCallSiteInfo(callNode));
+                        }
+                    } else {
+                        callNode.getInlinedRoot().accept(this);
+                    }
                 }
                 return true;
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/UnsupportedSpecializationTest.java	Wed Jan 29 15:02:19 2014 -0800
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2012, 2013, 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.truffle.api.dsl.test;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.UnsupportedSpecializationTestFactory.Undefined1Factory;
+
+public class UnsupportedSpecializationTest {
+
+    @Test
+    public void testUndefined1() {
+        TestRootNode<Undefined1> root = TestHelper.createRoot(Undefined1Factory.getInstance());
+        try {
+            TestHelper.executeWith(root, "");
+            Assert.fail();
+        } catch (UnsupportedSpecializationException e) {
+            Assert.assertNotNull(e.getSuppliedValues());
+            Assert.assertEquals(1, e.getSuppliedValues().length);
+            Assert.assertEquals("", e.getSuppliedValues()[0]);
+            Assert.assertEquals(root.getNode(), e.getNode());
+        }
+    }
+
+    @NodeChild("a")
+    abstract static class Undefined1 extends ValueNode {
+
+        @Specialization
+        public int doInteger(@SuppressWarnings("unused") int a) {
+            throw new AssertionError();
+        }
+    }
+
+    // TODO more tests required
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/UnsupportedSpecializationException.java	Wed Jan 29 15:02:19 2014 -0800
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2012, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.dsl;
+
+import java.util.*;
+
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * Thrown by the generated code of Truffle-DSL if no compatible Specialization could be found for
+ * the provided values.
+ */
+public final class UnsupportedSpecializationException extends RuntimeException {
+
+    private static final long serialVersionUID = -2122892028296836269L;
+
+    private final Node node;
+    private final Object[] suppliedValues;
+
+    public UnsupportedSpecializationException(Node node, Object... suppliedValues) {
+        super("Unexpected values provided for " + node + ": " + Arrays.toString(suppliedValues));
+        this.node = node;
+        this.suppliedValues = suppliedValues;
+    }
+
+    /**
+     * Returns the {@link Node} that caused the this {@link UnsupportedSpecializationException}.
+     */
+    public Node getNode() {
+        return node;
+    }
+
+    /**
+     * Returns the dynamic values that were supplied to the node.
+     */
+    public Object[] getSuppliedValues() {
+        return suppliedValues;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java	Wed Jan 29 15:02:19 2014 -0800
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2012, 2013, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.nodes;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.impl.*;
+
+/**
+ * This node represents a call to a static {@link CallTarget}. This node should be used whenever a
+ * {@link CallTarget} is considered constant at a certain location in the tree. This enables the
+ * Truffle runtime to perform inlining or other optimizations for this call-site.
+ * 
+ * @see #create(CallTarget) to create a CallNode instance.
+ */
+public abstract class CallNode extends Node {
+
+    protected final CallTarget callTarget;
+
+    private CallNode(CallTarget callTarget) {
+        this.callTarget = callTarget;
+    }
+
+    /**
+     * @return the constant {@link CallTarget} that is associated with this {@link CallNode}.
+     */
+    public CallTarget getCallTarget() {
+        return callTarget;
+    }
+
+    /**
+     * Calls this constant target passing a caller frame and arguments.
+     * 
+     * @param caller the caller frame
+     * @param arguments the arguments that should be passed to the callee
+     * @return the return result of the call
+     */
+    public abstract Object call(PackedFrame caller, Arguments arguments);
+
+    /**
+     * Returns <code>true</code> if the {@link CallTarget} contained in this {@link CallNode} can be
+     * inlined. A {@link CallTarget} is considered inlinable if it was created using
+     * {@link TruffleRuntime#createCallTarget(RootNode)} and if the enclosed {@link RootNode}
+     * returns <code>true</code> for {@link RootNode#isInlinable()}.
+     */
+    public abstract boolean isInlinable();
+
+    /**
+     * @return true if this {@link CallNode} was already inlined.
+     */
+    public abstract boolean isInlined();
+
+    /**
+     * Enforces an inlining optimization on this {@link CallNode} instance. If not performed
+     * manually the Truffle runtime may perform inlining using an heuristic to optimize the
+     * performance of the execution. It is recommended to implement an version of
+     * {@link RootNode#inline()} that adapts the inlining for possible guest language specific
+     * behavior. If the this {@link CallNode} is not inlinable or is already inlined
+     * <code>false</code> is returned.
+     * 
+     * @return <code>true</code> if the inlining operation was successful.
+     */
+    public abstract boolean inline();
+
+    /**
+     * Returns the inlined root node if the call node was inlined. If the {@link CallNode} was not
+     * inlined <code>null</code> is returned.
+     * 
+     * @return the inlined root node returned by {@link RootNode#inline()}
+     */
+    public RootNode getInlinedRoot() {
+        return null;
+    }
+
+    /**
+     * Creates a new {@link CallNode} using a {@link CallTarget}.
+     * 
+     * @param target the {@link CallTarget} to call
+     * @return a call node that calls the provided target
+     */
+    public static CallNode create(CallTarget target) {
+        if (isInlinable(target)) {
+            return new InlinableCallNode(target);
+        } else {
+            return new DefaultCallNode(target);
+        }
+    }
+
+    /**
+     * Warning: this is internal API and may change without notice.
+     */
+    public static int internalGetCallCount(CallNode callNode) {
+        if (callNode.isInlinable() && !callNode.isInlined()) {
+            return ((InlinableCallNode) callNode).getCallCount();
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Warning: this is internal API and may change without notice.
+     */
+    public static void internalResetCallCount(CallNode callNode) {
+        if (callNode.isInlinable() && !callNode.isInlined()) {
+            ((InlinableCallNode) callNode).resetCallCount();
+            return;
+        }
+    }
+
+    private static boolean isInlinable(CallTarget callTarget) {
+        if (callTarget instanceof DefaultCallTarget) {
+            return (((DefaultCallTarget) callTarget).getRootNode()).isInlinable();
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return getParent() != null ? getParent().toString() : super.toString();
+    }
+
+    static final class DefaultCallNode extends CallNode {
+
+        public DefaultCallNode(CallTarget target) {
+            super(target);
+        }
+
+        @Override
+        public Object call(PackedFrame caller, Arguments arguments) {
+            return callTarget.call(caller, arguments);
+        }
+
+        @Override
+        public boolean inline() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlinable() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlined() {
+            return false;
+        }
+
+    }
+
+    static final class InlinableCallNode extends CallNode {
+
+        private int callCount;
+
+        public InlinableCallNode(CallTarget target) {
+            super(target);
+        }
+
+        @Override
+        public Object call(PackedFrame parentFrame, Arguments arguments) {
+            if (CompilerDirectives.inInterpreter()) {
+                callCount++;
+            }
+            return callTarget.call(parentFrame, arguments);
+        }
+
+        @Override
+        public boolean inline() {
+            DefaultCallTarget defaultTarget = (DefaultCallTarget) getCallTarget();
+            RootNode originalRootNode = defaultTarget.getRootNode();
+            if (originalRootNode.isInlinable()) {
+                RootNode inlinedRootNode = defaultTarget.getRootNode().inline();
+                inlinedRootNode.setCallTarget(callTarget);
+                inlinedRootNode.setParentInlinedCall(this);
+                replace(new InlinedCallNode(defaultTarget, inlinedRootNode));
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean isInlined() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlinable() {
+            return true;
+        }
+
+        /* Truffle internal API. */
+        int getCallCount() {
+            return callCount;
+        }
+
+        /* Truffle internal API. */
+        void resetCallCount() {
+            callCount = 0;
+        }
+
+    }
+
+    static final class InlinedCallNode extends CallNode {
+
+        private final RootNode inlinedRoot;
+
+        public InlinedCallNode(DefaultCallTarget callTarget, RootNode inlinedRoot) {
+            super(callTarget);
+            this.inlinedRoot = inlinedRoot;
+        }
+
+        @Override
+        public Object call(PackedFrame caller, Arguments arguments) {
+            return inlinedRoot.execute(Truffle.getRuntime().createVirtualFrame(caller, arguments, inlinedRoot.getFrameDescriptor()));
+        }
+
+        @Override
+        public InlinedCallNode copy() {
+            return new InlinedCallNode((DefaultCallTarget) getCallTarget(), NodeUtil.cloneNode(inlinedRoot));
+        }
+
+        @Override
+        public RootNode getInlinedRoot() {
+            return inlinedRoot;
+        }
+
+        @Override
+        public boolean inline() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlinable() {
+            return true;
+        }
+
+        @Override
+        public boolean isInlined() {
+            return true;
+        }
+
+    }
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/InlinableCallSite.java	Wed Jan 29 14:06:32 2014 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.nodes;
-
-import com.oracle.truffle.api.*;
-
-/**
- * Denotes a call node that can inline the tree of its associated call target.
- * 
- * @see InlinedCallSite
- */
-public interface InlinableCallSite {
-
-    /**
-     * Returns the number of calls since the last reset of the call count.
-     * 
-     * @return the current call count.
-     */
-    int getCallCount();
-
-    /**
-     * Resets the call count to 0.
-     */
-    void resetCallCount();
-
-    /**
-     * Returns the tree that would be inlined by a call to {@link #inline(FrameFactory)}.
-     * 
-     * @return the node tree to be inlined.
-     */
-    Node getInlineTree();
-
-    /**
-     * Returns the call target associated with this call site.
-     * 
-     * @return the inlinable {@link CallTarget}.
-     */
-    CallTarget getCallTarget();
-
-    /**
-     * Instructs the call node to inline the associated call target.
-     * 
-     * @param factory Frame factory for creating new virtual frames for inlined calls.
-     * @return {@code true} if call target was inlined; {@code false} otherwise.
-     */
-    boolean inline(FrameFactory factory);
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/InlinedCallSite.java	Wed Jan 29 14:06:32 2014 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.nodes;
-
-import com.oracle.truffle.api.*;
-
-/**
- * Denotes a call node with an inlined call target. Allows for recursive call detection.
- * 
- * @see InlinableCallSite
- */
-public interface InlinedCallSite {
-
-    /**
-     * Returns the call target that has been inlined at this call site.
-     * 
-     * @return the inlined call target.
-     */
-    CallTarget getCallTarget();
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Wed Jan 29 15:02:19 2014 -0800
@@ -246,7 +246,7 @@
     }
 
     private void reportReplace() {
-        RootNode rootNode = getRootNode();
+        RootNode rootNode = NodeUtil.findOutermostRootNode(this);
         if (rootNode != null) {
             if (rootNode.getCallTarget() instanceof ReplaceObserver) {
                 ((ReplaceObserver) rootNode.getCallTarget()).nodeReplaced();
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Wed Jan 29 15:02:19 2014 -0800
@@ -451,6 +451,31 @@
         return null;
     }
 
+    /**
+     * Returns the outermost not inlined {@link RootNode} which is a parent of this node.
+     * 
+     * @see RootNode#getParentInlinedCall()
+     * @param node to search
+     * @return the outermost {@link RootNode}
+     */
+    public static RootNode findOutermostRootNode(Node node) {
+        Node parent = node;
+        while (parent != null) {
+            if (parent instanceof RootNode) {
+                RootNode root = (RootNode) parent;
+                Node next = root.getParentInlinedCall();
+                if (next != null) {
+                    parent = next;
+                } else {
+                    return root;
+                }
+            } else {
+                parent = parent.getParent();
+            }
+        }
+        return null;
+    }
+
     public static <T> T findParent(Node start, Class<T> clazz) {
         Node parent = start.getParent();
         if (parent == null) {
@@ -571,24 +596,26 @@
     }
 
     public static int countNodes(Node root) {
-        return countNodes(root, null);
+        return countNodes(root, null, false);
     }
 
-    public static int countNodes(Node root, Class<?> clazz) {
-        NodeCountVisitor nodeCount = new NodeCountVisitor(root, clazz);
+    public static int countNodes(Node root, Class<?> clazz, boolean countInlinedCallNodes) {
+        NodeCountVisitor nodeCount = new NodeCountVisitor(root, clazz, countInlinedCallNodes);
         root.accept(nodeCount);
         return nodeCount.nodeCount;
     }
 
     private static final class NodeCountVisitor implements NodeVisitor {
 
+        private Node root;
+        private boolean inspectInlinedCalls;
         int nodeCount;
-        private final Node root;
         private final Class<?> clazz;
 
-        private NodeCountVisitor(Node root, Class<?> clazz) {
+        private NodeCountVisitor(Node root, Class<?> clazz, boolean inspectInlinedCalls) {
             this.root = root;
             this.clazz = clazz;
+            this.inspectInlinedCalls = inspectInlinedCalls;
         }
 
         @Override
@@ -596,9 +623,18 @@
             if (node instanceof RootNode && node != root) {
                 return false;
             }
+
             if (clazz == null || clazz.isInstance(node)) {
                 nodeCount++;
             }
+
+            if (inspectInlinedCalls && node instanceof CallNode) {
+                CallNode call = (CallNode) node;
+                if (call.isInlined()) {
+                    call.getInlinedRoot().getChildren().iterator().next().accept(this);
+                }
+            }
+
             return true;
         }
     }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Wed Jan 29 15:02:19 2014 -0800
@@ -25,6 +25,7 @@
 package com.oracle.truffle.api.nodes;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.*;
 import com.oracle.truffle.api.frame.*;
 
 /**
@@ -37,6 +38,12 @@
     private CallTarget callTarget;
     private final FrameDescriptor frameDescriptor;
 
+    /*
+     * Internal field to keep reference to the inlined call node. The inlined parent should not be
+     * the same as the Node parent to keep the same tree hierarchy if inlined vs not inlined.
+     */
+    @CompilationFinal private CallNode parentInlinedCall;
+
     protected RootNode() {
         this(null, null);
     }
@@ -55,6 +62,58 @@
     }
 
     /**
+     * Creates a copy of the current {@link RootNode} for use as inlined AST. The default
+     * implementation copies this {@link RootNode} and all its children recursively. It is
+     * recommended to override this method to provide an implementation that copies an uninitialized
+     * version of this AST. An uninitialized version of an AST was usually never executed which
+     * means that it has not yet collected any profiling feedback. Please note that changes in the
+     * behavior of this method might also require changes in {@link #getInlineNodeCount()}.
+     * 
+     * @see RootNode#getInlineNodeCount()
+     * @see RootNode#isInlinable()
+     * 
+     * @return the copied RootNode for inlining
+     * @throws UnsupportedOperationException if {@link #isInlinable()} returns false
+     */
+    public RootNode inline() {
+        if (!isInlinable()) {
+            throw new UnsupportedOperationException("Inlining is not enabled.");
+        }
+        return NodeUtil.cloneNode(this);
+    }
+
+    /**
+     * Returns the number of nodes that would be returned if {@link #inline()} would get invoked.
+     * This node count may be used for the calculation in a smart inlining heuristic.
+     * 
+     * @see RootNode#inline()
+     * @see RootNode#isInlinable()
+     * 
+     * @return the number of nodes that will get inlined
+     * @throws UnsupportedOperationException if {@link #isInlinable()} returns false
+     */
+    public int getInlineNodeCount() {
+        if (!isInlinable()) {
+            throw new UnsupportedOperationException("Inlining is not enabled.");
+        }
+        return NodeUtil.countNodes(this);
+    }
+
+    /**
+     * Returns true if this RootNode can be inlined. If this method returns true implementations of
+     * {@link #inline()} and {@link #getInlineNodeCount()} must be provided. Returns
+     * <code>true</code> by default.
+     * 
+     * @see RootNode#inline()
+     * @see RootNode#getInlineNodeCount()
+     * 
+     * @return true if this RootNode can be inlined
+     */
+    public boolean isInlinable() {
+        return true;
+    }
+
+    /**
      * Executes this function using the specified frame and returns the result value.
      * 
      * @param frame the frame of the currently executing guest language method
@@ -66,11 +125,27 @@
         return callTarget;
     }
 
-    public FrameDescriptor getFrameDescriptor() {
+    public final FrameDescriptor getFrameDescriptor() {
         return frameDescriptor;
     }
 
     public void setCallTarget(CallTarget callTarget) {
         this.callTarget = callTarget;
     }
+
+    /* Internal API. Do not use. */
+    void setParentInlinedCall(CallNode inlinedParent) {
+        this.parentInlinedCall = inlinedParent;
+    }
+
+    /**
+     * Returns the {@link CallNode} that uses this {@link RootNode} for an inlined call. Returns
+     * <code>null</code> if this {@link RootNode} is not inlined into a caller. This method can be
+     * used to also traverse parent {@link CallTarget} that have been inlined into this call.
+     * 
+     * @return the responsible {@link CallNode} for inlining.
+     */
+    public final CallNode getParentInlinedCall() {
+        return parentInlinedCall;
+    }
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Wed Jan 29 15:02:19 2014 -0800
@@ -331,11 +331,10 @@
     }
 
     protected void emitEncounteredSynthetic(CodeTreeBuilder builder, TemplateMethod current) {
-        builder.startThrow().startNew(getContext().getType(UnsupportedOperationException.class));
-        builder.startCall("createInfo0");
-        builder.doubleQuote("Unsupported values");
+        builder.startThrow().startNew(getContext().getType(UnsupportedSpecializationException.class));
+        builder.string("this");
         addInternalValueParameterNames(builder, current, current, null, false, null);
-        builder.end().end().end();
+        builder.end().end();
     }
 
     private static List<ExecutableElement> findUserConstructors(TypeMirror nodeType) {
--- a/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyASTPrinter.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyASTPrinter.java	Wed Jan 29 15:02:19 2014 -0800
@@ -19,7 +19,7 @@
 import com.oracle.truffle.api.nodes.NodeUtil.NodeFieldKind;
 import com.oracle.truffle.api.nodes.instrument.*;
 import com.oracle.truffle.ruby.nodes.*;
-import com.oracle.truffle.ruby.nodes.call.*;
+import com.oracle.truffle.ruby.nodes.call.CallNode;
 import com.oracle.truffle.ruby.nodes.literal.*;
 import com.oracle.truffle.ruby.nodes.methods.*;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLSimpleTestSuite.java	Wed Jan 29 15:02:19 2014 -0800
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, 2013, 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.truffle.sl.test;
+
+import org.junit.runner.*;
+
+@RunWith(SLTestRunner.class)
+@SLTestSuite({"graal/com.oracle.truffle.sl.test/tests", "tests"})
+public class SLSimpleTestSuite {
+
+    public static void main(String[] args) throws Exception {
+        SLTestRunner.runInMain(SLSimpleTestSuite.class, args);
+    }
+
+}
--- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java	Wed Jan 29 15:02:19 2014 -0800
@@ -23,103 +23,143 @@
 package com.oracle.truffle.sl.test;
 
 import java.io.*;
+import java.nio.charset.*;
 import java.nio.file.*;
 import java.nio.file.attribute.*;
 import java.util.*;
 
 import org.junit.*;
+import org.junit.internal.*;
+import org.junit.runner.*;
+import org.junit.runner.manipulation.*;
+import org.junit.runner.notification.*;
+import org.junit.runners.*;
+import org.junit.runners.model.*;
 
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.sl.*;
 import com.oracle.truffle.sl.runtime.*;
+import com.oracle.truffle.sl.test.SLTestRunner.TestCase;
 
-public class SLTestRunner {
+public final class SLTestRunner extends ParentRunner<TestCase> {
 
     private static final int REPEATS = 10;
-    private static final String TEST_DIR = "graal/com.oracle.truffle.sl.test/tests";
+
     private static final String INPUT_SUFFIX = ".sl";
     private static final String OUTPUT_SUFFIX = ".output";
 
-    static class TestCase {
-        protected final String name;
-        protected final Source input;
-        protected final String expectedOutput;
-        protected String actualOutput;
+    private static final String LF = System.getProperty("line.separator");
 
-        protected TestCase(String name, Source input, String expectedOutput) {
-            this.name = name;
+    public static final class TestCase {
+        private final Source input;
+        private final String expectedOutput;
+        private final Description name;
+
+        public TestCase(Class<?> testClass, String name, Source input, String expectedOutput) {
             this.input = input;
             this.expectedOutput = expectedOutput;
+            this.name = Description.createTestDescription(testClass, name);
+        }
+    }
+
+    private final SourceManager sourceManager = new SourceManager();
+    private final List<TestCase> testCases;
+
+    public SLTestRunner(Class<?> runningClass) throws InitializationError {
+        super(runningClass);
+        try {
+            testCases = createTests(runningClass);
+        } catch (IOException e) {
+            throw new InitializationError(e);
         }
     }
 
-    protected boolean useConsole = false;
+    @Override
+    protected Description describeChild(TestCase child) {
+        return child.name;
+    }
 
-    protected final SourceManager sourceManager = new SourceManager();
-    protected final List<TestCase> testCases = new ArrayList<>();
+    @Override
+    protected List<TestCase> getChildren() {
+        return testCases;
+    }
+
+    @Override
+    public void filter(Filter filter) throws NoTestsRemainException {
+        super.filter(filter);
+    }
 
-    protected boolean runTests(String namePattern) throws IOException {
-        Path testsRoot = FileSystems.getDefault().getPath(TEST_DIR);
+    protected List<TestCase> createTests(final Class<?> c) throws IOException, InitializationError {
+        SLTestSuite suite = c.getAnnotation(SLTestSuite.class);
+        if (suite == null) {
+            throw new InitializationError(String.format("@%s annotation required on class '%s' to run with '%s'.", SLTestSuite.class.getSimpleName(), c.getName(), SLTestRunner.class.getSimpleName()));
+        }
+
+        String[] pathes = suite.value();
 
-        Files.walkFileTree(testsRoot, new SimpleFileVisitor<Path>() {
+        Path root = null;
+        for (String path : pathes) {
+            root = FileSystems.getDefault().getPath(path);
+            if (Files.exists(root)) {
+                break;
+            }
+        }
+        if (root == null && pathes.length > 0) {
+            throw new FileNotFoundException(pathes[0]);
+        }
+
+        final Path rootPath = root;
+
+        final List<TestCase> foundCases = new ArrayList<>();
+        Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
             @Override
             public FileVisitResult visitFile(Path inputFile, BasicFileAttributes attrs) throws IOException {
                 String name = inputFile.getFileName().toString();
                 if (name.endsWith(INPUT_SUFFIX)) {
-                    name = name.substring(0, name.length() - INPUT_SUFFIX.length());
-                    Path outputFile = inputFile.resolveSibling(name + OUTPUT_SUFFIX);
+                    String baseName = name.substring(0, name.length() - INPUT_SUFFIX.length());
+
+                    Path outputFile = inputFile.resolveSibling(baseName + OUTPUT_SUFFIX);
                     if (!Files.exists(outputFile)) {
                         throw new Error("Output file does not exist: " + outputFile);
                     }
 
-                    testCases.add(new TestCase(name, sourceManager.get(inputFile.toString()), new String(Files.readAllBytes(outputFile))));
+                    // fix line feeds for non unix os
+                    StringBuilder outFile = new StringBuilder();
+                    for (String line : Files.readAllLines(outputFile, Charset.defaultCharset())) {
+                        outFile.append(line);
+                        outFile.append(LF);
+                    }
+                    foundCases.add(new TestCase(c, baseName, sourceManager.get(inputFile.toString()), outFile.toString()));
                 }
                 return FileVisitResult.CONTINUE;
             }
         });
-
-        if (testCases.size() == 0) {
-            System.out.format("No test cases match filter %s", namePattern);
-            return false;
-        }
-
-        boolean success = true;
-        for (TestCase testCase : testCases) {
-            if (namePattern.length() == 0 || testCase.name.toLowerCase().contains(namePattern.toLowerCase())) {
-                success = success & executeTest(testCase);
-            }
-        }
-        return success;
+        return foundCases;
     }
 
-    protected boolean executeTest(TestCase testCase) {
-        System.out.format("Running %s\n", testCase.name);
+    @Override
+    protected void runChild(TestCase testCase, RunNotifier notifier) {
+        notifier.fireTestStarted(testCase.name);
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
-        PrintStream printer = new PrintStream(useConsole ? new SplitOutputStream(out, System.err) : out);
+        PrintStream printer = new PrintStream(out);
         PrintStream origErr = System.err;
         try {
             System.setErr(printer);
             SLContext context = new SLContext(sourceManager, printer);
             SLMain.run(context, testCase.input, null, REPEATS);
+
+            String actualOutput = new String(out.toByteArray());
+
+            Assert.assertEquals(repeat(testCase.expectedOutput, REPEATS), actualOutput);
+        } catch (AssertionError e) {
+            notifier.fireTestFailure(new Failure(testCase.name, e));
         } catch (Throwable ex) {
-            ex.printStackTrace(printer);
+            notifier.fireTestFailure(new Failure(testCase.name, ex));
         } finally {
             System.setErr(origErr);
-        }
-        testCase.actualOutput = new String(out.toByteArray());
-
-        if (testCase.actualOutput.equals(repeat(testCase.expectedOutput, REPEATS))) {
-            System.out.format("OK %s\n", testCase.name);
-            return true;
-        } else {
-            if (!useConsole) {
-                System.out.format("== Expected ==\n%s\n", testCase.expectedOutput);
-                System.out.format("== Actual ==\n%s\n", testCase.actualOutput);
-            }
-            System.out.format("FAILED %s\n", testCase.name);
-            return false;
+            notifier.fireTestFinished(testCase.name);
         }
     }
 
@@ -131,19 +171,35 @@
         return result.toString();
     }
 
-    public static void main(String[] args) throws IOException {
-        String namePattern = "";
+    public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException {
+        JUnitCore core = new JUnitCore();
+        core.addListener(new TextListener(System.out));
+        SLTestRunner suite = new SLTestRunner(testClass);
         if (args.length > 0) {
-            namePattern = args[0];
+            suite.filter(new NameFilter(args[0]));
         }
-        boolean success = new SLTestRunner().runTests(namePattern);
-        if (!success) {
+        Result r = core.run(suite);
+        if (!r.wasSuccessful()) {
             System.exit(1);
         }
     }
 
-    @Test
-    public void test() throws IOException {
-        Assert.assertTrue(runTests(""));
+    private static final class NameFilter extends Filter {
+        private final String pattern;
+
+        private NameFilter(String pattern) {
+            this.pattern = pattern.toLowerCase();
+        }
+
+        @Override
+        public boolean shouldRun(Description description) {
+            return description.getMethodName().toLowerCase().contains(pattern);
+        }
+
+        @Override
+        public String describe() {
+            return "Filter contains " + pattern;
+        }
     }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestSuite.java	Wed Jan 29 15:02:19 2014 -0800
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 2013, 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.truffle.sl.test;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface SLTestSuite {
+
+    /**
+     * Defines the base path of the test suite. Multiple base pathes can be specified. However only
+     * the first base that exists is used to lookup the test cases.
+     */
+    String[] value();
+
+}
--- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SplitOutputStream.java	Wed Jan 29 14:06:32 2014 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2012, 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.truffle.sl.test;
-
-import java.io.*;
-
-public class SplitOutputStream extends OutputStream {
-
-    private final OutputStream[] outputs;
-
-    public SplitOutputStream(OutputStream... outputs) {
-        this.outputs = outputs;
-    }
-
-    @Override
-    public void write(int b) throws IOException {
-        for (OutputStream out : outputs) {
-            out.write(b);
-        }
-    }
-
-    @Override
-    public void write(byte[] b) throws IOException {
-        for (OutputStream out : outputs) {
-            out.write(b);
-        }
-    }
-
-    @Override
-    public void write(byte[] b, int off, int len) throws IOException {
-        for (OutputStream out : outputs) {
-            out.write(b, off, len);
-        }
-    }
-
-    @Override
-    public void flush() throws IOException {
-        for (OutputStream out : outputs) {
-            out.flush();
-        }
-    }
-
-    @Override
-    public void close() throws IOException {
-        for (OutputStream out : outputs) {
-            out.close();
-        }
-    }
-}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Wed Jan 29 15:02:19 2014 -0800
@@ -73,8 +73,19 @@
         return inlineImmediatly;
     }
 
-    public SLExpressionNode inline() {
-        return NodeUtil.cloneNode(uninitializedBody);
+    @Override
+    public RootNode inline() {
+        return new SLRootNode(getFrameDescriptor().shallowCopy(), NodeUtil.cloneNode(uninitializedBody), name, inlineImmediatly);
+    }
+
+    @Override
+    public int getInlineNodeCount() {
+        return NodeUtil.countNodes(uninitializedBody);
+    }
+
+    @Override
+    public boolean isInlinable() {
+        return true;
     }
 
     public Node getUninitializedBody() {
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLDirectDispatchNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLDirectDispatchNode.java	Wed Jan 29 15:02:19 2014 -0800
@@ -27,27 +27,29 @@
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.sl.runtime.*;
 
-abstract class SLDirectDispatchNode extends SLAbstractDispatchNode {
+final class SLDirectDispatchNode extends SLAbstractDispatchNode {
 
     protected final SLFunction cachedFunction;
     protected final RootCallTarget cachedCallTarget;
     protected final Assumption cachedCallTargetStable;
 
+    @Child protected CallNode callNode;
     @Child protected SLAbstractDispatchNode nextNode;
 
     protected SLDirectDispatchNode(SLAbstractDispatchNode next, SLFunction cachedFunction) {
         this.cachedFunction = cachedFunction;
         this.cachedCallTarget = cachedFunction.getCallTarget();
         this.cachedCallTargetStable = cachedFunction.getCallTargetStable();
+        this.callNode = adoptChild(CallNode.create(cachedCallTarget));
         this.nextNode = adoptChild(next);
     }
 
     @Override
-    protected final Object executeCall(VirtualFrame frame, SLFunction function, SLArguments arguments) {
+    protected Object executeCall(VirtualFrame frame, SLFunction function, SLArguments arguments) {
         if (this.cachedFunction == function) {
             try {
                 cachedCallTargetStable.check();
-                return executeCurrent(frame, arguments);
+                return callNode.call(frame.pack(), arguments);
             } catch (InvalidAssumptionException ex) {
                 /*
                  * Remove ourselfs from the polymorphic inline cache, so that we fail the check only
@@ -61,6 +63,4 @@
         }
         return nextNode.executeCall(frame, function, arguments);
     }
-
-    protected abstract Object executeCurrent(VirtualFrame frame, SLArguments arguments);
 }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLInlinableDirectDispatchNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2013, 2013, 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.truffle.sl.nodes.call;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.sl.nodes.*;
-import com.oracle.truffle.sl.runtime.*;
-
-final class SLInlinableDirectDispatchNode extends SLDirectDispatchNode implements InlinableCallSite {
-
-    @CompilationFinal private int callCount;
-
-    protected SLInlinableDirectDispatchNode(SLAbstractDispatchNode next, SLFunction cachedFunction) {
-        super(next, cachedFunction);
-    }
-
-    @Override
-    protected Object executeCurrent(VirtualFrame frame, SLArguments arguments) {
-        if (CompilerDirectives.inInterpreter()) {
-            callCount++;
-        }
-        return cachedCallTarget.call(frame.pack(), arguments);
-    }
-
-    @Override
-    public boolean inline(FrameFactory factory) {
-        CompilerAsserts.neverPartOfCompilation();
-        RootNode root = cachedCallTarget.getRootNode();
-        SLExpressionNode inlinedNode = ((SLRootNode) root).inline();
-        assert inlinedNode != null;
-        replace(new SLInlinedDirectDispatchNode(this, inlinedNode), "Inlined " + root);
-        /* We are always able to inline if required. */
-        return true;
-    }
-
-    @Override
-    public int getCallCount() {
-        return callCount;
-    }
-
-    @Override
-    public void resetCallCount() {
-        callCount = 0;
-    }
-
-    @Override
-    public Node getInlineTree() {
-        RootNode root = cachedCallTarget.getRootNode();
-        if (root instanceof SLRootNode) {
-            return ((SLRootNode) root).getUninitializedBody();
-        }
-        return null;
-    }
-
-    @Override
-    public CallTarget getCallTarget() {
-        return cachedCallTarget;
-    }
-}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLInlinedDirectDispatchNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2013, 2013, 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.truffle.sl.nodes.call;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.sl.nodes.*;
-import com.oracle.truffle.sl.runtime.*;
-
-final class SLInlinedDirectDispatchNode extends SLDirectDispatchNode implements InlinedCallSite {
-
-    private final FrameDescriptor descriptor;
-    @Child private SLExpressionNode inlinedBody;
-
-    protected SLInlinedDirectDispatchNode(SLInlinableDirectDispatchNode prev, SLExpressionNode inlinedBody) {
-        super(prev.nextNode, prev.cachedFunction);
-        this.descriptor = cachedCallTarget.getRootNode().getFrameDescriptor();
-        this.inlinedBody = adoptChild(inlinedBody);
-    }
-
-    @Override
-    protected Object executeCurrent(VirtualFrame frame, SLArguments arguments) {
-        VirtualFrame newFrame = Truffle.getRuntime().createVirtualFrame(frame.pack(), arguments, descriptor);
-        return inlinedBody.executeGeneric(newFrame);
-    }
-
-    @Override
-    public CallTarget getCallTarget() {
-        return cachedCallTarget;
-    }
-}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLUninitializedCallNode.java	Wed Jan 29 14:06:32 2014 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLUninitializedCallNode.java	Wed Jan 29 15:02:19 2014 -0800
@@ -43,7 +43,7 @@
         SLAbstractDispatchNode specialized;
         if (depth < INLINE_CACHE_SIZE) {
             SLAbstractDispatchNode next = new SLUninitializedCallNode();
-            SLAbstractDispatchNode direct = new SLInlinableDirectDispatchNode(next, function);
+            SLAbstractDispatchNode direct = new SLDirectDispatchNode(next, function);
             specialized = replace(direct);
         } else {
             SLAbstractDispatchNode generic = new SLGenericDispatchNode();
--- a/src/share/vm/graal/graalCompilerToVM.cpp	Wed Jan 29 14:06:32 2014 -0800
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Wed Jan 29 15:02:19 2014 -0800
@@ -553,7 +553,7 @@
   //------------------------------------------------------------------------------------------------
 
   set_int("graalCountersThreadOffset", in_bytes(JavaThread::graal_counters_offset()));
-  set_int("graalCountersSize", (jint) GRAAL_COUNTERS_SIZE);
+  set_int("graalCountersSize", (jint) GraalCounterSize);
 
 #undef set_boolean
 #undef set_int
@@ -816,7 +816,7 @@
 C2V_END
 
 C2V_VMENTRY(jlongArray, collectCounters, (JNIEnv *env, jobject))
-  typeArrayOop arrayOop = oopFactory::new_longArray(GRAAL_COUNTERS_SIZE, CHECK_NULL);
+  typeArrayOop arrayOop = oopFactory::new_longArray(GraalCounterSize, CHECK_NULL);
   JavaThread::collect_counters(arrayOop);
   return (jlongArray) JNIHandles::make_local(arrayOop);
 C2V_END
--- a/src/share/vm/graal/graalGlobals.hpp	Wed Jan 29 14:06:32 2014 -0800
+++ b/src/share/vm/graal/graalGlobals.hpp	Wed Jan 29 15:02:19 2014 -0800
@@ -55,6 +55,12 @@
   product(intx, TraceGraal, 0,                                              \
           "Trace level for Graal")                                          \
                                                                             \
+  product(intx, GraalCounterSize, 0,                                        \
+          "Reserved size for benchmark counters")                           \
+                                                                            \
+  product(bool, GraalCountersExcludeCompiler, true,                         \
+          "Exclude Graal compiler threads from benchmark counters")         \
+                                                                            \
   product(bool, GraalDeferredInitBarriers, true,                            \
           "Defer write barriers of young objects")                          \
                                                                             \
--- a/src/share/vm/runtime/thread.cpp	Wed Jan 29 14:06:32 2014 -0800
+++ b/src/share/vm/runtime/thread.cpp	Wed Jan 29 15:02:19 2014 -0800
@@ -1418,31 +1418,27 @@
 
 #ifdef GRAAL
 
-#if GRAAL_COUNTERS_SIZE > 0
-jlong JavaThread::_graal_old_thread_counters[GRAAL_COUNTERS_SIZE];
+jlong* JavaThread::_graal_old_thread_counters;
 
 bool graal_counters_include(oop threadObj) {
-  return !GRAAL_COUNTERS_EXCLUDE_COMPILER_THREADS || threadObj == NULL || threadObj->klass() != SystemDictionary::CompilerThread_klass();
+  return !GraalCountersExcludeCompiler || threadObj == NULL || threadObj->klass() != SystemDictionary::CompilerThread_klass();
 }
 
 void JavaThread::collect_counters(typeArrayOop array) {
-  MutexLocker tl(Threads_lock);
-  for (int i = 0; i < array->length(); i++) {
-    array->long_at_put(i, _graal_old_thread_counters[i]);
-  }
-  for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) {
-    if (graal_counters_include(tp->threadObj())) {
-      for (int i = 0; i < array->length(); i++) {
-        array->long_at_put(i, array->long_at(i) + tp->_graal_counters[i]);
+  if (GraalCounterSize > 0) {
+    MutexLocker tl(Threads_lock);
+    for (int i = 0; i < array->length(); i++) {
+      array->long_at_put(i, _graal_old_thread_counters[i]);
+    }
+    for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) {
+      if (graal_counters_include(tp->threadObj())) {
+        for (int i = 0; i < array->length(); i++) {
+          array->long_at_put(i, array->long_at(i) + tp->_graal_counters[i]);
+        }
       }
     }
   }
 }
-#else
-void JavaThread::collect_counters(typeArrayOop array) {
-  // empty
-}
-#endif // GRAAL_COUNTERS_SIZE > 0
 
 #endif // GRAAL
 
@@ -1486,11 +1482,12 @@
   _graal_alternate_call_target = NULL;
   _graal_implicit_exception_pc = NULL;
   _graal_compiling = false;
-#if GRAAL_COUNTERS_SIZE > 0
-  for (int i = 0; i < GRAAL_COUNTERS_SIZE; i++) {
-    _graal_counters[i] = 0;
+  if (GraalCounterSize > 0) {
+    _graal_counters = NEW_C_HEAP_ARRAY(jlong, GraalCounterSize, mtInternal);
+    memset(_graal_counters, 0, sizeof(jlong) * GraalCounterSize);
+  } else {
+    _graal_counters = NULL;
   }
-#endif // GRAAL_COUNTER_SIZE > 0
 #endif // GRAAL
   (void)const_cast<oop&>(_exception_oop = NULL);
   _exception_pc  = 0;
@@ -1680,13 +1677,14 @@
   if (_thread_profiler != NULL) delete _thread_profiler;
   if (_thread_stat != NULL) delete _thread_stat;
 
-#if defined(GRAAL) && (GRAAL_COUNTERS_SIZE > 0)
-  if (graal_counters_include(threadObj())) {
-    for (int i = 0; i < GRAAL_COUNTERS_SIZE; i++) {
+#ifdef GRAAL
+  if (GraalCounterSize > 0 && graal_counters_include(threadObj())) {
+    for (int i = 0; i < GraalCounterSize; i++) {
       _graal_old_thread_counters[i] += _graal_counters[i];
     }
+    FREE_C_HEAP_ARRAY(jlong, _graal_counters, mtInternal);
   }
-#endif
+#endif // GRAAL
 }
 
 
@@ -3458,6 +3456,15 @@
   // Initialize global data structures and create system classes in heap
   vm_init_globals();
 
+#ifdef GRAAL
+  if (GraalCounterSize > 0) {
+    JavaThread::_graal_old_thread_counters = NEW_C_HEAP_ARRAY(jlong, GraalCounterSize, mtInternal);
+    memset(JavaThread::_graal_old_thread_counters, 0, sizeof(jlong) * GraalCounterSize);
+  } else {
+    JavaThread::_graal_old_thread_counters = NULL;
+  }
+#endif // GRAAL
+
   // Attach the main thread to this os thread
   JavaThread* main_thread = new JavaThread();
   main_thread->set_thread_state(_thread_in_vm);
@@ -4109,6 +4116,12 @@
 
   delete thread;
 
+#ifdef GRAAL
+  if (GraalCounterSize > 0) {
+    FREE_C_HEAP_ARRAY(jlong, JavaThread::_graal_old_thread_counters, mtInternal);
+  }
+#endif // GRAAL
+
   // exit_globals() will delete tty
   exit_globals();
 
--- a/src/share/vm/runtime/thread.hpp	Wed Jan 29 14:06:32 2014 -0800
+++ b/src/share/vm/runtime/thread.hpp	Wed Jan 29 15:02:19 2014 -0800
@@ -922,16 +922,10 @@
   address   _graal_implicit_exception_pc;  // pc at which the most recent implicit exception occurred
   bool      _graal_compiling;
 
-  // number of counters, increase as needed. 0 == disabled
-#define GRAAL_COUNTERS_SIZE (0)
-#define GRAAL_COUNTERS_EXCLUDE_COMPILER_THREADS (true)
-
-#if GRAAL_COUNTERS_SIZE > 0
-  jlong     _graal_counters[GRAAL_COUNTERS_SIZE];
-  static jlong _graal_old_thread_counters[GRAAL_COUNTERS_SIZE];
-#endif // GRAAL_COUNTERS_SIZE > 0
+  jlong*    _graal_counters;
 
  public:
+  static jlong* _graal_old_thread_counters;
   static void collect_counters(typeArrayOop array);
  private:
 #endif // GRAAL
@@ -1394,11 +1388,7 @@
 #ifdef GRAAL
   static ByteSize graal_alternate_call_target_offset() { return byte_offset_of(JavaThread, _graal_alternate_call_target); }
   static ByteSize graal_implicit_exception_pc_offset() { return byte_offset_of(JavaThread, _graal_implicit_exception_pc); }
-#if GRAAL_COUNTERS_SIZE > 0
   static ByteSize graal_counters_offset()        { return byte_offset_of(JavaThread, _graal_counters      ); }
-#else
-  static ByteSize graal_counters_offset()        { return in_ByteSize(0); }
-#endif // GRAAL_COUNTERS_SIZE > 0
 #endif // GRAAL
   static ByteSize exception_oop_offset()         { return byte_offset_of(JavaThread, _exception_oop       ); }
   static ByteSize exception_pc_offset()          { return byte_offset_of(JavaThread, _exception_pc        ); }