Truffle: executeCached, get, and other abstract methods? And a little generator bug.
Christian Humer
christian.humer at gmail.com
Mon Nov 4 07:50:00 PST 2013
Hi Stefan,
The generated methods named executeCached* are not intended to be used from
your code and are entirely an implementation detail. So they are subject of
change or removal any time. Actually its a bug that you are able to access
it. I will try to give you some insight on these executeCached* methods and
why they are generated, further below.
I think, the feature you need is the generation of execute methods with
evaluated children. This is also the feature you are already using, but the
method should not be called executeCached* but instead executeEvaluated or
something different. I will try to give you a brief introduction to this
feature: An execute method is the contract to the parent node on how to be
executed. The code generator for the DSL parses the execute methods that a
node declares and tries to generate an implementation for them. So usually
one would provide execute methods in a node or better in its base class
like this:
@NodeChild(value = "receiver", type = TestNode .class)
public abstract class TestNode extends Node {
public abstract Object execute(VirtualFrame frame);
// ... specialiations omitted ...
}
The code generator would now see the execute methods and will generate
implementation code for it in generated subclasses. In this case the
generated code for the execute method would look like:
@Override
public Object execute(VirtualFrame frame) {
Object receiverValue = receiver.execute(frame);
// call to proper specialization(s) with receiverValue
}
But the code generator also supports additional execute methods with
evaluated arguments like this:
@NodeChild(value = "receiver", type = TestNode .class)
public abstract class TestNode extends Node {
public abstract Object execute(VirtualFrame frame);
public abstract Object executeEvaluated(VirtualFrame frame, Object
receiverValue);
// ... specialiations omitted ...
}
In this case the generated implementation of executeEvaluated would look
like this:
@Override
public Object executeEvaluated(VirtualFrame frame, Object
receiverEvaluated) {
Object receiverValue = receiverEvaluated;
// call to proper specialization(s) with receiverValue
}
The code generator also supports other signatures for execute methods like:
public abstract int executeInt(VirtualFrame frame) throws
UnexpectedResultException;
public abstract Object executeEvaluated(VirtualFrame frame, int
receiverEvaluated);
public abstract int executeIntEvaluated(VirtualFrame frame, Object
receiverEvaluated) throws UnexpectedResultException;
And it also works for any number of evaluated values.
I hope this answers your question. There are just these two magic features
(the rest is more explicit using annotations): Getters for fields/children
and the automatic implementation of execute methods. While the first is a
convenience feature the second is an essential part of the DSL.
So now some insight on these generated executeCachedGeneric methods. These
methods are used to implement the polymorphic chain of specializations. So
imagine you have an operation node which defines a lot of different
specializations. Some of them may be specializations for type int, double
or String. When we create a node using the generated factory it starts off
in the uninitialized state. When its first invoked the node replaces itself
to a node just compatible to one specialization, lets assume an int
specialization. We also call this state the monomorphic state. If the node
is now executed again and a double value comes in from the child we do not
want to go directly to a generic case because this would also contain the
code for the String specialization. Instead we build up a polymorphic chain
which just contains code for the int and double specialization. This is
called the polymorphic state. After some depth of the polymorphic chain
(which is optionally configurable by @PolymorphicDepth on the operation
node) is reached, we rewrite to the generic case which we also call the
megamorphic state.
Lets illustrate how the node tree looks in each state for an operation node
called MyNode with one arbitrary child operand:
State Uninitialized:
UninitializedMyNode
operand = ...
State Monomorphic:
IntMyNode
operand = ...
State Polymorphic :
PolymorphicMyNode
operand = ...
next0 =IntMyNode
next0= DoubleMyNode
next0 = UninitializedMyNode
State Megamorphic:
GenericMyNode
operand = ...
When the PolymorphicMyNode now gets called by its parent using an execute
method it first executes its child operand. It then calls the first element
of the polymorphic chain which is IntMyNode using executeChachedGeneric
with the evaluated value of operand. The implementation of
executeChachedGeneric in IntMyNode will then check if the value is
compatible to this specialization and invoke the specialization if so. If
not it will just call the next specialization in the chain stored in the
next0 field. If an UninitializedMyNode is reached another entry to the
polymorphic chain is added or if the maximum depth was reached the whole
tree gets rewritten to GenericMyNode.
Regarding the NPE: Null is not supported as a value by the DSL at them
moment (but it is supported by Truffle in general). If you want to
represent null using the DSL, use a singleton value like MyNull.INSTANCE.
It should not make a difference performance wise. It is on my list of
things I want to add support for at some point. Please tell me if you need
it. In this case I can try to give it a higher priority.
Cheers,
- Christian Humer
On Mon, Nov 4, 2013 at 12:49 PM, Stefan Marr <Stefan.Marr at vub.ac.be> wrote:
> Hi:
>
> I found another useful abstract method in truffle node classes:
> executeCachedGeneric, which allows me to execute the code of a specialized
> node after replacing a more general one.
> After inspecting the generator code, I also found that executeCached* is
> probably working also for other kind of specializations.
>
> And since there are also the accessors for node children (get$childName),
> I was wondering whether the TruffleDSL has other similar gems hidden in
> it, which might be useful?
>
> As a related note, I also saw a null pointer exception that can be
> reproduced with the code snippet below.
>
> Best regards
> Stefan
>
>
> package som.interpreter.nodes.specialized;
>
> import som.interpreter.Types;
> import som.interpreter.nodes.ExpressionNode;
> import som.vmobjects.SAbstractObject;
>
> import com.oracle.truffle.api.dsl.NodeChild;
> import com.oracle.truffle.api.dsl.Specialization;
> import com.oracle.truffle.api.dsl.TypeSystemReference;
> import com.oracle.truffle.api.frame.VirtualFrame;
> import com.oracle.truffle.api.nodes.Node;
>
> @TypeSystemReference(Types.class)
> @NodeChild(value = "receiver", type = ExpressionNode.class)
> public abstract class TestNode extends Node {
> public abstract SAbstractObject executeCachedGeneric(final VirtualFrame
> frame, final Object receiver);
>
> @Specialization
> public SAbstractObject doGeneric(final VirtualFrame frame,
> final SAbstractObject receiver) {
> return null;
> }
> }
>
>
> --
> Stefan Marr
> Software Languages Lab
> Vrije Universiteit Brussel
> Pleinlaan 2 / B-1050 Brussels / Belgium
> http://soft.vub.ac.be/~smarr
> Phone: +32 2 629 2974
> Fax: +32 2 629 3525
>
>
More information about the graal-dev
mailing list