[9] RFR(S): 8034216: assert(false) failed: infinite loop in PhaseIterGVN::optimize

Albert albert.noll at oracle.com
Fri Apr 11 08:14:02 UTC 2014


Hi Vladimir,

I updated the patch according to your suggestion:

http://cr.openjdk.java.net/~anoll/8034216/webrev.05/

Thanks,
Albert

On 04/10/2014 07:11 PM, Vladimir Kozlov wrote:
> Albert,
>
> You could do simple fix (--i) in original changes instead of adding 
> new list. And actually you don't need to call remove() because it will 
> be removed by calling replace_node(), see remove_globally_dead_node():
>
>
> +   for (uint i = 0; i < _worklist.size(); i++) {
> +     Node* n = _worklist.at(i);
> +
> +     if (n->is_ConstraintCast()) {
> +       Node* nn = n->Identity(this);
> +       if (nn != n) {
> +         replace_node(n, nn);
> +         --i;
> +       }
> +     }
> +   }
>
> Thanks,
> Vladimir
>
> On 4/10/14 8:12 AM, Albert wrote:
>> Hi Vladimir,
>>
>> Here is an updated version of the castPP node removal solution.
>>
>> http://cr.openjdk.java.net/~anoll/8034216/webrev.04/
>> I executed specjvm98 with these changes on my local desktop and I 
>> could not see a performance regression.
>>
>> Best,
>> Albert
>>
>> On 04/10/2014 07:46 AM, Albert wrote:
>>> Hi Vladimir,
>>>
>>> thanks a lot for looking into this again. I will prepare a patch 
>>> with which removes CastPP node (this was Roland's
>>> idea). I will also run SpecJVM98 to see if there
>>> is an impact on performance.
>>>
>>> Best,
>>> Albert
>>>
>>>
>>> On 04/09/2014 09:07 PM, Vladimir Kozlov wrote:
>>>> Hi Albert,
>>>>
>>>> I am continue thinking about this problem.
>>>> Your suggestion to process CastPP node first could be less 
>>>> intrusive than other solutions. It will do the same thing
>>>> as current code if such nodes are first on worklist. It would be 
>>>> still nice to continue investigate the possibility
>>>> to keep these nodes in graph after CCP but it should not be very 
>>>> high priority (file RFE).
>>>>
>>>> Note, you can't iterate the _worklist the way you do because 
>>>> remove() will put last element into current slot so you
>>>> have to check it again.
>>>> Also using remove(uint i) version will be faster.
>>>>
>>>> Thanks,
>>>> Vladimir
>>>>
>>>> On 4/7/14 7:04 PM, Vladimir Kozlov wrote:
>>>>> Hi Albert,
>>>>>
>>>>> Can you push the cleanup first under a different bug?
>>>>> We may need to continue discussion how to fix the problem but I think
>>>>> cleanup can be pushed now. Please, send a separate webrev for it 
>>>>> under
>>>>> new bug id.
>>>>>
>>>>> About the problem.
>>>>>
>>>>> It looks like 6916062 all over again :(  See attached mail thread 
>>>>> when I
>>>>> did investigation:
>>>>>
>>>>> [217]   CheckCastPP  . .  # java/util/ArrayList:NotNull*
>>>>> [229]   CheckCastPP  . .  #
>>>>> java/util/Collections$UnmodifiableCollection:NotNull*
>>>>> [292]   Phi . [217] [229] # java/lang/Object:NotNull*
>>>>> [11637] CastPP . [292]    # _type = java/util/Collection* but
>>>>> igvn.type(cast) = java/util/Collection:NotNull*
>>>>> [11638] AddP . [11637][11637] + 8 # igvn.type(addp)
>>>>> java/util/Collection:NotNull+8 *
>>>>> [11639] LoadNKlass . . [11638]
>>>>>
>>>>> and we hit the assert in the 6916062 fix code:
>>>>>
>>>>> http://hg.openjdk.java.net/hsx/hsx19/baseline/rev/9a19ee0490e0
>>>>>
>>>>>
>>>>> Types are converged during CCP phase when all CastPP nodes are still
>>>>> present. Unfortunately, after CastPP nodes are removed, types could
>>>>> degradate after your change:
>>>>>
>>>>> http://cr.openjdk.java.net/~anoll/8034216/webrev.01/
>>>>>
>>>>> In your case cached type for AddP (NotNull) will be replaced by 
>>>>> input's
>>>>> type because this is what AddPNode::Value() returns.
>>>>>
>>>>> I afraid this will affect types more than we want.
>>>>>
>>>>> Note, the only information about NotNull is located in the cached 
>>>>> type
>>>>> because corresponding CastPP was also removed.
>>>>>
>>>>>
>>>>> Removing CastPP nodes first will replace cached type of AddP to even
>>>>> more general j.l.Object (from Phi node).
>>>>>
>>>>> May be we should investigate if the next performance statement about
>>>>> jvm98 is still true and solve it differently (in PhiNode::Ideal() for
>>>>> example):
>>>>>
>>>>> // I tried to leave the CastPP's in.  This makes the graph more 
>>>>> accurate in
>>>>> // some sense; we get to keep around the knowledge that an oop is 
>>>>> not-null
>>>>> // after some test.  Alas, the CastPP's interfere with GVN (some 
>>>>> values are
>>>>> // the regular oop, some are the CastPP of the oop, all merge at 
>>>>> Phi's
>>>>> which
>>>>> // cannot collapse, etc).  This cost us 10% on SpecJVM, even when 
>>>>> I removed
>>>>> // some of the more trivial cases in the optimizer. Removing more 
>>>>> useless
>>>>> // Phi's started allowing Loads to illegally float above null 
>>>>> checks.  I
>>>>> gave
>>>>> // up on this approach.  CNC 10/20/2000
>>>>>
>>>>> Regards,
>>>>> Vladimir
>>>>>
>>>>> On 4/4/14 8:09 AM, Albert wrote:
>>>>>> Hi Vladimir,
>>>>>>
>>>>>> thanks for looking at this.  Please see comments inline:
>>>>>>
>>>>>> On 04/04/2014 04:03 AM, Vladimir Kozlov wrote:
>>>>>>> On 4/3/14 4:14 PM, Vladimir Kozlov wrote:
>>>>>>>> Hi Albert,
>>>>>>>>
>>>>>>>> Thank you for doing this investigation. I hope it was good C2
>>>>>>>> learning exercise :)
>>>>>>>>
>>>>>> Yes, I learned a lot. Many thanks to you and Roland for teaching me.
>>>>>>
>>>>>>>> I prefer the second solution, webrev.01. I don't like that we are
>>>>>>>> loosing more concrete type with first solution.
>>>>>>>
>>>>>>> Actually it would be more correct and complete to have a solution
>>>>>>> which does not put a load node on worklist again when there is no
>>>>>>> progress with type. Even with webrev.01 we loose NotNull 
>>>>>>> information.
>>>>>> I have one general question here: Are types expected to converge 
>>>>>> at this
>>>>>> point if everything would work as expected?
>>>>>> Why do we loose NotNull information if types converge to
>>>>>> java/util/Spliterator:NotNull+8 * ?
>>>>>>
>>>>>> I think that your suggestion, not pushing a node on the worklist 
>>>>>> if we
>>>>>> don't make progress, will result in the type:
>>>>>> java/util/Spliterator for the LoadKlass node -  so the NotNull
>>>>>> information would be lost in that case. If you think that
>>>>>> this is the solution we should go for I'll look more into it.
>>>>>>
>>>>>> Here is a new webrev of the cleanup. I included all your 
>>>>>> suggestion. I
>>>>>> deleted the two lines
>>>>>> 1123     // Move users of node to worklist
>>>>>> 1124     add_users_to_worklist( k );
>>>>>>
>>>>>> accidentally. Thanks for catching this.
>>>>>> http://cr.openjdk.java.net/~anoll/8034216/webrev.03/
>>>>>>
>>>>>>>
>>>>>>> Thanks,
>>>>>>> Vladimir
>>>>>>>
>>>>>>>>
>>>>>>>> It would be nice to detect such situations and bailout 
>>>>>>>> compilation in
>>>>>>>> product VM and throw assert in debug VM.
>>>>>>>>
>>>>>>>> The clean up is nice, go for it. Note that you should put new 
>>>>>>>> methods
>>>>>>>> under #ifndef PRODUCT instead of using
>>>>>>>> PRODUCT_RETURN because they are called only in NOT_PRODUCT().
>>>>>>>>
>>>>>>>> Why you removed next code?:
>>>>>>>>
>>>>>>>> 1123     // Move users of node to worklist
>>>>>>>> 1124     add_users_to_worklist( k );
>>>>>>>>
>>>>>>>> Next code should be under #ifdef ASSERT because 
>>>>>>>> TraceIterativeGVN is
>>>>>>>> develop flag. So you don't need for NOT_PRODUCT()
>>>>>>>> and DEBUG_ONLY() in the code:
>>>>>>>>
>>>>>>>> +     if (TraceIterativeGVN && Verbose) {
>>>>>>>> +       tty->print("  Pop ");
>>>>>>>> +       NOT_PRODUCT(n->dump();)
>>>>>>>> +       DEBUG_ONLY(if( (num_processed++ % 100) == 0 )
>>>>>>>> _worklist.print_set();)
>>>>>>>> +     }
>>>>>>>>
>>>>>>>> For next I also think is better to use #ifdef ASSERT:
>>>>>>>>
>>>>>>>> +       DEBUG_ONLY(n->dump(4);)
>>>>>>>> +       DEBUG_ONLY(_worklist.dump();)
>>>>>>>> +       assert(false, "infinite loop in PhaseIterGVN::optimize");
>>>>>>>>
>>>>>>>> And #ifndef PRODUCT for next:
>>>>>>>>
>>>>>>>> +       NOT_PRODUCT(trace_PhaseIterGVN(n, nn, oldtype));
>>>>>>>> +       NOT_PRODUCT(if (nn != n) verify_step((Node*) NULL);)  //
>>>>>>>> ignore n, it might be subsumed
>>>>>>>>
>>>>>>>>
>>>>>>>> On 4/3/14 5:40 AM, Albert wrote:
>>>>>>>>> Hi,
>>>>>>>>>
>>>>>>>>> could I get reviews for this patch?
>>>>>>>>>
>>>>>>>>> Bug:
>>>>>>>>> ------
>>>>>>>>> https://bugs.openjdk.java.net/browse/JDK-8034216
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Problem:
>>>>>>>>> ------------
>>>>>>>>> The endless loop occurs, since there are 2 nodes of type 
>>>>>>>>> LoadKlass in
>>>>>>>>> the worklist that keep pushing themselves
>>>>>>>>> onto the worklist. The current implementation can only handle 
>>>>>>>>> 1 node
>>>>>>>>> that keeps pushing itself on the worklist.
>>>>>>>>> The endless loop occurs in the IGVN pass that comes after CCP 
>>>>>>>>> phase.
>>>>>>>>>
>>>>>>>>> This piece of code in memnode.cpp is responsible for the 
>>>>>>>>> endless loop:
>>>>>>>>>
>>>>>>>>> Node *MemNode::Ideal_common(PhaseGVN *phase, bool can_reshape) {
>>>>>>>>>    ...
>>>>>>>>>    if (can_reshape && igvn != NULL &&
>>>>>>>>>        (igvn->_worklist.member(address) ||
>>>>>>>>>         igvn->_worklist.size() > 0 && (t_adr != adr_type())) ) {
>>>>>>>>>      // The address's base and type may change when the 
>>>>>>>>> address is
>>>>>>>>> processed.
>>>>>>>>>      // Delay this mem node transformation until the address is
>>>>>>>>> processed.
>>>>>>>>> phase->is_IterGVN()->_worklist.push(this);
>>>>>>>>>      return NodeSentinel; // caller will return NULL
>>>>>>>>>    }
>>>>>>>>>    ...
>>>>>>>>> }
>>>>>>>>> As seen above, we keep pushing the current node onto the 
>>>>>>>>> worklist if
>>>>>>>>> t_adr != adr_type().
>>>>>>>>> Unfortunately the types do not converge. See the following 
>>>>>>>>> node dump:
>>>>>>>>>
>>>>>>>>>     5559 272 b 4 java.util.concurrent.CountedCompleter::exec 
>>>>>>>>> (6 bytes)
>>>>>>>>>   2054 CheckCastPP === 4086 2023 [[ 2098 2064 2064 ]]
>>>>>>>>> #java/util/Spliterators$DoubleArraySpliterator:NotNull:exact *
>>>>>>>>> Oop:java/util/Spliterators$DoubleArraySpliterator:NotNull:exact *
>>>>>>>>> !jvms:
>>>>>>>>> AbstractTask::compute @ bci:6 CountedCompleter::exec @ bci:-1
>>>>>>>>>   2044 CheckCastPP === 4090 2023 [[ 2098 2077 2077 ]]
>>>>>>>>> #java/util/ArrayList$ArrayListSpliterator:NotNull:exact *
>>>>>>>>> Oop:java/util/ArrayList$ArrayListSpliterator:NotNull:exact * 
>>>>>>>>> !jvms:
>>>>>>>>> AbstractTask::compute @ bci:6 CountedCompleter::exec @ bci:-1
>>>>>>>>>   2095 Region === 2095 2085 2072 [[ 2095 2120 2096 2097 2098 
>>>>>>>>> 2099 ]]
>>>>>>>>> !jvms: AbstractTask::compute @ bci:6 CountedCompleter::exec @ 
>>>>>>>>> bci:-1
>>>>>>>>>   2169 CmpL === _ 2099 2164 [[ 2170 ]] !jvms: 
>>>>>>>>> AbstractTask::compute @
>>>>>>>>> bci:29 CountedCompleter::exec @ bci:-1
>>>>>>>>>   2121 IfTrue === 2120 [[ 2125 ]] #1 !jvms:
>>>>>>>>> AbstractTask::getTargetSize
>>>>>>>>> @ bci:8 AbstractTask::compute @ bci:14 CountedCompleter::exec 
>>>>>>>>> @ bci:-1
>>>>>>>>>   2122 IfFalse === 2120 [[ 2125 2162 ]] #0 !jvms:
>>>>>>>>> AbstractTask::getTargetSize @ bci:8 AbstractTask::compute @ 
>>>>>>>>> bci:14
>>>>>>>>> CountedCompleter::exec @ bci:-1
>>>>>>>>>   2098 Phi === 2095 2044 2054 [[ 2229 ]] 
>>>>>>>>> #java/lang/Object:NotNull *
>>>>>>>>> Oop:java/lang/Object:NotNull * !orig=2181 !jvms:
>>>>>>>>> AbstractTask::compute @
>>>>>>>>> bci:6 CountedCompleter::exec @ bci:-1
>>>>>>>>>   2170 Bool === _ 2169 [[ 2171 ]] [le] !jvms: 
>>>>>>>>> AbstractTask::compute @
>>>>>>>>> bci:29 CountedCompleter::exec @ bci:-1
>>>>>>>>>   2125 Region === 2125 2122 2121 [[ 2125 2171 2163 2164 ]] !jvms:
>>>>>>>>> AbstractTask::getTargetSize @ bci:24 AbstractTask::compute @ 
>>>>>>>>> bci:14
>>>>>>>>> CountedCompleter::exec @ bci:-1
>>>>>>>>>   22 ConL === 0 [[ 24 2037 81 2230 288 ]] #long:8
>>>>>>>>>   2229 CastPP === 2173 2098 [[ 2240 2240 2230 2230 2237 ]]
>>>>>>>>> #java/util/Spliterator * Interface:java/util/Spliterator * !jvms:
>>>>>>>>> AbstractTask::compute @ bci:33 CountedCompleter::exec @ bci:-1
>>>>>>>>>   3 Start === 3 0 [[ 3 5 6 7 8 9 10 ]] #{0:control, 1:abIO, 
>>>>>>>>> 2:memory,
>>>>>>>>> 3:rawptr:BotPTR, 4:return_address,
>>>>>>>>> 5:java/util/concurrent/CountedCompleter:NotNull *}
>>>>>>>>>   2171 If === 2125 2170 [[ 2172 2173 ]] P=0.500000, 
>>>>>>>>> C=-1.000000 !jvms:
>>>>>>>>> AbstractTask::compute @ bci:29 CountedCompleter::exec @ bci:-1
>>>>>>>>>   2230 AddP === _ 2229 2229 22 [[ 2231 ]]
>>>>>>>>> Interface:java/util/Spliterator+8 * !jvms: 
>>>>>>>>> AbstractTask::compute @
>>>>>>>>> bci:33 CountedCompleter::exec @ bci:-1
>>>>>>>>>   7 Parm === 3 [[ 103 289 110 123 25 82 2231 2077 47 2064 2057 
>>>>>>>>> 2038 65
>>>>>>>>> 2031 75 2023 ]] Memory Memory: @BotPTR *+bot, idx=Bot; !jvms:
>>>>>>>>> CountedCompleter::exec @ bci:-1
>>>>>>>>>   2173 IfFalse === 2171 [[ 4079 2231 2229 ]] #0 !orig=[2195] 
>>>>>>>>> !jvms:
>>>>>>>>> AbstractTask::compute @ bci:29 CountedCompleter::exec @ bci:-1
>>>>>>>>>   2231 LoadKlass === 2173 7 2230 [[ 2232 ]] 
>>>>>>>>> @java/lang/Object+8 *,
>>>>>>>>> idx=4; #klass java/util/Spliterator: 0x0000000002252c98 *
>>>>>>>>> Interface:klass java/util/Spliterator: 0x0000000002252c98 * !jvms
>>>>>>>>>
>>>>>>>>> t_adr: is the cached type of the Address input (2230 AddP):
>>>>>>>>> java/util/Spliterator:NotNull
>>>>>>>>> adr_type(): is the bottom type of the address input (2230 AddP):
>>>>>>>>> java/util/Spliterator
>>>>>>>>>
>>>>>>>>> The bottom_type of the AddP node is determined by the bottom 
>>>>>>>>> type of
>>>>>>>>> it's address input node ( 2229 CastPP).
>>>>>>>>> The bottom type of the CastPP node is set in the CCP phase:
>>>>>>>>>
>>>>>>>>> Node *PhaseCCP::transform_once( Node *n ) {
>>>>>>>>>    ...
>>>>>>>>>    Node *nn = n->Ideal_DU_postCCP(this);
>>>>>>>>>    ...
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> In the concrete execution, the CCP phase sets the bottom type 
>>>>>>>>> of the
>>>>>>>>> castPP node equal to the type of its input (in(1)).
>>>>>>>>> in(1) of the castPP node is a phi-node. Since the two types 
>>>>>>>>> are equal,
>>>>>>>>> the castPP node is redundant and could be removed
>>>>>>>>> in the subsequent IGVN phase. However, the castPP node is not 
>>>>>>>>> removed,
>>>>>>>>> since another node in the worklist (a redundant
>>>>>>>>> region node) is processed prior to the castPP node. Removing the
>>>>>>>>> redundant region node results in a change of in(1) of the
>>>>>>>>> castPP node. As a result, the type of in(1) of the castPP node 
>>>>>>>>> changes
>>>>>>>>> as well and the castPP node is not removed. Not
>>>>>>>>> removing the castPP node makes the types not converge as 
>>>>>>>>> expected.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Solution:
>>>>>>>>> -----------
>>>>>>>>> Add a pass after CCP that removes the redundant castPP node. As a
>>>>>>>>> result, the types converge to
>>>>>>>>> java/lang/Object:NotNull+8 * and the endless loop does not occur.
>>>>>>>>>
>>>>>>>>> Here is the webrev:
>>>>>>>>> http://cr.openjdk.java.net/~anoll/8034216/webrev.00/
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Alternative solution:
>>>>>>>>> --------------------------
>>>>>>>>> Instead of adding the extra pass over the IR, we could raise the
>>>>>>>>> bottom
>>>>>>>>> type of a type node
>>>>>>>>> in IGVN. As a result, the types converge to
>>>>>>>>> java/util/Spliterator:NotNull+8 * and the endless loop
>>>>>>>>> does not occur.
>>>>>>>>>
>>>>>>>>> Here is the webrev:
>>>>>>>>> http://cr.openjdk.java.net/~anoll/8034216/webrev.01/
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Cleanup:
>>>>>>>>> ------------
>>>>>>>>> In addition, I have worked on refactoring/cleaning up the 
>>>>>>>>> affected
>>>>>>>>> code.
>>>>>>>>> This code cleanup comes
>>>>>>>>> in a separate webrev to make it easier to figure out the actual
>>>>>>>>> changes
>>>>>>>>> that fix the bug. The code
>>>>>>>>> cleanup is optional.
>>>>>>>>>
>>>>>>>>> Here is the webrev:
>>>>>>>>> http://cr.openjdk.java.net/~anoll/8034216/webrev.02/
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Testing:
>>>>>>>>> ----------
>>>>>>>>> Both solutions successfully passed JPRT and CTW (rt.jar).
>>>>>>>>>
>>>>>>>>> Many thanks in advance,
>>>>>>>>> Albert
>>>>>>
>>>
>>



More information about the hotspot-compiler-dev mailing list