From c24f5970307751aa1a93f241bdc14052a3d42109 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Thu, 2 Nov 2023 12:42:15 +0100 Subject: [PATCH 1/5] Move the identity hashcode from the object header into the object. --- .../HotSpotAllocationSnippets.java | 31 +++++-- .../replacements/AllocationSnippets.java | 26 ++---- .../core/genscavenge/ObjectHeaderImpl.java | 38 ++++---- .../oracle/svm/core/genscavenge/Space.java | 3 +- .../svm/core/SubstrateTargetDescription.java | 17 +--- .../oracle/svm/core/config/ObjectLayout.java | 80 ++++++++++++----- .../jdk/SubstrateObjectCloneSnippets.java | 55 +++++++----- .../snippets/SubstrateAllocationSnippets.java | 88 +++++++++++-------- .../com/oracle/svm/core/hub/DynamicHub.java | 25 +++--- .../src/com/oracle/svm/core/hub/Hybrid.java | 4 +- .../oracle/svm/core/hub/LayoutEncoding.java | 14 +-- .../IdentityHashCodeSupport.java | 21 +++-- .../SubstrateIdentityHashCodeNode.java | 20 ++--- .../SubstrateIdentityHashCodeSnippets.java | 55 ++++++------ .../svm/hosted/HostedConfiguration.java | 49 +++++------ .../svm/hosted/NativeImageGenerator.java | 10 +-- .../svm/hosted/meta/HostedInstanceClass.java | 2 + .../svm/hosted/meta/UniverseBuilder.java | 38 ++++---- 18 files changed, 316 insertions(+), 260 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replacements/HotSpotAllocationSnippets.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replacements/HotSpotAllocationSnippets.java index 3751cff94609..b0d563aa9647 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replacements/HotSpotAllocationSnippets.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replacements/HotSpotAllocationSnippets.java @@ -24,9 +24,6 @@ */ package jdk.graal.compiler.hotspot.replacements; -import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateRecompile; -import static jdk.vm.ci.meta.DeoptimizationAction.None; -import static jdk.vm.ci.meta.DeoptimizationReason.RuntimeConstraint; import static jdk.graal.compiler.core.common.GraalOptions.MinimalBulkZeroingSize; import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.NO_SIDE_EFFECT; import static jdk.graal.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_OPTIONVALUES; @@ -73,6 +70,12 @@ import static jdk.graal.compiler.replacements.ReplacementsUtil.staticAssert; import static jdk.graal.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER; import static jdk.graal.compiler.replacements.nodes.CStringConstant.cstring; +import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateRecompile; +import static jdk.vm.ci.meta.DeoptimizationAction.None; +import static jdk.vm.ci.meta.DeoptimizationReason.RuntimeConstraint; + +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.api.replacements.Fold.InjectedParameter; @@ -118,9 +121,6 @@ import jdk.graal.compiler.replacements.SnippetTemplate.Arguments; import jdk.graal.compiler.replacements.SnippetTemplate.SnippetInfo; import jdk.graal.compiler.word.Word; -import org.graalvm.word.UnsignedWord; -import org.graalvm.word.WordFactory; - import jdk.vm.ci.code.CodeUtil; import jdk.vm.ci.code.Register; import jdk.vm.ci.hotspot.HotSpotResolvedObjectType; @@ -437,13 +437,28 @@ protected final int instanceHeaderSize() { return HotSpotReplacementsUtil.instanceHeaderSize(INJECTED_VMCONFIG); } + @Override + protected UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize) { + int alignment = objectAlignment(); + return WordFactory.unsigned(arrayAllocationSize(length, arrayBaseOffset, log2ElementSize, alignment)); + } + + public static long arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize, int alignment) { + /* + * We do an unsigned multiplication so that a negative array length will result in an array + * size greater than Integer.MAX_VALUE. + */ + long size = ((length & 0xFFFFFFFFL) << log2ElementSize) + arrayBaseOffset + (alignment - 1); + long mask = ~(alignment - 1); + return size & mask; + } + @Override public final int arrayLengthOffset() { return HotSpotReplacementsUtil.arrayLengthOffset(INJECTED_VMCONFIG); } - @Override - protected final int objectAlignment() { + private static int objectAlignment() { return HotSpotReplacementsUtil.objectAlignment(INJECTED_VMCONFIG); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/AllocationSnippets.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/AllocationSnippets.java index 12154f7ebb33..dacd7ef8fd1d 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/AllocationSnippets.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/AllocationSnippets.java @@ -31,6 +31,10 @@ import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.probability; import static jdk.graal.compiler.replacements.nodes.ExplodeLoopNode.explodeLoop; +import org.graalvm.word.LocationIdentity; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + import jdk.graal.compiler.nodes.PrefetchAllocateNode; import jdk.graal.compiler.nodes.extended.MembarNode; import jdk.graal.compiler.nodes.memory.address.OffsetAddressNode; @@ -38,9 +42,6 @@ import jdk.graal.compiler.replacements.nodes.ExplodeLoopNode; import jdk.graal.compiler.replacements.nodes.ZeroMemoryNode; import jdk.graal.compiler.word.Word; -import org.graalvm.word.LocationIdentity; -import org.graalvm.word.UnsignedWord; -import org.graalvm.word.WordFactory; /** * Snippets used for implementing NEW, ANEWARRAY and NEWARRAY. @@ -114,21 +115,6 @@ protected Object newMultiArrayImpl(Word hub, int rank, int[] dimensions) { return callNewMultiArrayStub(hub, rank, dims); } - protected UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize) { - int alignment = objectAlignment(); - return WordFactory.unsigned(arrayAllocationSize(length, arrayBaseOffset, log2ElementSize, alignment)); - } - - /** - * We do an unsigned multiplication so that a negative array length will result in an array size - * greater than Integer.MAX_VALUE. - */ - public static long arrayAllocationSize(long length, int arrayBaseOffset, int log2ElementSize, int alignment) { - long size = ((length & 0xFFFFFFFFL) << log2ElementSize) + arrayBaseOffset + (alignment - 1); - long mask = ~(alignment - 1); - return size & mask; - } - /** * Maximum number of long stores to emit when zeroing an object with a constant size. Larger * objects have their bodies initialized in a loop. @@ -354,6 +340,8 @@ public void emitPrefetchAllocate(Word address, boolean isArray) { protected abstract int instanceHeaderSize(); + protected abstract UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize); + public abstract void initializeObjectHeader(Word memory, Word hub, boolean isArray); protected abstract Object callNewInstanceStub(Word hub); @@ -370,8 +358,6 @@ public void emitPrefetchAllocate(Word address, boolean isArray) { public abstract int arrayLengthOffset(); - protected abstract int objectAlignment(); - public enum FillContent { DO_NOT_FILL, WITH_ZEROES, diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java index 0582a8a18509..fc054e060ab1 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java @@ -24,11 +24,6 @@ */ package com.oracle.svm.core.genscavenge; -import jdk.graal.compiler.api.directives.GraalDirectives; -import jdk.graal.compiler.api.replacements.Fold; -import jdk.graal.compiler.replacements.ReplacementsUtil; -import jdk.graal.compiler.word.ObjectAccess; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.LocationIdentity; @@ -51,6 +46,11 @@ import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.api.directives.GraalDirectives; +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.replacements.ReplacementsUtil; +import jdk.graal.compiler.word.ObjectAccess; +import jdk.graal.compiler.word.Word; import jdk.vm.ci.code.CodeUtil; /** @@ -90,13 +90,13 @@ public final class ObjectHeaderImpl extends ObjectHeader { numAlignmentBits = CodeUtil.log2(ConfigurationValues.getObjectLayout().getAlignment()); int numMinimumReservedBits = 3; VMError.guarantee(numMinimumReservedBits <= numAlignmentBits, "Minimum set of reserved bits must be provided by object alignment"); - if (hasFixedIdentityHashField()) { - numReservedBits = numMinimumReservedBits; - } else { + if (isIdentityHasFieldOptional()) { VMError.guarantee(ReferenceAccess.singleton().haveCompressedReferences(), "Ensures hubs (at the start of the image heap) remain addressable"); numReservedBits = numMinimumReservedBits + 2; VMError.guarantee(numReservedBits <= numAlignmentBits || hasShift(), "With no shift, forwarding references are stored directly in the header (with 64-bit, must be) and we cannot use non-alignment header bits"); + } else { + numReservedBits = numMinimumReservedBits; } numReservedExtraBits = numReservedBits - numAlignmentBits; reservedBitsMask = (1 << numReservedBits) - 1; @@ -158,9 +158,9 @@ public void initializeHeaderOfNewObject(Pointer objectPointer, Word encodedHub) @Override public boolean hasOptionalIdentityHashField(Word header) { if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.staticAssert(!hasFixedIdentityHashField(), "use only when fields are not fixed"); + ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "use only when optional hashcode fields are support"); } else { - VMError.guarantee(!hasFixedIdentityHashField(), "use only when fields are not fixed"); + VMError.guarantee(isIdentityHasFieldOptional(), "use only when optional hashcode fields are support"); } UnsignedWord inFieldState = IDHASH_STATE_IN_FIELD.shiftLeft(IDHASH_STATE_SHIFT); return header.and(IDHASH_STATE_BITS).equal(inFieldState); @@ -169,7 +169,7 @@ public boolean hasOptionalIdentityHashField(Word header) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) void setIdentityHashInField(Object o) { assert VMOperation.isGCInProgress(); - VMError.guarantee(!hasFixedIdentityHashField()); + VMError.guarantee(isIdentityHasFieldOptional()); UnsignedWord oldHeader = readHeaderFromObject(o); UnsignedWord inFieldState = IDHASH_STATE_IN_FIELD.shiftLeft(IDHASH_STATE_SHIFT); UnsignedWord newHeader = oldHeader.and(IDHASH_STATE_BITS.not()).or(inFieldState); @@ -195,9 +195,9 @@ void setIdentityHashInField(Object o) { @Override public void setIdentityHashFromAddress(Pointer ptr, Word currentHeader) { if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.staticAssert(!hasFixedIdentityHashField(), "must always access field"); + ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "must always access field"); } else { - VMError.guarantee(!hasFixedIdentityHashField()); + VMError.guarantee(isIdentityHasFieldOptional()); assert !hasIdentityHashFromAddress(currentHeader); } UnsignedWord fromAddressState = IDHASH_STATE_FROM_ADDRESS.shiftLeft(IDHASH_STATE_SHIFT); @@ -217,8 +217,10 @@ public boolean hasIdentityHashFromAddress(Word header) { @AlwaysInline("GC performance") @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) static boolean hasIdentityHashFromAddressInline(Word header) { - if (hasFixedIdentityHashField()) { - return false; + if (GraalDirectives.inIntrinsic()) { + ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "must always access field"); + } else { + assert isIdentityHasFieldOptional(); } UnsignedWord fromAddressState = IDHASH_STATE_FROM_ADDRESS.shiftLeft(IDHASH_STATE_SHIFT); return header.and(IDHASH_STATE_BITS).equal(fromAddressState); @@ -309,7 +311,7 @@ public long encodeAsImageHeapObjectHeader(ImageHeapObject obj, long hubOffsetFro assert obj.getPartition() instanceof FillerObjectDummyPartition; } } - if (!hasFixedIdentityHashField()) { + if (isIdentityHasFieldOptional()) { header |= (IDHASH_STATE_IN_FIELD.rawValue() << IDHASH_STATE_SHIFT); } return header; @@ -427,7 +429,7 @@ static boolean hasShift() { } @Fold - static boolean hasFixedIdentityHashField() { - return ConfigurationValues.getObjectLayout().hasFixedIdentityHashField(); + static boolean isIdentityHasFieldOptional() { + return ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional(); } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java index 29b6f24cb1e5..7847560cfa29 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java @@ -28,7 +28,6 @@ import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.VERY_SLOW_PATH_PROBABILITY; import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.probability; -import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; @@ -399,7 +398,7 @@ private Object copyAlignedObject(Object originalObj) { UnsignedWord originalSize = LayoutEncoding.getSizeFromObjectInlineInGC(originalObj, false); UnsignedWord copySize = originalSize; boolean addIdentityHashField = false; - if (!ConfigurationValues.getObjectLayout().hasFixedIdentityHashField()) { + if (ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional()) { Word header = ObjectHeader.readHeaderFromObject(originalObj); if (probability(SLOW_PATH_PROBABILITY, ObjectHeaderImpl.hasIdentityHashFromAddressInline(header))) { addIdentityHashField = true; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateTargetDescription.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateTargetDescription.java index 0ce0f4a13549..07d078a1b665 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateTargetDescription.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateTargetDescription.java @@ -24,17 +24,16 @@ */ package com.oracle.svm.core; +import java.util.EnumSet; + import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.code.RuntimeCodeCache; -import com.oracle.svm.core.deopt.DeoptimizedFrame; import jdk.vm.ci.code.Architecture; import jdk.vm.ci.code.TargetDescription; -import java.util.EnumSet; - public class SubstrateTargetDescription extends TargetDescription { @Platforms(Platform.HOSTED_ONLY.class) public static boolean shouldInlineObjectsInImageCode() { @@ -45,24 +44,14 @@ public static boolean shouldInlineObjectsInRuntimeCode() { return SubstrateOptions.SpawnIsolates.getValue() && RuntimeCodeCache.Options.WriteableCodeCache.getValue(); } - private final int deoptScratchSpace; private final EnumSet runtimeCheckedCPUFeatures; @Platforms(Platform.HOSTED_ONLY.class) - public SubstrateTargetDescription(Architecture arch, boolean isMP, int stackAlignment, int implicitNullCheckLimit, int deoptScratchSpace, EnumSet runtimeCheckedCPUFeatures) { + public SubstrateTargetDescription(Architecture arch, boolean isMP, int stackAlignment, int implicitNullCheckLimit, EnumSet runtimeCheckedCPUFeatures) { super(arch, isMP, stackAlignment, implicitNullCheckLimit, shouldInlineObjectsInImageCode()); - this.deoptScratchSpace = deoptScratchSpace; this.runtimeCheckedCPUFeatures = runtimeCheckedCPUFeatures; } - /** - * Returns the amount of scratch space which must be reserved for return value registers in - * {@link DeoptimizedFrame}. - */ - public int getDeoptScratchSpace() { - return deoptScratchSpace; - } - public EnumSet getRuntimeCheckedCPUFeatures() { return runtimeCheckedCPUFeatures; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java index f8ef0f3122ff..2db72329e7f2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java @@ -25,12 +25,13 @@ package com.oracle.svm.core.config; import org.graalvm.nativeimage.AnnotationAccess; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.constant.CEnum; import org.graalvm.word.WordBase; import com.oracle.svm.core.SubstrateTargetDescription; import com.oracle.svm.core.Uninterruptible; -import com.oracle.svm.core.deopt.DeoptimizedFrame; import jdk.graal.compiler.api.directives.GraalDirectives; import jdk.graal.compiler.core.common.NumUtil; @@ -43,6 +44,16 @@ /** * Immutable class that holds all sizes and offsets that contribute to the object layout. + * + * Identity hashcode fields can either be: + *
    + *
  1. In the object header, at a fixed offset for all objects (see + * {@link #hasFixedIdentityHashField()}).
  2. + *
  3. In a synthetic field (outside the object header), at a type or object specific offset (see + * {@link #isIdentityHashFieldSynthetic()}).
  4. + *
  5. Outside the object header, at a type and object state specific offset (see + * {@link #isIdentityHashFieldOptional()}).
  6. + *
*/ public final class ObjectLayout { @@ -55,14 +66,20 @@ public final class ObjectLayout { private final int arrayLengthOffset; private final int arrayBaseOffset; private final int fixedIdentityHashOffset; + private final boolean isIdentityHashFieldSynthetic; + private final boolean isIdentityHashFieldOptional; - public ObjectLayout(SubstrateTargetDescription target, int referenceSize, int objectAlignment, int hubOffset, - int firstFieldOffset, int arrayLengthOffset, int arrayBaseOffset, int fixedIdentityHashOffset) { + public ObjectLayout(SubstrateTargetDescription target, int referenceSize, int objectAlignment, int hubOffset, int firstFieldOffset, int arrayLengthOffset, int arrayBaseOffset, + int fixedIdentityHashOffset, IdentityHashCodePosition identityHashCodePosition) { assert CodeUtil.isPowerOf2(referenceSize) : referenceSize; assert CodeUtil.isPowerOf2(objectAlignment) : objectAlignment; assert arrayLengthOffset % Integer.BYTES == 0; assert hubOffset < firstFieldOffset && hubOffset < arrayLengthOffset : hubOffset; - assert fixedIdentityHashOffset == -1 || (fixedIdentityHashOffset > 0 && fixedIdentityHashOffset < arrayLengthOffset) : fixedIdentityHashOffset; + assert identityHashCodePosition == IdentityHashCodePosition.OBJECT_HEADER || identityHashCodePosition == IdentityHashCodePosition.SYNTHETIC_FIELD || + identityHashCodePosition == IdentityHashCodePosition.OPTIONAL; + assert (identityHashCodePosition == IdentityHashCodePosition.OBJECT_HEADER && fixedIdentityHashOffset > 0 && fixedIdentityHashOffset < arrayLengthOffset && + fixedIdentityHashOffset % Integer.BYTES == 0) || + (identityHashCodePosition != IdentityHashCodePosition.OBJECT_HEADER && fixedIdentityHashOffset == -1); this.target = target; this.referenceSize = referenceSize; @@ -73,6 +90,8 @@ public ObjectLayout(SubstrateTargetDescription target, int referenceSize, int ob this.arrayLengthOffset = arrayLengthOffset; this.arrayBaseOffset = arrayBaseOffset; this.fixedIdentityHashOffset = fixedIdentityHashOffset; + this.isIdentityHashFieldSynthetic = identityHashCodePosition == IdentityHashCodePosition.SYNTHETIC_FIELD; + this.isIdentityHashFieldOptional = identityHashCodePosition == IdentityHashCodePosition.OPTIONAL; } /** The minimum alignment of objects (instances and arrays). */ @@ -93,14 +112,7 @@ public int getReferenceSize() { } /** - * Returns the amount of scratch space which must be reserved for return value registers in - * {@link DeoptimizedFrame}. - */ - public int getDeoptScratchSpace() { - return target.getDeoptScratchSpace(); - } - - /** + * * The size (in bytes) of values with the given kind. */ public int sizeInBytes(JavaKind kind) { @@ -151,32 +163,43 @@ public int getArrayLengthOffset() { return arrayLengthOffset; } - /** If {@link #hasFixedIdentityHashField()}, then returns that offset. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public int getFixedIdentityHashOffset() { - if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.dynamicAssert(hasFixedIdentityHashField(), "must check before calling"); - } else { - assert hasFixedIdentityHashField(); - } - return fixedIdentityHashOffset; + public boolean isIdentityHashFieldOptional() { + return isIdentityHashFieldOptional; + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public boolean isIdentityHashFieldSynthetic() { + return isIdentityHashFieldSynthetic; } /** * Indicates whether all objects, including arrays, always contain an identity hash code field - * at a specific offset. + * at a specific offset in the object header. */ + // TEMP (chaeubl): we could rename that method (isIdentityHashFieldInObjectHeader) @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public boolean hasFixedIdentityHashField() { return fixedIdentityHashOffset >= 0; } + /** If {@link #hasFixedIdentityHashField()}, then returns that offset. */ + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public int getFixedIdentityHashOffset() { + if (GraalDirectives.inIntrinsic()) { + ReplacementsUtil.dynamicAssert(hasFixedIdentityHashField(), "must check before calling"); + } else { + assert hasFixedIdentityHashField(); + } + return fixedIdentityHashOffset; + } + public int getArrayBaseOffset(JavaKind kind) { return NumUtil.roundUp(arrayBaseOffset, sizeInBytes(kind)); } public long getArrayElementOffset(JavaKind kind, int index) { - return getArrayBaseOffset(kind) + index * sizeInBytes(kind); + return getArrayBaseOffset(kind) + ((long) index) * sizeInBytes(kind); } public long getArraySize(JavaKind kind, int length, boolean withOptionalIdHashField) { @@ -192,6 +215,7 @@ public long getArrayOptionalIdentityHashOffset(JavaKind kind, int length) { return getArrayOptionalIdentityHashOffset(getArrayUnalignedSize(kind, length)); } + // TEMP (chaeubl): we should probably rename this method. @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public long getArrayOptionalIdentityHashOffset(long unalignedSize) { if (hasFixedIdentityHashField()) { @@ -204,7 +228,7 @@ public long getArrayOptionalIdentityHashOffset(long unalignedSize) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public long computeArrayTotalSize(long unalignedSize, boolean withOptionalIdHashField) { long size = unalignedSize; - if (withOptionalIdHashField && !hasFixedIdentityHashField()) { + if ((withOptionalIdHashField && isIdentityHashFieldOptional()) || isIdentityHashFieldSynthetic()) { size = getArrayOptionalIdentityHashOffset(size) + Integer.BYTES; } return alignUp(size); @@ -236,4 +260,14 @@ public static JavaKind getCallSignatureKind(boolean isEntryPoint, ResolvedJavaTy } return type.getJavaKind(); } + + @Platforms(Platform.HOSTED_ONLY.class) + public enum IdentityHashCodePosition { + /* At a fixed offset, for all objects (part of the object header). */ + OBJECT_HEADER, + /* At a type-specific offset (outside the object header). */ + SYNTHETIC_FIELD, + /* At a type and object-state specific offset (outside the object header). */ + OPTIONAL + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java index 2cc0e536dc85..6f8e15b00f4c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java @@ -30,30 +30,6 @@ import java.util.Map; -import jdk.graal.compiler.api.replacements.Snippet; -import jdk.graal.compiler.core.common.NumUtil; -import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor; -import jdk.graal.compiler.graph.Node; -import jdk.graal.compiler.graph.Node.ConstantNodeParameter; -import jdk.graal.compiler.graph.Node.NodeIntrinsic; -import jdk.graal.compiler.nodes.GraphState; -import jdk.graal.compiler.nodes.NodeView; -import jdk.graal.compiler.nodes.StructuredGraph; -import jdk.graal.compiler.nodes.ValueNode; -import jdk.graal.compiler.nodes.extended.BranchProbabilityNode; -import jdk.graal.compiler.nodes.extended.ForeignCallNode; -import jdk.graal.compiler.nodes.java.ArrayLengthNode; -import jdk.graal.compiler.nodes.spi.LoweringTool; -import jdk.graal.compiler.nodes.spi.VirtualizerTool; -import jdk.graal.compiler.nodes.virtual.VirtualObjectNode; -import jdk.graal.compiler.options.OptionValues; -import jdk.graal.compiler.phases.util.Providers; -import jdk.graal.compiler.replacements.SnippetTemplate; -import jdk.graal.compiler.replacements.SnippetTemplate.Arguments; -import jdk.graal.compiler.replacements.SnippetTemplate.SnippetInfo; -import jdk.graal.compiler.replacements.Snippets; -import jdk.graal.compiler.replacements.nodes.ObjectClone; -import jdk.graal.compiler.word.BarrieredAccess; import org.graalvm.word.LocationIdentity; import org.graalvm.word.WordFactory; @@ -79,6 +55,31 @@ import com.oracle.svm.core.util.UnsignedUtils; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.api.replacements.Snippet; +import jdk.graal.compiler.core.common.NumUtil; +import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor; +import jdk.graal.compiler.graph.Node; +import jdk.graal.compiler.graph.Node.ConstantNodeParameter; +import jdk.graal.compiler.graph.Node.NodeIntrinsic; +import jdk.graal.compiler.nodes.GraphState; +import jdk.graal.compiler.nodes.NodeView; +import jdk.graal.compiler.nodes.StructuredGraph; +import jdk.graal.compiler.nodes.ValueNode; +import jdk.graal.compiler.nodes.extended.BranchProbabilityNode; +import jdk.graal.compiler.nodes.extended.ForeignCallNode; +import jdk.graal.compiler.nodes.java.ArrayLengthNode; +import jdk.graal.compiler.nodes.spi.LoweringTool; +import jdk.graal.compiler.nodes.spi.VirtualizerTool; +import jdk.graal.compiler.nodes.virtual.VirtualObjectNode; +import jdk.graal.compiler.options.OptionValues; +import jdk.graal.compiler.phases.util.Providers; +import jdk.graal.compiler.replacements.SnippetTemplate; +import jdk.graal.compiler.replacements.SnippetTemplate.Arguments; +import jdk.graal.compiler.replacements.SnippetTemplate.SnippetInfo; +import jdk.graal.compiler.replacements.Snippets; +import jdk.graal.compiler.replacements.nodes.ObjectClone; +import jdk.graal.compiler.word.BarrieredAccess; +import jdk.graal.compiler.word.ObjectAccess; import jdk.internal.misc.Unsafe; import jdk.vm.ci.meta.ResolvedJavaType; @@ -171,6 +172,12 @@ private static Object doClone(Object original) throws CloneNotSupportedException BarrieredAccess.writeObject(result, monitorOffset, null); } + /* Reset identity hashcode if we use a synthetic field. */ + if (ConfigurationValues.getObjectLayout().isIdentityHashFieldSynthetic()) { + int offset = LayoutEncoding.getOptionalIdentityHashOffset(result); + ObjectAccess.writeInt(result, offset, 0); + } + return result; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java index a852b1dca410..59069a1870e1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java @@ -36,6 +36,36 @@ import java.util.Arrays; import java.util.Map; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.word.LocationIdentity; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.MissingRegistrationUtils; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.allocationprofile.AllocationCounter; +import com.oracle.svm.core.allocationprofile.AllocationSite; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.configure.ConfigurationFile; +import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; +import com.oracle.svm.core.graal.nodes.ForeignCallWithExceptionNode; +import com.oracle.svm.core.graal.nodes.NewPodInstanceNode; +import com.oracle.svm.core.graal.nodes.NewStoredContinuationNode; +import com.oracle.svm.core.graal.nodes.SubstrateNewHybridInstanceNode; +import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.heap.Pod; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.LayoutEncoding; +import com.oracle.svm.core.identityhashcode.IdentityHashCodeSupport; +import com.oracle.svm.core.meta.SharedType; +import com.oracle.svm.core.option.HostedOptionValues; +import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils; +import com.oracle.svm.core.snippets.SnippetRuntime; +import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor; +import com.oracle.svm.core.snippets.SubstrateForeignCallTarget; +import com.oracle.svm.core.thread.ContinuationSupport; +import com.oracle.svm.core.util.VMError; + import jdk.graal.compiler.api.directives.GraalDirectives; import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.api.replacements.Snippet; @@ -76,36 +106,6 @@ import jdk.graal.compiler.word.BarrieredAccess; import jdk.graal.compiler.word.ObjectAccess; import jdk.graal.compiler.word.Word; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.word.LocationIdentity; -import org.graalvm.word.UnsignedWord; -import org.graalvm.word.WordFactory; - -import com.oracle.svm.core.MissingRegistrationUtils; -import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.allocationprofile.AllocationCounter; -import com.oracle.svm.core.allocationprofile.AllocationSite; -import com.oracle.svm.core.config.ConfigurationValues; -import com.oracle.svm.core.configure.ConfigurationFile; -import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; -import com.oracle.svm.core.graal.nodes.ForeignCallWithExceptionNode; -import com.oracle.svm.core.graal.nodes.NewPodInstanceNode; -import com.oracle.svm.core.graal.nodes.NewStoredContinuationNode; -import com.oracle.svm.core.graal.nodes.SubstrateNewHybridInstanceNode; -import com.oracle.svm.core.heap.Heap; -import com.oracle.svm.core.heap.Pod; -import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.hub.LayoutEncoding; -import com.oracle.svm.core.identityhashcode.IdentityHashCodeSupport; -import com.oracle.svm.core.meta.SharedType; -import com.oracle.svm.core.option.HostedOptionValues; -import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils; -import com.oracle.svm.core.snippets.SnippetRuntime; -import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor; -import com.oracle.svm.core.snippets.SubstrateForeignCallTarget; -import com.oracle.svm.core.thread.ContinuationSupport; -import com.oracle.svm.core.util.VMError; - import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaType; @@ -420,6 +420,25 @@ protected final int instanceHeaderSize() { return ConfigurationValues.getObjectLayout().getFirstFieldOffset(); } + @Override + protected UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize) { + /* + * We do an unsigned multiplication so that a negative array length will result in an array + * size greater than Integer.MAX_VALUE. + */ + long size = ((length & 0xFFFFFFFFL) << log2ElementSize) + arrayBaseOffset; + + /* Add the identity hashcode field if necessary. */ + if (ConfigurationValues.getObjectLayout().isIdentityHashFieldSynthetic()) { + int align = Integer.BYTES; + size = (size + align - 1) & -align; + size += Integer.BYTES; + } + + size = (size + objectAlignment() - 1) & -objectAlignment(); + return WordFactory.unsigned(size); + } + @Fold public static int afterArrayLengthOffset() { return ConfigurationValues.getObjectLayout().getArrayLengthOffset() + ConfigurationValues.getObjectLayout().sizeInBytes(JavaKind.Int); @@ -446,14 +465,13 @@ protected final Object verifyOop(Object obj) { return obj; } - @Override - public final int arrayLengthOffset() { - return ConfigurationValues.getObjectLayout().getArrayLengthOffset(); + private static int objectAlignment() { + return ConfigurationValues.getObjectLayout().getAlignment(); } @Override - protected final int objectAlignment() { - return ConfigurationValues.getObjectLayout().getAlignment(); + public final int arrayLengthOffset() { + return ConfigurationValues.getObjectLayout().getArrayLengthOffset(); } public static int getArrayBaseOffset(int layoutEncoding) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index 601815a36bb9..ba5938c6fcc9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -116,9 +116,11 @@ import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError; +import jdk.graal.compiler.api.directives.GraalDirectives; import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.core.common.NumUtil; import jdk.graal.compiler.core.common.SuppressFBWarnings; +import jdk.graal.compiler.replacements.ReplacementsUtil; import jdk.internal.access.JavaLangReflectAccess; import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; @@ -206,6 +208,7 @@ public final class DynamicHub implements AnnotatedElement, java.lang.reflect.Typ @UnknownPrimitiveField(availability = AfterHostedUniverse.class)// private short monitorOffset; + // TEMP (chaeubl): rename this field @UnknownPrimitiveField(availability = AfterHostedUniverse.class)// private short optionalIdentityHashOffset; @@ -447,22 +450,16 @@ public void setClassInitializationInfo(ClassInitializationInfo classInitializati } @Platforms(Platform.HOSTED_ONLY.class) - public void setData(int layoutEncoding, int typeID, int monitorOffset, int optionalIdentityHashOffset, short typeCheckStart, short typeCheckRange, short typeCheckSlot, + public void setData(int layoutEncoding, int typeID, int monitorOffset, int identityHashOffset, short typeCheckStart, short typeCheckRange, short typeCheckSlot, short[] typeCheckSlots, CFunctionPointer[] vtable, long referenceMapIndex, boolean isInstantiated, boolean canInstantiateAsInstance, boolean isProxyClass, boolean isRegisteredForSerialization) { assert this.vtable == null : "Initialization must be called only once"; assert !(!isInstantiated && canInstantiateAsInstance); - if (LayoutEncoding.isPureInstance(layoutEncoding)) { - ObjectLayout ol = ConfigurationValues.getObjectLayout(); - assert ol.hasFixedIdentityHashField() ? (optionalIdentityHashOffset == ol.getFixedIdentityHashOffset()) : (optionalIdentityHashOffset > 0); - } else { - assert optionalIdentityHashOffset == -1; - } this.layoutEncoding = layoutEncoding; this.typeID = typeID; this.monitorOffset = NumUtil.safeToShort(monitorOffset); - this.optionalIdentityHashOffset = NumUtil.safeToShort(optionalIdentityHashOffset); + this.optionalIdentityHashOffset = NumUtil.safeToShort(identityHashOffset); this.typeCheckStart = typeCheckStart; this.typeCheckRange = typeCheckRange; this.typeCheckSlot = typeCheckSlot; @@ -634,13 +631,21 @@ public int getMonitorOffset() { return monitorOffset; } + // TEMP (chaeubl): should we rename this method as well? @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - int getOptionalIdentityHashOffset() { + public int getOptionalIdentityHashOffset() { ObjectLayout ol = ConfigurationValues.getObjectLayout(); if (ol.hasFixedIdentityHashField()) { // enable elimination of our field return ol.getFixedIdentityHashOffset(); } - return optionalIdentityHashOffset; + + int result = optionalIdentityHashOffset; + if (GraalDirectives.inIntrinsic()) { + ReplacementsUtil.dynamicAssert(result > 0, "must have an identity hash field"); + } else { + assert result > 0 : "must have an identity hash field"; + } + return result; } public DynamicHub getSuperHub() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/Hybrid.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/Hybrid.java index 5ba011451d5e..5e4f66e0c4c8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/Hybrid.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/Hybrid.java @@ -37,9 +37,7 @@ * *
  *    +--------------------------------------------------+
- *    | pointer to DynamicHub                            |
- *    +--------------------------------------------------+
- *    | identity hashcode                                |
+ *    | object header (see HostedConfiguration)          |
  *    +--------------------------------------------------+
  *    | array length                                     |
  *    +--------------------------------------------------+
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java
index bd57b4339266..d3731d63c52b 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java
@@ -24,9 +24,6 @@
  */
 package com.oracle.svm.core.hub;
 
-import jdk.graal.compiler.core.common.calc.UnsignedMath;
-import jdk.graal.compiler.nodes.java.ArrayLengthNode;
-import jdk.graal.compiler.word.Word;
 import org.graalvm.nativeimage.Platform;
 import org.graalvm.nativeimage.Platforms;
 import org.graalvm.word.Pointer;
@@ -43,6 +40,9 @@
 import com.oracle.svm.core.util.DuplicatedInNativeCode;
 import com.oracle.svm.core.util.VMError;
 
+import jdk.graal.compiler.core.common.calc.UnsignedMath;
+import jdk.graal.compiler.nodes.java.ArrayLengthNode;
+import jdk.graal.compiler.word.Word;
 import jdk.vm.ci.meta.ResolvedJavaType;
 
 /**
@@ -201,7 +201,7 @@ public static UnsignedWord getPureInstanceAllocationSize(int encoding) {
     public static UnsignedWord getPureInstanceSize(DynamicHub hub, boolean withOptionalIdHashField) {
         UnsignedWord size = getPureInstanceAllocationSize(hub.getLayoutEncoding());
         ObjectLayout ol = ConfigurationValues.getObjectLayout();
-        if (withOptionalIdHashField && !ol.hasFixedIdentityHashField()) {
+        if (withOptionalIdHashField && ol.isIdentityHashFieldOptional()) {
             int afterIdHashField = hub.getOptionalIdentityHashOffset() + Integer.BYTES;
             if (size.belowThan(afterIdHashField)) { // fits in a gap between fields
                 size = WordFactory.unsigned(ol.alignUp(afterIdHashField));
@@ -294,12 +294,14 @@ public static UnsignedWord getArraySize(int encoding, int length, boolean withOp
         return WordFactory.unsigned(totalSize);
     }
 
+    // TEMP (chaeubl): this should be renamed to getIdentityHashOffset?
     @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
     public static int getOptionalIdentityHashOffset(Object obj) {
         ObjectLayout ol = ConfigurationValues.getObjectLayout();
         if (ol.hasFixedIdentityHashField()) {
             return ol.getFixedIdentityHashOffset();
         }
+
         DynamicHub hub = KnownIntrinsics.readHub(obj);
         int encoding = hub.getLayoutEncoding();
         if (isArrayLike(encoding)) {
@@ -312,7 +314,7 @@ public static int getOptionalIdentityHashOffset(Object obj) {
 
     @Uninterruptible(reason = "Prevent a GC moving the object or interfering with its identity hash state.", callerMustBe = true)
     public static UnsignedWord getSizeFromObject(Object obj) {
-        boolean withOptionalIdHashField = !ConfigurationValues.getObjectLayout().hasFixedIdentityHashField() && checkOptionalIdentityHashField(obj);
+        boolean withOptionalIdHashField = ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional() && checkOptionalIdentityHashField(obj);
         return getSizeFromObjectInline(obj, withOptionalIdHashField);
     }
 
@@ -344,7 +346,7 @@ public static UnsignedWord getSizeFromObjectInlineInGC(Object obj) {
     @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
     public static UnsignedWord getSizeFromObjectInlineInGC(Object obj, boolean addOptionalIdHashField) {
         boolean withOptionalIdHashField = addOptionalIdHashField ||
-                        (!ConfigurationValues.getObjectLayout().hasFixedIdentityHashField() && checkOptionalIdentityHashField(obj));
+                        (ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional() && checkOptionalIdentityHashField(obj));
         return getSizeFromObjectInline(obj, withOptionalIdHashField);
     }
 
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java
index 549e28ef4110..abb8fcf1f4d4 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java
@@ -26,12 +26,6 @@
 
 import java.util.SplittableRandom;
 
-import jdk.graal.compiler.nodes.NamedLocationIdentity;
-import jdk.graal.compiler.options.OptionValues;
-import jdk.graal.compiler.phases.util.Providers;
-import jdk.graal.compiler.replacements.IdentityHashCodeSnippets;
-import jdk.graal.compiler.word.ObjectAccess;
-import jdk.graal.compiler.word.Word;
 import org.graalvm.word.LocationIdentity;
 import org.graalvm.word.SignedWord;
 import org.graalvm.word.WordFactory;
@@ -40,11 +34,18 @@
 import com.oracle.svm.core.config.ConfigurationValues;
 import com.oracle.svm.core.config.ObjectLayout;
 import com.oracle.svm.core.heap.Heap;
+import com.oracle.svm.core.hub.LayoutEncoding;
 import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
 import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
 import com.oracle.svm.core.threadlocal.FastThreadLocalObject;
 import com.oracle.svm.core.util.VMError;
 
+import jdk.graal.compiler.nodes.NamedLocationIdentity;
+import jdk.graal.compiler.options.OptionValues;
+import jdk.graal.compiler.phases.util.Providers;
+import jdk.graal.compiler.replacements.IdentityHashCodeSnippets;
+import jdk.graal.compiler.word.ObjectAccess;
+import jdk.graal.compiler.word.Word;
 import jdk.internal.misc.Unsafe;
 
 public final class IdentityHashCodeSupport {
@@ -74,10 +75,12 @@ public static IdentityHashCodeSnippets.Templates createSnippetTemplates(OptionVa
     @SubstrateForeignCallTarget(stubCallingConvention = false)
     public static int generateIdentityHashCode(Object obj) {
         ObjectLayout ol = ConfigurationValues.getObjectLayout();
-        VMError.guarantee(ol.hasFixedIdentityHashField(), "Snippet must handle other cases");
+        VMError.guarantee(!ol.isIdentityHashFieldOptional(), "Snippet must handle this case");
+
         int newHashCode = generateRandomHashCode();
-        if (!Unsafe.getUnsafe().compareAndSetInt(obj, ol.getFixedIdentityHashOffset(), 0, newHashCode)) {
-            newHashCode = ObjectAccess.readInt(obj, ol.getFixedIdentityHashOffset(), IDENTITY_HASHCODE_LOCATION);
+        int offset = LayoutEncoding.getOptionalIdentityHashOffset(obj);
+        if (!Unsafe.getUnsafe().compareAndSetInt(obj, offset, 0, newHashCode)) {
+            newHashCode = ObjectAccess.readInt(obj, offset, IDENTITY_HASHCODE_LOCATION);
         }
         VMError.guarantee(newHashCode != 0, "Missing identity hash code");
         return newHashCode;
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeNode.java
index e35a3c0844ab..6edfa7fba426 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeNode.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeNode.java
@@ -24,6 +24,10 @@
  */
 package com.oracle.svm.core.identityhashcode;
 
+import org.graalvm.word.LocationIdentity;
+
+import com.oracle.svm.core.config.ConfigurationValues;
+
 import jdk.graal.compiler.api.replacements.Fold;
 import jdk.graal.compiler.core.common.type.TypedConstant;
 import jdk.graal.compiler.graph.NodeClass;
@@ -32,10 +36,6 @@
 import jdk.graal.compiler.nodeinfo.NodeSize;
 import jdk.graal.compiler.nodes.ValueNode;
 import jdk.graal.compiler.replacements.nodes.IdentityHashCodeNode;
-import org.graalvm.word.LocationIdentity;
-
-import com.oracle.svm.core.config.ConfigurationValues;
-
 import jdk.vm.ci.meta.JavaConstant;
 
 @NodeInfo(cycles = NodeCycles.CYCLES_UNKNOWN, cyclesRationale = "Decided depending on identity hash code storage.", //
@@ -58,8 +58,8 @@ protected SubstrateIdentityHashCodeNode(ValueNode object, int bci) {
 
     @Override
     public LocationIdentity getKilledLocationIdentity() {
-        // Without a fixed field, we must write bits in the object header.
-        return haveFixedField() ? IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION : LocationIdentity.any();
+        // With optional identity hash codes, we must write bits in the object header.
+        return supportsOptionalIdentityHashField() ? LocationIdentity.any() : IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION;
     }
 
     @Override
@@ -69,16 +69,16 @@ protected int getIdentityHashCode(JavaConstant constant) {
 
     @Override
     public NodeCycles estimatedNodeCycles() {
-        return haveFixedField() ? NodeCycles.CYCLES_2 : NodeCycles.CYCLES_8;
+        return supportsOptionalIdentityHashField() ? NodeCycles.CYCLES_8 : NodeCycles.CYCLES_2;
     }
 
     @Override
     protected NodeSize dynamicNodeSizeEstimate() {
-        return haveFixedField() ? NodeSize.SIZE_8 : NodeSize.SIZE_32;
+        return supportsOptionalIdentityHashField() ? NodeSize.SIZE_32 : NodeSize.SIZE_8;
     }
 
     @Fold
-    static boolean haveFixedField() {
-        return ConfigurationValues.getObjectLayout().hasFixedIdentityHashField();
+    static boolean supportsOptionalIdentityHashField() {
+        return ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional();
     }
 }
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeSnippets.java
index db7ed4d4cb40..37523767027c 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeSnippets.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeSnippets.java
@@ -24,12 +24,20 @@
  */
 package com.oracle.svm.core.identityhashcode;
 
+import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.NO_SIDE_EFFECT;
 import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.LIKELY_PROBABILITY;
 import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.NOT_FREQUENT_PROBABILITY;
 import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.SLOW_PATH_PROBABILITY;
-import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.NO_SIDE_EFFECT;
 import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.probability;
 
+import com.oracle.svm.core.config.ConfigurationValues;
+import com.oracle.svm.core.config.ObjectLayout;
+import com.oracle.svm.core.heap.Heap;
+import com.oracle.svm.core.heap.ObjectHeader;
+import com.oracle.svm.core.hub.LayoutEncoding;
+import com.oracle.svm.core.snippets.SnippetRuntime;
+import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor;
+
 import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor;
 import jdk.graal.compiler.graph.Node.ConstantNodeParameter;
 import jdk.graal.compiler.graph.Node.NodeIntrinsic;
@@ -40,14 +48,6 @@
 import jdk.graal.compiler.word.ObjectAccess;
 import jdk.graal.compiler.word.Word;
 
-import com.oracle.svm.core.config.ConfigurationValues;
-import com.oracle.svm.core.config.ObjectLayout;
-import com.oracle.svm.core.heap.Heap;
-import com.oracle.svm.core.heap.ObjectHeader;
-import com.oracle.svm.core.hub.LayoutEncoding;
-import com.oracle.svm.core.snippets.SnippetRuntime;
-import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor;
-
 final class SubstrateIdentityHashCodeSnippets extends IdentityHashCodeSnippets {
 
     static final SubstrateForeignCallDescriptor GENERATE_IDENTITY_HASH_CODE = SnippetRuntime.findForeignCall(
@@ -59,28 +59,29 @@ static Templates createTemplates(OptionValues options, Providers providers) {
 
     @Override
     protected int computeIdentityHashCode(Object obj) {
-        int identityHashCode;
         ObjectLayout ol = ConfigurationValues.getObjectLayout();
-        if (ol.hasFixedIdentityHashField()) {
-            int offset = ol.getFixedIdentityHashOffset();
-            identityHashCode = ObjectAccess.readInt(obj, offset, IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION);
-            if (probability(SLOW_PATH_PROBABILITY, identityHashCode == 0)) {
-                identityHashCode = generateIdentityHashCode(GENERATE_IDENTITY_HASH_CODE, obj);
+        if (ol.isIdentityHashFieldOptional()) {
+            int identityHashCode;
+            ObjectHeader oh = Heap.getHeap().getObjectHeader();
+            Word objPtr = Word.objectToUntrackedPointer(obj);
+            Word header = ObjectHeader.readHeaderFromPointer(objPtr);
+            if (probability(LIKELY_PROBABILITY, oh.hasOptionalIdentityHashField(header))) {
+                int offset = LayoutEncoding.getOptionalIdentityHashOffset(obj);
+                identityHashCode = ObjectAccess.readInt(obj, offset, IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION);
+            } else {
+                identityHashCode = IdentityHashCodeSupport.computeHashCodeFromAddress(obj);
+                if (probability(NOT_FREQUENT_PROBABILITY, !oh.hasIdentityHashFromAddress(header))) {
+                    // This write leads to frame state issues that break scheduling if done earlier
+                    oh.setIdentityHashFromAddress(objPtr, header);
+                }
             }
             return identityHashCode;
         }
-        ObjectHeader oh = Heap.getHeap().getObjectHeader();
-        Word objPtr = Word.objectToUntrackedPointer(obj);
-        Word header = ObjectHeader.readHeaderFromPointer(objPtr);
-        if (probability(LIKELY_PROBABILITY, oh.hasOptionalIdentityHashField(header))) {
-            int offset = LayoutEncoding.getOptionalIdentityHashOffset(obj);
-            identityHashCode = ObjectAccess.readInt(obj, offset, IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION);
-        } else {
-            identityHashCode = IdentityHashCodeSupport.computeHashCodeFromAddress(obj);
-            if (probability(NOT_FREQUENT_PROBABILITY, !oh.hasIdentityHashFromAddress(header))) {
-                // Note this write leads to frame state issues that break scheduling if done earlier
-                oh.setIdentityHashFromAddress(objPtr, header);
-            }
+
+        int offset = LayoutEncoding.getOptionalIdentityHashOffset(obj);
+        int identityHashCode = ObjectAccess.readInt(obj, offset, IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION);
+        if (probability(SLOW_PATH_PROBABILITY, identityHashCode == 0)) {
+            identityHashCode = generateIdentityHashCode(GENERATE_IDENTITY_HASH_CODE, obj);
         }
         return identityHashCode;
     }
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java
index 554113f9cf5c..b3ac94674075 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java
@@ -100,40 +100,39 @@ public static void setDefaultIfEmpty() {
             CompressEncoding compressEncoding = new CompressEncoding(SubstrateOptions.SpawnIsolates.getValue() ? 1 : 0, 0);
             ImageSingletons.add(CompressEncoding.class, compressEncoding);
 
-            ObjectLayout objectLayout = createObjectLayout();
+            ObjectLayout objectLayout = createObjectLayout(ObjectLayout.IdentityHashCodePosition.SYNTHETIC_FIELD);
             ImageSingletons.add(ObjectLayout.class, objectLayout);
 
             ImageSingletons.add(HybridLayoutSupport.class, new HybridLayoutSupport());
         }
     }
 
-    public static ObjectLayout createObjectLayout() {
-        return createObjectLayout(JavaKind.Object, false);
+    public static ObjectLayout createObjectLayout(ObjectLayout.IdentityHashCodePosition identityHashCodeMode) {
+        return createObjectLayout(JavaKind.Object, identityHashCodeMode);
     }
 
     /**
-     * Defines the layout of objects. Identity hash code fields can be optional if the object header
-     * allows for it, in which case such a field is appended to individual objects after an identity
-     * hash code has been assigned to it (unless there is an otherwise unused gap in the object that
-     * can be used).
+     * Defines the layout of objects. The monitor slot and the identity hash code fields are
+     * appended to the individual objects (unless there is an otherwise unused gap in the object
+     * that can be used).
      *
      * The layout of instance objects is:
      * 
      *
      * The layout of array objects is:
      * 
      */
-    public static ObjectLayout createObjectLayout(JavaKind referenceKind, boolean disableOptionalIdentityHash) {
+    public static ObjectLayout createObjectLayout(JavaKind referenceKind, ObjectLayout.IdentityHashCodePosition identityHashCodePosition) {
         SubstrateTargetDescription target = ConfigurationValues.getTarget();
         int referenceSize = target.arch.getPlatformKind(referenceKind).getSizeInBytes();
         int intSize = target.arch.getPlatformKind(JavaKind.Int).getSizeInBytes();
@@ -142,21 +141,13 @@ public static ObjectLayout createObjectLayout(JavaKind referenceKind, boolean di
         int hubOffset = 0;
         int headerSize = hubOffset + referenceSize;
 
-        int identityHashCodeOffset;
-        if (!disableOptionalIdentityHash && SubstrateOptions.SpawnIsolates.getValue() && headerSize + referenceSize <= objectAlignment) {
-            /*
-             * References are relative to the heap base, so we should be able to use fewer bits in
-             * the object header to reference DynamicHubs which are located near the start of the
-             * heap. This means we could be unable to fit forwarding references in those header bits
-             * during GC, but every object is large enough to fit a separate forwarding reference
-             * outside its header. Therefore, we can avoid reserving an identity hash code field for
-             * every object during its allocation and use extra header bits to track if an
-             * individual object was assigned an identity hash code after allocation.
-             */
-            identityHashCodeOffset = -1;
-        } else { // need all object header bits except for lowest-order bits freed up by alignment
-            identityHashCodeOffset = headerSize;
+        int objectHeaderIdentityHashOffset;
+        if (identityHashCodePosition == ObjectLayout.IdentityHashCodePosition.OBJECT_HEADER) {
+            objectHeaderIdentityHashOffset = headerSize;
             headerSize += intSize;
+        } else {
+            assert identityHashCodePosition == ObjectLayout.IdentityHashCodePosition.SYNTHETIC_FIELD || identityHashCodePosition == ObjectLayout.IdentityHashCodePosition.OPTIONAL;
+            objectHeaderIdentityHashOffset = -1;
         }
 
         headerSize += SubstrateOptions.AdditionalHeaderBytes.getValue();
@@ -165,7 +156,7 @@ public static ObjectLayout createObjectLayout(JavaKind referenceKind, boolean di
         int arrayLengthOffset = headerSize;
         int arrayBaseOffset = arrayLengthOffset + intSize;
 
-        return new ObjectLayout(target, referenceSize, objectAlignment, hubOffset, firstFieldOffset, arrayLengthOffset, arrayBaseOffset, identityHashCodeOffset);
+        return new ObjectLayout(target, referenceSize, objectAlignment, hubOffset, firstFieldOffset, arrayLengthOffset, arrayBaseOffset, objectHeaderIdentityHashOffset, identityHashCodePosition);
     }
 
     public SVMHost createHostVM(OptionValues options, ClassLoader classLoader, ClassInitializationSupport classInitializationSupport,
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
index 9892af70963b..76fd3cbfa28e 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
@@ -316,6 +316,7 @@
 import jdk.graal.compiler.replacements.TargetGraphBuilderPlugins;
 import jdk.graal.compiler.word.WordOperationPlugin;
 import jdk.graal.compiler.word.WordTypes;
+import jdk.graal.compiler.word.WordTypes;
 import jdk.vm.ci.aarch64.AArch64;
 import jdk.vm.ci.amd64.AMD64;
 import jdk.vm.ci.code.Architecture;
@@ -476,24 +477,21 @@ public static SubstrateTargetDescription createTarget(Platform platform) {
                 }
             }
             AMD64 architecture = new AMD64(features, AMD64CPUFeatureAccess.allAMD64Flags());
-            int deoptScratchSpace = 2 * 8; // Space for two 64-bit registers: rax and xmm0
-            return new SubstrateTargetDescription(architecture, true, 16, 0, deoptScratchSpace, runtimeCheckedFeatures);
+            return new SubstrateTargetDescription(architecture, true, 16, 0, runtimeCheckedFeatures);
         } else if (includedIn(platform, Platform.AARCH64.class)) {
             EnumSet features = CPUTypeAArch64.getSelectedFeatures();
             features.addAll(parseCSVtoEnum(AArch64.CPUFeature.class, NativeImageOptions.CPUFeatures.getValue().values(), AArch64.CPUFeature.values()));
             AArch64 architecture = new AArch64(features, AArch64CPUFeatureAccess.enabledAArch64Flags());
             // runtime checked features are the same as static features on AArch64 for now
             EnumSet runtimeCheckedFeatures = architecture.getFeatures().clone();
-            int deoptScratchSpace = 2 * 8; // Space for two 64-bit registers: r0 and v0.
-            return new SubstrateTargetDescription(architecture, true, 16, 0, deoptScratchSpace, runtimeCheckedFeatures);
+            return new SubstrateTargetDescription(architecture, true, 16, 0, runtimeCheckedFeatures);
         } else if (includedIn(platform, Platform.RISCV64.class)) {
             EnumSet features = CPUTypeRISCV64.getSelectedFeatures();
             features.addAll(parseCSVtoEnum(RISCV64.CPUFeature.class, NativeImageOptions.CPUFeatures.getValue().values(), RISCV64.CPUFeature.values()));
             RISCV64 architecture = new RISCV64(features, RISCV64CPUFeatureAccess.enabledRISCV64Flags());
             // runtime checked features are the same as static features on RISCV64 for now
             EnumSet runtimeCheckedFeatures = architecture.getFeatures().clone();
-            int deoptScratchSpace = 2 * 8; // Space for two 64-bit registers: x0 and f0.
-            return new SubstrateTargetDescription(architecture, true, 16, 0, deoptScratchSpace, runtimeCheckedFeatures);
+            return new SubstrateTargetDescription(architecture, true, 16, 0, runtimeCheckedFeatures);
         } else {
             throw UserError.abort("Architecture specified by platform is not supported: %s", platform.getClass().getTypeName());
         }
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java
index 7ecc8c2664fa..e8355581bf9a 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java
@@ -115,10 +115,12 @@ public void setMonitorFieldOffset(int monitorFieldOffset) {
         this.monitorFieldOffset = monitorFieldOffset;
     }
 
+    // TEMP (chaeubl): rename
     public int getOptionalIdentityHashOffset() {
         return optionalIdentityHashOffset;
     }
 
+    // TEMP (chaeubl): rename
     public void setOptionalIdentityHashOffset(int offset) {
         assert this.optionalIdentityHashOffset == -1 : "setting identity hashcode field offset more than once";
         assert offset >= 0;
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java
index f4e596e8e248..3ee545c63681 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java
@@ -531,28 +531,32 @@ private void layoutInstanceFields(HostedInstanceClass clazz, HostedField[] super
          */
         allFields.sort(FIELD_LOCATION_COMPARATOR);
 
-        int sizeWithoutIdHashField = usedBytes.length();
+        int afterFieldsOffset = usedBytes.length();
 
         // Identity hash code
-        if (!clazz.isAbstract() && !HybridLayout.isHybrid(clazz)) {
-            int offset;
-            if (layout.hasFixedIdentityHashField()) {
-                offset = layout.getFixedIdentityHashOffset();
-            } else { // optional: place in gap if any, or append on demand during GC
-                int size = Integer.BYTES;
+        if (layout.hasFixedIdentityHashField()) {
+            clazz.setOptionalIdentityHashOffset(layout.getFixedIdentityHashOffset());
+        } else if (!clazz.isAbstract()) {
+            if (layout.isIdentityHashFieldSynthetic() || (layout.isIdentityHashFieldOptional() && !HybridLayout.isHybrid(clazz))) {
+                // place in gap if any, or append
+                int hashSize = Integer.BYTES;
                 int endOffset = usedBytes.length();
-                offset = findGapForField(usedBytes, 0, size, endOffset);
+                int offset = findGapForField(usedBytes, 0, hashSize, endOffset);
                 if (offset == -1) {
-                    offset = endOffset + getAlignmentAdjustment(endOffset, size);
+                    offset = endOffset + getAlignmentAdjustment(endOffset, hashSize);
+                    if (!layout.isIdentityHashFieldOptional()) {
+                        /* Include the identity hashcode field in the instance hashSize. */
+                        afterFieldsOffset = offset + hashSize;
+                    }
                 }
-                reserve(usedBytes, offset, size);
+                reserve(usedBytes, offset, hashSize);
+                clazz.setOptionalIdentityHashOffset(offset);
             }
-            clazz.setOptionalIdentityHashOffset(offset);
         }
 
         clazz.instanceFieldsWithoutSuper = allFields.toArray(new HostedField[0]);
-        clazz.afterFieldsOffset = sizeWithoutIdHashField;
-        clazz.instanceSize = layout.alignUp(clazz.afterFieldsOffset);
+        clazz.afterFieldsOffset = afterFieldsOffset;
+        clazz.instanceSize = layout.alignUp(afterFieldsOffset);
 
         if (clazz.instanceFieldsWithoutSuper.length == 0) {
             clazz.instanceFieldsWithSuper = superFields;
@@ -1022,7 +1026,7 @@ private void buildHubs() {
             int layoutHelper;
             boolean canInstantiateAsInstance = false;
             int monitorOffset = 0;
-            int optionalIdHashOffset = -1;
+            int identityHashOffset = -1;
             if (type.isInstanceClass()) {
                 HostedInstanceClass instanceClass = (HostedInstanceClass) type;
                 if (instanceClass.isAbstract()) {
@@ -1038,11 +1042,13 @@ private void buildHubs() {
                     canInstantiateAsInstance = type.isInstantiated();
                 }
                 monitorOffset = instanceClass.getMonitorFieldOffset();
-                optionalIdHashOffset = instanceClass.getOptionalIdentityHashOffset();
+                identityHashOffset = instanceClass.getOptionalIdentityHashOffset();
+                assert !ol.hasFixedIdentityHashField() || identityHashOffset == ol.getFixedIdentityHashOffset();
             } else if (type.isArray()) {
                 JavaKind storageKind = type.getComponentType().getStorageKind();
                 boolean isObject = (storageKind == JavaKind.Object);
                 layoutHelper = LayoutEncoding.forArray(type, isObject, ol.getArrayBaseOffset(storageKind), ol.getArrayIndexShift(storageKind));
+                identityHashOffset = ol.hasFixedIdentityHashField() ? ol.getFixedIdentityHashOffset() : -1;
             } else if (type.isInterface()) {
                 layoutHelper = LayoutEncoding.forInterface();
             } else if (type.isPrimitive()) {
@@ -1074,7 +1080,7 @@ private void buildHubs() {
 
             DynamicHub hub = type.getHub();
             SerializationRegistry s = ImageSingletons.lookup(SerializationRegistry.class);
-            hub.setData(layoutHelper, type.getTypeID(), monitorOffset, optionalIdHashOffset, type.getTypeCheckStart(), type.getTypeCheckRange(),
+            hub.setData(layoutHelper, type.getTypeID(), monitorOffset, identityHashOffset, type.getTypeCheckStart(), type.getTypeCheckRange(),
                             type.getTypeCheckSlot(), type.getTypeCheckSlots(), vtable, referenceMapIndex, type.isInstantiated(), canInstantiateAsInstance, isProxyClass,
                             s.isRegisteredForSerialization(type.getJavaClass()));
         }

From 03a7ee61066ba3957927dc619bb996d5b831aa77 Mon Sep 17 00:00:00 2001
From: Christian Haeubl 
Date: Thu, 2 Nov 2023 14:51:26 +0100
Subject: [PATCH 2/5] Renamings and small cleanups.

---
 .../core/genscavenge/ObjectHeaderImpl.java    |  8 +--
 .../oracle/svm/core/genscavenge/Space.java    |  2 +-
 .../oracle/svm/core/config/ObjectLayout.java  | 54 +++++++++----------
 .../jdk/SubstrateObjectCloneSnippets.java     |  2 +-
 .../com/oracle/svm/core/hub/DynamicHub.java   | 15 +++---
 .../oracle/svm/core/hub/LayoutEncoding.java   | 19 ++++---
 .../IdentityHashCodeSupport.java              |  2 +-
 .../SubstrateIdentityHashCodeNode.java        |  8 +--
 .../SubstrateIdentityHashCodeSnippets.java    |  4 +-
 .../svm/hosted/HostedConfiguration.java       | 11 ++--
 .../svm/hosted/config/HybridLayout.java       |  7 ++-
 .../image/NativeImageDebugInfoProvider.java   | 12 ++---
 .../hosted/image/NativeImageHeapWriter.java   |  6 +--
 .../svm/hosted/meta/HostedInstanceClass.java  | 14 +++--
 .../svm/hosted/meta/UniverseBuilder.java      | 12 ++---
 15 files changed, 85 insertions(+), 91 deletions(-)

diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java
index fc054e060ab1..a1cbfa48956d 100644
--- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java
+++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java
@@ -140,16 +140,16 @@ public void initializeHeaderOfNewObject(Pointer objectPointer, Word encodedHub)
         ObjectLayout ol = ConfigurationValues.getObjectLayout();
         if (getReferenceSize() == Integer.BYTES) {
             dynamicAssert(encodedHub.and(WordFactory.unsigned(0xFFFFFFFF00000000L)).isNull(), "hub can only use 32 bits");
-            if (ol.hasFixedIdentityHashField()) {
-                dynamicAssert(ol.getFixedIdentityHashOffset() == getHubOffset() + 4, "assumed layout to optimize initializing write");
+            if (ol.isIdentityHashFieldInObjectHeader()) {
+                dynamicAssert(ol.getObjectHeaderIdentityHashOffset() == getHubOffset() + 4, "assumed layout to optimize initializing write");
                 objectPointer.writeLong(getHubOffset(), encodedHub.rawValue(), LocationIdentity.INIT_LOCATION);
             } else {
                 objectPointer.writeInt(getHubOffset(), (int) encodedHub.rawValue(), LocationIdentity.INIT_LOCATION);
             }
         } else {
             objectPointer.writeWord(getHubOffset(), encodedHub, LocationIdentity.INIT_LOCATION);
-            if (ol.hasFixedIdentityHashField()) {
-                objectPointer.writeInt(ol.getFixedIdentityHashOffset(), 0, LocationIdentity.INIT_LOCATION);
+            if (ol.isIdentityHashFieldInObjectHeader()) {
+                objectPointer.writeInt(ol.getObjectHeaderIdentityHashOffset(), 0, LocationIdentity.INIT_LOCATION);
             }
         }
     }
diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java
index 7847560cfa29..f30b54b74744 100644
--- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java
+++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java
@@ -423,7 +423,7 @@ private Object copyAlignedObject(Object originalObj) {
         if (probability(SLOW_PATH_PROBABILITY, addIdentityHashField)) {
             // Must do first: ensures correct object size below and in other places
             int value = IdentityHashCodeSupport.computeHashCodeFromAddress(originalObj);
-            int offset = LayoutEncoding.getOptionalIdentityHashOffset(copy);
+            int offset = LayoutEncoding.getIdentityHashOffset(copy);
             ObjectAccess.writeInt(copy, offset, value, IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION);
             ObjectHeaderImpl.getObjectHeaderImpl().setIdentityHashInField(copy);
         }
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java
index 2db72329e7f2..c7f85dfc5e18 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java
@@ -48,7 +48,7 @@
  * Identity hashcode fields can either be:
  * 
    *
  1. In the object header, at a fixed offset for all objects (see - * {@link #hasFixedIdentityHashField()}).
  2. + * {@link #isIdentityHashFieldInObjectHeader()}). *
  3. In a synthetic field (outside the object header), at a type or object specific offset (see * {@link #isIdentityHashFieldSynthetic()}).
  4. *
  5. Outside the object header, at a type and object state specific offset (see @@ -65,21 +65,20 @@ public final class ObjectLayout { private final int firstFieldOffset; private final int arrayLengthOffset; private final int arrayBaseOffset; - private final int fixedIdentityHashOffset; + private final int objectHeaderIdentityHashOffset; private final boolean isIdentityHashFieldSynthetic; private final boolean isIdentityHashFieldOptional; public ObjectLayout(SubstrateTargetDescription target, int referenceSize, int objectAlignment, int hubOffset, int firstFieldOffset, int arrayLengthOffset, int arrayBaseOffset, - int fixedIdentityHashOffset, IdentityHashCodePosition identityHashCodePosition) { + int identityHashOffset, IdentityHashPosition identityHashPos) { assert CodeUtil.isPowerOf2(referenceSize) : referenceSize; assert CodeUtil.isPowerOf2(objectAlignment) : objectAlignment; assert arrayLengthOffset % Integer.BYTES == 0; assert hubOffset < firstFieldOffset && hubOffset < arrayLengthOffset : hubOffset; - assert identityHashCodePosition == IdentityHashCodePosition.OBJECT_HEADER || identityHashCodePosition == IdentityHashCodePosition.SYNTHETIC_FIELD || - identityHashCodePosition == IdentityHashCodePosition.OPTIONAL; - assert (identityHashCodePosition == IdentityHashCodePosition.OBJECT_HEADER && fixedIdentityHashOffset > 0 && fixedIdentityHashOffset < arrayLengthOffset && - fixedIdentityHashOffset % Integer.BYTES == 0) || - (identityHashCodePosition != IdentityHashCodePosition.OBJECT_HEADER && fixedIdentityHashOffset == -1); + assert arrayLengthOffset % Integer.BYTES == 0; + assert identityHashPos == IdentityHashPosition.OBJECT_HEADER || identityHashPos == IdentityHashPosition.SYNTHETIC_FIELD || identityHashPos == IdentityHashPosition.OPTIONAL; + assert (identityHashPos == IdentityHashPosition.OBJECT_HEADER && identityHashOffset > 0 && identityHashOffset < arrayLengthOffset && identityHashOffset % Integer.BYTES == 0) || + (identityHashPos != IdentityHashPosition.OBJECT_HEADER && identityHashOffset == -1); this.target = target; this.referenceSize = referenceSize; @@ -89,9 +88,10 @@ public ObjectLayout(SubstrateTargetDescription target, int referenceSize, int ob this.firstFieldOffset = firstFieldOffset; this.arrayLengthOffset = arrayLengthOffset; this.arrayBaseOffset = arrayBaseOffset; - this.fixedIdentityHashOffset = fixedIdentityHashOffset; - this.isIdentityHashFieldSynthetic = identityHashCodePosition == IdentityHashCodePosition.SYNTHETIC_FIELD; - this.isIdentityHashFieldOptional = identityHashCodePosition == IdentityHashCodePosition.OPTIONAL; + this.objectHeaderIdentityHashOffset = identityHashOffset; + this.isIdentityHashFieldSynthetic = identityHashPos == IdentityHashPosition.SYNTHETIC_FIELD; + this.isIdentityHashFieldOptional = identityHashPos == IdentityHashPosition.OPTIONAL; + } /** The minimum alignment of objects (instances and arrays). */ @@ -177,21 +177,20 @@ public boolean isIdentityHashFieldSynthetic() { * Indicates whether all objects, including arrays, always contain an identity hash code field * at a specific offset in the object header. */ - // TEMP (chaeubl): we could rename that method (isIdentityHashFieldInObjectHeader) @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public boolean hasFixedIdentityHashField() { - return fixedIdentityHashOffset >= 0; + public boolean isIdentityHashFieldInObjectHeader() { + return objectHeaderIdentityHashOffset >= 0; } - /** If {@link #hasFixedIdentityHashField()}, then returns that offset. */ + /** If {@link #isIdentityHashFieldInObjectHeader()}, then returns that offset. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public int getFixedIdentityHashOffset() { + public int getObjectHeaderIdentityHashOffset() { if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.dynamicAssert(hasFixedIdentityHashField(), "must check before calling"); + ReplacementsUtil.dynamicAssert(isIdentityHashFieldInObjectHeader(), "must check before calling"); } else { - assert hasFixedIdentityHashField(); + assert isIdentityHashFieldInObjectHeader(); } - return fixedIdentityHashOffset; + return objectHeaderIdentityHashOffset; } public int getArrayBaseOffset(JavaKind kind) { @@ -211,15 +210,14 @@ private long getArrayUnalignedSize(JavaKind kind, int length) { return getArrayBaseOffset(kind) + ((long) length << getArrayIndexShift(kind)); } - public long getArrayOptionalIdentityHashOffset(JavaKind kind, int length) { - return getArrayOptionalIdentityHashOffset(getArrayUnalignedSize(kind, length)); + public long getArrayIdentityHashOffset(JavaKind kind, int length) { + return getArrayIdentityHashOffset(getArrayUnalignedSize(kind, length)); } - // TEMP (chaeubl): we should probably rename this method. @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public long getArrayOptionalIdentityHashOffset(long unalignedSize) { - if (hasFixedIdentityHashField()) { - return getFixedIdentityHashOffset(); + public long getArrayIdentityHashOffset(long unalignedSize) { + if (isIdentityHashFieldInObjectHeader()) { + return getObjectHeaderIdentityHashOffset(); } int align = Integer.BYTES; return ((unalignedSize + align - 1) / align) * align; @@ -229,14 +227,14 @@ public long getArrayOptionalIdentityHashOffset(long unalignedSize) { public long computeArrayTotalSize(long unalignedSize, boolean withOptionalIdHashField) { long size = unalignedSize; if ((withOptionalIdHashField && isIdentityHashFieldOptional()) || isIdentityHashFieldSynthetic()) { - size = getArrayOptionalIdentityHashOffset(size) + Integer.BYTES; + size = getArrayIdentityHashOffset(size) + Integer.BYTES; } return alignUp(size); } public int getMinImageHeapInstanceSize() { int unalignedSize = firstFieldOffset; // assumes no always-present "synthetic fields" - if (!hasFixedIdentityHashField()) { + if (!isIdentityHashFieldInObjectHeader()) { int idHashOffset = NumUtil.roundUp(unalignedSize, Integer.BYTES); unalignedSize = idHashOffset + Integer.BYTES; } @@ -262,7 +260,7 @@ public static JavaKind getCallSignatureKind(boolean isEntryPoint, ResolvedJavaTy } @Platforms(Platform.HOSTED_ONLY.class) - public enum IdentityHashCodePosition { + public enum IdentityHashPosition { /* At a fixed offset, for all objects (part of the object header). */ OBJECT_HEADER, /* At a type-specific offset (outside the object header). */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java index 6f8e15b00f4c..bdea4a2e80fd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java @@ -174,7 +174,7 @@ private static Object doClone(Object original) throws CloneNotSupportedException /* Reset identity hashcode if we use a synthetic field. */ if (ConfigurationValues.getObjectLayout().isIdentityHashFieldSynthetic()) { - int offset = LayoutEncoding.getOptionalIdentityHashOffset(result); + int offset = LayoutEncoding.getIdentityHashOffset(result); ObjectAccess.writeInt(result, offset, 0); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index ba5938c6fcc9..d56f67270952 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -208,9 +208,8 @@ public final class DynamicHub implements AnnotatedElement, java.lang.reflect.Typ @UnknownPrimitiveField(availability = AfterHostedUniverse.class)// private short monitorOffset; - // TEMP (chaeubl): rename this field @UnknownPrimitiveField(availability = AfterHostedUniverse.class)// - private short optionalIdentityHashOffset; + private short identityHashOffset; /** * Bit-set for various boolean flags, to reduce size of instances. It is important that this @@ -459,7 +458,7 @@ public void setData(int layoutEncoding, int typeID, int monitorOffset, int ident this.layoutEncoding = layoutEncoding; this.typeID = typeID; this.monitorOffset = NumUtil.safeToShort(monitorOffset); - this.optionalIdentityHashOffset = NumUtil.safeToShort(identityHashOffset); + this.identityHashOffset = NumUtil.safeToShort(identityHashOffset); this.typeCheckStart = typeCheckStart; this.typeCheckRange = typeCheckRange; this.typeCheckSlot = typeCheckSlot; @@ -631,15 +630,15 @@ public int getMonitorOffset() { return monitorOffset; } - // TEMP (chaeubl): should we rename this method as well? + /** If possible, use {@link LayoutEncoding#getIdentityHashOffset(Object)} instead. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public int getOptionalIdentityHashOffset() { + public int getIdentityHashOffset() { ObjectLayout ol = ConfigurationValues.getObjectLayout(); - if (ol.hasFixedIdentityHashField()) { // enable elimination of our field - return ol.getFixedIdentityHashOffset(); + if (ol.isIdentityHashFieldInObjectHeader()) { // enable elimination of our field + return ol.getObjectHeaderIdentityHashOffset(); } - int result = optionalIdentityHashOffset; + int result = identityHashOffset; if (GraalDirectives.inIntrinsic()) { ReplacementsUtil.dynamicAssert(result > 0, "must have an identity hash field"); } else { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java index d3731d63c52b..56eef67e35e8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java @@ -202,7 +202,7 @@ public static UnsignedWord getPureInstanceSize(DynamicHub hub, boolean withOptio UnsignedWord size = getPureInstanceAllocationSize(hub.getLayoutEncoding()); ObjectLayout ol = ConfigurationValues.getObjectLayout(); if (withOptionalIdHashField && ol.isIdentityHashFieldOptional()) { - int afterIdHashField = hub.getOptionalIdentityHashOffset() + Integer.BYTES; + int afterIdHashField = hub.getIdentityHashOffset() + Integer.BYTES; if (size.belowThan(afterIdHashField)) { // fits in a gap between fields size = WordFactory.unsigned(ol.alignUp(afterIdHashField)); } @@ -294,27 +294,26 @@ public static UnsignedWord getArraySize(int encoding, int length, boolean withOp return WordFactory.unsigned(totalSize); } - // TEMP (chaeubl): this should be renamed to getIdentityHashOffset? @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static int getOptionalIdentityHashOffset(Object obj) { + public static int getIdentityHashOffset(Object obj) { ObjectLayout ol = ConfigurationValues.getObjectLayout(); - if (ol.hasFixedIdentityHashField()) { - return ol.getFixedIdentityHashOffset(); + if (ol.isIdentityHashFieldInObjectHeader()) { + return ol.getObjectHeaderIdentityHashOffset(); } DynamicHub hub = KnownIntrinsics.readHub(obj); int encoding = hub.getLayoutEncoding(); if (isArrayLike(encoding)) { long unalignedSize = getArrayElementOffset(encoding, ArrayLengthNode.arrayLength(obj)).rawValue(); - return (int) ol.getArrayOptionalIdentityHashOffset(unalignedSize); + return (int) ol.getArrayIdentityHashOffset(unalignedSize); } else { - return hub.getOptionalIdentityHashOffset(); + return hub.getIdentityHashOffset(); } } @Uninterruptible(reason = "Prevent a GC moving the object or interfering with its identity hash state.", callerMustBe = true) public static UnsignedWord getSizeFromObject(Object obj) { - boolean withOptionalIdHashField = ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional() && checkOptionalIdentityHashField(obj); + boolean withOptionalIdHashField = ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional() && hasOptionalIdentityHashField(obj); return getSizeFromObjectInline(obj, withOptionalIdHashField); } @@ -346,7 +345,7 @@ public static UnsignedWord getSizeFromObjectInlineInGC(Object obj) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static UnsignedWord getSizeFromObjectInlineInGC(Object obj, boolean addOptionalIdHashField) { boolean withOptionalIdHashField = addOptionalIdHashField || - (ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional() && checkOptionalIdentityHashField(obj)); + (ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional() && hasOptionalIdentityHashField(obj)); return getSizeFromObjectInline(obj, withOptionalIdHashField); } @@ -364,7 +363,7 @@ private static UnsignedWord getSizeFromObjectInline(Object obj, boolean withOpti @AlwaysInline("GC performance") @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private static boolean checkOptionalIdentityHashField(Object obj) { + private static boolean hasOptionalIdentityHashField(Object obj) { ObjectHeader oh = Heap.getHeap().getObjectHeader(); Word header = ObjectHeader.readHeaderFromPointer(Word.objectToUntrackedPointer(obj)); return oh.hasOptionalIdentityHashField(header); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java index abb8fcf1f4d4..c3781115f93e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java @@ -78,7 +78,7 @@ public static int generateIdentityHashCode(Object obj) { VMError.guarantee(!ol.isIdentityHashFieldOptional(), "Snippet must handle this case"); int newHashCode = generateRandomHashCode(); - int offset = LayoutEncoding.getOptionalIdentityHashOffset(obj); + int offset = LayoutEncoding.getIdentityHashOffset(obj); if (!Unsafe.getUnsafe().compareAndSetInt(obj, offset, 0, newHashCode)) { newHashCode = ObjectAccess.readInt(obj, offset, IDENTITY_HASHCODE_LOCATION); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeNode.java index 6edfa7fba426..15f2911a1bdf 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeNode.java @@ -59,7 +59,7 @@ protected SubstrateIdentityHashCodeNode(ValueNode object, int bci) { @Override public LocationIdentity getKilledLocationIdentity() { // With optional identity hash codes, we must write bits in the object header. - return supportsOptionalIdentityHashField() ? LocationIdentity.any() : IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION; + return isIdentityHashFieldOptional() ? LocationIdentity.any() : IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION; } @Override @@ -69,16 +69,16 @@ protected int getIdentityHashCode(JavaConstant constant) { @Override public NodeCycles estimatedNodeCycles() { - return supportsOptionalIdentityHashField() ? NodeCycles.CYCLES_8 : NodeCycles.CYCLES_2; + return isIdentityHashFieldOptional() ? NodeCycles.CYCLES_8 : NodeCycles.CYCLES_2; } @Override protected NodeSize dynamicNodeSizeEstimate() { - return supportsOptionalIdentityHashField() ? NodeSize.SIZE_32 : NodeSize.SIZE_8; + return isIdentityHashFieldOptional() ? NodeSize.SIZE_32 : NodeSize.SIZE_8; } @Fold - static boolean supportsOptionalIdentityHashField() { + static boolean isIdentityHashFieldOptional() { return ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional(); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeSnippets.java index 37523767027c..7f8803ddb4f5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/SubstrateIdentityHashCodeSnippets.java @@ -66,7 +66,7 @@ protected int computeIdentityHashCode(Object obj) { Word objPtr = Word.objectToUntrackedPointer(obj); Word header = ObjectHeader.readHeaderFromPointer(objPtr); if (probability(LIKELY_PROBABILITY, oh.hasOptionalIdentityHashField(header))) { - int offset = LayoutEncoding.getOptionalIdentityHashOffset(obj); + int offset = LayoutEncoding.getIdentityHashOffset(obj); identityHashCode = ObjectAccess.readInt(obj, offset, IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION); } else { identityHashCode = IdentityHashCodeSupport.computeHashCodeFromAddress(obj); @@ -78,7 +78,7 @@ protected int computeIdentityHashCode(Object obj) { return identityHashCode; } - int offset = LayoutEncoding.getOptionalIdentityHashOffset(obj); + int offset = LayoutEncoding.getIdentityHashOffset(obj); int identityHashCode = ObjectAccess.readInt(obj, offset, IdentityHashCodeSupport.IDENTITY_HASHCODE_LOCATION); if (probability(SLOW_PATH_PROBABILITY, identityHashCode == 0)) { identityHashCode = generateIdentityHashCode(GENERATE_IDENTITY_HASH_CODE, obj); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java index b3ac94674075..fd937d97d0ea 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java @@ -48,6 +48,7 @@ import com.oracle.svm.core.SubstrateTargetDescription; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.config.ObjectLayout; +import com.oracle.svm.core.config.ObjectLayout.IdentityHashPosition; import com.oracle.svm.core.graal.code.SubstrateMetaAccessExtensionProvider; import com.oracle.svm.core.graal.meta.RuntimeConfiguration; import com.oracle.svm.core.monitor.MultiThreadedMonitorSupport; @@ -100,14 +101,14 @@ public static void setDefaultIfEmpty() { CompressEncoding compressEncoding = new CompressEncoding(SubstrateOptions.SpawnIsolates.getValue() ? 1 : 0, 0); ImageSingletons.add(CompressEncoding.class, compressEncoding); - ObjectLayout objectLayout = createObjectLayout(ObjectLayout.IdentityHashCodePosition.SYNTHETIC_FIELD); + ObjectLayout objectLayout = createObjectLayout(IdentityHashPosition.SYNTHETIC_FIELD); ImageSingletons.add(ObjectLayout.class, objectLayout); ImageSingletons.add(HybridLayoutSupport.class, new HybridLayoutSupport()); } } - public static ObjectLayout createObjectLayout(ObjectLayout.IdentityHashCodePosition identityHashCodeMode) { + public static ObjectLayout createObjectLayout(IdentityHashPosition identityHashCodeMode) { return createObjectLayout(JavaKind.Object, identityHashCodeMode); } @@ -132,7 +133,7 @@ public static ObjectLayout createObjectLayout(ObjectLayout.IdentityHashCodePosit *
  6. 32 bit identity hashcode (if needed)
  7. * */ - public static ObjectLayout createObjectLayout(JavaKind referenceKind, ObjectLayout.IdentityHashCodePosition identityHashCodePosition) { + public static ObjectLayout createObjectLayout(JavaKind referenceKind, IdentityHashPosition identityHashCodePosition) { SubstrateTargetDescription target = ConfigurationValues.getTarget(); int referenceSize = target.arch.getPlatformKind(referenceKind).getSizeInBytes(); int intSize = target.arch.getPlatformKind(JavaKind.Int).getSizeInBytes(); @@ -142,11 +143,11 @@ public static ObjectLayout createObjectLayout(JavaKind referenceKind, ObjectLayo int headerSize = hubOffset + referenceSize; int objectHeaderIdentityHashOffset; - if (identityHashCodePosition == ObjectLayout.IdentityHashCodePosition.OBJECT_HEADER) { + if (identityHashCodePosition == IdentityHashPosition.OBJECT_HEADER) { objectHeaderIdentityHashOffset = headerSize; headerSize += intSize; } else { - assert identityHashCodePosition == ObjectLayout.IdentityHashCodePosition.SYNTHETIC_FIELD || identityHashCodePosition == ObjectLayout.IdentityHashCodePosition.OPTIONAL; + assert identityHashCodePosition == IdentityHashPosition.SYNTHETIC_FIELD || identityHashCodePosition == IdentityHashPosition.OPTIONAL; objectHeaderIdentityHashOffset = -1; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/HybridLayout.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/HybridLayout.java index 054d59ecfd69..d02d446b5c86 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/HybridLayout.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/HybridLayout.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.hosted.config; -import jdk.graal.compiler.core.common.NumUtil; - import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.hub.Hybrid; import com.oracle.svm.hosted.meta.HostedField; @@ -33,6 +31,7 @@ import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedType; +import jdk.graal.compiler.core.common.NumUtil; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaType; @@ -100,8 +99,8 @@ public long getTotalSize(int length, boolean withOptionalIdHashField) { return layout.computeArrayTotalSize(getArrayElementOffset(length), withOptionalIdHashField); } - public long getOptionalIdentityHashOffset(int length) { - return layout.getArrayOptionalIdentityHashOffset(getArrayElementOffset(length)); + public long getIdentityHashOffset(int length) { + return layout.getArrayIdentityHashOffset(getArrayElementOffset(length)); } public HostedField getArrayField() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 0a209ebd31c3..d8398029edc8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -39,10 +39,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.graal.compiler.code.CompilationResult; -import jdk.graal.compiler.debug.DebugContext; -import jdk.graal.compiler.graph.NodeSourcePosition; -import jdk.graal.compiler.java.StableMethodNameFormatter; import jdk.vm.ci.code.CallingConvention; import jdk.vm.ci.meta.AllocatableValue; import org.graalvm.collections.EconomicMap; @@ -91,6 +87,10 @@ import com.oracle.svm.hosted.substitute.SubstitutionMethod; import com.oracle.svm.util.ClassUtil; +import jdk.graal.compiler.code.CompilationResult; +import jdk.graal.compiler.debug.DebugContext; +import jdk.graal.compiler.graph.NodeSourcePosition; +import jdk.graal.compiler.java.StableMethodNameFormatter; import jdk.vm.ci.aarch64.AArch64; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.code.BytecodeFrame; @@ -435,8 +435,8 @@ private Stream computeHeaderTypeInfo() { int idHashSize = getObjectLayout().sizeInBytes(JavaKind.Int); int fixedIdHashOffset = -1; - if (getObjectLayout().hasFixedIdentityHashField()) { - fixedIdHashOffset = getObjectLayout().getFixedIdentityHashOffset(); + if (getObjectLayout().isIdentityHashFieldInObjectHeader()) { + fixedIdHashOffset = getObjectLayout().getObjectHeaderIdentityHashOffset(); objHeaderSize = Math.max(objHeaderSize, fixedIdHashOffset + idHashSize); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java index 35165f6b734e..f2503682b51c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java @@ -388,9 +388,9 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { final Object array = Array.get(hybridArray, i); writeConstant(buffer, elementIndex, elementStorageKind, array, info); } - idHashOffset = hybridLayout.getOptionalIdentityHashOffset(length); + idHashOffset = hybridLayout.getIdentityHashOffset(length); } else { - idHashOffset = ((HostedInstanceClass) clazz).getOptionalIdentityHashOffset(); + idHashOffset = ((HostedInstanceClass) clazz).getIdentityHashOffset(); } bufferBytes.putInt(info.getIndexInBuffer(idHashOffset), info.getIdentityHashCode()); @@ -401,7 +401,7 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { int length = heap.hConstantReflection.readArrayLength(constant); bufferBytes.putInt(info.getIndexInBuffer(objectLayout.getArrayLengthOffset()), length); - bufferBytes.putInt(info.getIndexInBuffer(objectLayout.getArrayOptionalIdentityHashOffset(kind, length)), info.getIdentityHashCode()); + bufferBytes.putInt(info.getIndexInBuffer(objectLayout.getArrayIdentityHashOffset(kind, length)), info.getIdentityHashCode()); if (constant instanceof ImageHeapConstant) { if (clazz.getComponentType().isPrimitive()) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java index e8355581bf9a..c83e95981b2d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedInstanceClass.java @@ -37,7 +37,7 @@ public class HostedInstanceClass extends HostedClass { protected int instanceSize; protected boolean monitorFieldNeeded = false; protected int monitorFieldOffset = 0; - protected int optionalIdentityHashOffset = -1; + protected int identityHashOffset = -1; public HostedInstanceClass(HostedUniverse universe, AnalysisType wrapped, JavaKind kind, JavaKind storageKind, HostedClass superClass, HostedInterface[] interfaces) { super(universe, wrapped, kind, storageKind, superClass, interfaces); @@ -115,15 +115,13 @@ public void setMonitorFieldOffset(int monitorFieldOffset) { this.monitorFieldOffset = monitorFieldOffset; } - // TEMP (chaeubl): rename - public int getOptionalIdentityHashOffset() { - return optionalIdentityHashOffset; + public int getIdentityHashOffset() { + return identityHashOffset; } - // TEMP (chaeubl): rename - public void setOptionalIdentityHashOffset(int offset) { - assert this.optionalIdentityHashOffset == -1 : "setting identity hashcode field offset more than once"; + public void setIdentityHashOffset(int offset) { + assert this.identityHashOffset == -1 : "setting identity hashcode field offset more than once"; assert offset >= 0; - this.optionalIdentityHashOffset = offset; + this.identityHashOffset = offset; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index 3ee545c63681..23a6e629e634 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -534,8 +534,8 @@ private void layoutInstanceFields(HostedInstanceClass clazz, HostedField[] super int afterFieldsOffset = usedBytes.length(); // Identity hash code - if (layout.hasFixedIdentityHashField()) { - clazz.setOptionalIdentityHashOffset(layout.getFixedIdentityHashOffset()); + if (layout.isIdentityHashFieldInObjectHeader()) { + clazz.setIdentityHashOffset(layout.getObjectHeaderIdentityHashOffset()); } else if (!clazz.isAbstract()) { if (layout.isIdentityHashFieldSynthetic() || (layout.isIdentityHashFieldOptional() && !HybridLayout.isHybrid(clazz))) { // place in gap if any, or append @@ -550,7 +550,7 @@ private void layoutInstanceFields(HostedInstanceClass clazz, HostedField[] super } } reserve(usedBytes, offset, hashSize); - clazz.setOptionalIdentityHashOffset(offset); + clazz.setIdentityHashOffset(offset); } } @@ -1042,13 +1042,13 @@ private void buildHubs() { canInstantiateAsInstance = type.isInstantiated(); } monitorOffset = instanceClass.getMonitorFieldOffset(); - identityHashOffset = instanceClass.getOptionalIdentityHashOffset(); - assert !ol.hasFixedIdentityHashField() || identityHashOffset == ol.getFixedIdentityHashOffset(); + identityHashOffset = instanceClass.getIdentityHashOffset(); + assert !ol.isIdentityHashFieldInObjectHeader() || identityHashOffset == ol.getObjectHeaderIdentityHashOffset(); } else if (type.isArray()) { JavaKind storageKind = type.getComponentType().getStorageKind(); boolean isObject = (storageKind == JavaKind.Object); layoutHelper = LayoutEncoding.forArray(type, isObject, ol.getArrayBaseOffset(storageKind), ol.getArrayIndexShift(storageKind)); - identityHashOffset = ol.hasFixedIdentityHashField() ? ol.getFixedIdentityHashOffset() : -1; + identityHashOffset = ol.isIdentityHashFieldInObjectHeader() ? ol.getObjectHeaderIdentityHashOffset() : -1; } else if (type.isInterface()) { layoutHelper = LayoutEncoding.forInterface(); } else if (type.isPrimitive()) { From a9e1161e41e269e01e6d5f7f5e3989a11d711b07 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Mon, 6 Nov 2023 09:17:55 +0100 Subject: [PATCH 3/5] For arrays, store the identity hashcode in the object header instead of a synthetic field. --- .../HotSpotAllocationSnippets.java | 19 +---- .../replacements/AllocationSnippets.java | 19 ++++- .../core/genscavenge/ObjectHeaderImpl.java | 24 ++++--- .../oracle/svm/core/genscavenge/Space.java | 1 + .../oracle/svm/core/c/NonmovableArrays.java | 7 +- .../oracle/svm/core/config/ObjectLayout.java | 72 +++++++++---------- .../jdk/SubstrateObjectCloneSnippets.java | 4 +- .../snippets/SubstrateAllocationSnippets.java | 32 ++------- .../oracle/svm/core/heap/FillerObject.java | 5 +- .../oracle/svm/core/heap/ObjectHeader.java | 9 +-- .../com/oracle/svm/core/hub/DynamicHub.java | 6 +- .../src/com/oracle/svm/core/hub/Hybrid.java | 2 +- .../oracle/svm/core/hub/LayoutEncoding.java | 5 +- .../IdentityHashCodeSupport.java | 2 +- .../svm/hosted/HostedConfiguration.java | 36 +++++----- .../svm/hosted/NativeImageGenerator.java | 1 - .../oracle/svm/hosted/heap/PodSupport.java | 10 +-- .../image/NativeImageDebugInfoProvider.java | 21 +++--- .../hosted/image/NativeImageHeapWriter.java | 1 + .../svm/hosted/meta/UniverseBuilder.java | 67 ++++++++++------- 20 files changed, 179 insertions(+), 164 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replacements/HotSpotAllocationSnippets.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replacements/HotSpotAllocationSnippets.java index b0d563aa9647..565d83f83bae 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replacements/HotSpotAllocationSnippets.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replacements/HotSpotAllocationSnippets.java @@ -437,28 +437,13 @@ protected final int instanceHeaderSize() { return HotSpotReplacementsUtil.instanceHeaderSize(INJECTED_VMCONFIG); } - @Override - protected UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize) { - int alignment = objectAlignment(); - return WordFactory.unsigned(arrayAllocationSize(length, arrayBaseOffset, log2ElementSize, alignment)); - } - - public static long arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize, int alignment) { - /* - * We do an unsigned multiplication so that a negative array length will result in an array - * size greater than Integer.MAX_VALUE. - */ - long size = ((length & 0xFFFFFFFFL) << log2ElementSize) + arrayBaseOffset + (alignment - 1); - long mask = ~(alignment - 1); - return size & mask; - } - @Override public final int arrayLengthOffset() { return HotSpotReplacementsUtil.arrayLengthOffset(INJECTED_VMCONFIG); } - private static int objectAlignment() { + @Override + protected final int objectAlignment() { return HotSpotReplacementsUtil.objectAlignment(INJECTED_VMCONFIG); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/AllocationSnippets.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/AllocationSnippets.java index dacd7ef8fd1d..390449534b72 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/AllocationSnippets.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/AllocationSnippets.java @@ -115,6 +115,21 @@ protected Object newMultiArrayImpl(Word hub, int rank, int[] dimensions) { return callNewMultiArrayStub(hub, rank, dims); } + protected UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize) { + int alignment = objectAlignment(); + return WordFactory.unsigned(arrayAllocationSize(length, arrayBaseOffset, log2ElementSize, alignment)); + } + + public static long arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize, int alignment) { + /* + * We do an unsigned multiplication so that a negative array length will result in an array + * size greater than Integer.MAX_VALUE. + */ + long size = ((length & 0xFFFFFFFFL) << log2ElementSize) + arrayBaseOffset + (alignment - 1); + long mask = ~(alignment - 1); + return size & mask; + } + /** * Maximum number of long stores to emit when zeroing an object with a constant size. Larger * objects have their bodies initialized in a loop. @@ -340,8 +355,6 @@ public void emitPrefetchAllocate(Word address, boolean isArray) { protected abstract int instanceHeaderSize(); - protected abstract UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize); - public abstract void initializeObjectHeader(Word memory, Word hub, boolean isArray); protected abstract Object callNewInstanceStub(Word hub); @@ -358,6 +371,8 @@ public void emitPrefetchAllocate(Word address, boolean isArray) { public abstract int arrayLengthOffset(); + protected abstract int objectAlignment(); + public enum FillContent { DO_NOT_FILL, WITH_ZEROES, diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java index a1cbfa48956d..3af14d157989 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java @@ -136,11 +136,13 @@ public Word encodeAsUnmanagedObjectHeader(DynamicHub hub) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @Override - public void initializeHeaderOfNewObject(Pointer objectPointer, Word encodedHub) { + public void initializeHeaderOfNewObject(Pointer objectPointer, Word encodedHub, boolean isArrayLike) { ObjectLayout ol = ConfigurationValues.getObjectLayout(); + boolean isIdentityHashFieldInObjectHeader = ol.isIdentityHashFieldInObjectHeader() || ol.isIdentityHashFieldAtTypeSpecificOffset() && isArrayLike; if (getReferenceSize() == Integer.BYTES) { dynamicAssert(encodedHub.and(WordFactory.unsigned(0xFFFFFFFF00000000L)).isNull(), "hub can only use 32 bits"); - if (ol.isIdentityHashFieldInObjectHeader()) { + if (isIdentityHashFieldInObjectHeader) { + /* Use a single 64-bit write to initialize the hub and the identity hashcode. */ dynamicAssert(ol.getObjectHeaderIdentityHashOffset() == getHubOffset() + 4, "assumed layout to optimize initializing write"); objectPointer.writeLong(getHubOffset(), encodedHub.rawValue(), LocationIdentity.INIT_LOCATION); } else { @@ -148,7 +150,7 @@ public void initializeHeaderOfNewObject(Pointer objectPointer, Word encodedHub) } } else { objectPointer.writeWord(getHubOffset(), encodedHub, LocationIdentity.INIT_LOCATION); - if (ol.isIdentityHashFieldInObjectHeader()) { + if (isIdentityHashFieldInObjectHeader) { objectPointer.writeInt(ol.getObjectHeaderIdentityHashOffset(), 0, LocationIdentity.INIT_LOCATION); } } @@ -158,10 +160,11 @@ public void initializeHeaderOfNewObject(Pointer objectPointer, Word encodedHub) @Override public boolean hasOptionalIdentityHashField(Word header) { if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "use only when optional hashcode fields are support"); + ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "use only when hashcode fields are optional"); } else { - VMError.guarantee(isIdentityHasFieldOptional(), "use only when optional hashcode fields are support"); + VMError.guarantee(isIdentityHasFieldOptional(), "use only when hashcode fields are optional"); } + UnsignedWord inFieldState = IDHASH_STATE_IN_FIELD.shiftLeft(IDHASH_STATE_SHIFT); return header.and(IDHASH_STATE_BITS).equal(inFieldState); } @@ -195,11 +198,12 @@ void setIdentityHashInField(Object o) { @Override public void setIdentityHashFromAddress(Pointer ptr, Word currentHeader) { if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "must always access field"); + ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "use only when hashcode fields are optional"); } else { - VMError.guarantee(isIdentityHasFieldOptional()); - assert !hasIdentityHashFromAddress(currentHeader); + assert isIdentityHasFieldOptional() : "use only when hashcode fields are optional"; + assert !hasIdentityHashFromAddress(currentHeader) : "must not already have a hashcode"; } + UnsignedWord fromAddressState = IDHASH_STATE_FROM_ADDRESS.shiftLeft(IDHASH_STATE_SHIFT); UnsignedWord newHeader = currentHeader.and(IDHASH_STATE_BITS.not()).or(fromAddressState); writeHeaderToObject(ptr.toObjectNonNull(), newHeader); @@ -218,14 +222,16 @@ public boolean hasIdentityHashFromAddress(Word header) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) static boolean hasIdentityHashFromAddressInline(Word header) { if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "must always access field"); + ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "use only when hashcode fields are optional"); } else { assert isIdentityHasFieldOptional(); } + UnsignedWord fromAddressState = IDHASH_STATE_FROM_ADDRESS.shiftLeft(IDHASH_STATE_SHIFT); return header.and(IDHASH_STATE_BITS).equal(fromAddressState); } + @AlwaysInline(value = "Helper method that needs to be optimized away.") @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) private static void dynamicAssert(boolean condition, String msg) { if (GraalDirectives.inIntrinsic()) { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java index f30b54b74744..83b27eff8126 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/Space.java @@ -28,6 +28,7 @@ import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.VERY_SLOW_PATH_PROBABILITY; import static jdk.graal.compiler.nodes.extended.BranchProbabilityNode.probability; +import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/NonmovableArrays.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/NonmovableArrays.java index 05a11db9ecf5..1b765e534da2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/NonmovableArrays.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/NonmovableArrays.java @@ -28,8 +28,6 @@ import java.nio.ByteBuffer; import java.util.Arrays; -import jdk.graal.compiler.nodes.java.ArrayLengthNode; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -56,6 +54,9 @@ import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.nodes.java.ArrayLengthNode; +import jdk.graal.compiler.word.Word; + /** * Support for allocating and accessing non-moving arrays. Such arrays are safe to access during * garbage collection. They can also be passed between isolates, provided that they do not contain @@ -99,7 +100,7 @@ private static > T createArray(int length, Class ObjectHeader header = Heap.getHeap().getObjectHeader(); Word encodedHeader = header.encodeAsUnmanagedObjectHeader(hub); - header.initializeHeaderOfNewObject(array, encodedHeader); + header.initializeHeaderOfNewObject(array, encodedHeader, true); array.writeInt(ConfigurationValues.getObjectLayout().getArrayLengthOffset(), length); // already zero-initialized thanks to calloc() diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java index c7f85dfc5e18..e4bdf93c3f9a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java @@ -25,8 +25,6 @@ package com.oracle.svm.core.config; import org.graalvm.nativeimage.AnnotationAccess; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.constant.CEnum; import org.graalvm.word.WordBase; @@ -49,8 +47,8 @@ *
      *
    1. In the object header, at a fixed offset for all objects (see * {@link #isIdentityHashFieldInObjectHeader()}).
    2. - *
    3. In a synthetic field (outside the object header), at a type or object specific offset (see - * {@link #isIdentityHashFieldSynthetic()}).
    4. + *
    5. At a type specific offset, potentially outside the object header (see + * {@link #isIdentityHashFieldAtTypeSpecificOffset()}).
    6. *
    7. Outside the object header, at a type and object state specific offset (see * {@link #isIdentityHashFieldOptional()}).
    8. *
    @@ -66,19 +64,17 @@ public final class ObjectLayout { private final int arrayLengthOffset; private final int arrayBaseOffset; private final int objectHeaderIdentityHashOffset; - private final boolean isIdentityHashFieldSynthetic; - private final boolean isIdentityHashFieldOptional; + private final int identityHashMode; public ObjectLayout(SubstrateTargetDescription target, int referenceSize, int objectAlignment, int hubOffset, int firstFieldOffset, int arrayLengthOffset, int arrayBaseOffset, - int identityHashOffset, IdentityHashPosition identityHashPos) { + int headerIdentityHashOffset, IdentityHashMode identityHashMode) { assert CodeUtil.isPowerOf2(referenceSize) : referenceSize; assert CodeUtil.isPowerOf2(objectAlignment) : objectAlignment; assert arrayLengthOffset % Integer.BYTES == 0; assert hubOffset < firstFieldOffset && hubOffset < arrayLengthOffset : hubOffset; - assert arrayLengthOffset % Integer.BYTES == 0; - assert identityHashPos == IdentityHashPosition.OBJECT_HEADER || identityHashPos == IdentityHashPosition.SYNTHETIC_FIELD || identityHashPos == IdentityHashPosition.OPTIONAL; - assert (identityHashPos == IdentityHashPosition.OBJECT_HEADER && identityHashOffset > 0 && identityHashOffset < arrayLengthOffset && identityHashOffset % Integer.BYTES == 0) || - (identityHashPos != IdentityHashPosition.OBJECT_HEADER && identityHashOffset == -1); + assert identityHashMode == IdentityHashMode.OBJECT_HEADER || identityHashMode == IdentityHashMode.TYPE_SPECIFIC || identityHashMode == IdentityHashMode.OPTIONAL; + assert (identityHashMode != IdentityHashMode.OPTIONAL && headerIdentityHashOffset > 0 && headerIdentityHashOffset < arrayLengthOffset && headerIdentityHashOffset % Integer.BYTES == 0) || + (identityHashMode == IdentityHashMode.OPTIONAL && headerIdentityHashOffset == -1); this.target = target; this.referenceSize = referenceSize; @@ -88,10 +84,8 @@ public ObjectLayout(SubstrateTargetDescription target, int referenceSize, int ob this.firstFieldOffset = firstFieldOffset; this.arrayLengthOffset = arrayLengthOffset; this.arrayBaseOffset = arrayBaseOffset; - this.objectHeaderIdentityHashOffset = identityHashOffset; - this.isIdentityHashFieldSynthetic = identityHashPos == IdentityHashPosition.SYNTHETIC_FIELD; - this.isIdentityHashFieldOptional = identityHashPos == IdentityHashPosition.OPTIONAL; - + this.objectHeaderIdentityHashOffset = headerIdentityHashOffset; + this.identityHashMode = identityHashMode.value; } /** The minimum alignment of objects (instances and arrays). */ @@ -112,7 +106,6 @@ public int getReferenceSize() { } /** - * * The size (in bytes) of values with the given kind. */ public int sizeInBytes(JavaKind kind) { @@ -163,32 +156,32 @@ public int getArrayLengthOffset() { return arrayLengthOffset; } + /** + * Indicates whether all objects, including arrays, always contain an identity hash code field + * at a specific offset in the object header. + */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public boolean isIdentityHashFieldOptional() { - return isIdentityHashFieldOptional; + public boolean isIdentityHashFieldInObjectHeader() { + return identityHashMode == IdentityHashMode.OBJECT_HEADER.value; } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public boolean isIdentityHashFieldSynthetic() { - return isIdentityHashFieldSynthetic; + public boolean isIdentityHashFieldAtTypeSpecificOffset() { + return identityHashMode == IdentityHashMode.TYPE_SPECIFIC.value; } - /** - * Indicates whether all objects, including arrays, always contain an identity hash code field - * at a specific offset in the object header. - */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public boolean isIdentityHashFieldInObjectHeader() { - return objectHeaderIdentityHashOffset >= 0; + public boolean isIdentityHashFieldOptional() { + return identityHashMode == IdentityHashMode.OPTIONAL.value; } /** If {@link #isIdentityHashFieldInObjectHeader()}, then returns that offset. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public int getObjectHeaderIdentityHashOffset() { if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.dynamicAssert(isIdentityHashFieldInObjectHeader(), "must check before calling"); + ReplacementsUtil.dynamicAssert(objectHeaderIdentityHashOffset > 0, "must check before calling"); } else { - assert isIdentityHashFieldInObjectHeader(); + assert objectHeaderIdentityHashOffset > 0 : "must check before calling"; } return objectHeaderIdentityHashOffset; } @@ -216,7 +209,7 @@ public long getArrayIdentityHashOffset(JavaKind kind, int length) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public long getArrayIdentityHashOffset(long unalignedSize) { - if (isIdentityHashFieldInObjectHeader()) { + if (isIdentityHashFieldInObjectHeader() || isIdentityHashFieldAtTypeSpecificOffset()) { return getObjectHeaderIdentityHashOffset(); } int align = Integer.BYTES; @@ -226,7 +219,7 @@ public long getArrayIdentityHashOffset(long unalignedSize) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public long computeArrayTotalSize(long unalignedSize, boolean withOptionalIdHashField) { long size = unalignedSize; - if ((withOptionalIdHashField && isIdentityHashFieldOptional()) || isIdentityHashFieldSynthetic()) { + if (withOptionalIdHashField && isIdentityHashFieldOptional()) { size = getArrayIdentityHashOffset(size) + Integer.BYTES; } return alignUp(size); @@ -234,7 +227,7 @@ public long computeArrayTotalSize(long unalignedSize, boolean withOptionalIdHash public int getMinImageHeapInstanceSize() { int unalignedSize = firstFieldOffset; // assumes no always-present "synthetic fields" - if (!isIdentityHashFieldInObjectHeader()) { + if (isIdentityHashFieldAtTypeSpecificOffset() || isIdentityHashFieldOptional()) { int idHashOffset = NumUtil.roundUp(unalignedSize, Integer.BYTES); unalignedSize = idHashOffset + Integer.BYTES; } @@ -259,13 +252,18 @@ public static JavaKind getCallSignatureKind(boolean isEntryPoint, ResolvedJavaTy return type.getJavaKind(); } - @Platforms(Platform.HOSTED_ONLY.class) - public enum IdentityHashPosition { + public enum IdentityHashMode { /* At a fixed offset, for all objects (part of the object header). */ - OBJECT_HEADER, - /* At a type-specific offset (outside the object header). */ - SYNTHETIC_FIELD, + OBJECT_HEADER(0), + /* At a type-specific offset (potentially outside the object header). */ + TYPE_SPECIFIC(1), /* At a type and object-state specific offset (outside the object header). */ - OPTIONAL + OPTIONAL(2); + + final int value; + + IdentityHashMode(int value) { + this.value = value; + } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java index bdea4a2e80fd..8c9e3d18594f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneSnippets.java @@ -172,8 +172,8 @@ private static Object doClone(Object original) throws CloneNotSupportedException BarrieredAccess.writeObject(result, monitorOffset, null); } - /* Reset identity hashcode if we use a synthetic field. */ - if (ConfigurationValues.getObjectLayout().isIdentityHashFieldSynthetic()) { + /* Reset identity hashcode if it is potentially outside the object header. */ + if (ConfigurationValues.getObjectLayout().isIdentityHashFieldAtTypeSpecificOffset()) { int offset = LayoutEncoding.getIdentityHashOffset(result); ObjectAccess.writeInt(result, offset, 0); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java index 59069a1870e1..a33e289fdd22 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java @@ -420,25 +420,6 @@ protected final int instanceHeaderSize() { return ConfigurationValues.getObjectLayout().getFirstFieldOffset(); } - @Override - protected UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize) { - /* - * We do an unsigned multiplication so that a negative array length will result in an array - * size greater than Integer.MAX_VALUE. - */ - long size = ((length & 0xFFFFFFFFL) << log2ElementSize) + arrayBaseOffset; - - /* Add the identity hashcode field if necessary. */ - if (ConfigurationValues.getObjectLayout().isIdentityHashFieldSynthetic()) { - int align = Integer.BYTES; - size = (size + align - 1) & -align; - size += Integer.BYTES; - } - - size = (size + objectAlignment() - 1) & -objectAlignment(); - return WordFactory.unsigned(size); - } - @Fold public static int afterArrayLengthOffset() { return ConfigurationValues.getObjectLayout().getArrayLengthOffset() + ConfigurationValues.getObjectLayout().sizeInBytes(JavaKind.Int); @@ -465,15 +446,16 @@ protected final Object verifyOop(Object obj) { return obj; } - private static int objectAlignment() { - return ConfigurationValues.getObjectLayout().getAlignment(); - } - @Override public final int arrayLengthOffset() { return ConfigurationValues.getObjectLayout().getArrayLengthOffset(); } + @Override + protected final int objectAlignment() { + return ConfigurationValues.getObjectLayout().getAlignment(); + } + public static int getArrayBaseOffset(int layoutEncoding) { return (int) LayoutEncoding.getArrayBaseOffset(layoutEncoding).rawValue(); } @@ -510,8 +492,8 @@ protected final Object callNewMultiArrayStub(Word objectHeader, int rank, Word d private static native Object callNewMultiArray(@ConstantNodeParameter ForeignCallDescriptor descriptor, Word hub, int rank, Word dimensions); @Override - public void initializeObjectHeader(Word memory, Word objectHeader, boolean isArray) { - Heap.getHeap().getObjectHeader().initializeHeaderOfNewObject(memory, objectHeader); + public void initializeObjectHeader(Word memory, Word objectHeader, boolean isArrayLike) { + Heap.getHeap().getObjectHeader().initializeHeaderOfNewObject(memory, objectHeader, isArrayLike); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/FillerObject.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/FillerObject.java index 53cd96dd793c..1bb5f325f25d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/FillerObject.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/FillerObject.java @@ -24,7 +24,10 @@ */ package com.oracle.svm.core.heap; -/** The smallest possible instance object, for filling in gaps in the heap. */ +/** + * The smallest possible instance object, for filling in gaps in the heap. Note that these instances + * may be larger than the object alignment. + */ public final class FillerObject { /** * This field is registered as accessed with the analysis and ensures that the class is diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectHeader.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectHeader.java index 0c443fb4ebbd..3f634de041d5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectHeader.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectHeader.java @@ -24,9 +24,6 @@ */ package com.oracle.svm.core.heap; -import jdk.graal.compiler.api.replacements.Fold; -import jdk.graal.compiler.word.ObjectAccess; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; @@ -38,6 +35,10 @@ import com.oracle.svm.core.image.ImageHeapObject; import com.oracle.svm.core.snippets.KnownIntrinsics; +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.word.ObjectAccess; +import jdk.graal.compiler.word.Word; + /** * An object header is a reference-sized collection of bits in each object instance. The object * header holds a reference to a {@link DynamicHub}, which identifies the class of the instance. It @@ -107,7 +108,7 @@ public Pointer readPotentialDynamicHubFromPointer(Pointer ptr) { public abstract Pointer extractPotentialDynamicHubFromHeader(Word header); @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public abstract void initializeHeaderOfNewObject(Pointer ptr, Word header); + public abstract void initializeHeaderOfNewObject(Pointer ptr, Word header, boolean isArrayLike); @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public boolean pointsToObjectHeader(Pointer ptr) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index d56f67270952..425a4b6d9f09 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -630,7 +630,11 @@ public int getMonitorOffset() { return monitorOffset; } - /** If possible, use {@link LayoutEncoding#getIdentityHashOffset(Object)} instead. */ + /** + * If possible, use {@link LayoutEncoding#getIdentityHashOffset(Object)} instead. If the hash + * code field is optional, note that this method may return an offset that is outside the bounds + * of a freshly the object. + */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public int getIdentityHashOffset() { ObjectLayout ol = ConfigurationValues.getObjectLayout(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/Hybrid.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/Hybrid.java index 5e4f66e0c4c8..a5d88f74cdd4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/Hybrid.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/Hybrid.java @@ -37,7 +37,7 @@ * *
      *    +--------------------------------------------------+
    - *    | object header (see HostedConfiguration)          |
    + *    | object header (same header as for arrays)        |
      *    +--------------------------------------------------+
      *    | array length                                     |
      *    +--------------------------------------------------+
    diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java
    index 56eef67e35e8..5e66f82ff986 100644
    --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java
    +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java
    @@ -203,7 +203,8 @@ public static UnsignedWord getPureInstanceSize(DynamicHub hub, boolean withOptio
             ObjectLayout ol = ConfigurationValues.getObjectLayout();
             if (withOptionalIdHashField && ol.isIdentityHashFieldOptional()) {
                 int afterIdHashField = hub.getIdentityHashOffset() + Integer.BYTES;
    -            if (size.belowThan(afterIdHashField)) { // fits in a gap between fields
    +            if (size.belowThan(afterIdHashField)) {
    +                /* Identity hash is at the end of the object and does not fit in a gap. */
                     size = WordFactory.unsigned(ol.alignUp(afterIdHashField));
                 }
             }
    @@ -303,7 +304,7 @@ public static int getIdentityHashOffset(Object obj) {
     
             DynamicHub hub = KnownIntrinsics.readHub(obj);
             int encoding = hub.getLayoutEncoding();
    -        if (isArrayLike(encoding)) {
    +        if (ol.isIdentityHashFieldOptional() && isArrayLike(encoding)) {
                 long unalignedSize = getArrayElementOffset(encoding, ArrayLengthNode.arrayLength(obj)).rawValue();
                 return (int) ol.getArrayIdentityHashOffset(unalignedSize);
             } else {
    diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java
    index c3781115f93e..8384c4ac1b04 100644
    --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java
    +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/identityhashcode/IdentityHashCodeSupport.java
    @@ -75,7 +75,7 @@ public static IdentityHashCodeSnippets.Templates createSnippetTemplates(OptionVa
         @SubstrateForeignCallTarget(stubCallingConvention = false)
         public static int generateIdentityHashCode(Object obj) {
             ObjectLayout ol = ConfigurationValues.getObjectLayout();
    -        VMError.guarantee(!ol.isIdentityHashFieldOptional(), "Snippet must handle this case");
    +        VMError.guarantee(!ol.isIdentityHashFieldOptional(), "Optional hash is handled in snippet");
     
             int newHashCode = generateRandomHashCode();
             int offset = LayoutEncoding.getIdentityHashOffset(obj);
    diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java
    index fd937d97d0ea..bfaeb872e6cd 100644
    --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java
    +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java
    @@ -48,7 +48,7 @@
     import com.oracle.svm.core.SubstrateTargetDescription;
     import com.oracle.svm.core.config.ConfigurationValues;
     import com.oracle.svm.core.config.ObjectLayout;
    -import com.oracle.svm.core.config.ObjectLayout.IdentityHashPosition;
    +import com.oracle.svm.core.config.ObjectLayout.IdentityHashMode;
     import com.oracle.svm.core.graal.code.SubstrateMetaAccessExtensionProvider;
     import com.oracle.svm.core.graal.meta.RuntimeConfiguration;
     import com.oracle.svm.core.monitor.MultiThreadedMonitorSupport;
    @@ -101,21 +101,21 @@ public static void setDefaultIfEmpty() {
                 CompressEncoding compressEncoding = new CompressEncoding(SubstrateOptions.SpawnIsolates.getValue() ? 1 : 0, 0);
                 ImageSingletons.add(CompressEncoding.class, compressEncoding);
     
    -            ObjectLayout objectLayout = createObjectLayout(IdentityHashPosition.SYNTHETIC_FIELD);
    +            ObjectLayout objectLayout = createObjectLayout(IdentityHashMode.TYPE_SPECIFIC);
                 ImageSingletons.add(ObjectLayout.class, objectLayout);
     
                 ImageSingletons.add(HybridLayoutSupport.class, new HybridLayoutSupport());
             }
         }
     
    -    public static ObjectLayout createObjectLayout(IdentityHashPosition identityHashCodeMode) {
    -        return createObjectLayout(JavaKind.Object, identityHashCodeMode);
    +    public static ObjectLayout createObjectLayout(IdentityHashMode identityHashMode) {
    +        return createObjectLayout(JavaKind.Object, identityHashMode);
         }
     
         /**
    -     * Defines the layout of objects. The monitor slot and the identity hash code fields are
    -     * appended to the individual objects (unless there is an otherwise unused gap in the object
    -     * that can be used).
    +     * Defines the serial/epsilon GC object layout. The monitor slot and the identity hash code
    +     * fields are appended to instance objects (unless there is an otherwise unused gap in the
    +     * object that can be used).
          *
          * The layout of instance objects is:
          * 
      @@ -128,12 +128,12 @@ public static ObjectLayout createObjectLayout(IdentityHashPosition identityHashC * The layout of array objects is: *
        *
      • 64 bit hub reference
      • + *
      • 32 bit identity hashcode
      • *
      • 32 bit array length
      • - *
      • array elements (length * reference or primitive)
      • - *
      • 32 bit identity hashcode (if needed)
      • + *
      • array elements (length * elementSize)
      • *
      */ - public static ObjectLayout createObjectLayout(JavaKind referenceKind, IdentityHashPosition identityHashCodePosition) { + public static ObjectLayout createObjectLayout(JavaKind referenceKind, IdentityHashMode identityHashMode) { SubstrateTargetDescription target = ConfigurationValues.getTarget(); int referenceSize = target.arch.getPlatformKind(referenceKind).getSizeInBytes(); int intSize = target.arch.getPlatformKind(JavaKind.Int).getSizeInBytes(); @@ -142,22 +142,24 @@ public static ObjectLayout createObjectLayout(JavaKind referenceKind, IdentityHa int hubOffset = 0; int headerSize = hubOffset + referenceSize; - int objectHeaderIdentityHashOffset; - if (identityHashCodePosition == IdentityHashPosition.OBJECT_HEADER) { - objectHeaderIdentityHashOffset = headerSize; + int extraArrayHeaderSize = 0; + int headerIdentityHashOffset = headerSize; + if (identityHashMode == IdentityHashMode.OBJECT_HEADER) { headerSize += intSize; + } else if (identityHashMode == IdentityHashMode.TYPE_SPECIFIC) { + extraArrayHeaderSize = intSize; } else { - assert identityHashCodePosition == IdentityHashPosition.SYNTHETIC_FIELD || identityHashCodePosition == IdentityHashPosition.OPTIONAL; - objectHeaderIdentityHashOffset = -1; + assert identityHashMode == IdentityHashMode.OPTIONAL; + headerIdentityHashOffset = -1; } headerSize += SubstrateOptions.AdditionalHeaderBytes.getValue(); int firstFieldOffset = headerSize; - int arrayLengthOffset = headerSize; + int arrayLengthOffset = headerSize + extraArrayHeaderSize; int arrayBaseOffset = arrayLengthOffset + intSize; - return new ObjectLayout(target, referenceSize, objectAlignment, hubOffset, firstFieldOffset, arrayLengthOffset, arrayBaseOffset, objectHeaderIdentityHashOffset, identityHashCodePosition); + return new ObjectLayout(target, referenceSize, objectAlignment, hubOffset, firstFieldOffset, arrayLengthOffset, arrayBaseOffset, headerIdentityHashOffset, identityHashMode); } public SVMHost createHostVM(OptionValues options, ClassLoader classLoader, ClassInitializationSupport classInitializationSupport, diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 76fd3cbfa28e..7f984272f9d2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -316,7 +316,6 @@ import jdk.graal.compiler.replacements.TargetGraphBuilderPlugins; import jdk.graal.compiler.word.WordOperationPlugin; import jdk.graal.compiler.word.WordTypes; -import jdk.graal.compiler.word.WordTypes; import jdk.vm.ci.aarch64.AArch64; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.code.Architecture; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodSupport.java index f06138551552..6842bb550c47 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodSupport.java @@ -98,7 +98,7 @@ static PodSupport singleton() { boolean isPodClass(Class clazz); - boolean mustReserveLengthField(Class clazz); + boolean mustReserveArrayFields(Class clazz); } @AutomaticallyRegisteredFeature @@ -114,7 +114,7 @@ final class PodFeature implements PodSupport, InternalFeature { * other fields where the length field must be. If a pod subclasses {@link Object} itself, it is * itself included in this set. */ - private final Set> classesNeedingLengthFields = ConcurrentHashMap.newKeySet(); + private final Set> classesNeedingArrayFields = ConcurrentHashMap.newKeySet(); private BeforeAnalysisAccess analysisAccess; private volatile boolean instantiated = false; @@ -189,7 +189,7 @@ public void registerSuperclass(Class superClass, Class factoryInterface) { while (sup.getSuperclass() != Object.class) { sup = sup.getSuperclass(); } - classesNeedingLengthFields.add(sup); + classesNeedingArrayFields.add(sup); } /** @@ -278,8 +278,8 @@ public boolean isPodClass(Class clazz) { } @Override - public boolean mustReserveLengthField(Class clazz) { - return classesNeedingLengthFields.contains(clazz); + public boolean mustReserveArrayFields(Class clazz) { + return classesNeedingArrayFields.contains(clazz); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index d8398029edc8..01b93643d288 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -25,6 +25,9 @@ */ package com.oracle.svm.hosted.image; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.CONTRACT; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND; + import java.lang.reflect.Modifier; import java.nio.file.Path; import java.util.ArrayList; @@ -39,8 +42,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.vm.ci.code.CallingConvention; -import jdk.vm.ci.meta.AllocatableValue; import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.struct.CPointerTo; @@ -58,6 +59,7 @@ import com.oracle.svm.core.code.CompilationResultFrameTree.FrameNode; import com.oracle.svm.core.code.CompilationResultFrameTree.Visitor; import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.graal.code.SubstrateBackend.SubstrateMarkId; import com.oracle.svm.core.graal.meta.RuntimeConfiguration; import com.oracle.svm.core.image.ImageHeapPartition; @@ -95,9 +97,11 @@ import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.BytecodePosition; +import jdk.vm.ci.code.CallingConvention; import jdk.vm.ci.code.Register; import jdk.vm.ci.code.RegisterValue; import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.meta.AllocatableValue; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaValue; @@ -111,9 +115,6 @@ import jdk.vm.ci.meta.Signature; import jdk.vm.ci.meta.Value; -import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.CONTRACT; -import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND; - /** * Implementation of the DebugInfoProvider API interface that allows type, code and heap data info * to be passed to an ObjectFile when generation of debug info is enabled. @@ -428,15 +429,17 @@ public Path filePath() { } private Stream computeHeaderTypeInfo() { + ObjectLayout ol = getObjectLayout(); + List infos = new LinkedList<>(); - int hubOffset = getObjectLayout().getHubOffset(); + int hubOffset = ol.getHubOffset(); int hubFieldSize = referenceSize; int objHeaderSize = hubOffset + hubFieldSize; - int idHashSize = getObjectLayout().sizeInBytes(JavaKind.Int); + int idHashSize = ol.sizeInBytes(JavaKind.Int); int fixedIdHashOffset = -1; - if (getObjectLayout().isIdentityHashFieldInObjectHeader()) { - fixedIdHashOffset = getObjectLayout().getObjectHeaderIdentityHashOffset(); + if (ol.isIdentityHashFieldInObjectHeader()) { + fixedIdHashOffset = ol.getObjectHeaderIdentityHashOffset(); objHeaderSize = Math.max(objHeaderSize, fixedIdHashOffset + idHashSize); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java index f2503682b51c..b2d1ea99dca2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java @@ -392,6 +392,7 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { } else { idHashOffset = ((HostedInstanceClass) clazz).getIdentityHashOffset(); } + assert idHashOffset > 0; bufferBytes.putInt(info.getIndexInBuffer(idHashOffset), info.getIdentityHashCode()); } else if (clazz.isArray()) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index 23a6e629e634..8995f0322ac1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -428,13 +428,14 @@ private void layoutInstanceFields() { layoutInstanceFields(hUniverse.getObjectClass(), new HostedField[0], usedBytes); } - private static boolean mustReserveLengthField(HostedInstanceClass clazz) { - if (PodSupport.isPresent() && PodSupport.singleton().mustReserveLengthField(clazz.getJavaClass())) { + private static boolean mustReserveArrayFields(HostedInstanceClass clazz) { + if (PodSupport.isPresent() && PodSupport.singleton().mustReserveArrayFields(clazz.getJavaClass())) { return true; } if (HybridLayout.isHybrid(clazz)) { - // A pod ancestor subclassing Object must have already reserved a length field, unless - // the pod subclasses Object itself, in which case we would have returned true earlier. + // A pod ancestor subclassing Object must have already reserved memory for the array + // fields, unless the pod subclasses Object itself, in which case we would have returned + // true earlier. return !PodSupport.isPresent() || !PodSupport.singleton().isPodClass(clazz.getJavaClass()); } return false; @@ -458,16 +459,21 @@ private void layoutInstanceFields(HostedInstanceClass clazz, HostedField[] super HostedConfiguration.instance().findAllFieldsForLayout(hUniverse, hMetaAccess, hUniverse.fields, rawFields, allFields, clazz); - if (mustReserveLengthField(clazz)) { - int lengthOffset = layout.getArrayLengthOffset(); - int lengthSize = layout.sizeInBytes(JavaKind.Int); - reserve(usedBytes, lengthOffset, lengthSize); + if (mustReserveArrayFields(clazz)) { + int intSize = layout.sizeInBytes(JavaKind.Int); + int firstFieldOffset = layout.getFirstFieldOffset(); + int afterArrayLengthOffset = layout.getArrayLengthOffset() + intSize; - // Type check fields in DynamicHub. + /* Reserve the extra memory that array fields may use (at least the array length). */ + int arrayFieldBytes = afterArrayLengthOffset - firstFieldOffset; + assert arrayFieldBytes >= intSize; + reserve(usedBytes, firstFieldOffset, arrayFieldBytes); + + /* Type check fields in DynamicHub. */ if (clazz.equals(hMetaAccess.lookupJavaType(DynamicHub.class))) { /* Each type check id slot is 2 bytes. */ int slotsSize = typeCheckBuilder.getNumTypeCheckSlots() * 2; - reserve(usedBytes, lengthOffset + lengthSize, slotsSize); + reserve(usedBytes, afterArrayLengthOffset, slotsSize); } } @@ -534,24 +540,30 @@ private void layoutInstanceFields(HostedInstanceClass clazz, HostedField[] super int afterFieldsOffset = usedBytes.length(); // Identity hash code - if (layout.isIdentityHashFieldInObjectHeader()) { + if (clazz.isAbstract()) { + /* No identity hash field needed. */ + } else if (layout.isIdentityHashFieldInObjectHeader()) { clazz.setIdentityHashOffset(layout.getObjectHeaderIdentityHashOffset()); - } else if (!clazz.isAbstract()) { - if (layout.isIdentityHashFieldSynthetic() || (layout.isIdentityHashFieldOptional() && !HybridLayout.isHybrid(clazz))) { - // place in gap if any, or append - int hashSize = Integer.BYTES; - int endOffset = usedBytes.length(); - int offset = findGapForField(usedBytes, 0, hashSize, endOffset); - if (offset == -1) { - offset = endOffset + getAlignmentAdjustment(endOffset, hashSize); - if (!layout.isIdentityHashFieldOptional()) { - /* Include the identity hashcode field in the instance hashSize. */ - afterFieldsOffset = offset + hashSize; - } + } else if (HybridLayout.isHybrid(clazz)) { + if (layout.isIdentityHashFieldAtTypeSpecificOffset()) { + clazz.setIdentityHashOffset(layout.getObjectHeaderIdentityHashOffset()); + } else { + /* Nothing to do - will be treated like an array. */ + } + } else if (layout.isIdentityHashFieldAtTypeSpecificOffset() || layout.isIdentityHashFieldOptional()) { + /* Add a synthetic field (in gap if any, or append). */ + int hashSize = Integer.BYTES; + int endOffset = usedBytes.length(); + int offset = findGapForField(usedBytes, 0, hashSize, endOffset); + if (offset == -1) { + offset = endOffset + getAlignmentAdjustment(endOffset, hashSize); + if (layout.isIdentityHashFieldAtTypeSpecificOffset()) { + /* Include the identity hashcode field in the instance size. */ + afterFieldsOffset = offset + hashSize; } - reserve(usedBytes, offset, hashSize); - clazz.setIdentityHashOffset(offset); } + reserve(usedBytes, offset, hashSize); + clazz.setIdentityHashOffset(offset); } clazz.instanceFieldsWithoutSuper = allFields.toArray(new HostedField[0]); @@ -1043,12 +1055,13 @@ private void buildHubs() { } monitorOffset = instanceClass.getMonitorFieldOffset(); identityHashOffset = instanceClass.getIdentityHashOffset(); - assert !ol.isIdentityHashFieldInObjectHeader() || identityHashOffset == ol.getObjectHeaderIdentityHashOffset(); } else if (type.isArray()) { JavaKind storageKind = type.getComponentType().getStorageKind(); boolean isObject = (storageKind == JavaKind.Object); layoutHelper = LayoutEncoding.forArray(type, isObject, ol.getArrayBaseOffset(storageKind), ol.getArrayIndexShift(storageKind)); - identityHashOffset = ol.isIdentityHashFieldInObjectHeader() ? ol.getObjectHeaderIdentityHashOffset() : -1; + if (ol.isIdentityHashFieldInObjectHeader() || ol.isIdentityHashFieldAtTypeSpecificOffset()) { + identityHashOffset = NumUtil.safeToInt(ol.getArrayIdentityHashOffset(0L)); + } } else if (type.isInterface()) { layoutHelper = LayoutEncoding.forInterface(); } else if (type.isPrimitive()) { From d84e1ed70392bfa53506381cd98606990528fd72 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Thu, 16 Nov 2023 17:37:30 +0100 Subject: [PATCH 4/5] Minor cleanups and documentation changes. --- .../core/genscavenge/ObjectHeaderImpl.java | 20 +++++++++---------- .../oracle/svm/core/config/ObjectLayout.java | 10 +++++----- .../com/oracle/svm/core/hub/DynamicHub.java | 2 +- .../svm/hosted/meta/UniverseBuilder.java | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java index 3af14d157989..6c414ee63a13 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ObjectHeaderImpl.java @@ -90,7 +90,7 @@ public final class ObjectHeaderImpl extends ObjectHeader { numAlignmentBits = CodeUtil.log2(ConfigurationValues.getObjectLayout().getAlignment()); int numMinimumReservedBits = 3; VMError.guarantee(numMinimumReservedBits <= numAlignmentBits, "Minimum set of reserved bits must be provided by object alignment"); - if (isIdentityHasFieldOptional()) { + if (isIdentityHashFieldOptional()) { VMError.guarantee(ReferenceAccess.singleton().haveCompressedReferences(), "Ensures hubs (at the start of the image heap) remain addressable"); numReservedBits = numMinimumReservedBits + 2; VMError.guarantee(numReservedBits <= numAlignmentBits || hasShift(), @@ -160,9 +160,9 @@ public void initializeHeaderOfNewObject(Pointer objectPointer, Word encodedHub, @Override public boolean hasOptionalIdentityHashField(Word header) { if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "use only when hashcode fields are optional"); + ReplacementsUtil.staticAssert(isIdentityHashFieldOptional(), "use only when hashcode fields are optional"); } else { - VMError.guarantee(isIdentityHasFieldOptional(), "use only when hashcode fields are optional"); + VMError.guarantee(isIdentityHashFieldOptional(), "use only when hashcode fields are optional"); } UnsignedWord inFieldState = IDHASH_STATE_IN_FIELD.shiftLeft(IDHASH_STATE_SHIFT); @@ -172,7 +172,7 @@ public boolean hasOptionalIdentityHashField(Word header) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) void setIdentityHashInField(Object o) { assert VMOperation.isGCInProgress(); - VMError.guarantee(isIdentityHasFieldOptional()); + VMError.guarantee(isIdentityHashFieldOptional()); UnsignedWord oldHeader = readHeaderFromObject(o); UnsignedWord inFieldState = IDHASH_STATE_IN_FIELD.shiftLeft(IDHASH_STATE_SHIFT); UnsignedWord newHeader = oldHeader.and(IDHASH_STATE_BITS.not()).or(inFieldState); @@ -198,9 +198,9 @@ void setIdentityHashInField(Object o) { @Override public void setIdentityHashFromAddress(Pointer ptr, Word currentHeader) { if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "use only when hashcode fields are optional"); + ReplacementsUtil.staticAssert(isIdentityHashFieldOptional(), "use only when hashcode fields are optional"); } else { - assert isIdentityHasFieldOptional() : "use only when hashcode fields are optional"; + assert isIdentityHashFieldOptional() : "use only when hashcode fields are optional"; assert !hasIdentityHashFromAddress(currentHeader) : "must not already have a hashcode"; } @@ -222,9 +222,9 @@ public boolean hasIdentityHashFromAddress(Word header) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) static boolean hasIdentityHashFromAddressInline(Word header) { if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.staticAssert(isIdentityHasFieldOptional(), "use only when hashcode fields are optional"); + ReplacementsUtil.staticAssert(isIdentityHashFieldOptional(), "use only when hashcode fields are optional"); } else { - assert isIdentityHasFieldOptional(); + assert isIdentityHashFieldOptional(); } UnsignedWord fromAddressState = IDHASH_STATE_FROM_ADDRESS.shiftLeft(IDHASH_STATE_SHIFT); @@ -317,7 +317,7 @@ public long encodeAsImageHeapObjectHeader(ImageHeapObject obj, long hubOffsetFro assert obj.getPartition() instanceof FillerObjectDummyPartition; } } - if (isIdentityHasFieldOptional()) { + if (isIdentityHashFieldOptional()) { header |= (IDHASH_STATE_IN_FIELD.rawValue() << IDHASH_STATE_SHIFT); } return header; @@ -435,7 +435,7 @@ static boolean hasShift() { } @Fold - static boolean isIdentityHasFieldOptional() { + static boolean isIdentityHashFieldOptional() { return ConfigurationValues.getObjectLayout().isIdentityHashFieldOptional(); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java index e4bdf93c3f9a..9165ea1f2bb0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java @@ -49,8 +49,10 @@ * {@link #isIdentityHashFieldInObjectHeader()}). *
    • At a type specific offset, potentially outside the object header (see * {@link #isIdentityHashFieldAtTypeSpecificOffset()}).
    • - *
    • Outside the object header, at a type and object state specific offset (see - * {@link #isIdentityHashFieldOptional()}).
    • + *
    • Outside the object header, at a type- or object-specific offset (see + * {@link #isIdentityHashFieldOptional()}). Note that the field is not part of every object. When an + * object needs the field, the object is resized during garbage collection to accommodate the + * field.
    • *
*/ public final class ObjectLayout { @@ -72,7 +74,6 @@ public ObjectLayout(SubstrateTargetDescription target, int referenceSize, int ob assert CodeUtil.isPowerOf2(objectAlignment) : objectAlignment; assert arrayLengthOffset % Integer.BYTES == 0; assert hubOffset < firstFieldOffset && hubOffset < arrayLengthOffset : hubOffset; - assert identityHashMode == IdentityHashMode.OBJECT_HEADER || identityHashMode == IdentityHashMode.TYPE_SPECIFIC || identityHashMode == IdentityHashMode.OPTIONAL; assert (identityHashMode != IdentityHashMode.OPTIONAL && headerIdentityHashOffset > 0 && headerIdentityHashOffset < arrayLengthOffset && headerIdentityHashOffset % Integer.BYTES == 0) || (identityHashMode == IdentityHashMode.OPTIONAL && headerIdentityHashOffset == -1); @@ -175,7 +176,6 @@ public boolean isIdentityHashFieldOptional() { return identityHashMode == IdentityHashMode.OPTIONAL.value; } - /** If {@link #isIdentityHashFieldInObjectHeader()}, then returns that offset. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public int getObjectHeaderIdentityHashOffset() { if (GraalDirectives.inIntrinsic()) { @@ -257,7 +257,7 @@ public enum IdentityHashMode { OBJECT_HEADER(0), /* At a type-specific offset (potentially outside the object header). */ TYPE_SPECIFIC(1), - /* At a type and object-state specific offset (outside the object header). */ + /* At a type- or object-specific offset (outside the object header). */ OPTIONAL(2); final int value; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index 425a4b6d9f09..81bd810f3081 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -633,7 +633,7 @@ public int getMonitorOffset() { /** * If possible, use {@link LayoutEncoding#getIdentityHashOffset(Object)} instead. If the hash * code field is optional, note that this method may return an offset that is outside the bounds - * of a freshly the object. + * of a newly allocated object. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public int getIdentityHashOffset() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index 8995f0322ac1..579eeef276cc 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -1060,7 +1060,7 @@ private void buildHubs() { boolean isObject = (storageKind == JavaKind.Object); layoutHelper = LayoutEncoding.forArray(type, isObject, ol.getArrayBaseOffset(storageKind), ol.getArrayIndexShift(storageKind)); if (ol.isIdentityHashFieldInObjectHeader() || ol.isIdentityHashFieldAtTypeSpecificOffset()) { - identityHashOffset = NumUtil.safeToInt(ol.getArrayIdentityHashOffset(0L)); + identityHashOffset = NumUtil.safeToInt(ol.getObjectHeaderIdentityHashOffset()); } } else if (type.isInterface()) { layoutHelper = LayoutEncoding.forInterface(); From 34b13d81041c847b516ee1a07006b86c8ca47f83 Mon Sep 17 00:00:00 2001 From: Christian Haeubl Date: Mon, 20 Nov 2023 15:33:53 +0100 Subject: [PATCH 5/5] Update DebugInfo documentation. --- docs/reference-manual/native-image/DebugInfo.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/reference-manual/native-image/DebugInfo.md b/docs/reference-manual/native-image/DebugInfo.md index fef7e03fef54..2cfc6e7815c9 100644 --- a/docs/reference-manual/native-image/DebugInfo.md +++ b/docs/reference-manual/native-image/DebugInfo.md @@ -154,10 +154,10 @@ So, for example in the DWARF debug info model `java.lang.String` identifies a C+ This class layout type declares the expected fields like `hash` of type `int` and `value` of type `byte[]` and methods like `String(byte[])`, `charAt(int)`, etc. However, the copy constructor which appears in Java as `String(String)` appears in `gdb` with the signature `String(java.lang.String *)`. The C++ layout class inherits fields and methods from class (layout) type `java.lang.Object` using C++ public inheritance. -The latter in turn inherits standard oop (ordinary object pointer) header fields from a special struct class named `_objhdr` which includes two fields. The first field is called -`hub` and its type is `java.lang.Class *` i.e. it is a pointer to the object's -class. The second field is called `idHash` and has type `int`. It stores an -identity hashcode for the object. +The latter in turn inherits standard oop (ordinary object pointer) header fields from a special struct class named `_objhdr` which includes up to two fields (depending on the VM configuration). +The first field is called `hub` and its type is `java.lang.Class *` i.e. it is a pointer to the object's class. +The second field (optional) is called `idHash` and has type `int`. +It stores an identity hashcode for the object. The `ptype` command can be used to print details of a specific type. Note that the Java type name must be specified in quotes because to escape the embedded `.` characters.