changeset 18162:ab62800259ff

Truffle: renamed the splitting to callTargetCloning. Made RootNode cloning an implementation detail of the Truffle runtime.
author Christian Humer <christian.humer@gmail.com>
date Thu, 23 Oct 2014 17:20:10 +0200
parents 94f16a759646
children c88ab4f1f04a
files CHANGELOG.md graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/builtins/SLDisableSplittingBuiltin.java graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/builtins/SLGraalRuntimeBuiltin.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleSplittingStrategy.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleSplittingStrategyNew.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetLog.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedDirectCallNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultDirectCallNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/DirectCallNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java
diffstat 14 files changed, 114 insertions(+), 105 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG.md	Thu Oct 23 13:45:59 2014 +0200
+++ b/CHANGELOG.md	Thu Oct 23 17:20:10 2014 +0200
@@ -9,6 +9,11 @@
 * Relaxed declared type restriction on child fields to allow for interface types in addition to Node subclasses.
 * The BranchProfile constructor is now private. Use BranchProfile#create() instead.
 * Renamed @CompilerDirectives.SlowPath to @CompilerDirectives.TruffleBoundary
+* Renamed RootNode#isSplittable to RootNode#isCloningAllowed
+* Removed RootNode#split. Cloning ASTs for splitting is now an implementation detail of the Truffle runtime implementation. 
+* Renamed DirectCallNode#isSplittable to DirectCallNode#isCallTargetCloningAllowed
+* Renamed DirectCallNode#split to DirectCallNode#cloneCallTarget
+* Renamed DirectCallNode#isSplit to DirectCallNode#isCallTargetCloned
 * ...
 
 ## Version 0.5
--- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Thu Oct 23 17:20:10 2014 +0200
@@ -107,18 +107,27 @@
 
     @Override
     public RootCallTarget createCallTarget(RootNode rootNode) {
+        return createCallTargetImpl(null, rootNode);
+    }
+
+    private RootCallTarget createCallTargetImpl(OptimizedCallTarget source, RootNode rootNode) {
         CompilationPolicy compilationPolicy;
         if (acceptForCompilation(rootNode)) {
             compilationPolicy = new CounterBasedCompilationPolicy();
         } else {
             compilationPolicy = new InterpreterOnlyCompilationPolicy();
         }
-        OptimizedCallTarget target = new OptimizedCallTarget(rootNode, this, compilationPolicy, new HotSpotSpeculationLog());
+        OptimizedCallTarget target = new OptimizedCallTarget(source, rootNode, this, compilationPolicy, new HotSpotSpeculationLog());
         callTargets.put(target, null);
         return target;
     }
 
     @Override
+    public RootCallTarget createClonedCallTarget(OptimizedCallTarget source, RootNode root) {
+        return createCallTargetImpl(source, root);
+    }
+
+    @Override
     public Replacements getReplacements() {
         if (truffleReplacements == null) {
             truffleReplacements = HotSpotTruffleReplacements.makeInstance();
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/builtins/SLDisableSplittingBuiltin.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/builtins/SLDisableSplittingBuiltin.java	Thu Oct 23 17:20:10 2014 +0200
@@ -42,7 +42,7 @@
     public SLFunction disableSplitting(SLFunction function) {
         OptimizedCallTarget target = (OptimizedCallTarget) function.getCallTarget();
         for (OptimizedCallTarget oct : findDuplicateCallTargets(target)) {
-            ((SLRootNode) oct.getRootNode()).setSplittable(false);
+            ((SLRootNode) oct.getRootNode()).setCloningAllowed(false);
         }
         return function;
     }
@@ -51,7 +51,7 @@
     @TruffleBoundary
     public SLNull disableSplitting(@SuppressWarnings("unused") SLNull argument) {
         RootNode parentRoot = Truffle.getRuntime().getCallerFrame().getCallNode().getRootNode();
-        ((SLRootNode) parentRoot).setSplittable(false);
+        ((SLRootNode) parentRoot).setCloningAllowed(false);
         return SLNull.SINGLETON;
     }
 
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/builtins/SLGraalRuntimeBuiltin.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/builtins/SLGraalRuntimeBuiltin.java	Thu Oct 23 17:20:10 2014 +0200
@@ -53,7 +53,7 @@
         for (RootCallTarget target : Truffle.getRuntime().getCallTargets()) {
             if (target instanceof OptimizedCallTarget) {
                 OptimizedCallTarget oct = (OptimizedCallTarget) target;
-                if (oct.getSplitSource() == originalCallTarget) {
+                if (oct.getSourceCallTarget() == originalCallTarget) {
                     allCallTargets.add(oct);
                 }
             }
@@ -83,7 +83,7 @@
             public boolean visit(Node node) {
                 if (node instanceof DirectCallNode) {
                     DirectCallNode callNode = (DirectCallNode) node;
-                    if (callNode.getCallTarget() == originalCallTarget || callNode.getSplitCallTarget() == originalCallTarget) {
+                    if (callNode.getCallTarget() == originalCallTarget || callNode.getClonedCallTarget() == originalCallTarget) {
                         allCallNodes.add(callNode);
                     }
                 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleSplittingStrategy.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleSplittingStrategy.java	Thu Oct 23 17:20:10 2014 +0200
@@ -42,23 +42,23 @@
     }
 
     public void forceSplitting() {
-        if (call.isSplit()) {
+        if (call.isCallTargetCloned()) {
             return;
         }
-        call.installSplitCallTarget(call.getCallTarget().split());
+        call.installSplitCallTarget(call.getCallTarget().cloneUninitialized());
     }
 
     public void afterCall(Object returnValue) {
     }
 
     private boolean shouldSplit() {
-        if (call.getSplitCallTarget() != null) {
+        if (call.getClonedCallTarget() != null) {
             return false;
         }
         if (!TruffleCompilerOptions.TruffleSplitting.getValue()) {
             return false;
         }
-        if (!call.isSplittable()) {
+        if (!call.isCallTargetCloningAllowed()) {
             return false;
         }
         OptimizedCallTarget splitTarget = call.getCallTarget();
@@ -69,7 +69,7 @@
 
         // disable recursive splitting for now
         OptimizedCallTarget root = (OptimizedCallTarget) call.getRootNode().getCallTarget();
-        if (root == splitTarget || root.getSplitSource() == splitTarget) {
+        if (root == splitTarget || root.getSourceCallTarget() == splitTarget) {
             // recursive call found
             return false;
         }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleSplittingStrategyNew.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleSplittingStrategyNew.java	Thu Oct 23 17:20:10 2014 +0200
@@ -51,7 +51,7 @@
         if (!TruffleCompilerOptions.TruffleSplitting.getValue()) {
             return false;
         }
-        if (!call.isSplittable()) {
+        if (!call.isCallTargetCloningAllowed()) {
             return false;
         }
         if (TruffleCompilerOptions.TruffleSplittingAggressive.getValue()) {
@@ -80,8 +80,8 @@
         OptimizedCallTarget currentTarget = call.getCurrentCallTarget();
 
         if (splittingForced) {
-            if (!call.isSplit()) {
-                call.installSplitCallTarget(currentTarget.split());
+            if (!call.isCallTargetCloned()) {
+                call.installSplitCallTarget(currentTarget.cloneUninitialized());
             }
             return;
         }
@@ -123,7 +123,7 @@
                 // we are the only caller + the profile is not polluted by other call sites
                 // -> reuse the currentTarget but update the profile if necessary
                 newTarget = currentTarget;
-                if (currentTarget.getSplitSource() != null) {
+                if (currentTarget.getSourceCallTarget() != null) {
                     profiles.remove(oldProfile);
                     profiles.put(newProfile, newTarget);
                 }
@@ -131,7 +131,7 @@
                 newTarget = profiles.get(newProfile);
                 if (newTarget == null) {
                     // in case no compatible target was found we need to split
-                    newTarget = target.split();
+                    newTarget = target.cloneUninitialized();
                     profiles.put(newProfile, newTarget);
                     split = true;
                 }
@@ -141,7 +141,7 @@
         call.installSplitCallTarget(newTarget);
 
         if (split && TruffleCompilerOptions.TraceTruffleSplitting.getValue()) {
-            traceSplit(currentTarget.getSplitSource() != null ? oldProfile : currentTarget.getArgumentStamp(), newProfile);
+            traceSplit(currentTarget.getSourceCallTarget() != null ? oldProfile : currentTarget.getArgumentStamp(), newProfile);
         }
 
         cleanup(currentTarget);
@@ -151,7 +151,7 @@
     private void traceSplit(TruffleStamp oldStamp, TruffleStamp newStamp) {
         OptimizedCallTarget callTarget = call.getCallTarget();
         Map<TruffleStamp, OptimizedCallTarget> splitTargets = callTarget.getSplitVersions();
-        String label = String.format("split %3s-%-4s-%-4s ", splitChangeCount++, call.getCurrentCallTarget().getSplitIndex(), call.getCallCount());
+        String label = String.format("split %3s-%-4s-%-4s ", splitChangeCount++, call.getCurrentCallTarget().getCloneIndex(), call.getCallCount());
         OptimizedCallTargetLog.log(0, label, callTarget.toString(), callTarget.getDebugProperties());
         logProfile(callTarget.getArgumentStamp(), callTarget, oldStamp, newStamp);
         for (TruffleStamp profile : splitTargets.keySet()) {
@@ -160,7 +160,7 @@
     }
 
     private static void logProfile(TruffleStamp stamp, OptimizedCallTarget target, TruffleStamp oldStamp, TruffleStamp newStamp) {
-        String id = String.format("@%8h %s", target.hashCode(), target.getSplitSource() == null ? "orig." : "split");
+        String id = String.format("@%8h %s", target.hashCode(), target.getSourceCallTarget() == null ? "orig." : "split");
         String plusMinus = stamp.equals(newStamp) ? "+ " : (stamp.equals(oldStamp) ? "- " : "");
         System.out.printf("%16s%-20sCallers: %3d, Nodes:%10s %s%n", plusMinus, id, target.getKnownCallSiteCount(), //
                         String.format("%d (%d/%d)", count(target, NodeCost.MONOMORPHIC), count(target, NodeCost.POLYMORPHIC), count(target, NodeCost.MEGAMORPHIC)),//
@@ -176,8 +176,8 @@
     }
 
     private static void cleanup(OptimizedCallTarget currentTarget) {
-        if (currentTarget.getKnownCallSiteCount() == 0 && currentTarget.getSplitSource() != null) {
-            OptimizedCallTarget removed = currentTarget.getSplitSource().getSplitVersions().remove(currentTarget.getArgumentStamp());
+        if (currentTarget.getKnownCallSiteCount() == 0 && currentTarget.getSourceCallTarget() != null) {
+            OptimizedCallTarget removed = currentTarget.getSourceCallTarget().getSplitVersions().remove(currentTarget.getArgumentStamp());
             if (removed != null) {
                 disposeTarget(removed);
             }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java	Thu Oct 23 17:20:10 2014 +0200
@@ -185,6 +185,8 @@
         }
     }
 
+    public abstract RootCallTarget createClonedCallTarget(OptimizedCallTarget sourceCallTarget, RootNode root);
+
     public abstract Replacements getReplacements();
 
     public abstract void compile(OptimizedCallTarget optimizedCallTarget, boolean mayBeAsynchronous);
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Thu Oct 23 17:20:10 2014 +0200
@@ -52,13 +52,14 @@
     private SpeculationLog speculationLog;
     protected final CompilationProfile compilationProfile;
     protected final CompilationPolicy compilationPolicy;
-    private OptimizedCallTarget splitSource;
+    private final OptimizedCallTarget sourceCallTarget;
     private final AtomicInteger callSitesKnown = new AtomicInteger(0);
     @CompilationFinal private Class<?>[] profiledArgumentTypes;
     @CompilationFinal private Assumption profiledArgumentTypesAssumption;
     @CompilationFinal private Class<?> profiledReturnType;
     @CompilationFinal private Assumption profiledReturnTypeAssumption;
 
+    private final RootNode uninitializedRootNode;
     private final RootNode rootNode;
 
     /* Experimental fields for new splitting. */
@@ -78,14 +79,16 @@
         return rootNode;
     }
 
-    public OptimizedCallTarget(RootNode rootNode, GraalTruffleRuntime runtime, CompilationPolicy compilationPolicy, SpeculationLog speculationLog) {
+    public OptimizedCallTarget(OptimizedCallTarget sourceCallTarget, RootNode rootNode, GraalTruffleRuntime runtime, CompilationPolicy compilationPolicy, SpeculationLog speculationLog) {
         super(rootNode.toString());
+        this.sourceCallTarget = sourceCallTarget;
         this.runtime = runtime;
         this.speculationLog = speculationLog;
         this.rootNode = rootNode;
+        this.compilationPolicy = compilationPolicy;
         this.rootNode.adoptChildren();
         this.rootNode.setCallTarget(this);
-        this.compilationPolicy = compilationPolicy;
+        this.uninitializedRootNode = sourceCallTarget == null ? cloneRootNode(rootNode) : sourceCallTarget.uninitializedRootNode;
         if (TruffleCallTargetProfiling.getValue()) {
             this.compilationProfile = new TraceCompilationProfile();
         } else {
@@ -94,6 +97,13 @@
         this.nodeRewritingAssumption = new CyclicAssumption("nodeRewritingAssumption of " + rootNode.toString());
     }
 
+    private static RootNode cloneRootNode(RootNode root) {
+        if (root == null || !root.isCloningAllowed()) {
+            return null;
+        }
+        return NodeUtil.cloneNode(root);
+    }
+
     public Assumption getNodeRewritingAssumption() {
         return nodeRewritingAssumption.getAssumption();
     }
@@ -106,19 +116,19 @@
         return argumentStamp;
     }
 
-    private int splitIndex;
+    private int cloneIndex;
 
-    public int getSplitIndex() {
-        return splitIndex;
+    public int getCloneIndex() {
+        return cloneIndex;
     }
 
-    public OptimizedCallTarget split() {
-        if (!getRootNode().isSplittable()) {
+    public OptimizedCallTarget cloneUninitialized() {
+        RootNode copiedRoot = cloneRootNode(uninitializedRootNode);
+        if (copiedRoot == null) {
             return null;
         }
-        OptimizedCallTarget splitTarget = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(getRootNode().split());
-        splitTarget.splitSource = this;
-        splitTarget.splitIndex = splitIndex++;
+        OptimizedCallTarget splitTarget = (OptimizedCallTarget) runtime.createClonedCallTarget(this, copiedRoot);
+        splitTarget.cloneIndex = cloneIndex++;
         return splitTarget;
     }
 
@@ -375,12 +385,8 @@
         callSitesKnown.decrementAndGet();
     }
 
-    public final OptimizedCallTarget getSplitSource() {
-        return splitSource;
-    }
-
-    public final void setSplitSource(OptimizedCallTarget splitSource) {
-        this.splitSource = splitSource;
+    public final OptimizedCallTarget getSourceCallTarget() {
+        return sourceCallTarget;
     }
 
     @Override
@@ -389,8 +395,8 @@
         if (isValid()) {
             superString += " <opt>";
         }
-        if (splitSource != null) {
-            superString += " <split-" + splitIndex + "-" + argumentStamp.toStringShort() + ">";
+        if (sourceCallTarget != null) {
+            superString += " <split-" + cloneIndex + "-" + argumentStamp.toStringShort() + ">";
         }
         return superString;
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetLog.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetLog.java	Thu Oct 23 17:20:10 2014 +0200
@@ -258,8 +258,8 @@
         Map<OptimizedCallTarget, List<OptimizedCallTarget>> groupedTargets = Truffle.getRuntime().getCallTargets().stream()//
         .map(target -> (OptimizedCallTarget) target)//
         .collect(Collectors.groupingBy(target -> {
-            if (target.getSplitSource() != null) {
-                return target.getSplitSource();
+            if (target.getSourceCallTarget() != null) {
+                return target.getSourceCallTarget();
             }
             return target;
         }));
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedDirectCallNode.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedDirectCallNode.java	Thu Oct 23 17:20:10 2014 +0200
@@ -110,8 +110,8 @@
     }
 
     @Override
-    public boolean isSplittable() {
-        return getCallTarget().getRootNode().isSplittable();
+    public boolean isCallTargetCloningAllowed() {
+        return getCallTarget().getRootNode().isCloningAllowed();
     }
 
     @Override
@@ -129,7 +129,7 @@
     }
 
     @Override
-    public OptimizedCallTarget getSplitCallTarget() {
+    public OptimizedCallTarget getClonedCallTarget() {
         return splitCallTarget;
     }
 
@@ -158,7 +158,7 @@
         // dummy replace to report the split
         replace(this, "Split call " + newTarget.toString());
 
-        if (newTarget.getSplitSource() == null) {
+        if (newTarget.getSourceCallTarget() == null) {
             splitCallTarget = null;
         } else {
             splitCallTarget = newTarget;
@@ -166,7 +166,7 @@
     }
 
     @Override
-    public boolean split() {
+    public boolean cloneCallTarget() {
         splittingStrategy.forceSplitting();
         return true;
     }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultDirectCallNode.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultDirectCallNode.java	Thu Oct 23 17:20:10 2014 +0200
@@ -79,17 +79,17 @@
     }
 
     @Override
-    public CallTarget getSplitCallTarget() {
+    public CallTarget getClonedCallTarget() {
         return null;
     }
 
     @Override
-    public boolean split() {
+    public boolean cloneCallTarget() {
         return false;
     }
 
     @Override
-    public boolean isSplittable() {
+    public boolean isCallTargetCloningAllowed() {
         return false;
     }
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/DirectCallNode.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/DirectCallNode.java	Thu Oct 23 17:20:10 2014 +0200
@@ -32,17 +32,17 @@
  * {@link CallTarget} remains the same for each consecutive call. This part of the Truffle API
  * enables the runtime system to perform additional optimizations on direct calls.
  *
- * Optimizations that can be applied to a {@link DirectCallNode} are inlining and splitting.
- * Inlining inlines this call site into the call graph of the parent {@link CallTarget}. Splitting
- * duplicates the {@link CallTarget} using {@link RootNode#split()} to collect call site sensitive
- * profiling information.
+ * Optimizations that can be applied to a {@link DirectCallNode} are inlining and call site
+ * sensitive AST duplication. Inlining inlines this call site into the call graph of the parent
+ * {@link CallTarget}. Call site sensitive AST duplication duplicates the {@link CallTarget} in an
+ * uninitialized state to collect call site sensitive profiling information.
  *
  * Please note: This class is not intended to be subclassed by guest language implementations.
  *
  * @see IndirectCallNode for calls with a non-constant target
  * @see TruffleRuntime#createDirectCallNode(CallTarget)
  * @see #forceInlining()
- * @see #split()
+ * @see #cloneCallTarget()
  */
 public abstract class DirectCallNode extends Node {
 
@@ -111,46 +111,48 @@
     }
 
     /**
-     * Returns <code>true</code> if this {@link DirectCallNode} can be split. A
-     * {@link DirectCallNode} can only be split if the runtime system supports splitting and if the
-     * {@link RootNode} contained the {@link CallTarget} returns <code>true</code> for
-     * {@link RootNode#isSplittable()}.
+     * Returns <code>true</code> if the runtime system supports cloning and the {@link RootNode}
+     * returns <code>true</code> in {@link RootNode#isCloningAllowed()}.
      *
-     * @return <code>true</code> if the target can be split
+     * @return <code>true</code> if the target is allowed to be cloned.
      */
-    public abstract boolean isSplittable();
+    public abstract boolean isCallTargetCloningAllowed();
 
     /**
-     * Enforces the runtime system to split the {@link CallTarget}. If the {@link DirectCallNode} is
-     * not splittable this methods has no effect.
+     * Clones the {@link CallTarget} instance returned by {@link #getCallTarget()} in an
+     * uninitialized state for this {@link DirectCallNode}. This can be sensible to gather call site
+     * sensitive profiling information for this {@link DirectCallNode}. If
+     * {@link #isCallTargetCloningAllowed()} returns <code>false</code> this method has no effect
+     * and returns <code>false</code>.
      */
-    public abstract boolean split();
+    public abstract boolean cloneCallTarget();
 
     /**
-     * Returns <code>true</code> if the target of the {@link DirectCallNode} was split.
+     * Returns <code>true</code> if the target of the {@link DirectCallNode} was cloned by the
+     * runtime system or by the guest language implementation.
      *
      * @return if the target was split
      */
-    public final boolean isSplit() {
-        return getSplitCallTarget() != null;
+    public final boolean isCallTargetCloned() {
+        return getClonedCallTarget() != null;
     }
 
     /**
-     * Returns the split {@link CallTarget} if this method is split.
+     * Returns the split {@link CallTarget} if this call site's {@link CallTarget} is cloned.
      *
      * @return the split {@link CallTarget}
      */
-    public abstract CallTarget getSplitCallTarget();
+    public abstract CallTarget getClonedCallTarget();
 
     /**
      * Returns the used call target when {@link #call(VirtualFrame, Object[])} is invoked. If the
      * {@link CallTarget} was split this method returns the {@link CallTarget} returned by
-     * {@link #getSplitCallTarget()}.
+     * {@link #getClonedCallTarget()}.
      *
      * @return the used {@link CallTarget} when node is called
      */
     public CallTarget getCurrentCallTarget() {
-        CallTarget split = getSplitCallTarget();
+        CallTarget split = getClonedCallTarget();
         if (split != null) {
             return split;
         } else {
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Thu Oct 23 17:20:10 2014 +0200
@@ -25,6 +25,7 @@
 package com.oracle.truffle.api.nodes;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.source.*;
 
@@ -36,7 +37,7 @@
 public abstract class RootNode extends Node {
 
     private RootCallTarget callTarget;
-    private final FrameDescriptor frameDescriptor;
+    @CompilationFinal private FrameDescriptor frameDescriptor;
 
     protected RootNode() {
         this(null, null);
@@ -55,26 +56,24 @@
         }
     }
 
-    /**
-     * Creates a split {@link RootNode} based on the current {@link RootNode}. This method should
-     * return an AST that was never executed and must not be shared with other {@link RootNode} or
-     * {@link CallTarget} instances. This method is intended to be overridden by a subclass.
-     *
-     * @return the split {@link RootNode}
-     */
-    public RootNode split() {
-        throw new UnsupportedOperationException();
+    @Override
+    public Node copy() {
+        RootNode root = (RootNode) super.copy();
+        root.frameDescriptor = frameDescriptor.shallowCopy();
+        return root;
     }
 
     /**
-     * Returns <code>true</code> if this {@link RootNode} can be split. A {@link RootNode} can be
-     * split inside of a {@link CallTarget} that is invoked using a {@link DirectCallNode}. If this
-     * method returns <code>true</code> a proper implementation of {@link #split()} must also be
-     * provided. This method is intended to be overridden by a subclass.
+     * Returns <code>true</code> if this {@link RootNode} is allowed to be cloned. The runtime
+     * system might decide to create deep copies of the {@link RootNode} in order to gather context
+     * sensitive profiling feedback. The default implementation returns <code>false</code>. Guest
+     * language specific implementations may want to return <code>true</code> here to indicate that
+     * gathering call site specific profiling information might make sense for this {@link RootNode}
+     * .
      *
-     * @return <code>true</code> if splittable else <code>false</code>.
+     * @return <code>true</code> if cloning is allowed else <code>false</code>.
      */
-    public boolean isSplittable() {
+    public boolean isCloningAllowed() {
         return false;
     }
 
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Thu Oct 23 13:45:59 2014 +0200
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Thu Oct 23 17:20:10 2014 +0200
@@ -41,25 +41,16 @@
     /** The function body that is executed, and specialized during execution. */
     @Child private SLExpressionNode bodyNode;
 
-    /**
-     * A copy of the uninitialized body. When performing method inlining, it is beneficial to inline
-     * the unspecialized function body, so that it is specialized in the context of the caller. This
-     * makes the specializations of the inlined function more precise.
-     */
-    private final SLExpressionNode uninitializedBodyNode;
-
     /** The name of the function, for printing purposes only. */
     private final String name;
 
     /** The Simple execution context for this tree **/
     private final SLContext context;
 
-    @CompilationFinal private boolean isSplittable;
+    @CompilationFinal private boolean isCloningAllowed;
 
     public SLRootNode(SLContext context, FrameDescriptor frameDescriptor, SLExpressionNode bodyNode, String name) {
         super(null, frameDescriptor);
-        /* Deep copy the body before any specialization occurs during execution. */
-        this.uninitializedBodyNode = NodeUtil.cloneNode(bodyNode);
         this.bodyNode = bodyNode;
         this.name = name;
         this.context = context;
@@ -74,8 +65,8 @@
         return name;
     }
 
-    public void setSplittable(boolean isSplittable) {
-        this.isSplittable = isSplittable;
+    public void setCloningAllowed(boolean isCloningAllowed) {
+        this.isCloningAllowed = isCloningAllowed;
     }
 
     public SLExpressionNode getBodyNode() {
@@ -83,13 +74,8 @@
     }
 
     @Override
-    public boolean isSplittable() {
-        return isSplittable;
-    }
-
-    @Override
-    public RootNode split() {
-        return new SLRootNode(this.context, getFrameDescriptor().shallowCopy(), NodeUtil.cloneNode(uninitializedBodyNode), name);
+    public boolean isCloningAllowed() {
+        return isCloningAllowed;
     }
 
     @Override