Annotation Interface Cached
A parameter annotated with Cached in a Specialization refers to a cached
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
Specialization.guards().
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
CompilerAsserts.compilationConstant(Object) on any cached parameter. For consistency
between specialization declarations cached parameters must be declared last in a specialization
method.
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 (==, !=, <, <=,
>, >=), logical negation (!), logical disjunction (||), null, true, false, 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 Node then the Node instance is allowed
to use the Node.replace(Node) method to replace itself. Bound elements without receivers
are resolved using the following order:
- Dynamic and cached parameters of the enclosing specialization.
- Fields defined using
NodeFieldfor the enclosing node. - Public constructors of the type of the annotated parameter using the
newkeyword as method name. - Public and static methods or fields of the type of the annotated parameter.
- Non-private, static or virtual methods or fields of enclosing node.
- Non-private, static or virtual methods or fields of super types of the enclosing node.
- Public and static methods or fields imported using
ImportStatic.
Cached annotation. All of the
examples have to be enclosed in the following node declaration:
@NodeChild("operand")
abstract TestNode extends Node {
abstract void execute(Object operandValue);
// ... example here ...
}
- 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).
@Specialization void doCached(int operand,
@Cached("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) - 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
intvalues and for each individualintvalue a new specialization would get instantiated. TheSpecialization.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 anUnsupportedSpecializationExceptionis thrown. The default specialization instantiation limit is3.@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 - To handle the limit overflow we extend our example by an additional specialization named
doNormal. This specialization has the same type restrictions but does not have local state nor the operand identity guard. It is also declared afterdoCachedtherefore it is only instantiated if the limit of thedoCachedspecialization has been reached. In other wordsdoNormalis more generic thandoCached. ThedoNormalspecialization usesreplaces="doCached"to specify that all instantiations ofdoCachedget removed ifdoNormalis instantiated. Alternatively if thereplacesrelation is omitted then alldoCachedinstances remain but no new instances are created.@Specialization(guards = "operand == cachedOperand") void doCached(int operand,
@Cached("operand") int cachedOperand) { CompilerAsserts.compilationConstant(cachedOperand); ... } @Specialization(replaces = "doCached") void doNormal(int operand) {...} Example executions with replaces = "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 replaces = "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) - 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
private.@Specialization void s(int operand,
@Cached("transformLocal(operand)") int cachedOperand) { } int transformLocal(int operand) { return operand & 0x42; } - The
newkeyword can be used to initialize a cached parameter using a constructor of the parameter type.@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(); } } - 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
BranchProfile.create()is used to instantiate theBranchProfileinstance.@Specialization void s(int operand,
@Cached("create()") BranchProfile profile) { }
- Since:
- 0.8 or earlier
- See Also:
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic @interfaceDisallows any sharing with other cached parameters.static @interfaceAllows sharing between multiple Cached parameters between multiple specializations or exported library messages. -
Optional Element Summary
Optional ElementsModifier and TypeOptional ElementDescriptionbooleanSpecifies whether the cached parameter values of typeNodeInterfaceshould be adopted as its child by the current node.booleanAllows thevalue()to be used foruncached().intSpecifies the number of array dimensions to be marked ascompilation final.booleanEnables inlining of the cached parameter if supported.Specifies an alternative method name for node object inlining.booleanReturnstrueif the cache initializer never returns the default value of its parameter type at runtime.String[]Specifies the bindings used for the $parameters variable in cached or uncached initializers.Defines the initializer that is used foruncachednodes or uncached versions of exported library messages.Defines the initializer expression of the cached parameter value.booleanIf set totruethen weak references will be used to refer to this cached value in the generated node.
-
Element Details
-
value
String valueDefines the initializer expression of the cached parameter value.- Since:
- 0.8 or earlier
- See Also:
- Default:
"create($parameters)"
-
uncached
-
dimensions
int dimensionsSpecifies the number of array dimensions to be marked ascompilation final. This value must be specified for all array-typed cached values exceptnodearrays and must be left unspecified in other cases where it has no meaning. The allowed range is from 0 to the number of declared array dimensions (inclusive). Specifically, adimensionsvalue of 0 marks only the reference to the (outermost) array as final but not its elements, a value of 1 marks the outermost array and all its elements as final but not the elements of any nested arrays. If not specified and the cached value type is an array type then this will cause a warning and in later releases an error.- Since:
- 0.26
- See Also:
- Default:
-1
-
allowUncached
boolean allowUncachedAllows thevalue()to be used foruncached(). This is useful if the expression is the same forvalue()anduncached(). By settingallowUncached()totrueit is not necessary to repeat thevalue()expression in theuncached()expression. This flag cannot be set in combination withuncached().- Since:
- 19.0
- Default:
false
-
inline
boolean inlineEnables inlining of the cached parameter if supported. The type of a cached parameter is considered inlinable if it declares a static method calledinlinewith a single parameter of typeInlineSupport.InlineTarget. For specializing DSL nodes theGenerateInlineis sufficient to be enabled for the cached target type, the static inline method will be resolved automatically from the generated code of the target type.Inlining is enabled by default if:
GenerateInlineis set totruefor the declaring specializing node type.- The
GenerateCached.alwaysInlineCached()property is set totruefor the declaring specializing node type. - If a target parameter type is a specializing node and has
GenerateInlineenabled butGenerateCacheddisabled.
- Since:
- 23.0
- See Also:
- Default:
false
-
inlineMethod
String inlineMethodSpecifies an alternative method name for node object inlining. Instead of looking up the inline method from the receiver type use an accessible enclosing method of the given name instead. The method must have a single parameterInlineSupport.InlineTargetand return a type compatible to the cached type. This can be useful if you want to route calls to the inline method through an abstraction that does not allow direct type access to the node classes. It is expected that this property is only needed rarely.- Since:
- 23.0
- Default:
""
-
parameters
String[] parametersSpecifies the bindings used for the $parameters variable in cached or uncached initializers.- Since:
- 19.0
- Default:
{}
-
weak
boolean weakIf set totruethen weak references will be used to refer to this cached value in the generated node. The default value isfalse. The weak cached parameter is guaranteed to not becomenullin guards or specialization method invocations. If a weak cached parameter gets collected by the GC, then any compiled code remain unaffected and the specialization instance will not be removed. Specializations with collected cached references continue to count to the specialization limit. This is necessary to provide an upper bound for the number of invalidations that may happen for this specialization.A weak cached parameter implicitly adds a
weakRef.get() != nullguard that is invoked before the cached value is referenced for the first time. This means that specializations which previously did not result in fall-through behavior may now fall-through. This is important if used in combination withFallback. Weak cached parameters that are used as part ofuncachednodes, execute the cached initializer for each execution and therefore implicitly do not use a weak reference.Example usage:
@GenerateUncached abstract class WeakInlineCacheNode extends Node { abstract Object execute(Object arg0); @Specialization(guards = "cachedArg.equals(arg)", limit = "3") Object s0(String arg, @Cached(value = "arg", weak = true) String cachedArg) { assertNotNull(cachedStorage); return arg; } }- Since:
- 20.2
- See Also:
- Default:
false
-
adopt
boolean adoptSpecifies whether the cached parameter values of typeNodeInterfaceshould be adopted as its child by the current node. The default value istrue, therefore all cached values of typeNodeInterfaceand arrays of the same type are adopted. If the value is set tofalse, then no adoption is performed. It is useful to set adopt tofalsewhen nodes need to be referenced more than once in the AST.If the type of the field is an
NodeInterfacearray and adopt is set tofalse, then the compilation finaldimensionsattribute needs to be specified explicitly.- Since:
- 20.2
- Default:
true
-
neverDefault
boolean neverDefaultReturnstrueif the cache initializer never returns the default value of its parameter type at runtime. For reference types the default value isnull, for primitive values 0. By default, default values in cache initializers are allowed. The DSL may use the default state of a cache for optimizations in the generated code layout. The DSL informs with a warning when the use of this property has an effect and is recommended to be set. Alternatively to specifyingneverDefault(), theNeverDefaultannotation may be used on the method or field bound by the cache initializer.If a cache initializer returns the illegal default value when this property is set to
truethen the node will throw anIllegalStateExceptionat runtime.- Since:
- 23.0
- See Also:
- Default:
false
-