Bounds checks with unsafe array access

Paul Sandoz paul.sandoz at oracle.com
Wed Sep 17 20:27:06 UTC 2014


On Sep 12, 2014, at 7:04 PM, John Rose <john.r.rose at oracle.com> wrote:
> On Sep 12, 2014, at 5:40 AM, Paul Sandoz <paul.sandoz at oracle.com> wrote:
> 
>> On Sep 11, 2014, at 10:51 PM, John Rose <john.r.rose at oracle.com> wrote:
>>> In some cases of highly tuned code the author can work with the JIT engineer to insert the right code shapes from the start.
>>> The "test length against zero" code shape is one of these, and can often be merged with a check that is required any way (e.g., is the data structure logically empty?).
>>> For example:
>>> http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/f08705540498/src/java.base/share/classes/java/util/HashMap.java#l569
>>> 
>> 
>> A great example, i was just recently looking at that :-)
>> 
>> My understanding is that check ("(n = tab.length) > 0") is in place solely to generate more efficient bounds checks, since it should always return true (table is either null or non-null with a length > 0). If/when JDK-8003585 gets fixed we might be able to update the code and remove that check (or turn it into an assert if it did not interfere with inlining...).
> 
> Actually, that would be a step backwards.  It is best to initialize HashMap.table to a constant zero-length array:
>  https://bugs.openjdk.java.net/browse/JDK-8003586
> If the table is never null, then the null check becomes mergeable into the following load instruction.
> 

I thought the explicit null check would get merged with the load of the array length, at least in a simple case that is what i observe:

  0x000000010c2c958c: mov    0x24(%rsi),%r10d   ;*getfield table
                                                ; - java.util.HashMap::getNode at 1 (line 568)

  0x000000010c2c9590: mov    0xc(%r12,%r10,8),%r8d  ;*arraylength
                                                ; - java.util.HashMap::getNode at 10 (line 568)
                                                ; implicit exception: dispatches to 0x000000010c2c964d
  0x000000010c2c9595: test   %r8d,%r8d
  0x000000010c2c9598: jle    0x000000010c2c960d  ;*ifle
                                                ; - java.util.HashMap::getNode at 14 (line 568)

  0x000000010c2c959a: mov    %r8d,%ebp
  0x000000010c2c959d: dec    %ebp
  0x000000010c2c959f: and    %edx,%ebp          ;*iand
                                                ; - java.util.HashMap::getNode at 23 (line 568)

  0x000000010c2c95a1: cmp    %r8d,%ebp
  0x000000010c2c95a4: jae    0x000000010c2c95f1   <--- this will go away
  0x000000010c2c95a6: shl    $0x3,%r10
  0x000000010c2c95aa: mov    0x10(%r10,%rbp,4),%r11d  ;*aaload


>> I was naively pondering whether that table field:
>> 
>> http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/f08705540498/src/java.base/share/classes/java/util/HashMap.java#l395
>> 
>> could be annotated with something like @ArrayHasElementsIfNonNull, so that a write to that field would be guarded by a check which would throw an assertion error if the field would otherwise be set to an empty array.
> 
> If we can auto-detect this sort of thing then we don't need annotations.  A simple class-wide analysis at JIT time could detect such invariants.

And/or perhaps AOT?


>  The missing bit (from the JVM perspective) is a robust way to protect those invariants from invasive changes, via JNI, reflection, or (perhaps in some cases) subclassing.
> 
>> 
>> Final field optimizations would be a bigger bang for the buck though.
> 
> And they would supply that important missing bit.
> 
>> Combine that with specialization and perhaps there is an alternative implementation approach to the current VarHandles prototype?
> 
> Yes, of course.  You know, any class in java.lang.invoke has a special perk:  the final field optimization already works on it:
>  http://hg.openjdk.java.net/jdk9/jdk9/hotspot/file/efe1eb043ee1/src/share/vm/ci/ciField.cpp#l187

I was pleasantly surprised to find that out :-) and recently experimented with it after some discussions with Remi on the valhalla list.


> 
>> class FieldHandle<R, any V> {
>>  /* really */ final Class<?> rType;
>>  /* really */ final Class<?> vType;
>>  /* really */ final long foffset;
>> 
>>  <where reference V>
>>  V getVolatile(R r) {
>>      r.getClass();
>>      rType.cast(r); // Should go away (well almost!) if FieldHandle instance is constant
>>      return vType.cast(U.getObjectVolatile(r, foffset));
>>  }
>> 
>>  <where V = int>
>>  int getVolatile(R r) {
>>      r.getClass();
>>      rType.cast(r);
>>      return U.getIntVolatile(r, foffset);
>>  }
>> }
> 
> Type "any" is the right way to do full polymorphism for single arguments, no?
> 

Perhaps for a comment set?  I presume certain atomic operations are unlikely to be supported for all primitive types, and certain operations are only applicable to primitive types (such as getAndAdd, although i wonder how should that apply, if at all, to say a LongLong value type).

Also, maybe there is an intermediate step if specialization gets there before the value machinery, as you sketch below, is in place. 


> The "where" bits don't quite extend to value types, without some sort of value-generic magic for Unsafe.  Which is doable:
> 
>>  <where value V>
>>  V getVolatile(R r) {
>>      r.getClass();
>>      rType.cast(r);
>>      return vType.cast(U.<V.box>getValueVolatile(r, foffset, V.unbox.class));
>>  }
> 
> The magic there would be something like (1) Class.cast works on boxed values only, (2) there is always implicit box/unbox conversion, (3) the JIT is aggressive about cleaning out boxes, and (4) the internals of U.getValueVolatile would operate on the unboxed value, and simply return a box with the expectation of immediate unboxing.
> 
> (There are language design issues with which occurrences of the value type denote boxes, etc.)
> 

Thanks, seems to be some intersection here with potential LF improvements for cleaning out boxes.

Paul.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 841 bytes
Desc: Message signed with OpenPGP using GPGMail
URL: <http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/attachments/20140917/3e95dd43/signature-0001.asc>


More information about the hotspot-compiler-dev mailing list