Mercurial > hg > graal-compiler
changeset 19288:3a37116ef37f
Truffle-DSL: added @Cached annotation, added limit property and updated @Specialization javadoc.
author | Christian Humer <christian.humer@gmail.com> |
---|---|
date | Mon, 29 Dec 2014 18:32:03 +0100 |
parents | 19c6359c6191 |
children | 62c43fcf5be2 |
files | graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/Cached.java graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/Specialization.java |
diffstat | 2 files changed, 538 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/Cached.java Mon Dec 29 18:32:03 2014 +0100 @@ -0,0 +1,235 @@ +/* + * 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. 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.lang.annotation.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; + +/** + * <p> + * A parameter annotated with {@link Cached} in a {@link Specialization} refers to a <b>cached</b> + * value of a specialization instance. A cached parameter value is initialized once using the + * initializer expression at specialization instantiation. For each call of the specialization + * method the cached value is provided by using the annotated parameter from the method body. Cache + * initializers are potentially executed before guard expressions declared in + * {@link Specialization#guards()}. + * </p> + * <p> + * A typical specialization may define multiple dynamic and multiple cached parameters. Dynamic + * parameter values are typically provided by executing child nodes of the operation. Cached + * parameters are initialized and stored once per specialization instantiation. Cached parameters + * are always constant at compile time. You may verify this by invoking + * {@link CompilerAsserts#compilationConstant(Object)} on any cached parameter. For consistency + * between specialization declarations cached parameters must be declared last in a specialization + * method. + * </p> + * <p> + * The initializer expression of a cached parameter is defined using a subset of Java. This subset + * includes field/parameter accesses, function calls, type exact infix comparisons (==, !=, <, <=, + * >, >=) and integer literals. The return type of the initializer expression must be assignable to + * the parameter type. If the annotated parameter type is derived from {@link Node} then the + * {@link Node} instance is allowed to use the {@link Node#replace(Node)} method to replace itself. + * Bound elements without receivers are resolved using the following order: + * <ol> + * <li>Dynamic and cached parameters of the enclosing specialization.</li> + * <li>Fields defined using {@link NodeField} for the enclosing node.</li> + * <li>Public constructors of the type of the annotated parameter using the <code>new</code> keyword + * as method name.</li> + * <li>Public and static methods or fields of the type of the annotated parameter.</li> + * <li>Non-private, static or virtual methods or fields of enclosing node.</li> + * <li>Non-private, static or virtual methods or fields of super types of the enclosing node.</li> + * <li>Public and static methods or fields imported using {@link ImportStatic}.</li> + * </ol> + * + * The following examples explain the intended use of the {@link Cached} annotation. All of the + * examples have to be enclosed in the following node declaration: + * </p> + * + * <pre> + * @NodeChild("operand") + * abstract TestNode extends Node { + * abstract void execute(Object operandValue); + * // ... example here ... + * } + * </pre> + * + * <ol> + * <li> + * This example defines one dynamic and one cached parameter. The operand parameter is representing + * the dynamic value of the operand while the cachedOperand is initialized once at first execution + * of the specialization (specialization instantiation time). + * + * <pre> + * @Specialization + * void doCached(int operand, @Local("operand") int cachedOperand) { + * CompilerAsserts.compilationConstant(cachedOperand); + * ... + * } + * + * Example executions: + * execute(1) => doCached(1, 1) // new instantiation, localOperand is bound to 1 + * execute(0) => doCached(0, 1) + * execute(2) => doCached(2, 1) + * + * </pre> + * + * </li> + * <li> + * We extend the previous example by a guard for the cachedOperand value to be equal to the dynamic + * operand value. This specifies that the specialization is instantiated for each individual operand + * value that is provided. There are a lot of individual <code>int</code> values and for each + * individual <code>int</code> value a new specialization would get instantiated. The + * {@link Specialization#limit()} property defines a limit for the number of specializations that + * can get instantiated. If the specialization instantiation limit is reached then no further + * specializations are instantiated. Like for other specializations if there are no more + * specializations defined an {@link UnsupportedSpecializationException} is thrown. The default + * specialization instantiation limit is <code>3</code>. + * + * <pre> + * @Specialization(guards = "==(operand, cachedOperand)") + * void doCached(int operand, @Cached("operand") int cachedOperand) { + * CompilerAsserts.compilationConstant(cachedOperand); + * ... + * } + * + * Example executions: + * execute(0) => doCached(0, 0) // new instantiation, cachedOperand is bound to 0 + * execute(1) => doCached(1, 1) // new instantiation, cachedOperand is bound to 1 + * execute(1) => doCached(1, 1) + * execute(2) => doCached(2, 2) // new instantiation, cachedOperand is bound to 2 + * execute(3) => throws UnsupportedSpecializationException // instantiation limit overflows + * + * </pre> + * + * </li> + * <li> + * To handle the limit overflow we extend our example by an additional specialization named + * <code>doNormal</code>. This specialization has the same type restrictions but does not have local + * state nor the operand identity guard. It is also declared after <code>doCached</code> therefore + * it is only instantiated if the limit of the <code>doCached</code> specialization has been + * reached. In other words <code>doNormal</code> is more generic than <code>doCached</code> . The + * <code>doNormal</code> specialization uses <code>contains="doCached"</code> to specify + * that all instantiations of <code>doCached</code> get removed if <code>doNormal</code> is + * instantiated. Alternatively if the <code>contains</code> relation is omitted then all + * <code>doCached</code> instances remain but no new instances are created. + * + * <pre> + * @Specialization(guards = "==(operand, cachedOperand)") + * void doCached(int operand, @Cached("operand") int cachedOperand) { + * CompilerAsserts.compilationConstant(cachedOperand); + * ... + * } + * + * @Specialization(contains = "doCached") + * void doNormal(int operand) {...} + * + * Example executions with contains = "doCached": + * execute(0) => doCached(0, 0) // new instantiation, cachedOperand is bound to 0 + * execute(1) => doCached(1, 1) // new instantiation, cachedOperand is bound to 1 + * execute(1) => doCached(1, 1) + * execute(2) => doCached(2, 2) // new instantiation, cachedOperand is bound to 2 + * execute(3) => doNormal(3) // new instantiation of doNormal due to limit overflow; doCached gets removed. + * execute(1) => doNormal(1) + * + * Example executions without contains = "doCached": + * execute(0) => doCached(0, 0) // new instantiation, cachedOperand is bound to 0 + * execute(1) => doCached(1, 1) // new instantiation, cachedOperand is bound to 1 + * execute(1) => doCached(1, 1) + * execute(2) => doCached(2, 2) // new instantiation, cachedOperand is bound to 2 + * execute(3) => doNormal(3) // new instantiation of doNormal due to limit overflow + * execute(1) => doCached(1, 1) + * + * </pre> + * + * </li> + * <li> + * This next example shows how methods from the enclosing node can be used to initialize cached + * parameters. Please note that the visibility of transformLocal must not be <code>private</code>. + * + * <pre> + * @Specialization + * void s(int operand, @Cached("transformLocal(operand)") int cachedOperand) { + * } + * + * int transformLocal(int operand) { + * return operand & 0x42; + * } + * + * </li> + * </pre> + * <li> + * The <code>new</code> keyword can be used to initialize a cached parameter using a constructor of + * the parameter type. + * + * <pre> + * @Specialization + * void s(Object operand, @Cached("new()") OtherNode someNode) { + * someNode.execute(operand); + * } + * + * static class OtherNode extends Node { + * + * public String execute(Object value) { + * throw new UnsupportedOperationException(); + * } + * } + * + * </pre> + * + * </li> + * <li> + * Java types without public constructor but with a static factory methods can be initialized by + * just referencing its static factory method and its parameters. In this case + * {@link BranchProfile#create()} is used to instantiate the {@link BranchProfile} instance. + * + * <pre> + * @Specialization + * void s(int operand, @Local("create()") BranchProfile profile) { + * } + * </pre> + * + * </li> + * </ol> + * + * @see Specialization#guards() + * @see Specialization#contains() + * @see Specialization#limit() + * @see ImportStatic + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PARAMETER}) +public @interface Cached { + + /** + * Defines the initializer expression of the cached parameter value. + * + * @see Cached + */ + String value(); + +}
--- a/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/Specialization.java Wed Feb 11 12:13:43 2015 +0100 +++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/Specialization.java Mon Dec 29 18:32:03 2014 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -26,6 +26,91 @@ import java.lang.annotation.*; +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * <p> + * Defines a method of a node subclass to represent one specialization of an operation. Multiple + * specializations can be defined in a node representing an operation. A specialization defines + * which kind of input is expected using the method signature and the annotation attributes. The + * specialized semantics of the operation are defined using the body of the annotated Java method. A + * specialization method must be declared in a class that is derived from {@link Node} that + * references a {@link TypeSystem}. At least one specialization must be defined per operation. If no + * specialization is valid for the given set of input values then an + * {@link UnsupportedSpecializationException} is thrown instead of invoking any specialization + * method. + * </p> + * <p> + * A specialization must have at least as many parameters as there are {@link NodeChild} annotations + * declared for the enclosing operation node. These parameters are declared in the same order as the + * {@link NodeChild} annotations (linear execution order). We call such parameters dynamic input + * parameters. Every specialization that is declared within an operation must have an equal number + * of dynamic input parameters. + * </p> + * <p> + * The supported kind of input values for a specialization are declared using guards. A + * specialization may provide declarative specifications for four kinds of guards: + * <ul> + * <li><b>Type guards</b> optimistically assume the type of an input value. A value that matches the + * type is cast to its expected type automatically. Type guards are modeled using the parameter type + * of the specialization method. Types used for type guards must be defined in the + * {@link TypeSystem}. If the type of the parameter is {@link Object} then no type guard is used for + * the dynamic input parameter.</li> + * + * <li><b>Expression guards</b> optimistically assume the return value of a user-defined expression + * to be <code>true</code>. Expression guards are modeled using Java expressions that return a + * <code>boolean</code> value. If the guard expression returns <code>false</code>, the + * specialization is no longer applicable and the operation is re-specialized. Guard expressions are + * declared using the {@link #guards()} attribute.</li> + * + * <li><b>Event guards</b> trigger re-specialization in case an exception is thrown in the + * specialization body. The {@link #rewriteOn()} attribute can be used to declare a list of such + * exceptions. Guards of this kind are useful to avoid calculating a value twice when it is used in + * the guard and its specialization.</li> + * + * <li><b>Assumption guards</b> optimistically assume that the state of an {@link Assumption} + * remains <code>true</code>. Assumptions can be assigned to specializations using the + * {@link #assumptions()} attribute.</li> + * </ul> + * </p> + * <p> + * The enclosing {@link Node} of a specialization method must have at least one <code>public</code> + * and non-<code>final</code> execute method. An execute method is a method that starts with + * 'execute'. If all execute methods declare the first parameter type as {@link Frame}, + * {@link VirtualFrame} or {@link MaterializedFrame} then the same frame type can be used as + * optional first parameter of the specialization. This parameter does not count to the number of + * dynamic parameters. + * </p> + * <p> + * A specialization method may declare multiple parameters annotated with {@link Cached}. Cached + * parameters are initialized and stored once per specialization instantiation. For consistency + * between specialization declarations cached parameters must be declared last in a specialization + * method. + * </p> + * <p> + * If the operation is re-specialized or if it is executed for the first time then all declared + * specializations of the operation are tried in declaration order until the guards of the first + * specialization accepts the current input values. The new specialization is then added to the + * chain of current specialization instances which might consist of one (monomorph) or multiple + * instances (polymorph). If an assumption of an instantiated specialization is violated then + * re-specialization is triggered again. + * </p> + * <p> + * With guards in combination with cached parameters it is possible that multiple instances of the + * same specialization are created. The {@link #limit()} attribute can be used to limit the number + * of instantiations per specialization. + * </p> + * + * @see NodeChild + * @see ShortCircuit + * @see Fallback + * @see Cached + * @see TypeSystem + * @see TypeSystemReference + * @see UnsupportedSpecializationException + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Specialization { @@ -36,39 +121,242 @@ @Deprecated int DEFAULT_ORDER = -1; /** - * The order has no effect anymore. The declaration order specialization methods is used - * instead. - * * @deprecated use declaration order instead. Will get removed in the next release. */ @Deprecated int order() default DEFAULT_ORDER; /** - * Inserts this and all specializations that are declared after this specialization before a - * specialization in the superclass. By default all specializations of the subclass are appended - * to the specializations of the superclass. + * References a specialization of a super class by its method name where this specialization is + * inserted before. The declaration order of a specialization is not usable for nodes where + * specializations are partly declared in the super class and partly declared in a derived + * class. By default all specializations declared in the derived class are appended to those in + * the super class. This attribute can be used to override the default behavior. */ String insertBefore() default ""; + /** + * <p> + * Declares an event guards that trigger re-specialization in case an exception is thrown in the + * specialization body. This attribute can be used to declare a list of such exceptions. Guards + * of this kind are useful to avoid calculating a value twice when it is used in the guard and + * its specialization. + * </p> + * + * <p> + * If an event guard exception is triggered then all instantiations of this specialization are + * removed. If one of theses exceptions is thrown once then no further instantiations of this + * specialization are going to be created for this node. A specialization that rewrites on an + * exception must ensure that no non-repeatable side-effect is caused until the rewrite is + * triggered. + * </p> + * + * <b>Example usage:</b> + * + * <pre> + * @Specialization(rewriteOn = ArithmeticException.class) + * int doAddNoOverflow(int a, int b) { + * return ExactMath.addExact(a, b); + * } + * + * @Specialization + * long doAddWithOverflow(int a, int b) { + * return a + b; + * } + * ... + * + * Example executions: + * execute(Integer.MAX_VALUE - 1, 1) => doAddNoOverflow(Integer.MAX_VALUE - 1, 1) + * execute(Integer.MAX_VALUE, 1) => doAddNoOverflow(Integer.MAX_VALUE, 1) + * => throws ArithmeticException + * => doAddWithOverflow(Integer.MAX_VALUE, 1) + * execute(Integer.MAX_VALUE - 1, 1) => doAddWithOverflow(Integer.MAX_VALUE - 1, 1) + * </pre> + * + * </p> + * + * @see ExactMath#addExact(int, int) + */ Class<? extends Throwable>[] rewriteOn() default {}; /** - * The contains attribute declares all specializations that are contained by this - * specialization. A containing specialization must be strictly generic as the contained - * specialization. + * <p> + * Declares other specializations of the same node to be contained by this specialization. Other + * specializations are references using their unique method name. If this specialization is + * instantiated then all contained specialization instances are removed and never instantiated + * again for this node instance. Therefore this specialization should handle strictly more + * inputs than which were handled by the contained specialization, otherwise the removal of the + * contained specialization will lead to unspecialized types of input values. The contains + * declaration is transitive for multiple involved specializations. + * </p> + * <b>Example usage:</b> + * + * <pre> + * @Specialization(guards = "b == 2") + * void doDivPowerTwo(int a, int b) { + * return a >> 1; + * } + * + * @Specialization(contains ="doDivPowerTwo", guards = "b > 0") + * void doDivPositive(int a, int b) { + * return a / b; + * } + * ... + * + * Example executions with contains="doDivPowerTwo": + * execute(4, 2) => doDivPowerTwo(4, 2) + * execute(9, 3) => doDivPositive(9, 3) // doDivPowerTwo instances get removed + * execute(4, 2) => doDivPositive(4, 2) + * + * Same executions without contains="doDivPowerTwo" + * execute(4, 2) => doDivPowerTwo(4, 2) + * execute(9, 3) => doDivPositive(9, 3) + * execute(4, 2) => doDivPowerTwo(4, 2) + * </pre> + * + * </p> + * + * @see #guards() */ String[] contains() default {}; + /** + * <p> + * Declares <code>boolean</code> expressions that define whether or not input values are + * applicable to this specialization instance. Guard expressions must always return the same + * result for each combination of the enclosing node instance and the bound input values. + * </p> + * <p> + * If a guard expression does not bind any dynamic input parameters then the DSL assumes that + * the result will not change for this node after specialization instantiation. The DSL asserts + * this assumption if assertions are enabled (-ea). + * </p> + * <p> + * Guard expressions are defined using a subset of Java. This subset includes field/parameter + * accesses, function calls, type exact infix comparisons (==, !=, <, <=, >, >=), logical + * negation (!), logical disjunction (||) and integer literals. The return type of guard + * expressions must be <code>boolean</code>. Bound elements without receivers are resolved using + * the following order: + * <ol> + * <li>Dynamic and cached parameters of the enclosing specialization.</li> + * <li>Fields defined using {@link NodeField} for the enclosing node.</li> + * <li>Non-private, static or virtual methods or fields of enclosing node.</li> + * <li>Non-private, static or virtual methods or fields of super types of the enclosing node.</li> + * <li>Public and static methods or fields imported using {@link ImportStatic}.</li> + * </ol> + * </p> + * <p> + * <b>Example usage:</b> + * + * <pre> + * static boolean acceptOperand(int operand) { + * assert operand <= 42; + * return operand & 1 == 1; + * } + * + * @Specialization(guards = {"operand <= 42", "acceptOperand(operand)"}) + * void doSpecialization(int operand) {...} + * </pre> + * + * </p> + * + * @see Cached + * @see ImportStatic + */ String[] guards() default {}; /** - * Defines the assumptions to check for this specialization. When the specialization method is - * invoked it is guaranteed that these assumptions still hold. It is not guaranteed that they - * are checked before the {@link #guards()} methods. They may be checked before after or in - * between {@link #guards()}. To declare assumptions use the {@link NodeAssumptions} annotation - * at class level. + * <p> + * Declares assumption guards that optimistically assume that the state of an {@link Assumption} + * remains valid. Assumption expressions are cached once per specialization instantiation. If + * one of the returned assumptions gets invalidated then the specialization instance is removed. + * An assumption expression may return different assumptions per specialization instance. The + * returned assumption instance must not be <code>null</code>. + * </p> + * <p> + * Assumption expressions are defined using a subset of Java. This subset includes + * field/parameter accesses, function calls, type exact infix comparisons (==, !=, <, <=, >, + * >=), logical negation (!), logical disjunction (||) and integer literals. The return type of + * the expression must be {@link Assumption}. Assumption expressions are not allowed to bind to + * dynamic parameter values of the specialization. Bound elements without receivers are resolved + * using the following order: + * <ol> + * <li>Cached parameters of the enclosing specialization.</li> + * <li>Fields defined using {@link NodeField} for the enclosing node.</li> + * <li>Non-private, static or virtual methods or fields of enclosing node.</li> + * <li>Non-private, static or virtual methods or fields of super types of the enclosing node.</li> + * <li>Public and static methods or fields imported using {@link ImportStatic}.</li> + * </ol> + * </p> + * + * <p> + * <b>Example usage:</b> + * + * <pre> + * static abstract class DynamicObject() { + * abstract Shape getShape(); + * ... + * } + * static abstract class Shape() { + * abstract Assumption getUnmodifiedAssuption(); + * ... + * } + * + * @Specialization(guards = "operand.getShape() == cachedShape", assumptions = "cachedShape.getUnmodifiedAssumption()") + * void doAssumeUnmodifiedShape(DynamicObject operand, @Cached("operand.getShape()") Shape cachedShape) {...} + * </pre> + * + * </p> + * + * @see Cached + * @see ImportStatic */ String[] assumptions() default {}; + /** + * <p> + * Declares the expression that limits the number of specialization instantiations. The default + * limit for specialization instantiations is defined as <code>"3"</code>. If the limit is + * exceeded no more instantiations of the enclosing specialization method are created. Please + * note that the existing specialization instantiations are <b>not</b> removed from the + * specialization chain. You can use {@link #contains()} to remove unnecessary specializations + * instances. + * </p> + * <p> + * The limit expression is defined using a subset of Java. This subset includes field/parameter + * accesses, function calls, type exact infix comparisons (==, !=, <, <=, >, >=), logical + * negation (!), logical disjunction (||) and integer literals. The return type of the limit + * expression must be <code>int</code>. Limit expressions are not allowed to bind to dynamic + * parameter values of the specialization. Bound elements without receivers are resolved using + * the following order: + * <ol> + * <li>Cached parameters of the enclosing specialization.</li> + * <li>Fields defined using {@link NodeField} for the enclosing node.</li> + * <li>Non-private, static or virtual methods or fields of enclosing node.</li> + * <li>Non-private, static or virtual methods or fields of super types of the enclosing node.</li> + * <li>Public and static methods or fields imported using {@link ImportStatic}.</li> + * </ol> + * </p> + * + * <p> + * <b>Example usage:</b> + * + * <pre> + * static int getCacheLimit() { + * return Integer.parseInt(System.getProperty("language.cacheLimit")); + * } + * + * @Specialization(guards = "operand == cachedOperand", limit = "getCacheLimit()") + * void doCached(Object operand, @Cached("operand") Object cachedOperand) {...} + * </pre> + * + * </p> + * + * @see #guards() + * @see #contains() + * @see Cached + * @see ImportStatic + */ + String limit() default ""; + }