RFR: JDK-8213199: GC abstraction for Assembler::needs_explicit_null_check()

Erik Österlund erik.osterlund at oracle.com
Tue Nov 6 17:20:43 UTC 2018


Hi Andrew,

On 2018-11-06 17:46, Andrew Dinn wrote:
> On 06/11/18 16:04, Erik Österlund wrote:
>> New webrev:
>> http://cr.openjdk.java.net/~eosterlund/8213199/webrev.01/
> 
> Thanks for the updated version.
> 
>> On 2018-11-06 16:13, Andrew Dinn wrote:
>>> In that case, I hear you ask, why does the old code check for values in
>>> the range [heap_base - cell_header_size, heap_base + os::vm_page_size)?
>>> Good question. I don't know the answer. I think that test is bogus with
>>> no Shenandoah GC present.
>>
>> Interesting. It turns out that both ranges are relevant
>> [-cell_header_size, os::vm_page_size) and [heap_base - cell_header_size,
>> heap_base + os::vm_page_size). Looks like we both missed that from the
>> original check.
>> I updated my webrev to explicitly reflect those two ranges, as two
>> different checks.
> 
> Well, you say that but -- as I said before -- I am not sure why *both*
> are relevant to the non-Shenandoah case. And, given Roman's correction
> (doh, yes of course! the space for the Brooks pointer field had better
> lie in RW-mapped memory :-) I don't see why it is relevant to the
> Shenandoah case either.

The reason it is needed is because there may or may not be null checks 
in both the decode() step to get the base pointer of an access, and the 
access itself, given some base pointer. This results in two ranges for 
those two cases correspondingly. If the decoding uses decode_not_null, 
with an implicit null check in the decoding itself that blindly adds the 
heap base without checking for null, we will crash into the first page 
of the heap which is protected when performing accesses into that. But 
sometimes we have a decode()ed base pointer (with null check), that has 
already been fixed up to NULL, and then perform an access at an offset 
into that with an implicit null check. This results in a trap at the 
first page. So in summary, both ranges are needed because the 
implicitness of the null check may be both in the access and the 
narrowOop decoding.

But as I pointed out, this (seemingly incorrectly) assumes there may not 
be any offset values in the (heap_base, heap_base + os::vm_page_size) 
range. If you hit such an offset, we will generate incorrect code, due 
to the lack of distinction between addresses and offsets.

> I'd be very glad to hear from someone who knows about the history of the
> original function as to why the extra test for addresses above heap_base
> is included [hint: John Rose? David Holmes? Coleen? anyone else who
> might have been around when it was added?]
> 
> If that check is not actually needed then getting rid of it would make
> the whole thing a lot simpler.

It would indeed. But unfortunately we need it.

>>>> Just expressing these trivial ranges separately would have been very
>>>> easy to understand.
>>> Except it's not that easy. Also, with Shenandoah there is a new wrinkle.
>>> It is possible for an object to be allocated at the first non-guarded
>>> address in the heap area i.e. heap_base + os::vm_page_size. I that case
>>> a dereference of its Brooks pointer will lie in the protected page.
>>
>> Looks like Roman just said that's not possible.
> 
> Yes, my dumb!
> 
>> Fixed. Might need some help testing this on AArch64 though if we decide
>> to go with it.
> 
> I am sure Roman and Aleksey can do that but if needed I will.
> 
>> Do you agree though, that the current version that merges the address
>> and offset case relies on offsets being smaller than the heap base for
>> its magic check to work, and that we actually do not have such a
>> guarantee in the VM? Apart from readability preferences, this seems like
>> it is broken in the current form, unless there are seat belts I am not
>> aware of. In particular, if you have an offset value that is in the
>> virtual address range (heap_base, heap_base + os::vm_page_size()), which
>> I argue can happen, then it looks like we will normalize that as if it
>> was an address (even though it is not), and incorrectly say that this
>> offset does not need explicit null checks, despite needing explicit null
>> checks.
> Well, without the answer as to whether the check against heap_base is
> needed I am not sure. Assuming it is then then ...
> 
> Yes, it is possible, in theory, that an oop offset could turn out to
> have value (heap_base + D) where 0 <= D < os_page_size(). In that case
> the offset might wrongly be classified as *not* needing a null check
> even though it really does. I suspect it is unlikely in practice given
> how mmap hands out addresses but you never know.
> 
> As for readibility, I think the two functions are probably clearer --
> expecially if they stay adjacent in the source code.

Okay, sounds like we are on the same page. I agree it is unlikely that 
this will crash the VM (as evidenced by the lack of crashes observed). 
But I don't like relying on luck for correctness.

Thanks,
/Erik

> regards,
> 
> 
> Andrew Dinn
> -----------
> Senior Principal Software Engineer
> Red Hat UK Ltd
> Registered in England and Wales under Company Registration No. 03798903
> Directors: Michael Cunningham, Michael ("Mike") O'Neill, Eric Shander
> 


More information about the hotspot-gc-dev mailing list