3939import java .util .Optional ;
4040import java .util .Set ;
4141import java .util .concurrent .ForkJoinTask ;
42+ import java .util .function .Function ;
43+ import java .util .stream .Collectors ;
4244
4345import org .graalvm .collections .Pair ;
4446import org .graalvm .compiler .core .common .NumUtil ;
9698import com .oracle .svm .hosted .substitute .DeletedMethod ;
9799import com .oracle .svm .util .ReflectionUtil ;
98100
101+ import jdk .internal .vm .annotation .Contended ;
99102import jdk .vm .ci .meta .ConstantPool ;
100103import jdk .vm .ci .meta .ExceptionHandler ;
101104import jdk .vm .ci .meta .JavaKind ;
@@ -390,7 +393,7 @@ public static boolean isKnownImmutableType(Class<?> clazz) {
390393 }
391394
392395 private void layoutInstanceFields () {
393- layoutInstanceFields (hUniverse .getObjectClass (), ConfigurationValues .getObjectLayout ().getFirstFieldOffset (), new HostedField [0 ]);
396+ layoutInstanceFields (hUniverse .getObjectClass (), ConfigurationValues .getObjectLayout ().getFirstFieldOffset (), new HostedField [0 ], false );
394397 }
395398
396399 private static boolean mustReserveLengthField (HostedInstanceClass clazz ) {
@@ -405,7 +408,7 @@ private static boolean mustReserveLengthField(HostedInstanceClass clazz) {
405408 return false ;
406409 }
407410
408- private void layoutInstanceFields (HostedInstanceClass clazz , int superSize , HostedField [] superFields ) {
411+ private void layoutInstanceFields (HostedInstanceClass clazz , int superSize , HostedField [] superFields , boolean superFieldsContendedPadding ) {
409412 ArrayList <HostedField > rawFields = new ArrayList <>();
410413 ArrayList <HostedField > orderedFields = new ArrayList <>();
411414 ObjectLayout layout = ConfigurationValues .getObjectLayout ();
@@ -433,30 +436,45 @@ private void layoutInstanceFields(HostedInstanceClass clazz, int superSize, Host
433436 }
434437 }
435438
436- // Sort so that a) all Object fields are consecutive, and b) bigger types come first.
437- Collections .sort (rawFields , HostedUniverse .FIELD_COMPARATOR_RELAXED );
438-
439439 int nextOffset = startSize ;
440- while (rawFields .size () > 0 ) {
441- boolean progress = false ;
442- for (int i = 0 ; i < rawFields .size (); i ++) {
443- HostedField field = rawFields .get (i );
444- int fieldSize = layout .sizeInBytes (field .getStorageKind ());
445440
446- if (nextOffset % fieldSize == 0 ) {
447- field .setLocation (nextOffset );
448- nextOffset += fieldSize ;
441+ boolean hasLeadingPadding = superFieldsContendedPadding ;
442+ boolean isClassContended = clazz .isAnnotationPresent (Contended .class );
443+ if (!hasLeadingPadding && (isClassContended || (clazz .getSuperclass () != null && clazz .getSuperclass ().isAnnotationPresent (Contended .class )))) {
444+ nextOffset += getContendedPadding ();
445+ hasLeadingPadding = true ;
446+ }
449447
450- rawFields .remove (i );
451- orderedFields .add (field );
452- progress = true ;
453- break ;
454- }
455- }
456- if (!progress ) {
457- // Insert padding byte and try again.
458- nextOffset ++;
448+ int beginOfFieldsOffset = nextOffset ;
449+
450+ /*
451+ * Sort and group fields so that fields marked @Contended(tag) are grouped by their tag,
452+ * placing unannotated fields first in the object, and so that within groups, a) all Object
453+ * fields are consecutive, and b) bigger types come first.
454+ */
455+ Object uncontendedSentinel = new Object ();
456+ Function <HostedField , Object > getAnnotationGroup = field -> Optional .ofNullable (field .getAnnotation (Contended .class )).<Object > map (Contended ::value ).orElse (uncontendedSentinel );
457+ Map <Object , ArrayList <HostedField >> contentionGroups = rawFields .stream ()
458+ .sorted (HostedUniverse .FIELD_COMPARATOR_RELAXED )
459+ .collect (Collectors .groupingBy (getAnnotationGroup , Collectors .toCollection (ArrayList ::new )));
460+
461+ ArrayList <HostedField > uncontendedFields = contentionGroups .remove (uncontendedSentinel );
462+ if (uncontendedFields != null ) {
463+ assert !uncontendedFields .isEmpty ();
464+ nextOffset = placeFields (uncontendedFields , nextOffset , orderedFields , layout );
465+ }
466+
467+ for (ArrayList <HostedField > groupFields : contentionGroups .values ()) {
468+ boolean placedFieldsBefore = (nextOffset != beginOfFieldsOffset );
469+ if (placedFieldsBefore || !hasLeadingPadding ) {
470+ nextOffset += getContendedPadding ();
459471 }
472+ nextOffset = placeFields (groupFields , nextOffset , orderedFields , layout );
473+ }
474+
475+ boolean fieldsTrailingPadding = !contentionGroups .isEmpty ();
476+ if (fieldsTrailingPadding ) {
477+ nextOffset += getContendedPadding ();
460478 }
461479
462480 int endOfFieldsOffset = nextOffset ;
@@ -465,8 +483,6 @@ private void layoutInstanceFields(HostedInstanceClass clazz, int superSize, Host
465483 * Compute the offsets of the "synthetic" fields for this class (but not subclasses).
466484 * Synthetic fields are put after all the instance fields. They are included in the instance
467485 * size, but not in the offset passed to subclasses.
468- *
469- * TODO: Should there be a list of synthetic fields for a class?
470486 */
471487
472488 // A reference to a {@link java.util.concurrent.locks.ReentrantLock for "synchronized" or
@@ -478,6 +494,11 @@ private void layoutInstanceFields(HostedInstanceClass clazz, int superSize, Host
478494 nextOffset += referenceFieldAlignmentAndSize ;
479495 }
480496
497+ boolean placedSyntheticFields = (nextOffset != endOfFieldsOffset );
498+ if (isClassContended && (contentionGroups .isEmpty () || placedSyntheticFields )) {
499+ nextOffset += getContendedPadding ();
500+ }
501+
481502 clazz .instanceFieldsWithoutSuper = orderedFields .toArray (new HostedField [orderedFields .size ()]);
482503 clazz .instanceSize = layout .alignUp (nextOffset );
483504 clazz .afterFieldsOffset = nextOffset ;
@@ -499,9 +520,40 @@ private void layoutInstanceFields(HostedInstanceClass clazz, int superSize, Host
499520 * possible because each class that needs a synthetic field gets its own synthetic
500521 * field at the end of its instance fields.
501522 */
502- layoutInstanceFields ((HostedInstanceClass ) subClass , endOfFieldsOffset , clazz .instanceFieldsWithSuper );
523+ layoutInstanceFields ((HostedInstanceClass ) subClass , endOfFieldsOffset , clazz .instanceFieldsWithSuper , fieldsTrailingPadding );
524+ }
525+ }
526+ }
527+
528+ private static int getContendedPadding () {
529+ Integer value = SubstrateOptions .ContendedPaddingWidth .getValue ();
530+ return (value > 0 ) ? value : 0 ; // no alignment required, placing fields takes care of it
531+ }
532+
533+ private static int placeFields (ArrayList <HostedField > fields , int firstOffset , ArrayList <HostedField > orderedFields , ObjectLayout layout ) {
534+ int nextOffset = firstOffset ;
535+ while (!fields .isEmpty ()) {
536+ boolean progress = false ;
537+ for (int i = 0 ; i < fields .size (); i ++) {
538+ HostedField field = fields .get (i );
539+ int fieldSize = layout .sizeInBytes (field .getStorageKind ());
540+
541+ if (nextOffset % fieldSize == 0 ) {
542+ field .setLocation (nextOffset );
543+ nextOffset += fieldSize ;
544+
545+ fields .remove (i );
546+ orderedFields .add (field );
547+ progress = true ;
548+ break ;
549+ }
550+ }
551+ if (!progress ) {
552+ // Insert padding byte and try again.
553+ nextOffset ++;
503554 }
504555 }
556+ return nextOffset ;
505557 }
506558
507559 private void layoutStaticFields () {
0 commit comments