RFR(S): 8159611: C2: ArrayCopy elimination skips required parameter checks

Vladimir Kozlov vladimir.kozlov at oracle.com
Tue Aug 16 21:49:57 UTC 2016


Not generating exception is definitely bug.

First, about test case. It would be nice if it also verifies other 
IndexOutOfBoundsException cases.

Actually additional dynamic check will help in case of negative length 
is know during compilation. The allocation code will be eliminated very 
early instead of waiting macro expansion:

       int length = alloc->in(AllocateNode::ALength)->find_int_con(-1);
       if (length < 0) {
         NOT_PRODUCT(fail_eliminate = "Array's size is not constant";)
         can_eliminate = false;
       }

About additional length check in your new test. I think it may be 
collapsed with preceding check since it is generated after other checks.
So I would suggest to move it after offset + length check.

Thanks,
Vladimir

On 8/16/16 7:57 AM, Volker Simonis wrote:
> On Tue, Aug 16, 2016 at 7:24 AM, Tobias Hartmann
> <tobias.hartmann at oracle.com> wrote:
>> Hi Volker,
>>
>> thanks for taking care of this issue!
>>
>> Did you check what happens if the allocation is not eliminated and macro expansion phase emits another negative guard? Are the checks merged?
>>
>
> It depends. I just saw that in some cases the regression test worked
> before, because the length check was done in
> SharedRuntime::slow_arraycopy_C(). So in that case there's obviously
> nothing that can be merged. But the test case is obviously a
> degenerated example anyway, so I don't think that's a problem.
>
> If I do a more real-world example like this where the arracopy can not
> be eliminated because one of its arguments escapes:
>
>   public static boolean do_test2(int length, Object[] dest) {
>     try {
>       System.arraycopy(new Object[10], 1, dest, 1, length);
>       return false;
>     } catch (IndexOutOfBoundsException e) {
>       return true;
>     }
>   }
>
> and call it with:
>
> do_test2(8, new Object[10])
>
> the generated code for do_test2() unfortunately contains one more
> check now with my change (the 'length' field is in [rsp + #0]):
>
> 0a2   B4: #    B18 B5 <- B3  Freq: 0,999999
> 0a2       movl    R9, [rsp + #0]    # spill
> 0a6       testl   R9, R9
> 0a9       jl     B18  P=0,000001 C=-1,000000
> 0a9
> 0af   B5: #    B18 B6 <- B4  Freq: 0,999998
> 0af       movl    RBX, R9    # spill
> 0b2       incl    RBX    # int
> 0b4       cmpl    RBX, #10    # unsigned
> 0b7       jnbe,u  B18  P=0,000001 C=-1,000000
>
> The generated code before my change looked like this (againthe
> 'length' field is in [rsp + #0]):
>
> 0a1   B4: #    B17 B5 <- B3  Freq: 0,999999
> 0a1       movl    R11, [rsp + #8]    # spill
> 0a6       incl    R11    # int
> 0a9       cmpl    R11, #10    # unsigned
> 0ad       jnbe,u  B17  P=0,000001 C=-1,000000
>
> It seems that the 'length' check has been completely eliminated before.
>
> So I need to do some more tests to understand why the new check isn't
> eliminated.
>
> Do you think the new check results in a performance regression? Have
> you run some benchmarks?
>
>> I would prefer brackets around the if body but you don't need to send another webrev:
>>  if (EliminateAllocations) {
>>    generate_negative_guard(length, slow_region);
>>  }
>
> Yes, I agree.
>
>>
>> Best regards,
>> Tobias
>>
>> On 12.08.2016 21:13, Volker Simonis wrote:
>>> Hi,
>>>
>>> can I please have a review and sponsor for the following fix:
>>>
>>> http://cr.openjdk.java.net/~simonis/webrevs/2016/8159611
>>> https://bugs.openjdk.java.net/browse/JDK-8159611
>>>
>>>
>>> We are inserting several checks for the arguments of
>>> System.arraycopy() in LibraryCallKit::inline_arraycopy() before
>>> intensifying the call in LibraryCallKit::inline_arraycopy. However the
>>> check for the 'length' argument of arracopy is postponed to the macro
>>> expansion phase in PhaseMacroExpand::generate_arraycopy().
>>>
>>> But if we are running with EscapeAnalysis and EliminateAllocations,
>>> the array allocations inside a call to System.arraycopy() may get
>>> eliminated and thus the complete call to System.arraycopy() will be
>>> removed (see PhaseMacroExpand::process_users_of_allocation). In this
>>> case the extra 'length' check won't be added by
>>> PhaseMacroExpand::generate_arraycopy() any more because macro
>>> expansion happens after the elimination of macro nodes.
>>>
>>> In such a case it may happen that System.arraycopy() will silently
>>> accept an invalid (i.e. negative) 'length' parameter, although it
>>> should actually throw an ArrayOutOfBounds exception.
>>>
>>> The fix is simple: also insert a check for the length field in
>>> LibraryCallKit::inline_arraycopy() if we are running with
>>> EliminateAllocations.
>>>
>>> Regards,
>>> Volker
>>>


More information about the hotspot-compiler-dev mailing list