@Retention(value=CLASS) @Target(value=PARAMETER) public @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 (==, !=,
<, <=, >, >=) 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:
NodeField for the enclosing node.new keyword
as method name.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 ...
}
@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)
int values and for
each individual int value a new specialization would get instantiated. The
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 UnsupportedSpecializationException is thrown. The default
specialization instantiation limit is 3.
@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
doNormal. This specialization has the same type restrictions but does not have local
state nor the operand identity guard. It is also declared after doCached therefore
it is only instantiated if the limit of the doCached specialization has been
reached. In other words doNormal is more generic than doCached . The
doNormal specialization uses replaces="doCached" to specify
that all instantiations of doCached get removed if doNormal is
instantiated. Alternatively if the replaces relation is omitted then all
doCached 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)
private.
@Specialization
void s(int operand, @Cached("transformLocal(operand)") int cachedOperand) {
}
int transformLocal(int operand) {
return operand & 0x42;
}
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();
}
}
BranchProfile.create() is used to instantiate the
BranchProfile instance.
@Specialization
void s(int operand, @Cached("create()") BranchProfile profile) {
}
Specialization.guards(),
Specialization.replaces(),
Specialization.limit(),
ImportStatic| Modifier and Type | Required Element and Description |
|---|---|
String |
value
Defines the initializer expression of the cached parameter value.
|
| Modifier and Type | Optional Element and Description |
|---|---|
int |
dimensions
Specifies the number of array dimensions to be marked as
compilation
final. |
public abstract int dimensions
compilation
final. This value must be specified for all array-typed cached values except node 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, a dimensions 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.CompilerDirectives.CompilationFinal.dimensions()