Unsafe API and explicit VT allocation (original) (raw)
John Rose john.r.rose at oracle.com
Thu Jul 19 21:20:57 UTC 2018
- Previous message (by thread): hg: valhalla/valhalla: 8207790: [lworld] update reflection to generate ValueTypes attribute
- Next message (by thread): Unsafe API and explicit VT allocation
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
This is not critical for LW1, but I want to start the ball rolling on discussion of explicit low-level buffering intrinsics for value types. Specifically, I'd like to talk about the C2 IR shapes that support this. (I'm prompted to this by looking at some recent work by Tobias.)
So, we should talk about IR shapes for making private mutable buffers for value types, for use (only) by the Unsafe API.
I think it involves an intrinsic to trigger an allocate regardless of previous allocation state, which precedes the unsafe ops, and (maybe) a subsequent intrinsic (or the same intrinsic) to suppress the allocation state at the end of the Unsafe patching.
The Java code would be something like:
private static final Unsafe U = Unsafe.getUnsafe(); private static final long FO_counter = U.objectFieldOffset(MyVT.class.getField("counter")); MyVT peekPoke(MyVT x) { Object xbuf = U.bufferValue(x, MyVT.class); int counter = U.getInt(xbuf, FO_count); ++counter; U.put(xbuf, FO_count, counter); return U.unbufferValue(xbuf, MyVT.class); }
An alternative to that pattern would be to ask the Unsafe user to assemble the value in a 1-array containing the value under construction, but that fails to work unless the value array is flattened, which is not guaranteed (though is likely). So I think we need an intrinsic or two here (in the Unsafe API) to control buffering.
The bracketing of the Unsafe code which peeks and pokes the buffer would probably involve not only explicit allocation ops, but also CPU-order membars and/or Opaque masking of either the value or the memory states accessed by Unsafe.
The IR would be something like:
MyVT peekPoke(MyVT x) { //Object xbuf = U.bufferValue(x, MyVT.class); checkTypeOrTrap(x, MyVT); xbuf = ValueTypePtrNode::forceAllocate(x); //int counter = U.getInt(xbuf, FO_count); addr = field_addressing(T_INT, xbuf, longcon(FO_count)); counter = expand_unsafe_get(addr); //++counter; counter' = AddNode::make(counter, intcon(1)); //U.put(xbuf, FO_count, counter); expand_unsafe_put(addr, counter); //return U.unbufferValue(xbuf, MyVT.class); return_value = ValueTypePtrNode::discardBuffer(xbuf); }
If the Unsafe ops correspond correctly to normal field accesses, as observed by the JIT, then the buffer can be completely deleted, and the fieldwise computation released from any residual barriers (which suggests CPU-order is the wrong tool here).
The important thing in the IR is that everybody agrees that xbuf is totally local (doesn't escape from the IR block). The discardBuffer either removes the buffer node completely or hides it securely behind some sort of barrier or Opaque node.
For extra points, the bufferValue and unbufferValue intrinsics could also do interesting things with objects, such as a forced shallow clone. Such operations would be, well, "unsafe" for general objects, but would be a useful tool for writing "withers" for value-based classes (which approximate the characteristics of true value types).
In other words, like the rest of Unsafe, such intrinsics can be useful (occasionally even indispensable) when used with the cooperation of the class and proper respect for its invariants, though they are utterly dangerous otherwise.
— John
P.S. Another message is coming about the various internal formats we are using for value types…
- Previous message (by thread): hg: valhalla/valhalla: 8207790: [lworld] update reflection to generate ValueTypes attribute
- Next message (by thread): Unsafe API and explicit VT allocation
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]