Implict Casts in Truffle DSL
Christian Humer
christian.humer at gmail.com
Sat Dec 21 08:49:11 PST 2013
Hey folks,
There were some questions floating around about the ImplicitCast feature in
Truffle DSL.
So here is a short description of what it does:
The idea behind ImplicitCasts is that it tries to reduce the number of
specializations required to define an operation. Imagine you got
arithmetics with tree different types. That could be int, double and
complex. However in our guest language we have several different
representations of the same type for optimization reasons. For instance an
int could be represented as Java int, IntSequence, IntVector or even as
LogicalToIntVectorClosure (this example is borrowed from R).
This makes a lot of combinations of types in binary operation to fully
specialize itself. So assuming that double and complex have the same number
of different representations and their number may even rise, the number of
specializations will explode at some point in time.
The solution for this problem was to introduce a new concept to the DSL,
namely @ImplicitCast. The idea behind is that you can define casts that are
inserted before a specialization is called on demand.
So assume we have defined the specializations for a binary operation which
fully specializes for the types int and IntVector. Without implicit casts you
would have to define these four specializations.
@Specialization int doOp(int left, int right) {...}
@Specialization int doOp(IntVector left, int right) {...}
@Specialization int doOp(int left, IntVector right) {...}
@Specialization int doOp(IntVector left, IntVector right) {...}
However if we assume that all four specializations would want to specialize
to basically the same code we can use implicit casts to declare this more
elegantly. For this we introduce a new interface called AbstractIntVector
which is the base interface for IntVector, IntSequence etc. and we have to
define some implicit casts in the type system like this:
@ImplicitCast
public AbstractIntVector toAbstractIntVector(int value) {
return DataFactory.createIntVectorFromScalar(value);
}
@ImplicitCast
public AbstractIntVector toAbstractIntVector(IntVector vector) {
return vector;
}
@Specialization int doOp(AbstractIntVector left, AbstractIntVector right)
{...}
Now we can just define one specialization and we get the same
specialization combinations as in the first example. Please be aware that
this is not completely the same thing as before since the implicit cast method
toAbstractIntVector is invoked prior to the invocation of the
specialization. However Graal/Truffle is pretty good in removing
unnecessary allocations for boxing if it sees the position of boxing as
well as the position of the unboxing.
You may even combine both the upper with the lower approach, like this:
@Specialization int doOp(int left, int right) {...}
@Specialization int doOp(AbstractIntVector left, AbstractIntVector right)
{...}
In this case you can provide a more optimized version for (int, int) but
not having to specialize all the other combinations. Implicit casts are
even more useful if the number of representations of the same logical type
is high. This enables you to keep the number of specializations in binary
nodes under control without breaking your fingers.
Please note: On the contrary to custom @TypeCheck and @TypeCast methods,
@ImplicitCasts are inserted on-demand. This means that they do not
introduce additional overhead in compiled code as custom @TypeCheck or
@TypeCast methods would.
Feel free to ask further questions.
- Christian Humer
More information about the graal-dev
mailing list