changeset 19290:bf166845c7d8

Truffle-DSL: new test examples package to show and explain Truffle-DSL features.
author Christian Humer <christian.humer@gmail.com>
date Wed, 11 Feb 2015 12:13:44 +0100
parents 62c43fcf5be2
children f4792a544170
files graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/ExampleNode.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/ExampleTypes.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/FunctionCall.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/Interop.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/MathPow.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/RubyCall.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/StableDispatch.java
diffstat 7 files changed, 948 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/ExampleNode.java	Wed Feb 11 12:13:44 2015 +0100
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2015, 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.examples;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.internal.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+@TypeSystemReference(ExampleTypes.class)
+@NodeChild(value = "args", type = ExampleNode[].class)
+public abstract class ExampleNode extends Node {
+
+    public Object execute(@SuppressWarnings("unused") VirtualFrame frame) {
+        // will get implemented by the DSL.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String toString() {
+        if (this instanceof SpecializedNode) {
+            return ((SpecializedNode) this).getSpecializationNode().toString();
+        } else {
+            return super.toString();
+        }
+    }
+
+    public static CallTarget createTarget(ExampleNode node) {
+        return Truffle.getRuntime().createCallTarget(new ExampleRootNode(node));
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T getNode(CallTarget target) {
+        return (T) ((ExampleRootNode) ((RootCallTarget) target).getRootNode()).child;
+    }
+
+    public static ExampleNode[] createArguments(int count) {
+        ExampleNode[] nodes = new ExampleNode[count];
+        for (int i = 0; i < count; i++) {
+            nodes[i] = new ExampleArgumentNode(i);
+        }
+        return nodes;
+    }
+
+    private static class ExampleRootNode extends RootNode {
+
+        @Child ExampleNode child;
+
+        public ExampleRootNode(ExampleNode child) {
+            this.child = child;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            return child.execute(frame);
+        }
+
+    }
+
+    private static class ExampleArgumentNode extends ExampleNode {
+
+        private final int index;
+
+        public ExampleArgumentNode(int index) {
+            this.index = index;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] arguments = frame.getArguments();
+            if (index < arguments.length) {
+                return arguments[index];
+            }
+            return null;
+        }
+    }
+
+    public static CallTarget createDummyTarget(int argumentIndex) {
+        return Truffle.getRuntime().createCallTarget(new DummyCallRootNode(argumentIndex));
+    }
+
+    private static class DummyCallRootNode extends RootNode {
+
+        private final int argumentIndex;
+
+        public DummyCallRootNode(int argumentIndex) {
+            this.argumentIndex = argumentIndex;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            return frame.getArguments()[argumentIndex];
+        }
+
+        @Override
+        public String toString() {
+            return "DummyRootNode[arg = " + argumentIndex + "]";
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/ExampleTypes.java	Wed Feb 11 12:13:44 2015 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 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.examples;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.examples.FunctionCall.Function;
+import com.oracle.truffle.api.dsl.examples.Interop.TruffleObject;
+import com.oracle.truffle.api.dsl.examples.RubyCall.InternalMethod;
+import com.oracle.truffle.api.dsl.examples.RubyCall.RubyObject;
+import com.oracle.truffle.api.dsl.examples.StableDispatch.SLFunction;
+
+@TypeSystem({int.class, double.class, boolean.class, TruffleObject.class, SLFunction.class, RubyObject.class, Function.class, InternalMethod.class, int[].class, double[].class, Object[].class})
+public class ExampleTypes {
+
+    @ImplicitCast
+    public static double castInt(int intValue) {
+        return intValue;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/FunctionCall.java	Wed Feb 11 12:13:44 2015 +0100
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2015, 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.examples;
+
+import static com.oracle.truffle.api.dsl.examples.ExampleNode.*;
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.examples.FunctionCallFactory.FunctionCallNodeGen;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * This example illustrates how {@link Cached} can be used to implement function calls that use
+ * local state for its guards. If there are always distinct Function objects with distinct
+ * CallTargets then we can use the directCallFunctionGuard specialization. If there are two Function
+ * instances cached with the same CallTarget then we use the directCall cache. We do this because
+ * the directCallFunctionGuard specialization can use a faster guard.
+ */
+@SuppressWarnings("unused")
+public class FunctionCall {
+
+    @Test
+    public void testFunctionCall() {
+        assertEquals(2, FunctionCallNode.CACHE_SIZE);
+
+        CallTarget dummyTarget1 = createDummyTarget(0);
+        CallTarget dummyTarget2 = createDummyTarget(0);
+        CallTarget dummyTarget3 = createDummyTarget(0);
+
+        Function dummyFunction1 = new Function(dummyTarget1);
+        Function dummyFunction2 = new Function(dummyTarget2);
+        Function dummyFunction3 = new Function(dummyTarget2); // same target as dummyFunction2
+        Function dummyFunction4 = new Function(dummyTarget3);
+
+        FunctionCallNode node = FunctionCallNodeGen.create(createArguments(2));
+        CallTarget target = createTarget(node);
+        assertEquals(42, target.call(dummyFunction1, 42));
+        assertEquals(43, target.call(dummyFunction2, 43));
+        assertEquals(44, target.call(dummyFunction3, 44)); // transition to directCall
+        assertEquals(2, node.directCallFunctionGuard);
+        assertEquals(1, node.directCall);
+
+        assertEquals(42, target.call(dummyFunction1, 42));
+        assertEquals(43, target.call(dummyFunction2, 43));
+        assertEquals(2, node.directCallFunctionGuard);
+        assertEquals(3, node.directCall);
+
+        assertEquals(44, target.call(dummyFunction4, 44)); // transition to indirectCall
+        assertEquals(2, node.directCallFunctionGuard);
+        assertEquals(3, node.directCall);
+        assertEquals(1, node.indirectCall);
+
+        assertEquals(42, target.call(dummyFunction1, 42));
+        assertEquals(43, target.call(dummyFunction2, 43));
+        assertEquals(44, target.call(dummyFunction3, 44));
+        assertEquals(2, node.directCallFunctionGuard);
+        assertEquals(3, node.directCall);
+        assertEquals(4, node.indirectCall);
+    }
+
+    public static class FunctionCallNode extends ExampleNode {
+
+        public static final int CACHE_SIZE = 2;
+
+        private CallTarget[] cachedTargets = new CallTarget[CACHE_SIZE];
+
+        private int directCallFunctionGuard;
+        private int directCall;
+        private int indirectCall;
+
+        @Specialization(limit = "CACHE_SIZE", guards = {"function == cachedFunction", "!cacheFunctionTarget(cachedFunction)"})
+        public Object directCallFunctionGuard(VirtualFrame frame, Function function, Object argument,  //
+                        @Cached("function") Function cachedFunction, //
+                        @Cached("create(cachedFunction.getTarget())") DirectCallNode callNode) {
+            directCallFunctionGuard++;
+            return callNode.call(frame, new Object[]{argument});
+        }
+
+        protected final boolean cacheFunctionTarget(Function function) {
+            CompilerAsserts.neverPartOfCompilation();
+            if (cachedTargets != null) {
+                CallTarget target = function.getTarget();
+                for (int i = 0; i < cachedTargets.length; i++) {
+                    CallTarget cachedTarget = cachedTargets[i];
+                    if (cachedTarget == target) {
+                        cachedTargets = null;
+                        return true;
+                    } else if (cachedTarget == null) {
+                        cachedTargets[i] = target;
+                        return false;
+                    }
+                }
+            }
+            return false;
+        }
+
+        @Specialization(limit = "CACHE_SIZE", contains = "directCallFunctionGuard", guards = {"function.getTarget() == cachedTarget"})
+        protected Object directCall(VirtualFrame frame, Function function, Object argument,  //
+                        @Cached("function.getTarget()") CallTarget cachedTarget, //
+                        @Cached("create(cachedTarget)") DirectCallNode callNode) {
+            directCall++;
+            return callNode.call(frame, new Object[]{argument});
+        }
+
+        @Specialization(contains = "directCall")
+        protected Object indirectCall(VirtualFrame frame, Function function, Object argument, //
+                        @Cached("create()") IndirectCallNode callNode) {
+            indirectCall++;
+            return callNode.call(frame, function.getTarget(), new Object[]{argument});
+        }
+    }
+
+    public static class Function {
+
+        private final CallTarget target;
+
+        public Function(CallTarget target) {
+            this.target = target;
+        }
+
+        public CallTarget getTarget() {
+            return target;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/Interop.java	Wed Feb 11 12:13:44 2015 +0100
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2015, 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.examples;
+
+import static com.oracle.truffle.api.dsl.examples.ExampleNode.*;
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.examples.InteropFactory.UseInteropNodeGen;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * This example aims to illustrate how the {@link Cached} annotation can be used to implement a
+ * cache for a simplified language interoperability pattern.
+ */
+public class Interop {
+
+    @Test
+    public void testInterop() {
+        UseInterop node = UseInteropNodeGen.create(createArguments(2));
+        CallTarget target = createTarget(node);
+        TruffleObject o1 = new TruffleObject();
+        TruffleObject o2 = new TruffleObject();
+        TruffleObject o3 = new TruffleObject();
+        TruffleObject o4 = new TruffleObject();
+        assertEquals(42, target.call(o1, 42));
+        assertEquals(43, target.call(o2, 43));
+        assertEquals(44, target.call(o3, 44));
+        assertEquals(3, node.cached);
+        assertEquals(0, node.generic);
+        assertEquals(45, target.call(o4, 45)); // operation gets generic
+        assertEquals(42, target.call(o1, 42));
+        assertEquals(43, target.call(o2, 43));
+        assertEquals(44, target.call(o3, 44));
+        assertEquals(3, node.cached);
+        assertEquals(4, node.generic);
+    }
+
+    public static class UseInterop extends ExampleNode {
+
+        int cached = 0;
+        int generic = 0;
+
+        @Specialization(guards = "operation.accept(target)")
+        protected Object interopCached(VirtualFrame frame, TruffleObject target, Object value, //
+                        @Cached("target.createOperation()") TruffleObjectOperation operation) {
+            cached++;
+            return operation.execute(frame, target, value);
+        }
+
+        @Specialization(contains = "interopCached")
+        protected Object interopGeneric(VirtualFrame frame, TruffleObject target, Object value) {
+            generic++;
+            return target.createOperation().execute(frame, target, value);
+        }
+    }
+
+    public abstract static class TruffleObjectOperation extends Node {
+
+        public abstract boolean accept(TruffleObject object);
+
+        public abstract Object execute(VirtualFrame frame, Object target, Object value);
+
+    }
+
+    public static class TruffleObject {
+
+        @TruffleBoundary
+        public TruffleObjectOperation createOperation() {
+            return new TruffleObjectOperation() {
+                @Override
+                public Object execute(VirtualFrame frame, Object target, Object value) {
+                    return value;
+                }
+
+                @Override
+                public boolean accept(TruffleObject object) {
+                    return TruffleObject.this == object;
+                }
+            };
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/MathPow.java	Wed Feb 11 12:13:44 2015 +0100
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2015, 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.examples;
+
+import static com.oracle.truffle.api.dsl.examples.ExampleNode.*;
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.examples.MathPowFactory.MathPowNodeGen;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * This example shows possible specializations for a simplified math pow node. It demonstrates how
+ * multiple caches can coexist within in the same node. This example does not show the best possible
+ * specializations for math.pow.
+ *
+ * Note: int values are implicitly casted to double values.
+ */
+@SuppressWarnings("unused")
+public class MathPow extends Node {
+
+    @Test
+    public void testPow() {
+        MathPowNode node = MathPowNodeGen.create(createArguments(2));
+        CallTarget target = createTarget(node);
+
+        // start with doPowCached
+        assertEquals(1D, target.call(1D, 1));
+        assertEquals(2D, target.call(2D, 1));
+        assertEquals(3D, target.call(3D, 1));
+        assertEquals(3, node.doPowCached);
+        assertEquals(0, node.doPowCachedExponent);
+
+        // transition to doPowCachedExponent
+        assertEquals(4D, target.call(4D, 1));
+        assertEquals(5D, target.call(5D, 1));
+        assertEquals(6D, target.call(6D, 1));
+        assertEquals(16D, target.call(4D, 2));
+        assertEquals(125D, target.call(5D, 3));
+        assertEquals(5, node.doPowCachedExponent);
+        assertEquals(0, node.doPowDoubleInt);
+
+        // transition to doPowDoubleInt
+        assertEquals(4D * 4D * 4D * 4D, target.call(4D, 4));
+        assertEquals(5D * 5D * 5D * 5D * 5D, target.call(5D, 5));
+        assertEquals(5, node.doPowCachedExponent);
+        assertEquals(2, node.doPowDoubleInt);
+
+        // transition to doPow
+        assertEquals(5D, target.call(5D, 1D));
+        assertEquals(2D, target.call(2D, 1D));
+
+        assertEquals(3, node.doPowCached);
+        assertEquals(5, node.doPowCachedExponent);
+        assertEquals(2, node.doPowDoubleInt);
+        assertEquals(2, node.doPow);
+    }
+
+    public static class MathPowNode extends ExampleNode {
+
+        // test flags
+        int doPowCached;
+        int doPowCachedExponent;
+        int doPowDoubleInt;
+        int doPow;
+
+        @Specialization(guards = {"base == cachedBase", "exponent == cachedExponent"})
+        double doPowCached(double base, int exponent, //
+                        @Cached("base") double cachedBase, //
+                        @Cached("exponent") int cachedExponent, //
+                        @Cached("cachePow(cachedBase, cachedExponent)") double cachedResult) {
+            doPowCached++;
+            return cachedResult;
+        }
+
+        /*
+         * We could just use the doPow specialization instead. But this makes the number of doPow
+         * calls more difficult to assert.
+         */
+        protected static double cachePow(double base, int exponent) {
+            return Math.pow(base, exponent);
+        }
+
+        @Specialization(contains = "doPowCached", guards = {"exponent == cachedExponent", "cachedExponent <= 10"})
+        @ExplodeLoop
+        double doPowCachedExponent(double base, int exponent, @Cached("exponent") int cachedExponent) {
+            doPowCachedExponent++;
+            double result = 1.0;
+            for (int i = 0; i < cachedExponent; i++) {
+                result *= base;
+            }
+            return result;
+        }
+
+        @Specialization(contains = "doPowCachedExponent", guards = "exponent >= 0")
+        double doPowDoubleInt(double base, int exponent) {
+            doPowDoubleInt++;
+            // Uses binary decomposition to limit the number of
+            // multiplications; see the discussion in "Hacker's Delight" by Henry
+            // S. Warren, Jr., figure 11-6, page 213.
+            double b = base;
+            int e = exponent;
+            double result = 1;
+            while (e > 0) {
+                if ((e & 1) == 1) {
+                    result *= b;
+                }
+                e >>= 1;
+                b *= b;
+            }
+            return result;
+        }
+
+        @Specialization(contains = {"doPowCached", "doPowDoubleInt"})
+        double doPow(double base, double exponent) {
+            doPow++;
+            return Math.pow(base, exponent);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/RubyCall.java	Wed Feb 11 12:13:44 2015 +0100
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2015, 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.examples;
+
+import static com.oracle.truffle.api.dsl.examples.ExampleNode.*;
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.examples.RubyCallFactory.RubyDispatchNodeGen;
+import com.oracle.truffle.api.dsl.examples.RubyCallFactory.RubyHeadNodeGen;
+import com.oracle.truffle.api.dsl.examples.RubyCallFactory.RubyLookupNodeGen;
+import com.oracle.truffle.api.dsl.internal.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+
+/**
+ * This example illustrates a simplified version of a Ruby function call semantics (RubyHeadNode).
+ * The example usage shows how methods can be redefined in this implementation.
+ */
+@SuppressWarnings("unused")
+public class RubyCall {
+
+    @Test
+    public void testCall() {
+        RubyHeadNode node = RubyHeadNodeGen.create(createArguments(4));
+        CallTarget nodeTarget = createTarget(node);
+        final Object firstArgument = "someArgument";
+
+        // dummyMethod is just going to return the some argument of the function
+        final Object testMethodName = "getSomeArgument";
+        // implementation returns first argument
+        InternalMethod aClassTestMethod = new InternalMethod(ExampleNode.createDummyTarget(3));
+        // implementation returns second argument
+        InternalMethod bClassTestMethod = new InternalMethod(ExampleNode.createDummyTarget(4));
+        // implementation returns third argument
+        InternalMethod cClassTestMethod = new InternalMethod(ExampleNode.createDummyTarget(5));
+
+        // defines hierarchy C extends B extends A
+        RubyClass aClass = new RubyClass("A", null);
+        RubyClass bClass = new RubyClass("B", aClass);
+        RubyClass cClass = new RubyClass("C", bClass);
+
+        RubyObject aInstance = new RubyObject(aClass);
+        RubyObject bInstance = new RubyObject(bClass);
+        RubyObject cInstance = new RubyObject(cClass);
+
+        // undefined method call
+        assertEquals(RubyObject.NIL, nodeTarget.call(cInstance, testMethodName, null, new Object[]{firstArgument}));
+
+        // method defined in a
+        aClass.addMethod(testMethodName, aClassTestMethod);
+        assertEquals(firstArgument, nodeTarget.call(aInstance, testMethodName, null, new Object[]{firstArgument}));
+        assertEquals(firstArgument, nodeTarget.call(bInstance, testMethodName, null, new Object[]{firstArgument}));
+        assertEquals(firstArgument, nodeTarget.call(cInstance, testMethodName, null, new Object[]{firstArgument}));
+
+        // method redefined in b
+        bClass.addMethod(testMethodName, bClassTestMethod);
+        assertEquals(firstArgument, nodeTarget.call(aInstance, testMethodName, null, new Object[]{firstArgument}));
+        assertEquals(firstArgument, nodeTarget.call(bInstance, testMethodName, null, new Object[]{null, firstArgument}));
+        assertEquals(firstArgument, nodeTarget.call(cInstance, testMethodName, null, new Object[]{null, firstArgument}));
+
+        // method redefined in c
+        cClass.addMethod(testMethodName, cClassTestMethod);
+        assertEquals(firstArgument, nodeTarget.call(aInstance, testMethodName, null, new Object[]{firstArgument}));
+        assertEquals(firstArgument, nodeTarget.call(bInstance, testMethodName, null, new Object[]{null, firstArgument}));
+        assertEquals(firstArgument, nodeTarget.call(cInstance, testMethodName, null, new Object[]{null, null, firstArgument}));
+
+    }
+
+    public static class RubyHeadNode extends ExampleNode {
+
+        @Child private RubyLookupNode lookup = RubyLookupNodeGen.create(null);
+        @Child private RubyDispatchNode dispatch = RubyDispatchNodeGen.create(null);
+
+        @Specialization
+        public Object doCall(VirtualFrame frame, Object receiverObject, Object methodName, Object blockObject, Object... argumentsObjects) {
+            InternalMethod method = lookup.executeLookup(frame, receiverObject, methodName);
+
+            Object[] packedArguments = new Object[argumentsObjects.length + 3];
+            packedArguments[0] = method;
+            packedArguments[1] = receiverObject;
+            packedArguments[2] = blockObject;
+            System.arraycopy(argumentsObjects, 0, packedArguments, 3, argumentsObjects.length);
+
+            return dispatch.executeDispatch(frame, method, packedArguments);
+        }
+    }
+
+    public abstract static class RubyLookupNode extends ExampleNode {
+
+        public abstract InternalMethod executeLookup(VirtualFrame frame, Object receiverObject, Object methodName);
+
+        @Specialization(guards = "receiver.getRubyClass() == cachedClass", assumptions = "cachedClass.getDependentAssumptions()")
+        protected static InternalMethod cachedLookup(RubyObject receiver, Object name, //
+                        @Cached("receiver.getRubyClass()") RubyClass cachedClass, //
+                        @Cached("genericLookup(receiver, name)") InternalMethod cachedLookup) {
+            return cachedLookup;
+        }
+
+        @Specialization(contains = "cachedLookup")
+        protected static InternalMethod genericLookup(RubyObject receiver, Object name) {
+            return receiver.getRubyClass().lookup(name);
+        }
+
+    }
+
+    @ImportStatic(InternalMethod.class)
+    public abstract static class RubyDispatchNode extends ExampleNode {
+
+        public abstract Object executeDispatch(VirtualFrame frame, InternalMethod function, Object[] packedArguments);
+
+        /*
+         * Please note that cachedMethod != METHOD_MISSING is invoked once at specialization
+         * instantiation. It is never executed on the fast path.
+         */
+        @Specialization(guards = {"method == cachedMethod", "cachedMethod != METHOD_MISSING"})
+        protected static Object directCall(VirtualFrame frame, InternalMethod method, Object[] arguments, //
+                        @Cached("method") InternalMethod cachedMethod, //
+                        @Cached("create(cachedMethod.getTarget())") DirectCallNode callNode) {
+            return callNode.call(frame, arguments);
+        }
+
+        /*
+         * The method == METHOD_MISSING can fold if the RubyLookup results just in a single entry
+         * returning the constant METHOD_MISSING.
+         */
+        @Specialization(guards = "method == METHOD_MISSING")
+        protected static Object methodMissing(VirtualFrame frame, InternalMethod method, Object[] arguments) {
+            // a real implementation would do a call to a method named method_missing here
+            return RubyObject.NIL;
+        }
+
+        @Specialization(contains = "directCall", guards = "method != METHOD_MISSING")
+        protected static Object indirectCall(VirtualFrame frame, InternalMethod method, Object[] arguments, //
+                        @Cached("create()") IndirectCallNode callNode) {
+            return callNode.call(frame, method.getTarget(), arguments);
+        }
+
+        @Override
+        public String toString() {
+            return ((SpecializedNode) this).getSpecializationNode().toString();
+        }
+    }
+
+    public static final class RubyObject {
+
+        public static final RubyObject NIL = new RubyObject(null);
+
+        private final RubyClass rubyClass;
+
+        public RubyObject(RubyClass rubyClass) {
+            this.rubyClass = rubyClass;
+        }
+
+        public RubyClass getRubyClass() {
+            return rubyClass;
+        }
+
+        @Override
+        public String toString() {
+            return "RubyObject[class=" + rubyClass + "]";
+        }
+
+    }
+
+    public static final class RubyClass /* this would extend RubyModule */{
+
+        private final String name;
+        private final RubyClass parent; // this would be a RubyModule
+        private final CyclicAssumption unmodified;
+        private final Map<Object, InternalMethod> methods = new HashMap<>();
+        private Assumption[] cachedDependentAssumptions;
+        private final int depth;
+
+        public RubyClass(String name, RubyClass parent) {
+            this.name = name;
+            this.parent = parent;
+            this.unmodified = new CyclicAssumption("unmodified class " + name);
+
+            // lookup depth for array allocation
+            RubyClass clazz = parent;
+            int currentDepth = 1;
+            while (clazz != null) {
+                currentDepth++;
+                clazz = clazz.parent;
+            }
+            this.depth = currentDepth;
+        }
+
+        @TruffleBoundary
+        public InternalMethod lookup(Object methodName) {
+            InternalMethod method = methods.get(methodName);
+            if (method == null) {
+                if (parent != null) {
+                    return parent.lookup(methodName);
+                } else {
+                    return InternalMethod.METHOD_MISSING;
+                }
+            } else {
+                return method;
+            }
+        }
+
+        @TruffleBoundary
+        public void addMethod(Object methodName, InternalMethod method) {
+            // check for existing method omitted for simplicity
+            this.methods.put(methodName, method);
+            this.unmodified.invalidate();
+        }
+
+        /*
+         * Method collects all unmodified assumptions in the class hierarchy. The result is cached
+         * per class to void recreation per call site.
+         */
+        @TruffleBoundary
+        public Assumption[] getDependentAssumptions() {
+            Assumption[] dependentAssumptions = cachedDependentAssumptions;
+            if (dependentAssumptions != null) {
+                // we can use the cached dependent assumptions only if they are still valid
+                for (Assumption assumption : cachedDependentAssumptions) {
+                    if (!assumption.isValid()) {
+                        dependentAssumptions = null;
+                        break;
+                    }
+                }
+            }
+            if (dependentAssumptions == null) {
+                cachedDependentAssumptions = dependentAssumptions = createDependentAssumptions();
+            }
+            return dependentAssumptions;
+        }
+
+        @Override
+        public String toString() {
+            return "RubyClass[name=" + name + "]";
+        }
+
+        private Assumption[] createDependentAssumptions() {
+            Assumption[] dependentAssumptions;
+            RubyClass clazz = this;
+            dependentAssumptions = new Assumption[depth];
+
+            // populate array
+            int index = 0;
+            do {
+                dependentAssumptions[index] = clazz.unmodified.getAssumption();
+                index++;
+                clazz = clazz.parent;
+            } while (clazz != null);
+            return dependentAssumptions;
+        }
+    }
+
+    public static final class InternalMethod {
+
+        public static final InternalMethod METHOD_MISSING = new InternalMethod(null);
+
+        private final CallTarget target;
+
+        public InternalMethod(CallTarget target) {
+            this.target = target;
+        }
+
+        public CallTarget getTarget() {
+            return target;
+        }
+
+        @Override
+        public String toString() {
+            return "InternalMethod[target=" + getTarget() + "]";
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/examples/StableDispatch.java	Wed Feb 11 12:13:44 2015 +0100
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015, 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.examples;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+
+/**
+ * This example is based on the SLDispatchNode of SimpleLanguage. It shows how to implement a simple
+ * inline cache with an assumption that needs to be checked.
+ *
+ * Note that if an assumption is invalidated the specialization instantiation is removed.
+ */
+@SuppressWarnings("unused")
+@NodeChildren({@NodeChild("function"), @NodeChild("arguments")})
+public class StableDispatch {
+
+    public static class StableDispatchNode extends ExampleNode {
+
+        @Specialization(guards = "function == cachedFunction", assumptions = "cachedFunction.getCallTargetStable()")
+        protected static Object directDispatch(VirtualFrame frame, SLFunction function, Object[] arguments, //
+                        @Cached("function") SLFunction cachedFunction, //
+                        @Cached("create(cachedFunction.getCallTarget())") DirectCallNode callNode) {
+            return callNode.call(frame, arguments);
+        }
+
+        @Specialization(contains = "directDispatch")
+        protected static Object indirectDispatch(VirtualFrame frame, SLFunction function, Object[] arguments, //
+                        @Cached("create()") IndirectCallNode callNode) {
+            return callNode.call(frame, function.getCallTarget(), arguments);
+        }
+    }
+
+    public static final class SLFunction {
+
+        private CallTarget callTarget;
+        private final CyclicAssumption callTargetStable;
+
+        protected SLFunction(String name) {
+            this.callTargetStable = new CyclicAssumption(name);
+        }
+
+        protected void setCallTarget(CallTarget callTarget) {
+            this.callTarget = callTarget;
+            this.callTargetStable.invalidate();
+        }
+
+        public CallTarget getCallTarget() {
+            return callTarget;
+        }
+
+        public Assumption getCallTargetStable() {
+            return callTargetStable.getAssumption();
+        }
+    }
+
+}