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
NodeField
for the enclosing node. - Public constructors of the type of the annotated parameter using the
new
keyword 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
int
values and for each individualint
value 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 anUnsupportedSpecializationException
is 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 afterdoCached
therefore it is only instantiated if the limit of thedoCached
specialization has been reached. In other wordsdoNormal
is more generic thandoCached
. ThedoNormal
specialization usesreplaces="doCached"
to specify that all instantiations ofdoCached
get removed ifdoNormal
is instantiated. Alternatively if thereplaces
relation is omitted then alldoCached
instances 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
new
keyword 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 theBranchProfile
instance.@Specialization void s(int operand,
@Cached
("create()") BranchProfile profile) { }
- Since:
- 0.8 or earlier
- See Also:
-
Nested Class Summary
Modifier and TypeClassDescriptionstatic @interface
Disallows any sharing with other cached parameters.static @interface
Allows sharing between multiple Cached parameters between multiple specializations or exported library messages. -
Optional Element Summary
Modifier and TypeOptional ElementDescriptionboolean
Specifies whether the cached parameter values of typeNodeInterface
should be adopted as its child by the current node.boolean
Allows thevalue()
to be used foruncached()
.int
Specifies the number of array dimensions to be marked ascompilation final
.boolean
Enables inlining of the cached parameter if supported.Specifies an alternative method name for node object inlining.boolean
Returnstrue
if 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 foruncached
nodes or uncached versions of exported library messages.Defines the initializer expression of the cached parameter value.boolean
If set totrue
then 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
String uncachedDefines the initializer that is used foruncached
nodes or uncached versions of exported library messages.- Since:
- 19.0
- See Also:
- Default:
"getUncached($parameters)"
-
dimensions
int dimensionsSpecifies the number of array dimensions to be marked ascompilation final
. This value must be specified for all array-typed cached values exceptnode
arrays 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, adimensions
value 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()
totrue
it 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 calledinline
with a single parameter of typeInlineSupport.InlineTarget
. For specializing DSL nodes theGenerateInline
is 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:
GenerateInline
is set totrue
for the declaring specializing node type.- The
GenerateCached.alwaysInlineCached()
property is set totrue
for the declaring specializing node type. - If a target parameter type is a specializing node and has
GenerateInline
enabled butGenerateCached
disabled.
- 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.InlineTarget
and 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 totrue
then 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 becomenull
in 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() != null
guard 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 ofuncached
nodes, 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 typeNodeInterface
should be adopted as its child by the current node. The default value istrue
, therefore all cached values of typeNodeInterface
and arrays of the same type are adopted. If the value is set tofalse
, then no adoption is performed. It is useful to set adopt tofalse
when nodes need to be referenced more than once in the AST.If the type of the field is an
NodeInterface
array and adopt is set tofalse
, then the compilation finaldimensions
attribute needs to be specified explicitly.- Since:
- 20.2
- Default:
true
-
neverDefault
boolean neverDefaultReturnstrue
if 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()
, theNeverDefault
annotation 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
true
then the node will throw anIllegalStateException
at runtime.- Since:
- 23.0
- See Also:
- Default:
false
-