-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8351569: [lworld] Revisit atomic access modes in flat var handles #1402
Changes from 7 commits
31fb09c
7bd00e1
e845193
874e4cb
d4af8c1
856cb9a
8ca6bef
1cd8176
70af9a2
0e2eff2
b87221a
7ddd549
6b1b379
be6f00d
f8c7d9b
000917a
a823ba4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,9 +25,13 @@ | |
|
||
package java.lang.invoke; | ||
|
||
import jdk.internal.value.NullRestrictedCheckedType; | ||
import jdk.internal.value.ValueClass; | ||
import jdk.internal.vm.annotation.LooselyConsistentValue; | ||
import sun.invoke.util.Wrapper; | ||
|
||
import java.lang.foreign.MemoryLayout; | ||
import java.lang.reflect.AccessFlag; | ||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Method; | ||
|
@@ -50,11 +54,31 @@ static VarHandle makeFieldHandle(MemberName f, Class<?> refc, boolean isWriteAll | |
long foffset = MethodHandleNatives.objectFieldOffset(f); | ||
Class<?> type = f.getFieldType(); | ||
if (!type.isPrimitive()) { | ||
if (f.isFlat()) { | ||
if (type.isValue()) { | ||
int layout = f.getLayout(); | ||
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields | ||
? new VarHandleFlatValues.FieldInstanceReadOnly(refc, foffset, type, f.getCheckedFieldType(), layout) | ||
: new VarHandleFlatValues.FieldInstanceReadWrite(refc, foffset, type, f.getCheckedFieldType(), layout)); | ||
boolean isAtomic = isAtomicFlat(f); | ||
boolean isFlat = f.isFlat(); | ||
if (isFlat) { | ||
if (isAtomic) { | ||
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields | ||
? new VarHandleFlatValues.FieldInstanceReadOnly(refc, foffset, type, f.getCheckedFieldType(), layout) | ||
: new VarHandleFlatValues.FieldInstanceReadWrite(refc, foffset, type, f.getCheckedFieldType(), layout)); | ||
} else { | ||
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields | ||
? new VarHandleNonAtomicFlatValues.FieldInstanceReadOnly(refc, foffset, type, f.getCheckedFieldType(), layout) | ||
: new VarHandleNonAtomicFlatValues.FieldInstanceReadWrite(refc, foffset, type, f.getCheckedFieldType(), layout)); | ||
} | ||
} else { | ||
if (isAtomic) { | ||
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields | ||
? new VarHandleReferences.FieldInstanceReadOnly(refc, foffset, type, f.getCheckedFieldType()) | ||
: new VarHandleReferences.FieldInstanceReadWrite(refc, foffset, type, f.getCheckedFieldType())); | ||
} else { | ||
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While this case is not terribly important (typically, if something is non-flat, then it will be "atomic" from a programming model perspective), there are various JVM flags that can affect whether something is flattened or not. It's important here that we don't return a var handles that "overpromises" and support more access modes (just because we know that, underneath, we can use reference |
||
? new VarHandleNonAtomicReferences.FieldInstanceReadOnly(refc, foffset, type, f.getCheckedFieldType()) | ||
: new VarHandleNonAtomicReferences.FieldInstanceReadWrite(refc, foffset, type, f.getCheckedFieldType())); | ||
} | ||
} | ||
} else { | ||
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields | ||
? new VarHandleReferences.FieldInstanceReadOnly(refc, foffset, type, f.getCheckedFieldType()) | ||
|
@@ -119,12 +143,17 @@ static VarHandle makeStaticFieldVarHandle(Class<?> decl, MemberName f, boolean i | |
long foffset = MethodHandleNatives.staticFieldOffset(f); | ||
Class<?> type = f.getFieldType(); | ||
if (!type.isPrimitive()) { | ||
if (f.isFlat()) { | ||
assert false : ("static field is flat in " + decl + "." + f.getName()); | ||
int layout = f.getLayout(); | ||
return f.isFinal() && !isWriteAllowedOnFinalFields | ||
? new VarHandleFlatValues.FieldStaticReadOnly(decl, base, foffset, type, f.getCheckedFieldType(), layout) | ||
: new VarHandleFlatValues.FieldStaticReadWrite(decl, base, foffset, type, f.getCheckedFieldType(), layout); | ||
assert !f.isFlat() : ("static field is flat in " + decl + "." + f.getName()); | ||
if (type.isValue()) { | ||
if (isAtomicFlat(f)) { | ||
return f.isFinal() && !isWriteAllowedOnFinalFields | ||
? new VarHandleReferences.FieldStaticReadOnly(decl, base, foffset, type, f.getCheckedFieldType()) | ||
: new VarHandleReferences.FieldStaticReadWrite(decl, base, foffset, type, f.getCheckedFieldType()); | ||
} else { | ||
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields | ||
? new VarHandleNonAtomicReferences.FieldStaticReadOnly(decl, base, foffset, type, f.getCheckedFieldType()) | ||
: new VarHandleNonAtomicReferences.FieldStaticReadWrite(decl, base, foffset, type, f.getCheckedFieldType())); | ||
} | ||
} else { | ||
return f.isFinal() && !isWriteAllowedOnFinalFields | ||
? new VarHandleReferences.FieldStaticReadOnly(decl, base, foffset, type, f.getCheckedFieldType()) | ||
|
@@ -176,6 +205,36 @@ else if (type == double.class) { | |
} | ||
} | ||
|
||
static boolean isAtomicFlat(MemberName field) { | ||
boolean hasAtomicAccess = (field.getModifiers() & Modifier.VOLATILE) != 0 || | ||
!(field.getCheckedFieldType() instanceof NullRestrictedCheckedType) || | ||
!field.getFieldType().isAnnotationPresent(LooselyConsistentValue.class); | ||
return hasAtomicAccess && !HAS_OOPS.get(field.getFieldType()); | ||
} | ||
|
||
static boolean isAtomicFlat(Object array) { | ||
Class<?> componentType = array.getClass().componentType(); | ||
boolean hasAtomicAccess = ValueClass.isAtomicArray(array) || | ||
!ValueClass.isNullRestrictedArray(array) || | ||
!componentType.isAnnotationPresent(LooselyConsistentValue.class); | ||
return hasAtomicAccess && !HAS_OOPS.get(componentType); | ||
} | ||
|
||
static final ClassValue<Boolean> HAS_OOPS = new ClassValue<>() { | ||
@Override | ||
protected Boolean computeValue(Class<?> c) { | ||
for (Field f : c.getDeclaredFields()) { | ||
Class<?> ftype = f.getType(); | ||
if (UNSAFE.isFlatField(f) && HAS_OOPS.get(ftype)) { | ||
return true; | ||
} else if (!ftype.isPrimitive()) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
}; | ||
|
||
// Required by instance field handles | ||
static Field getFieldFromReceiverAndOffset(Class<?> receiverType, | ||
long offset, | ||
|
@@ -206,6 +265,39 @@ static Field getStaticFieldFromBaseAndOffset(Class<?> declaringClass, | |
throw new InternalError("Static field not found at offset"); | ||
} | ||
|
||
// This is invoked by non-flat array var handle code when attempting to access a flat array | ||
public static VarHandle flatArrayElementHandleFor(Object array) { | ||
assert ValueClass.isFlatArray(array); | ||
boolean isAtomic = isAtomicFlat(array); | ||
return isAtomic ? | ||
ATOMIC_FLAT_ARRAY_VAR_HANDLE.get(array.getClass()) : | ||
NON_ATOMIC_FLAT_ARRAY_VAR_HANDLE.get(array.getClass()); | ||
} | ||
|
||
private static final ClassValue<VarHandle> ATOMIC_FLAT_ARRAY_VAR_HANDLE = new ClassValue<>() { | ||
@Override | ||
protected VarHandle computeValue(Class<?> arrayClass) { | ||
// Todo: eventually, this code should not be class-based | ||
int aoffset = (int) UNSAFE.arrayBaseOffset(arrayClass); | ||
int ascale = UNSAFE.arrayIndexScale(arrayClass); | ||
int ashift = 31 - Integer.numberOfLeadingZeros(ascale); | ||
int layout = UNSAFE.arrayLayout(arrayClass); | ||
return new VarHandleFlatValues.Array(aoffset, ashift, arrayClass, layout); | ||
} | ||
}; | ||
|
||
private static final ClassValue<VarHandle> NON_ATOMIC_FLAT_ARRAY_VAR_HANDLE = new ClassValue<>() { | ||
@Override | ||
protected VarHandle computeValue(Class<?> arrayClass) { | ||
// Todo: eventually, this code should not be class-based | ||
int aoffset = (int) UNSAFE.arrayBaseOffset(arrayClass); | ||
int ascale = UNSAFE.arrayIndexScale(arrayClass); | ||
int ashift = 31 - Integer.numberOfLeadingZeros(ascale); | ||
int layout = UNSAFE.arrayLayout(arrayClass); | ||
return new VarHandleNonAtomicFlatValues.Array(aoffset, ashift, arrayClass, layout); | ||
} | ||
}; | ||
|
||
static VarHandle makeArrayElementHandle(Class<?> arrayClass) { | ||
if (!arrayClass.isArray()) | ||
throw new IllegalArgumentException("not an array: " + arrayClass); | ||
|
@@ -217,14 +309,9 @@ static VarHandle makeArrayElementHandle(Class<?> arrayClass) { | |
int ashift = 31 - Integer.numberOfLeadingZeros(ascale); | ||
|
||
if (!componentType.isPrimitive()) { | ||
VarHandle vh; | ||
if (UNSAFE.isFlatArray(arrayClass)) { | ||
int layout = UNSAFE.arrayLayout(arrayClass); | ||
vh = new VarHandleFlatValues.Array(aoffset, ashift, arrayClass, layout); | ||
} else { | ||
vh = new VarHandleReferences.Array(aoffset, ashift, arrayClass); | ||
} | ||
return maybeAdapt(vh); | ||
// do not check for atomicity now -- atomicity will be checked on the accessed array object | ||
// a sharper var handle will be obtained (dynamically) from flatArrayElementHandleFor(Object) | ||
return maybeAdapt(new VarHandleReferences.Array(aoffset, ashift, arrayClass)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: this works because:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another note: at the language level there's no mechanism to obtain a class mirror for a null-restricted, or atomic array. This means we can't really create an array var handle that works only on one array shape (which would allow us to eliminate the dynamic dispatch completely, at least for some array var handles). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, now it is crucial for us to benchmark these var handles - I suspect they are very sensitive to profile pollutions. |
||
} | ||
else if (componentType == boolean.class) { | ||
return maybeAdapt(new VarHandleBooleans.Array(aoffset, ashift)); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to do a bit of trickery here. Basically we have two pair of input values, which should use the same unsafe access primitives:
So, we now use
InputType
to model the input type to the template, and from there we deriveType
(which is really the Unsafe access type).There's also some other changes: