RFR: JDK-8205549 JDK-8205698 Support of flattened values in Unsafe
John Rose
john.r.rose at oracle.com
Fri Jun 29 21:12:33 UTC 2018
On Jun 29, 2018, at 1:36 PM, Frederic Parain <frederic.parain at oracle.com> wrote:
>
> I’d prefer asserts then just let the VM crash unpredictably.
> But we’re speaking about Unsafe, do whatever you want
> for getObject()/putObject().
Right, it's Unsafe. C++ asserts or even InternalError throws
are friendly and helpful, but we ruthlessly chop them out in
optimized code; that's how random crashes can happen if
you get your Unsafe stuff wrong. But it is likely you will run
your buggy code in the interpreter first, and from there you
will get a reasonably diagnosed failure. At least, that's the
theory of how to balance raw performance against a semblance
of debuggability.
> However, put*() methods should (must?) enforce value types immutability
> (this includes primitive fields too). I haven’t seen this in Mandy’s changeset
> (but I might I missed it).
That's a good point, although it is an optional target of opportunity,
under the theory that Unsafe requires the user to enforce all
relevant constraints. Storing a new value to an ACC_FINAL
object instance field is about as disastrous as storing a new
value to a buffered value instance field. Is it a user error?
Probably. Must the Unsafe API prevent the error? Probably
not, unless it's simple to do.
This also reminds me that we need a way to use Unsafe
to patch value instance fields. Here's how I would like to
do this, as a first cut:
public Object withFloat(Object value, long offset, float x);
Here's a throwaway implementation:
public Object withFloat(Object value, long offset, float x) {
Object[] buf = (Object[]) java.lang.reflect.Array.newInstance(value.getClass(), 1);
long bufBase = arrayBaseOffset(buf.getClass());
assert(isFlattenedArray(buf.getClass()));
buf[0] = value;
putFloat(buf, bufBase + (offset - VALUE_BUFFER_HEADER_SIZE), x);
value = buf[0];
return value;
}
I fear we *will* want all of the various withFoo to go with their getFoo
and putFoo brethren. But I hope we *can* implement them using lower
level peek/poke operations on buffered values.
Doing this with one-element arrays seems relatively workable, but
it requires that the arrays be flattened.
Now we get to exactly what Fred is warning as a bug, but I think it's
a necessary feature, which is patching value instances in place.
To do this we need an Unsafe API point for creating a privately
buffered value of a value type, which we can *then* safely patch
using putFoo. There should also be an API point for freezing
the private buffer, in case the optimizer cares, and for potential
error checks. It would be good (though not required) if the
interpreter entry points for putField would verify that, if the
Object in question is a value instance, then the object is a
private buffer, not an arbitrary shared buffer that found its
way into a dangerous neighborhood. See below.
To prevent putFoo on random values robustly we would want a
"larval bit" inside the temporary private buffer header. Larval objects
are something we are likely to do anyway, at some point, but
we can't now.
— John
public Object withFloat(Class<?> valueType, Object value, long offset, float x) {
Object buf = makePrivateBuffer(value);
putFloat(buf, offset, x);
return finishPrivateBuffer(buf);
}
// prototype version; must be intrinsified for real use
public Object makePrivateBuffer(Object value) {
assert(value.getClass().isValue());
Object[] a = (Object[]) java.lang.reflect.Array.newInstance(value.getClass(), 1);
assert(isFlattenedArray(bh.a.getClass()));
int i = 0;
a[i] = value;
{ class BlackHole { volatile Object[] a; volatile int i; }
var bh = new BlackHole();
bh.a = a; bh.i = i;
a = bh.a; i = bh.i;
}
value = a[i];
//value = enterLarvalState(value);
return value;
}
// prototype version; must be intrinsified for real use
public Object finishPrivateBuffer(Object value) {
//assert(isPrivateBufferInLarvalState(value));
//value = exitLarvalState(value);
return value;
}
More information about the valhalla-dev
mailing list