Implict Casts in Truffle DSL

Wei Zhang ndrzmansn at gmail.com
Fri Apr 4 23:51:45 UTC 2014


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