[9] RFR (S): 8160527: Check for final instance field updates can be omitted

Coleen Phillimore coleen.phillimore at oracle.com
Fri Jul 1 12:39:51 UTC 2016



On 7/1/16 7:52 AM, Zoltán Majó wrote:
> Hi Coleen,
>
>
> thank you for the feedback!Please see details below.
>
> On 06/30/2016 10:15 PM, Coleen Phillimore wrote:
>>
>> Hi,  Looking at this further:
>>
>> http://cr.openjdk.java.net/~zmajo/8160527/webrev.01/src/share/vm/interpreter/interpreterRuntime.cpp.udiff.html 
>>
>>
>> *+ bool final_instance_update = info.field_holder()->major_version() 
>> >= 53 &&*
>> *+ info.has_initialized_final_update() &&*
>> *+ (bytecode == Bytecodes::_putfield || bytecode == 
>> Bytecodes::_nofast_putfield);*
>> *+ *
>> *+ *
>>
>> I think you can test info.access_flags().is_final() and not use the 
>> new has_initialized_final_update() flag.
>>
>> If the current_class of the function with the putfield is not the 
>> same as the class owning the field, an error would be thrown in 
>> linkResolver.cpp and the bytecode would never be marked as resolved 
>> in the constant pool of the current_class (only the class owning the 
>> field).  If the field is_final, it'll only be initialized in the 
>> <init> function so no point marking it resolved in the cpCache of the 
>> resolved_class anyway.
>
> I agree,but based on your second suggestion (below), it seems we don't 
> need the variable 'final_instance_update' anymore. Please see below.
>
>>
>> It seems like this is_put part of this 'if' statement:
>>
>>   if (!uninitialized_static) {
>>     get_code = ((is_static) ? Bytecodes::_getstatic : 
>> Bytecodes::_getfield);
>>     if (is_put || !info.access_flags().is_final()) {
>>       put_code = ((is_static) ? Bytecodes::_putstatic : 
>> Bytecodes::_putfield);
>>     }
>>   }
>>
>> Should be moved up to:
>>
>>   // We need to delay resolving put instructions on final fields
>>   // until we actually invoke one. This is required so we throw
>>   // exceptions at the correct place. If we do not resolve completely
>>   // in the current pass, leaving the put_code set to zero will
>>   // cause the next put instruction to reresolve.
>>   Bytecodes::Code put_code = (Bytecodes::Code)0;
>>   if (is_put && !info.access_flags().is_final()) {
>>     put_code = ((is_static) ? Bytecodes::_putstatic : 
>> Bytecodes::_putfield);
>>   }
>>
>> Where it'll match the comment.
>
> Yes, that is a good idea.
>
> But in the original code 'put_code' is set only if 
> !uninitialized_static is true.
>
>  595   if (!uninitialized_static) {
>  596     get_code = ((is_static) ? Bytecodes::_getstatic : 
> Bytecodes::_getfield);
> 597 if (is_put || !info.access_flags().is_final()) {
>  598       put_code = ((is_static) ? Bytecodes::_putstatic : 
> Bytecodes::_putfield);
>  599     }
>  600   }
>
>
> Therefore, the conditionyou've suggested
>
>   if (is_put && !info.access_flags().is_final()) {
>
> should include !uninitialized_static as well
>
>   if (is_put && !info.access_flags().is_final() && 
> !uninitialized_static) {

Okay, I see it now.   uninitialized_static is to delay resolution for 
the nonfinal fields.
>
> The comment
>
>   // We also need to delay resolving getstatic instructions until the
>   // class is intitialized. This is required so that access to the static
> should be updated as well to
>
>   // We also need to delay resolving getstatic and *putstatic* 
> instructions until the
>   // class is intitialized. This is required so that access to the static
>
> as we delay putstatic instructions as well.
>
> I restructured the code (incl. comments) accordingly. Here is the 
> updated webrev:
> http://cr.openjdk.java.net/~zmajo/8160527/webrev.02/

Yes, this looks very good.   It's much clearer because it was confusing 
with all these conditions.


>
> JPRT testing passes.

There's a test for delaying resolution for non-final fields somewhere.  
Did you find that?  I think it's in the compiler jtreg directories.

Thanks,
Coleen
>
> Thank you!
>
> Best regards,
>
>
> Zoltan
>
>
>
>>
>> Coleen
>>
>> On 6/30/16 3:31 PM, Coleen Phillimore wrote:
>>>
>>> Zoltan,
>>>
>>> On 6/30/16 6:03 AM, Zoltán Majó wrote:
>>>> Hi Coleen,
>>>>
>>>>
>>>> thank you for the feedback and for the review! Please see below.
>>>>
>>>> On 06/29/2016 11:01 PM, Coleen Phillimore wrote:
>>>>>
>>>>> Zoltan,
>>>>>
>>>>> http://cr.openjdk.java.net/~zmajo/8160527/webrev.00/src/share/vm/interpreter/interpreterRuntime.cpp.udiff.html 
>>>>>
>>>>>
>>>>> Yes, this matches the general strategy.   You don't have to have 
>>>>> this line
>>>>>
>>>>> *+ (bytecode == Bytecodes::_putfield || bytecode == 
>>>>> Bytecodes::_nofast_putfield);*
>>>>>
>>>>>
>>>>> Because the test below is:
>>>>>
>>>>> *!if (_(_is_put_&& !final_instance_update)_|| 
>>>>> !info.access_flags().is_final()) {*
>>>>
>>>> I think we need the condition
>>>>
>>>>     (bytecode == Bytecodes::_putfield || bytecode == 
>>>> Bytecodes::_nofast_putfield);
>>>>
>>>> for 'final_instance_update'. The reason is that we need to detect 
>>>> invalid updates only to *instance* fields but not to *static* 
>>>> fields; the condition 'is_put' includes Bytecodes::_putstatic as well.
>>>>
>>>>     bool is_put = (bytecode == Bytecodes::_putfield || bytecode == 
>>>> Bytecodes::_nofast_putfield ||
>>>> -->                bytecode == Bytecodes::_putstatic);
>>>>
>>>> The reason do not need to include putstatic is that the current 
>>>> delaying mechanism [1] [2] works fine for static final fields (but 
>>>> it is insufficient for instance final fields).
>>>
>>> ok.
>>>
>>>>
>>>> To check that invalid updates to static fields are properly 
>>>> detected, I've added a new test, TestPutStatic.jasm. The test is 
>>>> analogous to the TestPutField.jasm test that you've seen in the 
>>>> previous webrev (webrev.00); the VM passes the test already in its 
>>>> current state (without the change).
>>>>
>>>> Here is the updated webrev:
>>>> http://cr.openjdk.java.net/~zmajo/8160527/webrev.01/
>>>
>>> This looks good.
>>>>
>>>> I re-tested with JPRT, all tests (incl. the newly added ones) pass.
>>>>
>>>>>
>>>>>
>>>>> Otherwise, looks fine.  I'm sorry I didn't realize this in the 
>>>>> initial code review.
>>>>
>>>> I did not realize either that this problem existed -- good that 
>>>> John has pointed it out.
>>>>
>>>
>>> Thanks,
>>> Coleen
>>>
>>>> Thank you!
>>>>
>>>> Best regards,
>>>>
>>>>
>>>> Zoltan
>>>>
>>>> [1] 
>>>> http://hg.openjdk.java.net/jdk9/hs/hotspot/file/2af3fb9f244f/src/share/vm/interpreter/interpreterRuntime.cpp#l579
>>>> [2] 
>>>> http://hg.openjdk.java.net/jdk9/hs/hotspot/file/2af3fb9f244f/src/share/vm/interpreter/interpreterRuntime.cpp#l586
>>>>
>>>>>
>>>>> Coleen
>>>>>
>>>>>
>>>>> On 6/29/16 11:00 AM, Zoltán Majó wrote:
>>>>>> Hi,
>>>>>>
>>>>>>
>>>>>> please review the patch for 8160527.
>>>>>>
>>>>>> https://bugs.openjdk.java.net/browse/JDK-8160527
>>>>>>
>>>>>> Problem: 8157181 added a check that verifies that final instance 
>>>>>> fields can be updated only by object initializer methods (as 
>>>>>> required by JVMS >=7) [1].
>>>>>>
>>>>>> Unfortunately, the newly added check can be circumvented due to 
>>>>>> constant pool caching: If the instance field update is executed 
>>>>>> in an <init> method first, the instruction updating the field is 
>>>>>> cached in the constant pool cache. Subsequent updates use the 
>>>>>> constant pool cache (and do not call into the VM where the check 
>>>>>> is executed). As a result, any method can update a final instance 
>>>>>> field if that field was first updated in an instance initializer 
>>>>>> (the method must be declared in the same class as the field, 
>>>>>> though).
>>>>>>
>>>>>> John's comment in the bug description provides more detailed 
>>>>>> information on how the above can happen.
>>>>>>
>>>>>>
>>>>>> Solution: Do not cache putfield instructions to final instance 
>>>>>> fields if the verifier has detected that the field is updated in 
>>>>>> other methods than <init> (i.e., has_initialized_field_update() 
>>>>>> returns true for the field). Avoiding caching results in the 
>>>>>> field access being re-resolved until the offending access is 
>>>>>> attempted; then an exception is thrown. Avoiding caching must be 
>>>>>> done very infrequently, as offending code is rare (e.g., such 
>>>>>> code cannot be produced by javac).
>>>>>>
>>>>>> I've also corrected a small mistake in the fix for 8157181 (the 
>>>>>> class name is printed in the error message instead of the 
>>>>>> method's name).
>>>>>>
>>>>>> Webrev:
>>>>>> http://cr.openjdk.java.net/~zmajo/8160527/webrev.00/
>>>>>>
>>>>>> Testing:
>>>>>> - JPRT (testset hotspot, incl. newly added test);
>>>>>> - verified that the newly added test triggers the problem with an 
>>>>>> unmodified VM.
>>>>>>
>>>>>> Thank you!
>>>>>>
>>>>>> Best regards,
>>>>>>
>>>>>>
>>>>>> Zoltan
>>>>>>
>>>>>> [1] 
>>>>>> http://hg.openjdk.java.net/jdk9/hs-comp/hotspot/rev/c558d46c1af2#l11.76 
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>



More information about the hotspot-runtime-dev mailing list