All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pkl.thirdparty.truffle.api.dsl.Specialization Maven / Gradle / Ivy

Go to download

Fat Jar containing pkl-cli, pkl-codegen-java, pkl-codegen-kotlin, pkl-config-java, pkl-core, pkl-doc, and their shaded third-party dependencies.

There is a newer version: 0.27.1
Show newest version
/*
 * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.pkl.thirdparty.truffle.api.dsl;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.pkl.thirdparty.truffle.api.Assumption;
import org.pkl.thirdparty.truffle.api.frame.Frame;
import org.pkl.thirdparty.truffle.api.frame.MaterializedFrame;
import org.pkl.thirdparty.truffle.api.frame.VirtualFrame;
import org.pkl.thirdparty.truffle.api.nodes.Node;
import org.pkl.thirdparty.truffle.api.nodes.UnexpectedResultException;

/**
 * 

* 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. *

*

* 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. *

*

* The supported kind of input values for a specialization are declared using guards. A * specialization may provide declarative specifications for four kinds of guards: *

    *
  • Type guards 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.
  • * *
  • Expression guards optimistically assume the return value of a user-defined expression * to be true. Expression guards are modeled using Java expressions that return a * boolean value. If the guard expression returns false, the * specialization is no longer applicable and the operation is re-specialized. Guard expressions are * declared using the {@link #guards()} attribute.
  • * *
  • Event guards 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.
  • * *
  • Assumption guards optimistically assume that the state of an {@link Assumption} * remains true. Assumptions can be assigned to specializations using the * {@link #assumptions()} attribute.
  • *
*

*

* The enclosing {@link Node} of a specialization method must have at least one public * and non-final 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. *

*

* 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. *

*

* 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. *

*

* 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. *

* * @see NodeChild * @see Fallback * @see Cached * @see TypeSystem * @see TypeSystemReference * @see UnsupportedSpecializationException * @since 0.8 or earlier */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Specialization { /** * 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. * * @since 0.8 or earlier */ String insertBefore() default ""; /** *

* 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. *

* *

* 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. * * In case of explicitly declared {@link UnexpectedResultException}s, the result from the * exception will be used. For all other exception types, the next available specialization will * be executed, so that the original specialization must ensure that no non-repeatable * side-effect is caused until the rewrite is triggered. *

* * Example usage: * *
     * @Specialization(rewriteOn = ArithmeticException.class)
     * int doAddNoOverflow(int a, int b) {
     *     return Math.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)
     * 
* *

* * @see Math#addExact(int, int) * @since 0.8 or earlier */ Class[] rewriteOn() default {}; /** *

* Declares other specializations of the same operation to be replaced by this specialization. * Other specializations are referenced using their unique method name. If this specialization * is instantiated then all replaced 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 replaced specialization, otherwise the removal of the * replaced specialization will lead to unspecialized types of input values. The replaces * declaration is transitive for multiple involved specializations. *

* Example usage: * *
     * @Specialization(guards = "b == 2")
     * void doDivPowerTwo(int a, int b) {
     *     return a >> 1;
     * }
     * @Specialization(replaces ="doDivPowerTwo", guards = "b > 0")
     * void doDivPositive(int a, int b) {
     *     return a / b;
     * }
     * ...
     * Example executions with replaces="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 replaces="doDivPowerTwo"
     *   execute(4, 2) => doDivPowerTwo(4, 2)
     *   execute(9, 3) => doDivPositive(9, 3)
     *   execute(4, 2) => doDivPowerTwo(4, 2)
     * 
* *

* * @see #guards() * @since 0.22 */ String[] replaces() default {}; /** *

* Declares boolean 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. *

*

* If a guard expression does not bind any dynamic input parameters then the DSL, by default, * assumes that the result will not change for this node after specialization instantiation. In * other words the DSL assumes idempotence for this guard on the fast-path, by default. The * {@link Idempotent} and {@link NonIdempotent} annotations may be used to configure this * explicitly. The DSL will also emit warnings in case the use of such annotations is * recommended. If assertions are enabled (-ea), then the DSL will assert that the idempotence * property does hold at runtime. *

*

* 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 (||), null, true, false, and integer literals. The return * type of guard expressions must be boolean. Bound elements without receivers are * resolved using the following order: *

    *
  1. Dynamic and cached parameters of the enclosing specialization.
  2. *
  3. Fields defined using {@link NodeField} for the enclosing node.
  4. *
  5. Non-private, static or virtual methods or fields of enclosing node.
  6. *
  7. Non-private, static or virtual methods or fields of super types of the enclosing node. *
  8. *
  9. Public and static methods or fields imported using {@link ImportStatic}.
  10. *
*

*

* Example usage: * *

     * static boolean acceptOperand(int operand) {
     *     assert operand <= 42;
     *     return (operand & 1) == 1;
     * }
     *
     * @Specialization(guards = {"operand <= 42", "acceptOperand(operand)"})
     * void doSpecialization(int operand) {...}
     * 
* *

* * @see Cached * @see ImportStatic * @since 0.8 or earlier */ String[] guards() default {}; /** *

* 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. * If the assumption expression returns an array of assumptions then all assumptions of the * array are checked. This is limited to one-dimensional arrays. *

*

* 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 (||), null, true, false, and integer literals. * The return type of the expression must be {@link Assumption} or an array of * {@link Assumption} instances. Assumption expressions are not allowed to bind to dynamic * parameter values of the specialization. Bound elements without receivers are resolved using * the following order: *

    *
  1. Cached parameters of the enclosing specialization.
  2. *
  3. Fields defined using {@link NodeField} for the enclosing node.
  4. *
  5. Non-private, static or virtual methods or fields of enclosing node.
  6. *
  7. Non-private, static or virtual methods or fields of super types of the enclosing node. *
  8. *
  9. Public and static methods or fields imported using {@link ImportStatic}.
  10. *
*

* *

* Example usage: * *

     * abstract static class DynamicObject() { abstract Shape getShape(); ... }
     * abstract static class Shape() { abstract Assumption getUnmodifiedAssuption(); ... }
     *
     * @Specialization(guards = "operand.getShape() == cachedShape", assumptions = "cachedShape.getUnmodifiedAssumption()")
     * void doAssumeUnmodifiedShape(DynamicObject operand, @Cached("operand.getShape()") Shape cachedShape) {...}
     * 
* *

* * @see Cached * @see ImportStatic * @since 0.8 or earlier */ String[] assumptions() default {}; /** *

* Declares the expression that limits the number of specialization instantiations. The default * limit for specialization instantiations is defined as "3". If the limit is * exceeded no more instantiations of the enclosing specialization method are created. Please * note that the existing specialization instantiations are not removed from the * specialization chain. You can use {@link #replaces()} to remove unnecessary specializations * instances. *

*

* 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 (||), null, true, false, and integer literals. The return * type of the limit expression must be int. Limit expressions are not allowed to * bind to dynamic parameter values of the specialization. Bound elements without receivers are * resolved using the following order: *

    *
  1. Cached parameters of the enclosing specialization.
  2. *
  3. Fields defined using {@link NodeField} for the enclosing node.
  4. *
  5. Non-private, static or virtual methods or fields of enclosing node.
  6. *
  7. Non-private, static or virtual methods or fields of super types of the enclosing node. *
  8. *
  9. Public and static methods or fields imported using {@link ImportStatic}.
  10. *
*

* *

* Example usage: * *

     * static int getCacheLimit() {
     *     return Integer.parseInt(System.getProperty("language.cacheLimit"));
     * }
     *
     * @Specialization(guards = "operand == cachedOperand", limit = "getCacheLimit()")
     * void doCached(Object operand, @Cached("operand") Object cachedOperand) {...}
     * 
* *

* * @see #guards() * @see #replaces() * @see Cached * @see ImportStatic * @since 0.8 or earlier */ String limit() default ""; /** * Instructs the specialization to unroll a specialization with multiple instances. Unrolling * causes fields of the inline cache to be directly stored in the node instead of a chained * inline cache. At most 8 instances of a specialization can be unrolled to avoid code explosion * in the interpreter. *

* A common use-case for this feature is to unroll the first instance of an inline cache. It is * often the case that specializations with multiple instances are instantiated only once. By * unrolling the first instance we can optimize for this common situation which may lead to * footprint and interpreter performance improvements. *

* This feature is prone to cause inefficiencies if used too aggressively. Extra care should be * taken, e.g. the generated code should be inspected and profiled to verify that the new code * is better than the previous version. *

* Consider the following example: * *

     * class MyNode extends Node {
     *
     *     static int limit = 2;
     *
     *     abstract int execute(int value);
     *
     *     @Specialization(guards = "value == cachedValue", limit = "limit", unroll = 1)
     *     int doDefault(int value,
     *                     @Cached("value") int cachedValue) {
     *         return value;
     *     }
     *
     * }
     * 
* * In this example we unroll the first instance of an inline cache on int values. * This is equivalent to manually specifying the following specializations: * *
     * class MyUnrollNode extends Node {
     *
     *     static int limit = 2;
     *
     *     abstract int execute(int value);
     *
     *     @Specialization(guards = "value == cachedValue", limit = "1")
     *     int doUnrolled0(int value,
     *                     @Cached("value") int cachedValue) {
     *         return value;
     *     }
     *
     *     @Specialization(guards = "value == cachedValue", limit = "limit - 1")
     *     int doDefault(int value,
     *                     @Cached("value") int cachedValue) {
     *         return value;
     *     }
     * }
     *
     * 
* * * @since 23.0 */ int unroll() default 0; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy