Implict Casts in Truffle DSL
Wei Zhang
ndrzmansn at gmail.com
Sat Apr 5 00:42:02 UTC 2014
Hi Christian,
SLMulNode is just an example. I actually had a problem in MulNode in ZipPy.
I was following your advice to replace the uses of @TypeCast and @TypeCheck
with @ImplicitCast.
Simple language has an example that uses @ImplicitCast to promote Java int
to BigInteger.
@ImplicitCast
public BigInteger castBigInteger(long value) {
return BigInteger.valueOf(value);
}
I add the same code to ZipPy's type system and had the problem that I
described with MulNode.
If there is a higher order specialization after BigInteger, it will rewrite
to that prematurely.
If @ImplicitCast is not the best way to replace @TypeCheck and @TypeCast,
could you give a simple example of your approach to handle type coercions?
Thanks,
/Wei
On Fri, Apr 4, 2014 at 5:12 PM, Christian Humer
<christian.humer at gmail.com>wrote:
> Hi Wei,
>
> SLMulNode does not use doubles. just longs. Therefore it makes no
> difference which Node is used. There are no guarantees in which order
> ambiguous specializations are used. Implicit casts are limited to the use
> between types which are just different representations of the same guest
> language type or if the operations are defined in the same way. Do not use
> them for explicit type casts. Also the @TypeCheck and @TypeCast are not
> suitable to do casts between types. For that we usually insert separate
> specializing cast nodes on the operands which do the job.
>
> - Christian Humer
>
>
> On Sat, Apr 5, 2014 at 1:51 AM, Wei Zhang <ndrzmansn at gmail.com> wrote:
>
>> But if SLMulNode has a specialization for doubles, SLMulBigIntegerNode
>> will rewrite to SLMulDoubleNode before it reaches the code that builds up
>> the polymorphic chain.
>> The SLMulDoubleNode produces a double which is wrong.
>> This is the problem that I had in ZipPy.
>>
>> I agree that simply reduce the minimalState by one does not cover the
>> case that you described.
>> But it is less likely to happen..
>>
>> Thanks,
>>
>> /Wei
>>
>>
>> On Fri, Apr 4, 2014 at 4:08 PM, Christian Humer <
>> christian.humer at gmail.com> wrote:
>>
>>> Hi Wei,
>>>
>>> I suggest to just decrease the minimumState value by 1, but you might
>>>> have better solutions.
>>>
>>>
>>> One of the important properties of Truffle ASTs is that they have to
>>> reach a stable state at some point. If the minimumState would be reduced by
>>> one and the values come in with alternating (implicit) types we would end
>>> up in a rewrite loop. That's the kind of behavior that is fatal for
>>> performance since the enclosed method will always get invalidated.
>>>
>>> In case of the SLMulNode if the types are alternating long and
>>> BigInteger the generated code will build up a polymorphic chain of
>>> specializations to cope with this case. Try to run this patch. It contains
>>> an example which illustrates my point. Debug the run with a breakpoint in
>>> executeAndSpecialize0 in SLMulBaseNode.
>>>
>>>
>>> diff -r a31d807757ee graal/
>>> com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/expression/SLMulNode.java
>>> --- a/graal/
>>> com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/expression/SLMulNode.java Thu
>>> Apr 03 19:06:58 2014 +0200
>>> +++ b/graal/
>>> com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/expression/SLMulNode.java Sat
>>> Apr 05 01:00:56 2014 +0200
>>> @@ -26,6 +26,7 @@
>>>
>>> import com.oracle.truffle.api.*;
>>> import com.oracle.truffle.api.dsl.*;
>>> +import com.oracle.truffle.api.frame.*;
>>> import com.oracle.truffle.api.nodes.*;
>>> import com.oracle.truffle.sl.nodes.*;
>>>
>>> @@ -44,4 +45,25 @@
>>> protected BigInteger mul(BigInteger left, BigInteger right) {
>>> return left.multiply(right);
>>> }
>>> +
>>> + public abstract Object executeEvaluated(VirtualFrame frame, Object
>>> left, Object right);
>>> +
>>> + public static void main(String[] args) {
>>> + final SLMulNode mulNode = SLMulNodeFactory.create(null, null);
>>> + RootNode root = new RootNode() {
>>> + @Child SLMulNode child = mulNode;
>>> +
>>> + @Override
>>> + public Object execute(VirtualFrame frame) {
>>> + child.executeEvaluated(null, 1L, 1L);
>>> + child.executeEvaluated(null, BigInteger.valueOf(1L),
>>> 1L);
>>> + child.executeEvaluated(null, 1L,
>>> BigInteger.valueOf(1L));
>>> + return null;
>>> + }
>>> + };
>>> + root.adoptChildren();
>>> + root.execute(null);
>>> +
>>> + NodeUtil.printTree(System.out, root);
>>> + }
>>> }
>>>
>>> Hope this helps.
>>>
>>>
>>>
>>> - Christian Humer
>>>
>>>
>>> On Thu, Apr 3, 2014 at 2:20 AM, Wei Zhang <ndrzmansn at gmail.com> wrote:
>>>
>>>> Hi Christian,
>>>>
>>>> A generated node using @ImplicitCast rewrites itself with the
>>>> minimunState value of itself.
>>>> This value prevents a node re-specialization to the same type but with
>>>> a promoted operand type.
>>>>
>>>> Say a SLMulBigIntegerNode has a long operand and a BigInteger operand.
>>>> At some point, the long operand become a BigInteger, and the node
>>>> starts to rewrite.
>>>> It should replace itself with another SLMulBigIntegerNode but with
>>>> updated operand implicit types.
>>>>
>>>> I hope that make sense to you.
>>>> I suggest to just decrease the minimumState value by 1, but you might
>>>> have better solutions.
>>>>
>>>> Thanks,
>>>>
>>>>
>>>> /Wei
>>>>
>>>>
>>>> On Wed, Apr 2, 2014 at 3:46 PM, Christian Humer <
>>>> christian.humer at gmail.com> wrote:
>>>>
>>>>> Unfortunately you have to specify all transitive implicit casts. That
>>>>> gives you more control, but is also more verbose. Let me know if the number
>>>>> of casts explodes. If so we could add a feature for that.
>>>>>
>>>>> (Sent using Android)
>>>>> Am 02.04.2014 23:27 schrieb "Wei Zhang" <ndrzmansn at gmail.com>:
>>>>>
>>>>> Thanks for the patch. It works nicely!
>>>>>>
>>>>>> Yes, I'm planning to simplify the uses of @TypeCheck and @TypeCast,
>>>>>> and use @ImplicitCast instead for most of the cases.
>>>>>> However, I have a further question on that.
>>>>>> To implement multiple levels of implicit type coercions, e.g., from
>>>>>> bool to int and then to double, do I have to specify all possible
>>>>>> conversions separately?
>>>>>> Or can the generated code chain more than one @ImplicitCasts together
>>>>>> to convert a bool directly to double?
>>>>>>
>>>>>> Thanks,
>>>>>>
>>>>>> /Wei
>>>>>>
>>>>>>
>>>>>> On Wed, Apr 2, 2014 at 8:50 AM, Christian Humer <
>>>>>> christian.humer at gmail.com> wrote:
>>>>>>
>>>>>>> Ups that was too quick. Forget my previous patch and use the one
>>>>>>> below instead. In principle the DSL supports execute methods without a
>>>>>>> VirtualFrame parameter. However there are some glitches here and there. So
>>>>>>> please try to avoid them for now and always add a VirtualFrame parameter to
>>>>>>> execute methods.
>>>>>>>
>>>>>>> diff -r ed373ed4b717
>>>>>>> graal/edu.uci.python.nodes/src/edu/uci/python/nodes/expression/CastToBooleanNode.java
>>>>>>> ---
>>>>>>> a/graal/edu.uci.python.nodes/src/edu/uci/python/nodes/expression/CastToBooleanNode.java Wed
>>>>>>> Apr 02 00:06:40 2014 -0700
>>>>>>> +++
>>>>>>> b/graal/edu.uci.python.nodes/src/edu/uci/python/nodes/expression/CastToBooleanNode.java Wed
>>>>>>> Apr 02 17:47:21 2014 +0200
>>>>>>> @@ -40,7 +40,7 @@
>>>>>>>
>>>>>>> public abstract class CastToBooleanNode extends UnaryOpNode {
>>>>>>>
>>>>>>> - public abstract boolean executeBoolean(Object value);
>>>>>>> + public abstract boolean executeBoolean(VirtualFrame frame,
>>>>>>> Object value);
>>>>>>>
>>>>>>> @Override
>>>>>>> public abstract boolean executeBoolean(VirtualFrame frame);
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> - Christian Humer
>>>>>>>
>>>>>>>
>>>>>>> On Wed, Apr 2, 2014 at 4:43 PM, Christian Humer <
>>>>>>> christian.humer at gmail.com> wrote:
>>>>>>>
>>>>>>>> Hi Wei,
>>>>>>>>
>>>>>>>> You found a bug. Thanks for the report. In the future don't
>>>>>>>> hesitate to send a report even if you are not sure if it is already fixed.
>>>>>>>> Here is a temporary patch for you. I will push this fix with my next
>>>>>>>> greater push to openjdk.
>>>>>>>>
>>>>>>>> BTW.: you should get rid of your overly complicated @TypeCheck and
>>>>>>>> @TypeCast implementations in the type system. There may be a major
>>>>>>>> performance problem hidden here. Ideally you should only use the ones that
>>>>>>>> are generated by default. I think you can get rid of them mostly by using
>>>>>>>> ImplicitCasts. But maybe that's the thing you wanted to do anyway. Feel
>>>>>>>> free to contact me again if you need help with that.
>>>>>>>>
>>>>>>>> diff -r ed373ed4b717
>>>>>>>> graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java
>>>>>>>> ---
>>>>>>>> a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java Wed
>>>>>>>> Apr 02 00:06:40 2014 -0700
>>>>>>>> +++
>>>>>>>> b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java Wed
>>>>>>>> Apr 02 16:34:32 2014 +0200
>>>>>>>> @@ -2032,7 +2032,11 @@
>>>>>>>>
>>>>>>>> CodeExecutableElement method = new
>>>>>>>> CodeExecutableElement(modifiers(PROTECTED, expectType != null ? STATIC :
>>>>>>>> FINAL), param.getType(), childExecuteName);
>>>>>>>>
>>>>>>>> method.getThrownTypes().add(getContext().getTruffleTypes().getUnexpectedValueException());
>>>>>>>> - method.addParameter(new
>>>>>>>> CodeVariableElement(getContext().getTruffleTypes().getFrame(),
>>>>>>>> "frameValue"));
>>>>>>>> +
>>>>>>>> + if (expectType == null) {
>>>>>>>> + method.addParameter(new
>>>>>>>> CodeVariableElement(getContext().getTruffleTypes().getFrame(),
>>>>>>>> "frameValue"));
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> if (expectType != null) {
>>>>>>>> method.addParameter(new
>>>>>>>> CodeVariableElement(expectType.getPrimitiveType(),
>>>>>>>> valueNameEvaluated(param)));
>>>>>>>> }
>>>>>>>>
>>>>>>>>
>>>>>>>> - Christian Humer
>>>>>>>>
>>>>>>>>
>>>>>>>> On Tue, Apr 1, 2014 at 10:10 PM, Wei Zhang <ndrzmansn at gmail.com>wrote:
>>>>>>>>
>>>>>>>>> Hi Christian,
>>>>>>>>>
>>>>>>>>> I'm experiencing a problem when using @ImplicitCast.
>>>>>>>>>
>>>>>>>>> I added the following code in PythonTypes.java:
>>>>>>>>>
>>>>>>>>> @ImplicitCast
>>>>>>>>> public String unboxPString(PString value) {
>>>>>>>>> return value.getValue();
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> This use causes a javac error in the generated
>>>>>>>>> CastToBooleanNodeFactory.java.
>>>>>>>>>
>>>>>>>>> You can pull the latest change from ZipPy to reproduce this.
>>>>>>>>> I've commented the use of @ImplicitCast in PythonTypes.
>>>>>>>>> I did not report this problem earlier because I have not merge
>>>>>>>>> with the Graal repo for a while and expected that it might be fixed in a
>>>>>>>>> recent commit.
>>>>>>>>>
>>>>>>>>> Any suggestion?
>>>>>>>>> Thanks,
>>>>>>>>>
>>>>>>>>> /Wei
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Sat, Dec 21, 2013 at 8:49 AM, Christian Humer <
>>>>>>>>> christian.humer at gmail.com> wrote:
>>>>>>>>>
>>>>>>>>>> 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