diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/StackValue.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/StackValue.java index bef78cd3e115..cf5a0e71e1c0 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/StackValue.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/StackValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -72,7 +72,7 @@ private StackValue() { *
      * ComplexValue numberOnStack = StackValue.get(ComplexValue.class);
      * numberOnStack.realPart(3.0);
-     * numberOnStack.imagineryPart(4.0);
+     * numberOnStack.imaginaryPart(4.0);
      * double absoluteValue = absoluteValue(numberOnStack);
      * assert 5.0 == absoluteValue;
      * 
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CStruct.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CStruct.java index 9cdd26aed243..ea7f2af783cc 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CStruct.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/struct/CStruct.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -67,10 +67,10 @@ * void realPart(double re); * * @CField("im") - * double imagineryPart(); + * double imaginaryPart(); * * @CField("im") - * void imagineryPart(double im); + * void imaginaryPart(double im); * } * * diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompactingOldGeneration.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompactingOldGeneration.java index a7b2df07733d..1c81aed263b6 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompactingOldGeneration.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/CompactingOldGeneration.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.genscavenge; +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; import static com.oracle.svm.core.snippets.KnownIntrinsics.readCallerStackPointer; import org.graalvm.nativeimage.IsolateThread; @@ -52,12 +53,16 @@ import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.ObjectHeader; import com.oracle.svm.core.heap.ObjectVisitor; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.HubType; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.metaspace.Metaspace; +import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.thread.VMThreads; import com.oracle.svm.core.threadlocal.VMThreadLocalSupport; import com.oracle.svm.core.util.Timer; +import jdk.graal.compiler.nodes.java.ArrayLengthNode; import jdk.graal.compiler.word.Word; /** @@ -111,6 +116,7 @@ final class CompactingOldGeneration extends OldGeneration { private final Space space = new Space("Old", "O", false, getAge()); private final MarkStack markStack = new MarkStack(); + private final MarkStack arrayMarkStack = new MarkStack(); private final GreyObjectsWalker toGreyObjectsWalker = new GreyObjectsWalker(); private final PlanningVisitor planningVisitor = new PlanningVisitor(); @@ -158,17 +164,56 @@ boolean scanGreyObjects(boolean incrementalGc) { } toGreyObjectsWalker.walkGreyObjects(); } else { - if (markStack.isEmpty()) { + if (markStack.isEmpty() && arrayMarkStack.isEmpty()) { return false; } GreyToBlackObjectVisitor visitor = GCImpl.getGCImpl().getGreyToBlackObjectVisitor(); do { - visitor.visitObject(markStack.pop()); - } while (!markStack.isEmpty()); + while (!markStack.isEmpty()) { + visitor.visitObject(markStack.popObject()); + } + + // Process array ranges one at a time to avoid bloating the marking stack + if (!arrayMarkStack.isEmpty()) { + scanArrayRange(visitor); + } + } while (!markStack.isEmpty() || !arrayMarkStack.isEmpty()); } return true; } + @AlwaysInline("GC performance") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private void pushOntoMarkStack(Object obj) { + DynamicHub objHub = KnownIntrinsics.readHub(obj); + if (objHub.getHubType() == HubType.OBJECT_ARRAY) { + if (ArrayLengthNode.arrayLength(obj) != 0) { + arrayMarkStack.pushObject(obj); + arrayMarkStack.pushInt(0); + } + } else { + markStack.pushObject(obj); + } + } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private void scanArrayRange(GreyToBlackObjectVisitor visitor) { + int index = arrayMarkStack.popInt(); + Object array = arrayMarkStack.popObject(); + + int length = ArrayLengthNode.arrayLength(array); + final int stride = 2048; + int endIndex = index + stride; + if (endIndex < length) { + arrayMarkStack.pushObject(array); + arrayMarkStack.pushInt(endIndex); + } else { + endIndex = length; + } + + visitor.visitObjectArrayRange(array, index, endIndex - index); + } + @AlwaysInline("GC performance") @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @Override @@ -196,7 +241,7 @@ public Object promoteAlignedObject(Object original, AlignedHeapChunk.AlignedHead assert !ObjectHeaderImpl.hasIdentityHashFromAddressInline(oh.readHeaderFromObject(result)); } ObjectHeaderImpl.setMarked(result); - markStack.push(result); + pushOntoMarkStack(result); return result; } @@ -212,7 +257,7 @@ protected Object promoteUnalignedObject(Object original, UnalignedHeapChunk.Unal assert originalSpace == space; if (!ObjectHeaderImpl.isMarked(original)) { ObjectHeaderImpl.setMarked(original); - markStack.push(original); + pushOntoMarkStack(original); } return original; } @@ -238,7 +283,7 @@ protected boolean promotePinnedObject(Object obj, HeapChunk.Header originalCh ((AlignedHeapChunk.AlignedHeader) originalChunk).setShouldSweepInsteadOfCompact(true); } ObjectHeaderImpl.setMarked(obj); - markStack.push(obj); + pushOntoMarkStack(obj); return true; } @@ -507,17 +552,20 @@ boolean verifySpaces() { @Override void checkSanityBeforeCollection() { assert markStack.isEmpty(); + assert arrayMarkStack.isEmpty(); } @Override void checkSanityAfterCollection() { assert markStack.isEmpty(); + assert arrayMarkStack.isEmpty(); } @Override @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) void tearDown() { markStack.tearDown(); + arrayMarkStack.tearDown(); space.tearDown(); } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjectVisitor.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjectVisitor.java index a07f09f67ba8..f7cffa183045 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjectVisitor.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjectVisitor.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.core.genscavenge; +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -53,4 +55,9 @@ public void visitObject(Object o) { ReferenceObjectProcessing.discoverIfReference(o, objRefVisitor); InteriorObjRefWalker.walkObjectInline(o, objRefVisitor); } + + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void visitObjectArrayRange(Object o, int firstIndex, int count) { + InteriorObjRefWalker.walkObjectArrayRangeInline(o, firstIndex, count, objRefVisitor); + } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/compacting/MarkStack.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/compacting/MarkStack.java index e652da9304ff..56522f0c91b8 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/compacting/MarkStack.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/compacting/MarkStack.java @@ -27,13 +27,13 @@ import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; import static jdk.vm.ci.code.CodeUtil.K; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.struct.RawField; import org.graalvm.nativeimage.c.struct.RawStructure; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.struct.UniqueLocationIdentity; +import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; @@ -42,21 +42,31 @@ import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.memory.NullableNativeMemory; import com.oracle.svm.core.nmt.NmtCategory; +import com.oracle.svm.core.stack.StackOverflowCheck; import com.oracle.svm.core.util.VMError; import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.word.ObjectAccess; +import jdk.graal.compiler.word.Word; /** * LIFO stack for objects to visit during the mark phase. Without it, recursive calls could exhaust - * the {@linkplain com.oracle.svm.core.stack.StackOverflowCheck yellow zone stack space} during GC. + * the {@linkplain StackOverflowCheck yellow zone stack space} during GC. Callers can also push + * other kinds of values, for example indexes for object arrays to keep track of the scanned range. */ public final class MarkStack { private static final int SEGMENT_SIZE = 64 * K - /* avoid potential malloc() overallocation */ 64; @Fold static int entriesPerSegment() { - return (SEGMENT_SIZE - SizeOf.get(Segment.class)) / ConfigurationValues.getObjectLayout().getReferenceSize(); + int headerSize = SizeOf.get(Segment.class); + assert headerSize % entrySize() == 0 : "must be aligned"; + return (SEGMENT_SIZE - headerSize) / entrySize(); + } + + @Fold + static int entrySize() { + return ConfigurationValues.getObjectLayout().getReferenceSize(); } private Segment top; @@ -68,42 +78,68 @@ public MarkStack() { @AlwaysInline("GC performance") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) - public void push(Object obj) { + public void pushObject(Object obj) { assert obj != null; + Pointer entry = pushSlot(); + ObjectAccess.writeObject(Word.nullPointer(), entry, obj); + } + + @AlwaysInline("GC performance") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public void pushInt(int value) { + Pointer entry = pushSlot(); + entry.writeInt(0, value); + } + @AlwaysInline("GC performance") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private Pointer pushSlot() { if (top.isNull() || cursor == entriesPerSegment()) { top = allocateSegment(top); cursor = 0; } - UnsignedWord offset = getOffsetAtIndex(cursor); - ObjectAccess.writeObject(top, offset, obj); + Pointer entry = getEntryAddress(cursor); cursor++; + return entry; } @AlwaysInline("GC performance") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) - public Object pop() { + public Object popObject() { assert !isEmpty(); - cursor--; - UnsignedWord offset = getOffsetAtIndex(cursor); - Object obj = ObjectAccess.readObject(top, offset); - + Pointer entry = getEntryAddress(cursor - 1); + Object obj = ObjectAccess.readObject(Word.nullPointer(), entry); assert obj != null; + popSlot(); + return obj; + } + + @AlwaysInline("GC performance") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public int popInt() { + assert !isEmpty(); + + Pointer entry = getEntryAddress(cursor - 1); + int v = entry.readInt(0); + + popSlot(); + return v; + } + + @AlwaysInline("GC performance") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private void popSlot() { + cursor--; if (cursor == 0) { if (top.getNext().isNonNull()) { // free eagerly, use cursor==0 only if completely empty - Segment t = top; - top = top.getNext(); - cursor = entriesPerSegment(); - NullableNativeMemory.free(t); + freeTopSegment(); } else { // keep a single segment } } - - return obj; } @AlwaysInline("GC performance") @@ -134,11 +170,19 @@ private static Segment allocateSegment(Segment next) { return segment; } + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + private void freeTopSegment() { + Segment t = top; + top = top.getNext(); + cursor = entriesPerSegment(); + NullableNativeMemory.free(t); + } + @AlwaysInline("GC performance") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) - private static UnsignedWord getOffsetAtIndex(int index) { - int refSize = ConfigurationValues.getObjectLayout().getReferenceSize(); - return Word.unsigned(index).multiply(refSize).add(SizeOf.unsigned(Segment.class)); + private Pointer getEntryAddress(int index) { + Pointer firstEntry = ((Pointer) top).add(SizeOf.unsigned(Segment.class)); + return firstEntry.add(Word.unsigned(index).multiply(entrySize())); } @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/InteriorObjRefWalker.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/InteriorObjRefWalker.java index 9463610bcef9..bb4ae29a37fa 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/InteriorObjRefWalker.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/InteriorObjRefWalker.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.core.hub; +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + import java.util.function.IntConsumer; import org.graalvm.word.Pointer; @@ -90,7 +92,8 @@ public static void walkObjectInline(Object obj, ObjectReferenceVisitor visitor) walkStoredContinuationInline(obj, visitor); return; case HubType.OBJECT_ARRAY: - walkObjectArrayInline(obj, visitor, objHub); + int length = ArrayLengthNode.arrayLength(obj); + walkObjectArrayRangeInline(obj, objHub, 0, length, visitor); return; case HubType.OTHER: default: @@ -98,6 +101,16 @@ public static void walkObjectInline(Object obj, ObjectReferenceVisitor visitor) } } + @AlwaysInline("De-virtualize calls to ObjectReferenceVisitor") + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) + public static void walkObjectArrayRangeInline(Object obj, int firstIndex, int count, ObjectReferenceVisitor visitor) { + DynamicHub objHub = KnownIntrinsics.readHub(obj); + assert objHub.getHubType() == HubType.OBJECT_ARRAY; + assert firstIndex >= 0 && count >= 0 && firstIndex + count >= 0; + assert firstIndex + count <= ArrayLengthNode.arrayLength(obj); + walkObjectArrayRangeInline(obj, objHub, firstIndex, count, visitor); + } + public static void walkInstanceReferenceOffsets(DynamicHub objHub, IntConsumer offsetConsumer) { if (objHub.getHubType() != HubType.INSTANCE && objHub.getHubType() != HubType.REFERENCE_INSTANCE) { throw new IllegalArgumentException("Unsupported hub type: " + objHub.getHubType()); @@ -166,11 +179,10 @@ private static void walkStoredContinuationInline(Object obj, ObjectReferenceVisi @AlwaysInline("GC performance") @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private static void walkObjectArrayInline(Object obj, ObjectReferenceVisitor visitor, DynamicHub objHub) { + private static void walkObjectArrayRangeInline(Object obj, DynamicHub objHub, int firstIndex, int count, ObjectReferenceVisitor visitor) { Pointer objPointer = Word.objectToUntrackedPointer(obj); - int length = ArrayLengthNode.arrayLength(obj); - Pointer firstObjRef = objPointer.add(LayoutEncoding.getArrayBaseOffset(objHub.getLayoutEncoding())); - callVisitorInline(obj, visitor, firstObjRef, length); + Pointer firstObjRef = objPointer.add(LayoutEncoding.getArrayElementOffset(objHub.getLayoutEncoding(), firstIndex)); + callVisitorInline(obj, visitor, firstObjRef, count); } @AlwaysInline("de-virtualize calls to ObjectReferenceVisitor")