[9] RFR(S): 8034216: assert(false) failed: infinite loop in PhaseIterGVN::optimize
Vladimir Kozlov
vladimir.kozlov at oracle.com
Fri Apr 11 08:32:37 UTC 2014
Looks good. Lets see what Nightly show.
Thanks,
Vladimir
On 4/11/14 1:14 AM, Albert wrote:
> 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