diff --git a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/AllocationSnippets.java b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/AllocationSnippets.java index ab3602c1c543..b8cb494e5189 100644 --- a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/AllocationSnippets.java +++ b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/AllocationSnippets.java @@ -112,7 +112,7 @@ protected Object newMultiArrayImpl(Word hub, int rank, int[] dimensions) { return callNewMultiArrayStub(hub, rank, dims); } - private UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize) { + protected UnsignedWord arrayAllocationSize(int length, int arrayBaseOffset, int log2ElementSize) { int alignment = objectAlignment(); return WordFactory.unsigned(arrayAllocationSize(length, arrayBaseOffset, log2ElementSize, alignment)); } @@ -304,7 +304,7 @@ public Object formatArray(Word hub, return memory.toObjectNonNull(); } - public void emitMemoryBarrierIf(boolean emitMemoryBarrier) { + protected void emitMemoryBarrierIf(boolean emitMemoryBarrier) { if (emitMemoryBarrier) { MembarNode.memoryBarrier(MembarNode.FenceKind.ALLOCATION_INIT, LocationIdentity.init()); } @@ -379,7 +379,7 @@ public static FillContent fromBoolean(boolean fillContents) { } public static class AllocationProfilingData { - final AllocationSnippetCounters snippetCounters; + public final AllocationSnippetCounters snippetCounters; public AllocationProfilingData(AllocationSnippetCounters snippetCounters) { this.snippetCounters = snippetCounters; @@ -395,9 +395,9 @@ public AllocationSnippetCounters(SnippetCounter.Group.Factory factory) { stub = new SnippetCounter(allocations, "stub", "alloc and zeroing via stub"); } - final SnippetCounter unrolledInit; - final SnippetCounter loopInit; - final SnippetCounter bulkInit; - final SnippetCounter stub; + public final SnippetCounter unrolledInit; + public final SnippetCounter loopInit; + public final SnippetCounter bulkInit; + public final SnippetCounter stub; } } diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java index 6b4e7aeaacbe..b4862955d1cc 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java @@ -40,44 +40,6 @@ */ package org.graalvm.nativeimage.impl; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.util.Map; -import java.util.Set; - public interface RuntimeReflectionSupport extends ReflectionRegistry { - Map, Set>> getReflectionInnerClasses(); - - Set getReflectionFields(); - - Set getReflectionExecutables(); - - Object getAccessor(Executable method); - - /* - * Returns the methods and fields that shadow a superclass element registered for reflection, to - * be excluded from reflection queries. - */ - Set getHidingReflectionFields(); - - Set getHidingReflectionMethods(); - - Object[] getRecordComponents(Class type); - - void registerHeapDynamicHub(Object hub); - - Set getHeapDynamicHubs(); - - void registerHeapReflectionObject(AccessibleObject object); - - Set getHeapReflectionObjects(); - - int getReflectionClassesCount(); - - int getReflectionMethodsCount(); - - int getReflectionFieldsCount(); - - boolean requiresProcessing(); + // needed as reflection-specific ImageSingletons key } 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 7ece8ff6a1cc..bd3ab89d9f3d 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 @@ -166,7 +166,7 @@ public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLev DynamicHub hub = (DynamicHub) headerHub.toObject(); log.string(" class: ").string(hub.getName()); Object entryAsObject = objectEntry.toObject(); - if (LayoutEncoding.isArray(entryAsObject)) { + if (LayoutEncoding.isArrayLike(entryAsObject)) { int length = ArrayLengthNode.arrayLength(entryAsObject); log.string(" length: ").signed(length); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java index e608ecc68200..3d75f830d03a 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ThreadLocalAllocation.java @@ -53,6 +53,7 @@ import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader; import com.oracle.svm.core.genscavenge.graal.nodes.FormatArrayNode; import com.oracle.svm.core.genscavenge.graal.nodes.FormatObjectNode; +import com.oracle.svm.core.genscavenge.graal.nodes.FormatPodNode; import com.oracle.svm.core.graal.snippets.DeoptTester; import com.oracle.svm.core.heap.OutOfMemoryUtil; import com.oracle.svm.core.hub.DynamicHub; @@ -222,7 +223,7 @@ private static void runSlowPathHooks() { private static Object slowPathNewInstanceWithoutAllocating(DynamicHub hub) { DeoptTester.disableDeoptTesting(); try { - HeapImpl.exitIfAllocationDisallowed("ThreadLocalAllocation.allocateNewInstance", DynamicHub.toClass(hub).getName()); + HeapImpl.exitIfAllocationDisallowed("ThreadLocalAllocation.slowPathNewInstanceWithoutAllocating", DynamicHub.toClass(hub).getName()); GCImpl.getGCImpl().maybeCollectOnAllocation(); AlignedHeader newTlab = HeapImpl.getChunkProvider().produceAlignedChunk(); @@ -234,6 +235,15 @@ private static Object slowPathNewInstanceWithoutAllocating(DynamicHub hub) { @SubstrateForeignCallTarget(stubCallingConvention = false) private static Object slowPathNewArray(Word objectHeader, int length, int fillStartOffset) { + return slowPathNewArrayOrPodImpl(objectHeader, length, fillStartOffset, null); + } + + @SubstrateForeignCallTarget(stubCallingConvention = false) + private static Object slowPathNewPodInstance(Word objectHeader, int arrayLength, int fillStartOffset, byte[] referenceMap) { + return slowPathNewArrayOrPodImpl(objectHeader, arrayLength, fillStartOffset, referenceMap); + } + + private static Object slowPathNewArrayOrPodImpl(Word objectHeader, int length, int fillStartOffset, byte[] podReferenceMap) { /* * Avoid stack overflow errors while producing memory chunks, because that could leave the * heap in an inconsistent state. @@ -257,7 +267,7 @@ private static Object slowPathNewArray(Word objectHeader, int length, int fillSt throw OutOfMemoryUtil.reportOutOfMemoryError(outOfMemoryError); } - Object result = slowPathNewArrayWithoutAllocating(hub, length, size, fillStartOffset); + Object result = slowPathNewArrayOrPodWithoutAllocating(hub, length, size, fillStartOffset, podReferenceMap); runSlowPathHooks(); return result; } finally { @@ -266,26 +276,26 @@ private static Object slowPathNewArray(Word objectHeader, int length, int fillSt } @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate in the implementation of allocation.") - private static Object slowPathNewArrayWithoutAllocating(DynamicHub hub, int length, UnsignedWord size, int fillStartOffset) { + private static Object slowPathNewArrayOrPodWithoutAllocating(DynamicHub hub, int length, UnsignedWord size, int fillStartOffset, byte[] podReferenceMap) { DeoptTester.disableDeoptTesting(); try { - HeapImpl.exitIfAllocationDisallowed("Heap.allocateNewArray", DynamicHub.toClass(hub).getName()); + HeapImpl.exitIfAllocationDisallowed("ThreadLocalAllocation.slowPathNewArrayOrPodWithoutAllocating", DynamicHub.toClass(hub).getName()); GCImpl.getGCImpl().maybeCollectOnAllocation(); if (size.aboveOrEqual(HeapParameters.getLargeArrayThreshold())) { /* Large arrays go into their own unaligned chunk. */ boolean needsZeroing = !HeapChunkProvider.areUnalignedChunksZeroed(); UnalignedHeapChunk.UnalignedHeader newTlabChunk = HeapImpl.getChunkProvider().produceUnalignedChunk(size); - return allocateLargeArrayInNewTlab(hub, length, size, fillStartOffset, newTlabChunk, needsZeroing); + return allocateLargeArrayOrPodInNewTlab(hub, length, size, fillStartOffset, newTlabChunk, needsZeroing, podReferenceMap); } /* Small arrays go into the regular aligned chunk. */ // We might have allocated in the caller and acquired a TLAB with enough space already // (but we need to check in an uninterruptible method to be safe) - Object array = allocateSmallArrayInCurrentTlab(hub, length, size, fillStartOffset); + Object array = allocateSmallArrayOrPodInCurrentTlab(hub, length, size, fillStartOffset, podReferenceMap); if (array == null) { // We need a new chunk. AlignedHeader newTlabChunk = HeapImpl.getChunkProvider().produceAlignedChunk(); - array = allocateSmallArrayInNewTlab(hub, length, size, fillStartOffset, newTlabChunk); + array = allocateSmallArrayOrPodInNewTlab(hub, length, size, fillStartOffset, newTlabChunk, podReferenceMap); } return array; } finally { @@ -295,28 +305,30 @@ private static Object slowPathNewArrayWithoutAllocating(DynamicHub hub, int leng @Uninterruptible(reason = "Holds uninitialized memory.") private static Object allocateInstanceInNewTlab(DynamicHub hub, AlignedHeader newTlabChunk) { - UnsignedWord size = LayoutEncoding.getInstanceSize(hub.getLayoutEncoding()); + UnsignedWord size = LayoutEncoding.getPureInstanceSize(hub.getLayoutEncoding()); Pointer memory = allocateRawMemoryInNewTlab(size, newTlabChunk); return FormatObjectNode.formatObject(memory, DynamicHub.toClass(hub), false, FillContent.WITH_ZEROES, true); } @Uninterruptible(reason = "Holds uninitialized memory.") - private static Object allocateSmallArrayInCurrentTlab(DynamicHub hub, int length, UnsignedWord size, int fillStartOffset) { + private static Object allocateSmallArrayOrPodInCurrentTlab(DynamicHub hub, int length, UnsignedWord size, int fillStartOffset, byte[] podReferenceMap) { if (size.aboveThan(availableTlabMemory(getTlab()))) { return null; } Pointer memory = allocateRawMemoryInTlab(size, getTlab()); - return FormatArrayNode.formatArray(memory, DynamicHub.toClass(hub), length, false, false, FillContent.WITH_ZEROES, fillStartOffset, true); + return formatArrayOrPod(memory, hub, length, false, FillContent.WITH_ZEROES, fillStartOffset, podReferenceMap); } @Uninterruptible(reason = "Holds uninitialized memory.") - private static Object allocateSmallArrayInNewTlab(DynamicHub hub, int length, UnsignedWord size, int fillStartOffset, AlignedHeader newTlabChunk) { + private static Object allocateSmallArrayOrPodInNewTlab(DynamicHub hub, int length, UnsignedWord size, int fillStartOffset, AlignedHeader newTlabChunk, byte[] podReferenceMap) { Pointer memory = allocateRawMemoryInNewTlab(size, newTlabChunk); - return FormatArrayNode.formatArray(memory, DynamicHub.toClass(hub), length, false, false, FillContent.WITH_ZEROES, fillStartOffset, true); + return formatArrayOrPod(memory, hub, length, false, FillContent.WITH_ZEROES, fillStartOffset, podReferenceMap); } @Uninterruptible(reason = "Holds uninitialized memory, modifies TLAB") - private static Object allocateLargeArrayInNewTlab(DynamicHub hub, int length, UnsignedWord size, int fillStartOffset, UnalignedHeapChunk.UnalignedHeader newTlabChunk, boolean needsZeroing) { + private static Object allocateLargeArrayOrPodInNewTlab(DynamicHub hub, int length, UnsignedWord size, int fillStartOffset, + UnalignedHeader newTlabChunk, boolean needsZeroing, byte[] podReferenceMap) { + ThreadLocalAllocation.Descriptor tlab = getTlab(); HeapChunk.setNext(newTlabChunk, tlab.getUnalignedChunk()); @@ -338,7 +350,16 @@ private static Object allocateLargeArrayInNewTlab(DynamicHub hub, int length, Un * any way. */ FillContent fillKind = needsZeroing ? FillContent.WITH_ZEROES : FillContent.DO_NOT_FILL; - return FormatArrayNode.formatArray(memory, DynamicHub.toClass(hub), length, false, true, fillKind, fillStartOffset, true); + return formatArrayOrPod(memory, hub, length, true, fillKind, fillStartOffset, podReferenceMap); + } + + @Uninterruptible(reason = "Holds uninitialized memory") + private static Object formatArrayOrPod(Pointer memory, DynamicHub hub, int length, boolean unaligned, FillContent fillContent, int fillStartOffset, byte[] podReferenceMap) { + Class clazz = DynamicHub.toClass(hub); + if (podReferenceMap != null) { + return FormatPodNode.formatPod(memory, clazz, length, podReferenceMap, false, unaligned, fillContent, fillStartOffset, true); + } + return FormatArrayNode.formatArray(memory, clazz, length, false, unaligned, fillContent, fillStartOffset, true); } @Uninterruptible(reason = "Returns uninitialized memory, modifies TLAB", callerMustBe = true) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeAllocationSnippets.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeAllocationSnippets.java index 7879cc2a185c..6fe8453216a2 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeAllocationSnippets.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeAllocationSnippets.java @@ -29,9 +29,8 @@ import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.api.replacements.Snippet; import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter; -import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.graph.Node; -import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NamedLocationIdentity; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.SnippetAnchorNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -43,26 +42,21 @@ import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo; import org.graalvm.compiler.replacements.Snippets; -import org.graalvm.compiler.word.ObjectAccess; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.word.LocationIdentity; import org.graalvm.word.UnsignedWord; import com.oracle.svm.core.genscavenge.ObjectHeaderImpl; import com.oracle.svm.core.genscavenge.graal.nodes.FormatArrayNode; import com.oracle.svm.core.genscavenge.graal.nodes.FormatObjectNode; -import com.oracle.svm.core.graal.nodes.NewStoredContinuationNode; +import com.oracle.svm.core.genscavenge.graal.nodes.FormatPodNode; import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; import com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets; import com.oracle.svm.core.graal.snippets.SubstrateTemplates; -import com.oracle.svm.core.heap.StoredContinuation; -import com.oracle.svm.core.heap.StoredContinuationImpl; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.LayoutEncoding; -import com.oracle.svm.core.meta.SharedType; -import com.oracle.svm.core.meta.SubstrateObjectConstant; -import com.oracle.svm.core.thread.Continuation; + +import jdk.vm.ci.meta.JavaKind; final class GenScavengeAllocationSnippets implements Snippets { @Snippet @@ -70,7 +64,7 @@ public static Object formatObjectSnippet(Word memory, DynamicHub hub, boolean re @ConstantParameter AllocationSnippets.AllocationSnippetCounters snippetCounters) { DynamicHub hubNonNull = (DynamicHub) PiNode.piCastNonNull(hub, SnippetAnchorNode.anchor()); int layoutEncoding = hubNonNull.getLayoutEncoding(); - UnsignedWord size = LayoutEncoding.getInstanceSize(layoutEncoding); + UnsignedWord size = LayoutEncoding.getPureInstanceSize(layoutEncoding); Word objectHeader = encodeAsObjectHeader(hubNonNull, rememberedSet, false); return alloc().formatObject(objectHeader, size, memory, fillContents, emitMemoryBarrier, false, snippetCounters); } @@ -83,35 +77,20 @@ public static Object formatArraySnippet(Word memory, DynamicHub hub, int length, int layoutEncoding = hubNonNull.getLayoutEncoding(); UnsignedWord size = LayoutEncoding.getArraySize(layoutEncoding, length); Word objectHeader = encodeAsObjectHeader(hubNonNull, rememberedSet, unaligned); - Object obj = alloc().formatArray(objectHeader, size, length, memory, fillContents, fillStartOffset, - false, false, supportsBulkZeroing, supportsOptimizedFilling, snippetCounters); - alloc().emitMemoryBarrierIf(emitMemoryBarrier); - return obj; + return alloc().formatArray(objectHeader, size, length, memory, fillContents, fillStartOffset, + emitMemoryBarrier, false, supportsBulkZeroing, supportsOptimizedFilling, snippetCounters); } @Snippet - public static Object allocateStoredContinuationInstance(@ConstantParameter DynamicHub hub, int payloadSize, @ConstantParameter DynamicHub byteArrayHub, - @ConstantParameter int byteArrayBaseOffset, @ConstantParameter AllocationSnippets.AllocationProfilingData profilingData) { - /* - * We allocate a byte[] first and then convert it to a StoredContinuation. We must pass - * parameters below that match the layout of a regular byte[], or we will run into problems - * because not all parameters are passed on to the slow path. - * - * Barrier code assumes that instance objects are always in aligned chunks, but a large - * StoredContinuation can end up in an unaligned chunk. Still, no barriers are needed - * because objects are immutable once filled and are then written only by GC. - */ - int arrayLength = StoredContinuationImpl.PAYLOAD_OFFSET + payloadSize - byteArrayBaseOffset; - Object result = alloc().allocateArrayImpl(SubstrateAllocationSnippets.encodeAsTLABObjectHeader(byteArrayHub), arrayLength, byteArrayBaseOffset, 0, - AllocationSnippets.FillContent.WITH_GARBAGE_IF_ASSERTIONS_ENABLED, - SubstrateAllocationSnippets.afterArrayLengthOffset(), false, false, false, false, profilingData); - UnsignedWord arrayHeader = ObjectHeaderImpl.readHeaderFromObject(result); - Word header = encodeAsObjectHeader(hub, ObjectHeaderImpl.hasRememberedSet(arrayHeader), ObjectHeaderImpl.isUnalignedHeader(arrayHeader)); - alloc().initializeObjectHeader(Word.objectToUntrackedPointer(result), header, false); - ObjectAccess.writeObject(result, hub.getMonitorOffset(), null, LocationIdentity.init()); - StoredContinuationImpl.initializeNewlyAllocated(result, payloadSize); - alloc().emitMemoryBarrierIf(true); - return PiNode.piCastToSnippetReplaceeStamp(result); + public static Object formatPodSnippet(Word memory, DynamicHub hub, int arrayLength, byte[] referenceMap, boolean rememberedSet, boolean unaligned, AllocationSnippets.FillContent fillContents, + int fillStartOffset, @ConstantParameter boolean emitMemoryBarrier, @ConstantParameter boolean supportsBulkZeroing, @ConstantParameter boolean supportsOptimizedFilling, + @ConstantParameter AllocationSnippets.AllocationSnippetCounters snippetCounters) { + + DynamicHub hubNonNull = (DynamicHub) PiNode.piCastNonNull(hub, SnippetAnchorNode.anchor()); + byte[] refMapNonNull = (byte[]) PiNode.piCastNonNull(referenceMap, SnippetAnchorNode.anchor()); + Word objectHeader = encodeAsObjectHeader(hubNonNull, rememberedSet, unaligned); + return alloc().formatPod(objectHeader, hubNonNull, arrayLength, refMapNonNull, memory, fillContents, fillStartOffset, + emitMemoryBarrier, false, supportsBulkZeroing, supportsOptimizedFilling, snippetCounters); } private static Word encodeAsObjectHeader(DynamicHub hub, boolean rememberedSet, boolean unaligned) { @@ -127,29 +106,20 @@ public static class Templates extends SubstrateTemplates { private final SubstrateAllocationSnippets.Templates baseTemplates; private final SnippetInfo formatObject; private final SnippetInfo formatArray; - private final SnippetInfo allocateStoredContinuationInstance; + private final SnippetInfo formatPod; Templates(OptionValues options, Providers providers, SubstrateAllocationSnippets.Templates baseTemplates) { super(options, providers); this.baseTemplates = baseTemplates; formatObject = snippet(GenScavengeAllocationSnippets.class, "formatObjectSnippet"); formatArray = snippet(GenScavengeAllocationSnippets.class, "formatArraySnippet"); - - allocateStoredContinuationInstance = !Continuation.isSupported() ? null - : snippet(GenScavengeAllocationSnippets.class, "allocateStoredContinuationInstance", SubstrateAllocationSnippets.ALLOCATION_LOCATIONS); + formatPod = snippet(GenScavengeAllocationSnippets.class, "formatPodSnippet", NamedLocationIdentity.getArrayLocation(JavaKind.Byte)); } public void registerLowering(Map, NodeLoweringProvider> lowerings) { - FormatObjectLowering formatObjectLowering = new FormatObjectLowering(); - lowerings.put(FormatObjectNode.class, formatObjectLowering); - - FormatArrayLowering formatArrayLowering = new FormatArrayLowering(); - lowerings.put(FormatArrayNode.class, formatArrayLowering); - - if (Continuation.isSupported()) { - NewStoredContinuationLowering newStoredContinuationLowering = new NewStoredContinuationLowering(); - lowerings.put(NewStoredContinuationNode.class, newStoredContinuationLowering); - } + lowerings.put(FormatObjectNode.class, new FormatObjectLowering()); + lowerings.put(FormatArrayNode.class, new FormatArrayLowering()); + lowerings.put(FormatPodNode.class, new FormatPodLowering()); } private class FormatObjectLowering implements NodeLoweringProvider { @@ -193,30 +163,26 @@ public void lower(FormatArrayNode node, LoweringTool tool) { } } - private class NewStoredContinuationLowering implements NodeLoweringProvider { + private class FormatPodLowering implements NodeLoweringProvider { @Override - public void lower(NewStoredContinuationNode node, LoweringTool tool) { + public void lower(FormatPodNode node, LoweringTool tool) { StructuredGraph graph = node.graph(); - if (graph.getGuardsStage() != StructuredGraph.GuardsStage.AFTER_FSA) { return; } - - DynamicHub hub = ((SharedType) tool.getMetaAccess().lookupJavaType(StoredContinuation.class)).getHub(); - assert hub.isStoredContinuationClass(); - ConstantNode hubConstant = ConstantNode.forConstant(SubstrateObjectConstant.forObject(hub), providers.getMetaAccess(), graph); - - DynamicHub byteArrayHub = ((SharedType) tool.getMetaAccess().lookupJavaType(byte[].class)).getHub(); - ConstantNode byteArrayHubConstant = ConstantNode.forConstant(SubstrateObjectConstant.forObject(byteArrayHub), providers.getMetaAccess(), graph); - int byteArrayBaseOffset = NumUtil.safeToInt(LayoutEncoding.getArrayBaseOffset(byteArrayHub.getLayoutEncoding()).rawValue()); - - Arguments args = new Arguments(allocateStoredContinuationInstance, graph.getGuardsStage(), tool.getLoweringStage()); - args.addConst("hub", hubConstant); - args.add("payloadSize", node.getPayloadSize()); - args.addConst("byteArrayHub", byteArrayHubConstant); - args.addConst("byteArrayBaseOffset", byteArrayBaseOffset); - args.addConst("profilingData", baseTemplates.getProfilingData(node, null)); - + Arguments args = new Arguments(formatPod, graph.getGuardsStage(), tool.getLoweringStage()); + args.add("memory", node.getMemory()); + args.add("hub", node.getHub()); + args.add("arrayLength", node.getArrayLength()); + args.add("referenceMap", node.getReferenceMap()); + args.add("rememberedSet", node.getRememberedSet()); + args.add("unaligned", node.getUnaligned()); + args.add("fillContents", node.getFillContents()); + args.add("fillStartOffset", node.getFillStartOffset()); + args.addConst("emitMemoryBarrier", node.getEmitMemoryBarrier()); + args.addConst("supportsBulkZeroing", tool.getLowerer().supportsBulkZeroing()); + args.addConst("supportsOptimizedFilling", tool.getLowerer().supportsOptimizedFilling(graph.getOptions())); + args.addConst("snippetCounters", baseTemplates.getSnippetCounters()); template(node, args).instantiate(providers.getMetaAccess(), node, SnippetTemplate.DEFAULT_REPLACER, args); } } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeAllocationSupport.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeAllocationSupport.java index 33533a815f80..26d3acb85d2d 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeAllocationSupport.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/GenScavengeAllocationSupport.java @@ -24,19 +24,21 @@ */ package com.oracle.svm.core.genscavenge.graal; +import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; +import org.graalvm.compiler.word.Word; +import org.graalvm.word.UnsignedWord; + import com.oracle.svm.core.genscavenge.HeapParameters; import com.oracle.svm.core.genscavenge.ThreadLocalAllocation; import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; import com.oracle.svm.core.graal.snippets.GCAllocationSupport; import com.oracle.svm.core.snippets.SnippetRuntime; -import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; -import org.graalvm.compiler.word.Word; -import org.graalvm.word.UnsignedWord; public class GenScavengeAllocationSupport implements GCAllocationSupport { private static final SnippetRuntime.SubstrateForeignCallDescriptor SLOW_NEW_INSTANCE = SnippetRuntime.findForeignCall(ThreadLocalAllocation.class, "slowPathNewInstance", true); private static final SnippetRuntime.SubstrateForeignCallDescriptor SLOW_NEW_ARRAY = SnippetRuntime.findForeignCall(ThreadLocalAllocation.class, "slowPathNewArray", true); - private static final SnippetRuntime.SubstrateForeignCallDescriptor[] FOREIGN_CALLS = new SnippetRuntime.SubstrateForeignCallDescriptor[]{SLOW_NEW_INSTANCE, SLOW_NEW_ARRAY}; + private static final SnippetRuntime.SubstrateForeignCallDescriptor SLOW_NEW_POD_INSTANCE = SnippetRuntime.findForeignCall(ThreadLocalAllocation.class, "slowPathNewPodInstance", true); + private static final SnippetRuntime.SubstrateForeignCallDescriptor[] FOREIGN_CALLS = new SnippetRuntime.SubstrateForeignCallDescriptor[]{SLOW_NEW_INSTANCE, SLOW_NEW_ARRAY, SLOW_NEW_POD_INSTANCE}; public static void registerForeignCalls(SubstrateForeignCallsProvider foreignCalls) { foreignCalls.register(FOREIGN_CALLS); @@ -52,6 +54,11 @@ public ForeignCallDescriptor getSlowNewArrayStub() { return SLOW_NEW_ARRAY; } + @Override + public ForeignCallDescriptor getSlowNewPodInstanceStub() { + return SLOW_NEW_POD_INSTANCE; + } + @Override public boolean useTLAB() { return true; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/SerialGCFeature.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/SerialGCFeature.java index 32454dce6b5a..640767c48d25 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/SerialGCFeature.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/SerialGCFeature.java @@ -28,7 +28,6 @@ import java.util.List; import java.util.Map; -import com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.util.Providers; @@ -50,8 +49,9 @@ import com.oracle.svm.core.graal.GraalFeature; import com.oracle.svm.core.graal.meta.RuntimeConfiguration; import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; -import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; import com.oracle.svm.core.graal.snippets.GCAllocationSupport; +import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; +import com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.HeapFeature; import com.oracle.svm.core.image.ImageHeapLayouter; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/nodes/FormatPodNode.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/nodes/FormatPodNode.java new file mode 100644 index 000000000000..3d3acb2d6fa9 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/graal/nodes/FormatPodNode.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.genscavenge.graal.nodes; + +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_64; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_64; + +import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.spi.Lowerable; +import org.graalvm.compiler.replacements.AllocationSnippets; +import org.graalvm.word.Pointer; + +@NodeInfo(cycles = CYCLES_64, size = SIZE_64) +public class FormatPodNode extends FixedWithNextNode implements Lowerable { + public static final NodeClass TYPE = NodeClass.create(FormatPodNode.class); + + @Input protected ValueNode memory; + @Input protected ValueNode hub; + @Input protected ValueNode arrayLength; + @Input protected ValueNode referenceMap; + @Input protected ValueNode rememberedSet; + @Input protected ValueNode unaligned; + @Input protected ValueNode fillContents; + @Input protected ValueNode fillStartOffset; + private final boolean emitMemoryBarrier; + + public FormatPodNode(ValueNode memory, ValueNode hub, ValueNode arrayLength, ValueNode referenceMap, ValueNode rememberedSet, + ValueNode unaligned, ValueNode fillContents, ValueNode fillStartOffset, boolean emitMemoryBarrier) { + super(TYPE, StampFactory.objectNonNull()); + this.memory = memory; + this.hub = hub; + this.arrayLength = arrayLength; + this.referenceMap = referenceMap; + this.rememberedSet = rememberedSet; + this.unaligned = unaligned; + this.fillContents = fillContents; + this.fillStartOffset = fillStartOffset; + this.emitMemoryBarrier = emitMemoryBarrier; + } + + public ValueNode getMemory() { + return memory; + } + + public ValueNode getHub() { + return hub; + } + + public ValueNode getArrayLength() { + return arrayLength; + } + + public ValueNode getReferenceMap() { + return referenceMap; + } + + public ValueNode getRememberedSet() { + return rememberedSet; + } + + public ValueNode getUnaligned() { + return unaligned; + } + + public ValueNode getFillContents() { + return fillContents; + } + + public ValueNode getFillStartOffset() { + return fillStartOffset; + } + + public boolean getEmitMemoryBarrier() { + return emitMemoryBarrier; + } + + @NodeIntrinsic + public static native Object formatPod(Pointer memory, Class hub, int arrayLength, byte[] referenceMap, boolean rememberedSet, boolean unaligned, + AllocationSnippets.FillContent fillContents, int fillStartOffset, @ConstantNodeParameter boolean emitMemoryBarrier); +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java index 09500eccd1e0..db20bc6b2feb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java @@ -315,7 +315,7 @@ private static boolean isObjectArray(DynamicHub hub) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) private static boolean isInstance(DynamicHub hub) { - return LayoutEncoding.isInstance(hub.getLayoutEncoding()); + return LayoutEncoding.isPureInstance(hub.getLayoutEncoding()); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Hybrid.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Hybrid.java index cb999f027cb9..dfc4ec629be9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Hybrid.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Hybrid.java @@ -37,11 +37,6 @@ * instance layouts and array layouts. The contents of a specified member array and (optional) * member type id slots are directly placed within the class layout. This saves one indirection when * accessing the array or type id slots. - * - *

- * The array length is located directly after the HUB pointer, like in regular array. Then (if - * present) the type id slots follow. Then the instance fields are placed. At the end of the layout, - * the array elements are located. * *

  *    +--------------------------------------------------+
@@ -64,38 +59,42 @@
  *
  * 

* Hybrid objects have {@link HubType#Instance} but a {@link LayoutEncoding} like an array. This is - * important to keep in mind because methods such as {@link Class#isArray()} will return - * {@code false}, while methods such as {@link LayoutEncoding#isArray} will return {@code true} for - * hybrid objects. + * important to keep in mind because methods such as {@link Class#isInstance} will return + * {@code true} and {@link Class#isArray()} will return {@code false}, while + * {@link LayoutEncoding#isPureInstance} will return {@code false} and + * {@link LayoutEncoding#isArrayLike} will return {@code true} for hybrid objects. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Hybrid { /** - * If {@code true}, we expect that the data in the hybrid fields can be duplicated between the - * hybrid object and a separate object for the array. For most objects, a duplication could - * occur if inlining and constant folding result in the internal reference to a hybrid field - * being constant folded to a constant value, which must be written into the image heap - * separately from the hybrid object. - * - * If {@code false}, we expect that this duplication of the hybrid fields can never happen. + * The component type of the array part of the hybrid class. Must be specified if no field + * annotated with @{@link Hybrid.Array} is declared, otherwise that field's type determines the + * type of the array part. */ - boolean canHybridFieldsBeDuplicated(); + Class componentType() default void.class; /** - * Specifies a single member array as the hybrid array. + * If {@code true}, allow the data in the hybrid fields to be duplicated between the hybrid + * object and a separate object for the array. For image heap objects, a duplication can occur + * if inlining and constant folding result in the internal reference to a hybrid field being + * folded to a constant value, which must be written into the image heap separately from the + * hybrid object. + * + * If {@code false}, a duplication of the hybrid fields must never happen. */ + boolean canHybridFieldsBeDuplicated() default false; + + /** Designates at most one field that refers to the array part of the hybrid object. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) - public @interface Array { + @interface Array { } - /** - * Specifies a single member type slots. - */ + /** Designates at most one field that refers to the type ID slots of the hybrid object. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) - public @interface TypeIDSlots { + @interface TypeIDSlots { } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java index fb8c12501536..fe74699b7ced 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java @@ -646,7 +646,7 @@ private static ValueInfo findActualArrayElement(ValueInfo[] actualObject, Unsign private static ValueInfo findActualField(ValueInfo[] actualObject, UnsignedWord expectedOffset) { DynamicHub hub = (DynamicHub) SubstrateObjectConstant.asObject(actualObject[0].getValue()); ObjectLayout objectLayout = ConfigurationValues.getObjectLayout(); - assert LayoutEncoding.isInstance(hub.getLayoutEncoding()); + assert LayoutEncoding.isPureInstance(hub.getLayoutEncoding()); return findActualValue(actualObject, expectedOffset, objectLayout, WordFactory.unsigned(objectLayout.getFirstFieldOffset()), 1); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java index 1488987566f9..02065dde0fc7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java @@ -990,6 +990,7 @@ private Object materializeObject(int virtualObjectId, FrameInfoQueryResult sourc curOffset = LayoutEncoding.getArrayBaseOffset(hub.getLayoutEncoding()); curIdx = 2; } else { + assert LayoutEncoding.isPureInstance(hub.getLayoutEncoding()); try { obj = Unsafe.getUnsafe().allocateInstance(DynamicHub.toClass(hub)); } catch (InstantiationException ex) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneNode.java index 3e228e47b514..529ffe7d986a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneNode.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.nodes.java.LoadFieldNode; import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringTool; +import org.graalvm.compiler.nodes.spi.VirtualizerTool; import org.graalvm.compiler.replacements.nodes.BasicObjectCloneNode; import org.graalvm.compiler.replacements.nodes.MacroNode; @@ -95,4 +96,11 @@ public void setStateBefore(FrameState f) { updateUsages(stateBefore, f); stateBefore = f; } + + @Override + public void virtualize(VirtualizerTool tool) { + if (SubstrateObjectCloneSnippets.canVirtualize(this, tool)) { + super.virtualize(tool); + } + } } 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 dc4726ab8350..2fea85c8c82c 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 @@ -25,26 +25,31 @@ package com.oracle.svm.core.graal.jdk; import static org.graalvm.compiler.nodes.PiNode.piCastToSnippetReplaceeStamp; +import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.FAST_PATH_PROBABILITY; import java.util.Map; import org.graalvm.compiler.api.replacements.Snippet; -import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.Node.ConstantNodeParameter; import org.graalvm.compiler.graph.Node.NodeIntrinsic; import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.extended.BranchProbabilityNode; import org.graalvm.compiler.nodes.extended.ForeignCallNode; import org.graalvm.compiler.nodes.java.ArrayLengthNode; import org.graalvm.compiler.nodes.spi.LoweringTool; +import org.graalvm.compiler.nodes.spi.VirtualizerTool; +import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.util.Providers; import org.graalvm.compiler.replacements.SnippetTemplate; import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo; import org.graalvm.compiler.replacements.Snippets; +import org.graalvm.compiler.replacements.nodes.ObjectClone; import org.graalvm.compiler.word.BarrieredAccess; import org.graalvm.word.LocationIdentity; import org.graalvm.word.WordFactory; @@ -57,16 +62,22 @@ import com.oracle.svm.core.graal.snippets.NodeLoweringProvider; import com.oracle.svm.core.graal.snippets.SubstrateTemplates; import com.oracle.svm.core.heap.InstanceReferenceMapEncoder; +import com.oracle.svm.core.heap.Pod; +import com.oracle.svm.core.heap.PodReferenceMapDecoder; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.DynamicHubSupport; import com.oracle.svm.core.hub.LayoutEncoding; +import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.snippets.KnownIntrinsics; 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.util.NonmovableByteArrayReader; +import com.oracle.svm.core.util.UnsignedUtils; +import com.oracle.svm.core.util.VMError; import jdk.internal.misc.Unsafe; +import jdk.vm.ci.meta.ResolvedJavaType; public final class SubstrateObjectCloneSnippets extends SubstrateTemplates implements Snippets { private static final SubstrateForeignCallDescriptor CLONE = SnippetRuntime.findForeignCall(SubstrateObjectCloneSnippets.class, "doClone", true, LocationIdentity.any()); @@ -86,65 +97,95 @@ private static Object doClone(Object original) throws CloneNotSupportedException DynamicHub hub = KnownIntrinsics.readHub(original); int layoutEncoding = hub.getLayoutEncoding(); - if (LayoutEncoding.isArray(layoutEncoding)) { - int length = ArrayLengthNode.arrayLength(original); - Object newArray = java.lang.reflect.Array.newInstance(DynamicHub.toClass(hub.getComponentHub()), length); - if (LayoutEncoding.isObjectArray(layoutEncoding)) { - JavaMemoryUtil.copyObjectArrayForward(original, 0, newArray, 0, length, layoutEncoding); + boolean isArrayLike = LayoutEncoding.isArrayLike(layoutEncoding); + + Object result; + if (isArrayLike) { + if (BranchProbabilityNode.probability(FAST_PATH_PROBABILITY, LayoutEncoding.isArray(layoutEncoding))) { + int length = ArrayLengthNode.arrayLength(original); + Object newArray = java.lang.reflect.Array.newInstance(DynamicHub.toClass(hub.getComponentHub()), length); + if (LayoutEncoding.isObjectArray(layoutEncoding)) { + JavaMemoryUtil.copyObjectArrayForward(original, 0, newArray, 0, length, layoutEncoding); + } else { + JavaMemoryUtil.copyPrimitiveArrayForward(original, 0, newArray, 0, length, layoutEncoding); + } + return newArray; + + } else if (Pod.RuntimeSupport.isPresent() && hub.isPodInstanceClass()) { + result = PodReferenceMapDecoder.clone(original, hub, layoutEncoding); + } else { - JavaMemoryUtil.copyPrimitiveArrayForward(original, 0, newArray, 0, length, layoutEncoding); + throw VMError.shouldNotReachHere("Hybrid classes do not support Object.clone()."); } - return newArray; } else { - Object result = Unsafe.getUnsafe().allocateInstance(DynamicHub.toClass(hub)); - int firstFieldOffset = ConfigurationValues.getObjectLayout().getFirstFieldOffset(); - int curOffset = firstFieldOffset; - - NonmovableArray referenceMapEncoding = DynamicHubSupport.getReferenceMapEncoding(); - int referenceSize = ConfigurationValues.getObjectLayout().getReferenceSize(); - int referenceMapIndex = hub.getReferenceMapIndex(); - int entryCount = NonmovableByteArrayReader.getS4(referenceMapEncoding, referenceMapIndex); - assert entryCount >= 0; - - // The UniverseBuilder actively groups object references together. So, this loop will - // typically be only executed for a very small number of iterations. - long entryStart = referenceMapIndex + InstanceReferenceMapEncoder.MAP_HEADER_SIZE; - for (long idx = entryStart; idx < entryStart + entryCount * InstanceReferenceMapEncoder.MAP_ENTRY_SIZE; idx += InstanceReferenceMapEncoder.MAP_ENTRY_SIZE) { - int objectOffset = NonmovableByteArrayReader.getS4(referenceMapEncoding, idx); - long count = NonmovableByteArrayReader.getU4(referenceMapEncoding, idx + 4); - assert objectOffset >= firstFieldOffset : "must not overwrite the object header"; - - // copy non-object data - int primitiveDataSize = objectOffset - curOffset; - assert primitiveDataSize >= 0; - assert curOffset >= 0; - JavaMemoryUtil.copyForward(original, WordFactory.unsigned(curOffset), result, WordFactory.unsigned(curOffset), WordFactory.unsigned(primitiveDataSize)); - curOffset += primitiveDataSize; - - // copy object data - assert curOffset >= 0; - assert count >= 0; - JavaMemoryUtil.copyReferencesForward(original, WordFactory.unsigned(curOffset), result, WordFactory.unsigned(curOffset), WordFactory.unsigned(count)); - curOffset += count * referenceSize; - } + result = Unsafe.getUnsafe().allocateInstance(DynamicHub.toClass(hub)); + } - // copy remaining non-object data - int objectSize = NumUtil.safeToInt(LayoutEncoding.getInstanceSize(layoutEncoding).rawValue()); - int primitiveDataSize = objectSize - curOffset; + int firstFieldOffset = ConfigurationValues.getObjectLayout().getFirstFieldOffset(); + int curOffset = firstFieldOffset; + + NonmovableArray referenceMapEncoding = DynamicHubSupport.getReferenceMapEncoding(); + int referenceSize = ConfigurationValues.getObjectLayout().getReferenceSize(); + int referenceMapIndex = hub.getReferenceMapIndex(); + int entryCount = NonmovableByteArrayReader.getS4(referenceMapEncoding, referenceMapIndex); + assert entryCount >= 0; + + // The UniverseBuilder actively groups object references together. So, this loop will + // typically be only executed for a very small number of iterations. + long entryStart = referenceMapIndex + InstanceReferenceMapEncoder.MAP_HEADER_SIZE; + for (long idx = entryStart; idx < entryStart + entryCount * InstanceReferenceMapEncoder.MAP_ENTRY_SIZE; idx += InstanceReferenceMapEncoder.MAP_ENTRY_SIZE) { + int objectOffset = NonmovableByteArrayReader.getS4(referenceMapEncoding, idx); + long count = NonmovableByteArrayReader.getU4(referenceMapEncoding, idx + 4); + assert objectOffset >= firstFieldOffset : "must not overwrite the object header"; + + // copy non-object data + int primitiveDataSize = objectOffset - curOffset; assert primitiveDataSize >= 0; assert curOffset >= 0; JavaMemoryUtil.copyForward(original, WordFactory.unsigned(curOffset), result, WordFactory.unsigned(curOffset), WordFactory.unsigned(primitiveDataSize)); curOffset += primitiveDataSize; - assert curOffset == objectSize; - // reset monitor to uninitialized values - int monitorOffset = hub.getMonitorOffset(); - if (monitorOffset != 0) { - BarrieredAccess.writeObject(result, monitorOffset, null); - } + // copy object data + assert curOffset >= 0; + assert count >= 0; + JavaMemoryUtil.copyReferencesForward(original, WordFactory.unsigned(curOffset), result, WordFactory.unsigned(curOffset), WordFactory.unsigned(count)); + curOffset += count * referenceSize; + } - return result; + // copy remaining non-object data + int endOffset = isArrayLike ? LayoutEncoding.getArrayBaseOffsetAsInt(layoutEncoding) + : UnsignedUtils.safeToInt(LayoutEncoding.getPureInstanceSize(layoutEncoding)); + int primitiveDataSize = endOffset - curOffset; + assert primitiveDataSize >= 0; + assert curOffset >= 0; + JavaMemoryUtil.copyForward(original, WordFactory.unsigned(curOffset), result, WordFactory.unsigned(curOffset), WordFactory.unsigned(primitiveDataSize)); + curOffset += primitiveDataSize; + assert curOffset == endOffset; + + // reset monitor to uninitialized values + int monitorOffset = hub.getMonitorOffset(); + if (monitorOffset != 0) { + BarrieredAccess.writeObject(result, monitorOffset, null); + } + + return result; + } + + static boolean canVirtualize(ObjectClone node, VirtualizerTool tool) { + ValueNode alias = tool.getAlias(node.getObject()); + if (alias instanceof VirtualObjectNode) { + return true; + } + ResolvedJavaType type = node.getConcreteType(alias.stamp(NodeView.DEFAULT)); + if (type instanceof SharedType) { + // Hybrids are instances with array-like encoding; cloning virtually is unimplemented. + int encoding = ((SharedType) type).getHub().getLayoutEncoding(); + return !LayoutEncoding.isHybrid(encoding); + } + if (type != null && type.isArray()) { + return true; // cannot be a hybrid } + return false; } @NodeIntrinsic(value = ForeignCallNode.class) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneWithExceptionNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneWithExceptionNode.java index 0e74d9abb4a2..117c122d0112 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneWithExceptionNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/jdk/SubstrateObjectCloneWithExceptionNode.java @@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.WithExceptionNode; import org.graalvm.compiler.nodes.memory.SingleMemoryKill; import org.graalvm.compiler.nodes.spi.Lowerable; +import org.graalvm.compiler.nodes.spi.VirtualizerTool; import org.graalvm.compiler.nodes.util.GraphUtil; import org.graalvm.compiler.replacements.nodes.MacroNode.MacroParams; import org.graalvm.compiler.replacements.nodes.ObjectClone; @@ -133,4 +134,11 @@ public FixedNode replaceWithNonThrowing() { GraphUtil.killCFG(oldException); return plainObjectClone; } + + @Override + public void virtualize(VirtualizerTool tool) { + if (SubstrateObjectCloneSnippets.canVirtualize(this, tool)) { + ObjectClone.super.virtualize(tool); + } + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/NewStoredContinuationNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/NewPodInstanceNode.java similarity index 52% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/NewStoredContinuationNode.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/NewPodInstanceNode.java index ebcc21001f52..f7c5f0fb2a65 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/NewStoredContinuationNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/NewPodInstanceNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,31 +25,47 @@ package com.oracle.svm.core.graal.nodes; import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.core.common.type.TypeReference; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.AbstractNewObjectNode; -import org.graalvm.compiler.nodes.spi.Lowerable; -import com.oracle.svm.core.heap.StoredContinuation; - -import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaType; @NodeInfo -public class NewStoredContinuationNode extends AbstractNewObjectNode implements Lowerable { +public final class NewPodInstanceNode extends AbstractNewObjectNode { + public static final NodeClass TYPE = NodeClass.create(NewPodInstanceNode.class); + + private final ResolvedJavaType knownInstanceType; + @Input ValueNode hub; + @Input ValueNode arrayLength; + @Input ValueNode referenceMap; + + public NewPodInstanceNode(ResolvedJavaType knownInstanceType, ValueNode hub, ValueNode arrayLength, ValueNode referenceMap) { + super(TYPE, StampFactory.objectNonNull(TypeReference.createExactTrusted(knownInstanceType)), true, null); + this.knownInstanceType = knownInstanceType; + this.hub = hub; + this.arrayLength = arrayLength; + this.referenceMap = referenceMap; + } - public static final NodeClass TYPE = NodeClass.create(NewStoredContinuationNode.class); - @Input private ValueNode payloadSize; + public ResolvedJavaType getKnownInstanceType() { + return knownInstanceType; + } + + public ValueNode getHub() { + return hub; + } - public NewStoredContinuationNode(ValueNode payloadSize) { - super(TYPE, StampFactory.forKind(JavaKind.fromJavaClass(StoredContinuation.class)), false, null); - this.payloadSize = payloadSize; + public ValueNode getArrayLength() { + return arrayLength; } - public ValueNode getPayloadSize() { - return payloadSize; + public ValueNode getReferenceMap() { + return referenceMap; } @NodeIntrinsic - public static native StoredContinuation allocate(int payloadSize); + public static native Object newPodInstance(@ConstantNodeParameter Class knownInstanceClass, Class runtimeClass, int arrayLength, byte[] referenceMap); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateNewHybridInstanceNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateNewHybridInstanceNode.java index 9847e1ed61cd..cd76c27a7ac1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateNewHybridInstanceNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/SubstrateNewHybridInstanceNode.java @@ -69,4 +69,7 @@ public ResolvedJavaType instanceClass() { public ResolvedJavaType elementType() { return elementType; } + + @NodeIntrinsic + public static native Object allocate(@ConstantNodeParameter Class instanceType, @ConstantNodeParameter Class elementType, int arrayLength); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java index 3a10f6dc0af6..4b0747898c5f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java @@ -96,9 +96,9 @@ public class SubstrateGraphKit extends GraphKit { private final FrameStateBuilder frameState; private int nextBCI; - public SubstrateGraphKit(DebugContext debug, ResolvedJavaMethod stubMethod, Providers providers, WordTypes wordTypes, GraphBuilderConfiguration.Plugins graphBuilderPlugins, - CompilationIdentifier compilationId) { - super(debug, stubMethod, providers, wordTypes, graphBuilderPlugins, compilationId, null, SubstrateOptions.parseOnce(), false); + public SubstrateGraphKit(DebugContext debug, ResolvedJavaMethod stubMethod, Providers providers, WordTypes wordTypes, + GraphBuilderConfiguration.Plugins graphBuilderPlugins, CompilationIdentifier compilationId, boolean forceTrackNodeSourcePosition) { + super(debug, stubMethod, providers, wordTypes, graphBuilderPlugins, compilationId, null, forceTrackNodeSourcePosition || SubstrateOptions.parseOnce(), false); assert wordTypes != null : "Support for Word types is mandatory"; frameState = new FrameStateBuilder(this, stubMethod, graph); frameState.disableKindVerification(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/GCAllocationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/GCAllocationSupport.java index d65a04880329..aa4697614b19 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/GCAllocationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/GCAllocationSupport.java @@ -37,6 +37,8 @@ public interface GCAllocationSupport { ForeignCallDescriptor getSlowNewArrayStub(); + ForeignCallDescriptor getSlowNewPodInstanceStub(); + boolean useTLAB(); boolean shouldAllocateInTLAB(UnsignedWord size, boolean isArray); 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 c4923441c66a..dcff947bab51 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 @@ -27,9 +27,11 @@ import static org.graalvm.compiler.nodes.PiArrayNode.piArrayCastToSnippetReplaceeStamp; import static org.graalvm.compiler.nodes.PiNode.piCastToSnippetReplaceeStamp; import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.EXTREMELY_FAST_PATH_PROBABILITY; +import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.FAST_PATH_PROBABILITY; import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability; import static org.graalvm.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER; +import java.util.Arrays; import java.util.Map; import org.graalvm.compiler.api.replacements.Fold; @@ -62,11 +64,13 @@ import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.util.Providers; import org.graalvm.compiler.replacements.AllocationSnippets; +import org.graalvm.compiler.replacements.ReplacementsUtil; import org.graalvm.compiler.replacements.SnippetCounter; import org.graalvm.compiler.replacements.SnippetTemplate; import org.graalvm.compiler.replacements.SnippetTemplate.Arguments; import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo; import org.graalvm.compiler.word.BarrieredAccess; +import org.graalvm.compiler.word.ObjectAccess; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.word.LocationIdentity; @@ -80,8 +84,10 @@ 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.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.meta.SharedType; @@ -145,7 +151,7 @@ public Object allocateInstanceDynamic(@NonNullParameter DynamicHub hub, @Constan protected Object allocateInstanceDynamicImpl(DynamicHub hub, FillContent fillContents, boolean emitMemoryBarrier, @SuppressWarnings("unused") boolean supportsBulkZeroing, @SuppressWarnings("unused") boolean supportsOptimizedFilling, AllocationProfilingData profilingData) { // The hub was already verified by a ValidateNewInstanceClassNode. - UnsignedWord size = LayoutEncoding.getInstanceSize(hub.getLayoutEncoding()); + UnsignedWord size = LayoutEncoding.getPureInstanceSize(hub.getLayoutEncoding()); Object result = allocateInstanceImpl(encodeAsTLABObjectHeader(hub), size, fillContents, emitMemoryBarrier, false, profilingData); return piCastToSnippetReplaceeStamp(result); } @@ -221,6 +227,8 @@ private static void instanceHubErrorStub(DynamicHub hub) throws InstantiationExc } else if (!hub.isInstantiated()) { throw new IllegalArgumentException("Type " + DynamicHub.toClass(hub).getTypeName() + " is instantiated reflectively but was never registered." + " Register the type by adding \"unsafeAllocated\" for the type in " + ConfigurationFile.REFLECTION.getFileName() + "."); + } else if (LayoutEncoding.isHybrid(hub.getLayoutEncoding())) { + throw new InstantiationException("Cannot allocate objects of special hybrid types."); } else { throw VMError.shouldNotReachHere(); } @@ -260,6 +268,60 @@ private static void arrayHubErrorStub(DynamicHub elementType) { } } + @Snippet + public Object allocatePod(@NonNullParameter DynamicHub hub, int arrayLength, byte[] referenceMap, @ConstantParameter boolean emitMemoryBarrier, @ConstantParameter boolean maybeUnroll, + @ConstantParameter boolean supportsBulkZeroing, @ConstantParameter boolean supportsOptimizedFilling, @ConstantParameter AllocationSnippets.AllocationProfilingData profilingData) { + + Word thread = getTLABInfo(); + Word top = readTlabTop(thread); + Word end = readTlabEnd(thread); + ReplacementsUtil.dynamicAssert(end.subtract(top).belowOrEqual(Integer.MAX_VALUE), "TLAB is too large"); + + int arrayBaseOffset = LayoutEncoding.getArrayBaseOffsetAsInt(hub.getLayoutEncoding()); + UnsignedWord allocationSize = arrayAllocationSize(arrayLength, arrayBaseOffset, 0); + Word newTop = top.add(allocationSize); + + Object instance; + if (useTLAB() && probability(FAST_PATH_PROBABILITY, shouldAllocateInTLAB(allocationSize, true)) && probability(FAST_PATH_PROBABILITY, newTop.belowOrEqual(end))) { + writeTlabTop(thread, newTop); + emitPrefetchAllocate(newTop, true); + instance = formatPod(encodeAsTLABObjectHeader(hub), hub, arrayLength, referenceMap, top, AllocationSnippets.FillContent.WITH_ZEROES, + afterArrayLengthOffset(), emitMemoryBarrier, maybeUnroll, supportsBulkZeroing, supportsOptimizedFilling, profilingData.snippetCounters); + } else { + profilingData.snippetCounters.stub.inc(); + instance = callSlowNewPodInstance(gcAllocationSupport().getSlowNewPodInstanceStub(), encodeAsTLABObjectHeader(hub), arrayLength, afterArrayLengthOffset(), referenceMap); + } + profileAllocation(profilingData, allocationSize); + return piArrayCastToSnippetReplaceeStamp(verifyOop(instance), arrayLength); + } + + @NodeIntrinsic(value = ForeignCallNode.class) + private static native Object callSlowNewPodInstance(@ConstantNodeParameter ForeignCallDescriptor descriptor, Word hub, int length, int fillStartOffset, byte[] referenceMap); + + public Object formatPod(Word objectHeader, DynamicHub hub, int arrayLength, byte[] referenceMap, Word memory, FillContent fillContents, int fillStartOffset, + boolean emitMemoryBarrier, boolean maybeUnroll, boolean supportsBulkZeroing, boolean supportsOptimizedFilling, AllocationSnippetCounters snippetCounters) { + + int layoutEncoding = hub.getLayoutEncoding(); + UnsignedWord allocationSize = LayoutEncoding.getArraySize(layoutEncoding, arrayLength); + + Object instance = formatArray(objectHeader, allocationSize, arrayLength, memory, fillContents, fillStartOffset, + false, maybeUnroll, supportsBulkZeroing, supportsOptimizedFilling, snippetCounters); + + int fromOffset = ConfigurationValues.getObjectLayout().getArrayBaseOffset(JavaKind.Byte); + int toOffset = LayoutEncoding.getArrayBaseOffsetAsInt(layoutEncoding) + arrayLength - referenceMap.length; + for (int i = 0; i < referenceMap.length; i++) { + byte b = ObjectAccess.readByte(referenceMap, fromOffset + i, byteArrayIdentity()); + ObjectAccess.writeByte(instance, toOffset + i, b, LocationIdentity.INIT_LOCATION); + } + emitMemoryBarrierIf(emitMemoryBarrier); + return instance; + } + + @Fold + static LocationIdentity byteArrayIdentity() { + return NamedLocationIdentity.getArrayLocation(JavaKind.Byte); + } + @Override protected final int getPrefetchStyle() { return SubstrateOptions.AllocatePrefetchStyle.getValue(); @@ -290,7 +352,7 @@ protected final int instanceHeaderSize() { } @Fold - public static int afterArrayLengthOffset() { + protected static int afterArrayLengthOffset() { return ConfigurationValues.getObjectLayout().getArrayLengthOffset() + ConfigurationValues.getObjectLayout().sizeInBytes(JavaKind.Int); } @@ -419,6 +481,8 @@ public static class Templates extends SubstrateTemplates { private final SnippetInfo validateNewInstanceClass; + private final SnippetInfo allocatePod; + public Templates(OptionValues options, Providers providers, SubstrateAllocationSnippets receiver) { super(options, providers); snippetCounters = new AllocationSnippetCounters(SnippetCounter.Group.NullFactory); @@ -430,6 +494,15 @@ public Templates(OptionValues options, Providers providers, SubstrateAllocationS allocateArrayDynamic = snippet(SubstrateAllocationSnippets.class, "allocateArrayDynamic", null, receiver, ALLOCATION_LOCATIONS); newmultiarray = snippet(SubstrateAllocationSnippets.class, "newmultiarray", null, receiver, ALLOCATION_LOCATIONS); validateNewInstanceClass = snippet(SubstrateAllocationSnippets.class, "validateNewInstanceClass", null, receiver, ALLOCATION_LOCATIONS); + + SnippetInfo allocatePodSnippet = null; + if (Pod.RuntimeSupport.isPresent()) { + Object[] podLocations = SubstrateAllocationSnippets.ALLOCATION_LOCATIONS; + podLocations = Arrays.copyOf(podLocations, podLocations.length + 1); + podLocations[podLocations.length - 1] = byteArrayIdentity(); + allocatePodSnippet = snippet(SubstrateAllocationSnippets.class, "allocatePod", null, receiver, podLocations); + } + allocatePod = allocatePodSnippet; } public void registerLowering(Map, NodeLoweringProvider> lowerings) { @@ -440,6 +513,10 @@ public void registerLowering(Map, NodeLoweringProvider> lowerings.put(DynamicNewArrayNode.class, new DynamicNewArrayLowering()); lowerings.put(NewMultiArrayNode.class, new NewMultiArrayLowering()); lowerings.put(ValidateNewInstanceClassNode.class, new ValidateNewInstanceClassLowering()); + + if (Pod.RuntimeSupport.isPresent()) { + lowerings.put(NewPodInstanceNode.class, new NewPodInstanceLowering()); + } } public AllocationSnippetCounters getSnippetCounters() { @@ -486,7 +563,7 @@ public void lower(NewInstanceNode node, LoweringTool tool) { DynamicHub hub = ensureMarkedAsInstantiated(type.getHub()); ConstantNode hubConstant = ConstantNode.forConstant(SubstrateObjectConstant.forObject(hub), providers.getMetaAccess(), graph); - long size = LayoutEncoding.getInstanceSize(hub.getLayoutEncoding()).rawValue(); + long size = LayoutEncoding.getPureInstanceSize(hub.getLayoutEncoding()).rawValue(); Arguments args = new Arguments(allocateInstance, graph.getGuardsStage(), tool.getLoweringStage()); args.add("hub", hubConstant); @@ -644,6 +721,32 @@ public void lower(ValidateNewInstanceClassNode node, LoweringTool tool) { template(node, args).instantiate(providers.getMetaAccess(), node, SnippetTemplate.DEFAULT_REPLACER, args); } } + + private class NewPodInstanceLowering implements NodeLoweringProvider { + @Override + public void lower(NewPodInstanceNode node, LoweringTool tool) { + StructuredGraph graph = node.graph(); + if (graph.getGuardsStage() != StructuredGraph.GuardsStage.AFTER_FSA) { + return; + } + + assert node.getKnownInstanceType() == null || (node.getHub().isConstant() && + providers.getConstantReflection().asJavaType(node.getHub().asConstant()).equals(node.getKnownInstanceType())); + assert node.fillContents() : "fillContents must be true for hybrid allocations"; + + Arguments args = new Arguments(allocatePod, graph.getGuardsStage(), tool.getLoweringStage()); + args.add("hub", node.getHub()); + args.add("arrayLength", node.getArrayLength()); + args.add("referenceMap", node.getReferenceMap()); + args.addConst("emitMemoryBarrier", node.emitMemoryBarrier()); + args.addConst("maybeUnroll", node.getArrayLength().isConstant()); + args.addConst("supportsBulkZeroing", tool.getLowerer().supportsBulkZeroing()); + args.addConst("supportsOptimizedFilling", tool.getLowerer().supportsOptimizedFilling(graph.getOptions())); + args.addConst("profilingData", getProfilingData(node, node.getKnownInstanceType())); + + template(node, args).instantiate(providers.getMetaAccess(), node, SnippetTemplate.DEFAULT_REPLACER, args); + } + } } /** diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Pod.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Pod.java new file mode 100644 index 000000000000..a92cda205ad8 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Pod.java @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.heap; + +import java.io.ByteArrayOutputStream; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.word.UnsignedWord; + +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.LayoutEncoding; +import com.oracle.svm.core.util.ImageHeapMap; +import com.oracle.svm.core.util.UnsignedUtils; + +import jdk.vm.ci.meta.JavaKind; + +/** + * A structure of fields, including object references, that is {@linkplain Builder modeled} at image + * runtime and instances of which are allocated on the Java heap. The name pod refers to it + * storing "plain old data" without further object-oriented features such as method dispatching or + * type information, apart from having a Java superclass. + * + * @param The interface of the {@linkplain #getFactory() factory} that allocates instances. + */ +public final class Pod { + private final RuntimeSupport.PodInfo podInfo; + private final int arrayLength; + private final byte[] referenceMap; + private final T factory; + + private Pod(RuntimeSupport.PodInfo podInfo, int arrayLength, byte[] referenceMap) { + this.podInfo = podInfo; + try { + @SuppressWarnings("unchecked") + T factoryInstance = (T) podInfo.factoryCtor.newInstance(this); + this.factory = factoryInstance; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + this.arrayLength = arrayLength; + this.referenceMap = referenceMap; + } + + public T getFactory() { + return factory; + } + + /** + * A builder for constructing pods with a specific superpod or superclass and factory interface + * and {@linkplain #addField additional fields}. + */ + public static final class Builder { + /** + * Create a builder for a pod that is derived directly from {@link Object} and + * {@link Supplier} as factory interface. This is supported without explicitly registering + * Object as superclass with {@code PodSupport}. + */ + public static Builder> create() { + return new Builder<>(Object.class, Supplier.class, null); + } + + /** + * Create a builder for a pod derived from the given superpod, which has the same + * superclass, uses the same factory interface for initialization, and has the same fields + * and in addition fields that are added with {@link #addField}. There are no guarantees + * with regard to Java type checks other than that {@link Object#getClass} on instances of + * the pods will return a {@link Class} to which the superclass of the two pods + * {@linkplain Class#isAssignableFrom is assignable from}. + */ + public static Builder createExtending(Pod superPod) { + return new Builder<>(null, null, superPod); + } + + /** + * Create a builder for a pod that extends the given superclass and instances of which can + * be allocated via the given factory interface. The specific pair of superclass and factory + * interface must have been registered during the image build with {@code PodSupport}. + */ + public static Builder createExtending(Class superClass, Class factoryInterface) { + return new Builder<>(superClass, factoryInterface, null); + } + + private final Pod superPod; + private final RuntimeSupport.PodInfo podInfo; + private final List fields = new ArrayList<>(); + private boolean built = false; + + private Builder(Class superClass, Class factoryInterface, Pod superPod) { + assert superPod == null || (superClass == null && factoryInterface == null); + if (!RuntimeSupport.isPresent()) { + throw new UnsupportedOperationException("Pods are not available in this native image. Only SerialGC currently supports pods."); + } + if (superPod != null) { + this.podInfo = superPod.podInfo; + } else if (superClass != null && factoryInterface != null) { + RuntimeSupport.PodSpec spec = new RuntimeSupport.PodSpec(superClass, factoryInterface); + this.podInfo = RuntimeSupport.singleton().getInfo(spec); + if (this.podInfo == null) { + throw new IllegalArgumentException("Pod superclass/factory interface pair was not registered during image build: " + superClass + ", " + factoryInterface); + } + } else { + throw new NullPointerException(); + } + assert DynamicHub.fromClass(this.podInfo.podClass).isPodInstanceClass(); + this.superPod = superPod; + } + + private void guaranteeUnbuilt() { + if (built) { + throw new IllegalStateException(); + } + } + + /** + * Add a field of a specified type to a pod. Once the pod has been {@linkplain #build + * built}, its offset is available via {@link Field#getOffset} and values of the given type + * can be written to instances, for example using {@code Unsafe}. + */ + public Field addField(Class type) { + guaranteeUnbuilt(); + Objects.requireNonNull(type); + if (type == void.class) { + throw new IllegalArgumentException("void is an illegal field type"); + } + + JavaKind kind = JavaKind.fromJavaClass(type); + int size = ConfigurationValues.getObjectLayout().sizeInBytes(kind); + Field f = new Field(size, kind.isObject()); + fields.add(f); + return f; + } + + /** + * Create and return a pod with the superclass/superpod given during builder creation and + * the {@linkplain #addField added fields}. This method can be called only once, after which + * the builder can no longer be used. + */ + public Pod build() { + guaranteeUnbuilt(); + built = true; + + /* + * We layout the requested fields in the hybrid object's array part in a similar fashion + * as UniverseBuilder does for regular instance fields, putting reference fields at the + * beginning and trying to put narrow fields in alignment gaps between wider fields. The + * entire layout of a pod might look as follows: + * + * [ hub pointer | identity hashcode | array length | superclass instance fields | + * instance fields | monitor | pod fields (ref, short, byte, long) | pod reference map ] + * + * The array length part would provide the combined length of the pod fields and pod + * reference map excluding any alignment padding at their beginning or the object's end. + */ + + Collections.sort(fields); + + UnsignedWord baseOffset = LayoutEncoding.getArrayBaseOffset( + DynamicHub.fromClass(podInfo.podClass).getLayoutEncoding()); + + byte[] superRefMap = null; + UnsignedWord nextOffset = baseOffset; + if (superPod != null) { + superRefMap = superPod.referenceMap; + nextOffset = nextOffset.add(superPod.arrayLength - superRefMap.length); + } + ReferenceMapEncoder refMapEncoder = new ReferenceMapEncoder(superRefMap); + while (!fields.isEmpty()) { + boolean progress = false; + for (int i = 0; i < fields.size(); i++) { + Field field = fields.get(i); + + if (nextOffset.unsignedRemainder(field.size).equal(0)) { + field.initOffset(UnsignedUtils.safeToInt(nextOffset)); + + if (field.isReference) { + refMapEncoder.add(UnsignedUtils.safeToInt(nextOffset.subtract(baseOffset)), field.size); + } + + fields.remove(i); + nextOffset = nextOffset.add(field.size); + progress = true; + break; + } + } + if (!progress) { + nextOffset = nextOffset.add(1); // insert padding byte and retry + } + } + + byte[] referenceMap = refMapEncoder.encode(); + int arrayLength = UnsignedUtils.safeToInt(nextOffset) + referenceMap.length; + return new Pod<>(podInfo, arrayLength, referenceMap); + } + } + + public static final class Field implements Comparable { + private final int size; + private final boolean isReference; + private int offset = -1; + + Field(int size, boolean isReference) { + assert size > 0; + this.size = size; + this.isReference = isReference; + } + + public int getSize() { + return size; + } + + public boolean isReference() { + return isReference; + } + + public int getOffset() { + if (offset == -1) { + throw new IllegalStateException("Pod must be built before field offsets are assigned"); + } + return offset; + } + + void initOffset(int value) { + assert this.offset == -1; + assert value >= 0; + this.offset = value; + } + + @Override + public int compareTo(Field f) { + if (isReference != f.isReference) { // references first + return Boolean.compare(f.isReference, isReference); + } + return f.size - size; // larger fields first + } + } + + public static final class RuntimeSupport { + @Fold + public static boolean isPresent() { + return ImageSingletons.contains(RuntimeSupport.class); + } + + @Fold + public static RuntimeSupport singleton() { + return ImageSingletons.lookup(RuntimeSupport.class); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface PodFactory { + Class podClass(); + } + + public static final class PodSpec { + final Class superClass; + final Class factoryInterface; + + public PodSpec(Class superClass, Class factoryInterface) { + assert superClass != null && factoryInterface != null; + this.superClass = superClass; + this.factoryInterface = factoryInterface; + } + + @Override + public boolean equals(Object obj) { + if (obj != this && getClass() == obj.getClass()) { + PodSpec other = (PodSpec) obj; + return superClass.equals(other.superClass) && factoryInterface.equals(other.factoryInterface); + } + return (obj == this); + } + + @Override + public int hashCode() { + return 31 * superClass.hashCode() + factoryInterface.hashCode(); + } + } + + public static final class PodInfo { + public final Class podClass; + public final Constructor factoryCtor; + + public PodInfo(Class podClass, Constructor factoryCtor) { + this.podClass = podClass; + this.factoryCtor = factoryCtor; + } + } + + private final EconomicMap pods = ImageHeapMap.create(); + + @Platforms(Platform.HOSTED_ONLY.class) + public RuntimeSupport() { + } + + @Platforms(Platform.HOSTED_ONLY.class) + public boolean registerPod(PodSpec spec, PodInfo info) { + return pods.putIfAbsent(spec, info) == null; + } + + PodInfo getInfo(PodSpec spec) { + return pods.get(spec); + } + } + + private static final class ReferenceMapEncoder { + private final BitSet bitset = new BitSet(); + private final int referenceSize = ConfigurationValues.getObjectLayout().getReferenceSize(); + + ReferenceMapEncoder(byte[] referenceMap) { + if (referenceMap != null) { + decode(referenceMap); + } + } + + void add(int offset, int size) { + assert offset % referenceSize == 0; + assert size == referenceSize; + + int index = offset / referenceSize; + assert !bitset.get(index); + bitset.set(index); + } + + /** + * Generates a reference map composed of unsigned byte pairs of {@code nrefs} (sequence of n + * references) and {@code gaps} (number of sequential reference-sized primitives). When a + * single lengthy sequence of references or primitives cannot be encoded in an unsigned + * byte, it is encoded in multiple pairs with either gaps or nrefs being 0. The reference + * map covers only the part of the array from the beginning until the last reference. The + * end of the reference map is indicated by a pair with {@code gaps} == 0, unless + * {@code nrefs} is 0xff which could also mean that the pair is part of an ongoing sequence, + * in which case an extra zero pair is added at the end. + * + * We try to place reference fields at the beginning, so the reference map begins with + * {@code nrefs} indicating the number of references right at the start of the array. If + * there are primitive fields at the beginning, this first value will be zero. + */ + byte[] encode() { + if (bitset.isEmpty()) { + return new byte[]{0, 0}; + } + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int previous; + int index = 0; + for (;;) { + previous = index; + index = bitset.nextClearBit(previous); + int nrefs = index - previous; + putUV(buffer, nrefs); + + previous = index; + index = bitset.nextSetBit(index); + if (index != -1) { + putUV(buffer, index - previous); // gap + } else { + buffer.write(0); + if ((nrefs & 0xff) == 0) { // needs an explicit end marker + buffer.write(0); + buffer.write(0); + } + break; + } + } + + // reverse so we can decode backwards from the known end + byte[] bytes = buffer.toByteArray(); + for (int i = 0, j = bytes.length - 1; i < j; i++, j--) { + byte t = bytes[i]; + bytes[i] = bytes[j]; + bytes[j] = t; + } + + assert bitset.equals(new ReferenceMapEncoder(bytes).bitset); + return bytes; + } + + private static void putUV(ByteArrayOutputStream buffer, int value) { + int v = value; + for (; v > 0xff; v -= 0xff) { // should be rare + buffer.write(0xff); + buffer.write(0); + } + buffer.write(v); + } + + private void decode(byte[] encoded) { + int nrefs; + int gap; + int bit = 0; + int i = encoded.length - 1; + do { + nrefs = Byte.toUnsignedInt(encoded[i]); + gap = Byte.toUnsignedInt(encoded[i - 1]); + i -= 2; + bitset.set(bit, bit + nrefs); + bit += nrefs + gap; + } while (gap != 0 || nrefs == 0xff); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PodReferenceMapDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PodReferenceMapDecoder.java new file mode 100644 index 000000000000..70a035590350 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/PodReferenceMapDecoder.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.heap; + +import org.graalvm.compiler.api.directives.GraalDirectives; +import org.graalvm.compiler.nodes.java.ArrayLengthNode; +import org.graalvm.compiler.word.BarrieredAccess; +import org.graalvm.word.LocationIdentity; +import org.graalvm.word.Pointer; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.JavaMemoryUtil; +import com.oracle.svm.core.annotate.AlwaysInline; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.graal.nodes.NewPodInstanceNode; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.LayoutEncoding; +import com.oracle.svm.core.util.UnsignedUtils; + +public final class PodReferenceMapDecoder { + @AlwaysInline("de-virtualize calls to ObjectReferenceVisitor") + public static boolean walkOffsetsFromPointer(Pointer baseAddress, int layoutEncoding, ObjectReferenceVisitor visitor, Object obj) { + int referenceSize = ConfigurationValues.getObjectLayout().getReferenceSize(); + boolean isCompressed = ReferenceAccess.singleton().haveCompressedReferences(); + + UnsignedWord refOffset = LayoutEncoding.getArrayBaseOffset(layoutEncoding); + UnsignedWord mapOffset = LayoutEncoding.getArrayElementOffset(layoutEncoding, ArrayLengthNode.arrayLength(obj)); + + int nrefs; + int gap; + do { + mapOffset = mapOffset.subtract(2); + gap = Byte.toUnsignedInt(baseAddress.readByte(mapOffset)); + nrefs = Byte.toUnsignedInt(baseAddress.readByte(mapOffset.add(1))); + + for (int i = 0; i < nrefs; i++) { + if (!visitor.visitObjectReferenceInline(baseAddress.add(refOffset), 0, isCompressed, obj)) { + return false; + } + refOffset = refOffset.add(referenceSize); + } + refOffset = refOffset.add(referenceSize * gap); + } while (gap != 0 || nrefs == 0xff); + + return true; + } + + /** + * Implements the allocation and the copying of the reference map and data stored in the hybrid + * array for {@link Object#clone()}. + */ + public static Object clone(Object original, DynamicHub hub, int layoutEncoding) { + Class nonNullHub = GraalDirectives.guardingNonNull(DynamicHub.toClass(hub)); + int length = ArrayLengthNode.arrayLength(original); + byte[] referenceMap = extractReferenceMap(original, layoutEncoding, length); + Object result = NewPodInstanceNode.newPodInstance(null, nonNullHub, length, referenceMap); + copyArray(original, result, layoutEncoding, length); + return result; + } + + /** + * We could optimize cloning if it turns out to be a bottleneck by avoiding this and passing the + * existing pod to {@link NewPodInstanceNode} (and its slow path), but this needs some + * duplication of nodes and snippets to avoid touching the {@link LocationIdentity} of extra + * objects in situations in which we don't need to. + */ + private static byte[] extractReferenceMap(Object obj, int layoutEncoding, int length) { + UnsignedWord mapEndOffset = LayoutEncoding.getArrayElementOffset(layoutEncoding, length); + UnsignedWord mapOffset = mapEndOffset; + + int gap; + int nrefs; + do { + mapOffset = mapOffset.subtract(2); + gap = Byte.toUnsignedInt(BarrieredAccess.readByte(obj, mapOffset)); + nrefs = Byte.toUnsignedInt(BarrieredAccess.readByte(obj, mapOffset.add(1))); + } while (gap != 0 || nrefs == 0xff); + + int refMapLength = UnsignedUtils.safeToInt(mapEndOffset.subtract(mapOffset)); + byte[] refMap = new byte[refMapLength]; + for (int i = 0; i < refMapLength; i++) { + refMap[i] = BarrieredAccess.readByte(obj, mapOffset.add(i)); + } + return refMap; + } + + private static void copyArray(Object original, Object copy, int layoutEncoding, int length) { + int referenceSize = ConfigurationValues.getObjectLayout().getReferenceSize(); + + UnsignedWord refOffset = LayoutEncoding.getArrayBaseOffset(layoutEncoding); + UnsignedWord mapOffset = LayoutEncoding.getArrayElementOffset(layoutEncoding, length); + + UnsignedWord gap; + UnsignedWord nrefs; + do { + mapOffset = mapOffset.subtract(2); + gap = WordFactory.unsigned(Byte.toUnsignedInt(BarrieredAccess.readByte(copy, mapOffset))); + nrefs = WordFactory.unsigned(Byte.toUnsignedInt(BarrieredAccess.readByte(copy, mapOffset.add(1)))); + + // Copy references separately with the required barriers + JavaMemoryUtil.copyReferencesForward(original, refOffset, copy, refOffset, nrefs); + + // Copy primitives in between + UnsignedWord primOffset = refOffset.add(nrefs.multiply(referenceSize)); + UnsignedWord primBytes = gap.multiply(referenceSize); + JavaMemoryUtil.copyForward(original, primOffset, copy, primOffset, primBytes); + + refOffset = primOffset.add(primBytes); + } while (gap.notEqual(0) || nrefs.equal(0xff)); + + // The loop above could be optimized for very long sequences of references or primitive + // values encoded in multiple reference map entries, but those should be rare in practice. + + // Copy primitives between last reference and reference map + UnsignedWord primBytes = mapOffset.subtract(refOffset); + JavaMemoryUtil.copyForward(original, refOffset, copy, refOffset, primBytes); + } + + private PodReferenceMapDecoder() { + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuation.java index 147dd678263f..4b05c86b2531 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuation.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuation.java @@ -24,10 +24,16 @@ */ package com.oracle.svm.core.heap; +import com.oracle.svm.core.annotate.Hybrid; + /** * This class is used for variably-sized objects that store continuation stack frames. * * For object layout and other implementation details, see {@link StoredContinuationImpl}. */ +@Hybrid(componentType = byte.class) public final class StoredContinuation { + /** Must be allocated via {@link StoredContinuationImpl}. */ + private StoredContinuation() { + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuationImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuationImpl.java index 9b54c4eeff53..e0d2d6a08735 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuationImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuationImpl.java @@ -31,11 +31,13 @@ import org.graalvm.compiler.api.directives.GraalDirectives; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.util.TypeConversion; +import org.graalvm.compiler.graph.Node.NodeIntrinsic; +import org.graalvm.compiler.nodes.java.ArrayLengthNode; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.c.function.CodePointer; -import org.graalvm.word.LocationIdentity; import org.graalvm.word.Pointer; +import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; import com.oracle.svm.core.UnmanagedMemoryUtil; @@ -48,7 +50,9 @@ import com.oracle.svm.core.code.FrameInfoQueryResult; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.deopt.DeoptimizedFrame; -import com.oracle.svm.core.graal.nodes.NewStoredContinuationNode; +import com.oracle.svm.core.graal.nodes.SubstrateNewHybridInstanceNode; +import com.oracle.svm.core.hub.LayoutEncoding; +import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.stack.JavaStackWalker; import com.oracle.svm.core.stack.StackFrameVisitor; import com.oracle.svm.core.thread.Continuation; @@ -59,21 +63,17 @@ import jdk.vm.ci.meta.JavaKind; /** - * Helper class to access a {@link StoredContinuation}. - * - * Memory layout of a {@link StoredContinuation} instance:e Each {@link StoredContinuation} has a - * fixed-size header with the size of the instance, followed by the data of the stored frame(s). + * Helper class to access a {@link StoredContinuation}, the payload layout of which is: * *

- *      +-0x0-|-0x4-|-0x8-|-0xa-+
- * 0x00 | hub | (1) | (2) |  -  | header: hub; 1) identity hash code; 2) object monitor;
- *      +-----------------------+               3) payload size in bytes; 4) number of frames n
- * 0x10 | (3) | (4) | (a1)|(b1) | 8-byte per-frame headers: a) size of frame in bytes; b) reference map index of frame
- *      +-----------------------+
- * 0x20 | (a2)|(b2) |  .. | ..  |
- *  .   | (an)|(bn) | c o n t i |
- *  .   : n u o u s   f r a m e :
- *  .   |  d a t a  |
+ *      +-0x0-|-0x4-|-0x8-|-0xc-+
+ * 0x00 | (1) | (a1)|(b1) | (a2)| 1) number of frames n; per-frame headers: a) size of frame,
+ *      +-----+-----+-----+-----+                                           b) reference map index of frame
+ * 0x10 |(b2) | ..  | ..  | (an)|
+ *      +-----+-----+-----+-----+
+ * 0x20 |(bn) | c o n t i n u o |
+ *  .   :  u s    f r a m e     :
+ * 0xnn |  d a t a  +-----------+
  *      +-----------+
  * 
*/ @@ -82,62 +82,29 @@ public interface RawFrameVisitor { boolean visitRawFrame(int index, Pointer sp); } - private static final int HEADER_SIZE = 0x18; - public static final int PAYLOAD_OFFSET = HEADER_SIZE; - public static final int OBJECT_MONITOR_OFFSET = 8; - - private static final int SIZE_OFFSET_TO_PAYLOAD = -HEADER_SIZE + 0x10; - private static final int FRAME_COUNT_OFFSET_TO_PAYLOAD = -HEADER_SIZE + 0x14; - - private static final int VALID_OFFSET_START = SIZE_OFFSET_TO_PAYLOAD; - - private static final int FRAME_META_START_OFFSET_TO_PAYLOAD = 0; + private static final int FRAME_COUNT_OFFSET = 0; + private static final int FRAME_METAS_START_OFFSET = 4; private static final int FRAME_META_SIZE = 8; private static final int SIZE_OFFSET_IN_FRAME_META = 0; private static final int REFERENCE_MAP_INDEX_OFFSET_IN_FRAME_META = 4; - @Uninterruptible(reason = "in allocation of StoredContinuation instance") - public static void initializeNewlyAllocated(Object obj, int payloadSize) { - Pointer p = Word.objectToUntrackedPointer(obj).add(PAYLOAD_OFFSET); - p.writeInt(SIZE_OFFSET_TO_PAYLOAD, payloadSize, LocationIdentity.init()); - // Keep GC from taking a closer look until frames are initialized - p.writeInt(FRAME_COUNT_OFFSET_TO_PAYLOAD, 0, LocationIdentity.init()); - } - - /* All method calls in this function except `allocate` should be `@Uninterruptible` */ private static StoredContinuation allocate(int payloadSize) { - assert payloadSize % 8 == 0; - StoredContinuation f = NewStoredContinuationNode.allocate(payloadSize); + StoredContinuation f = (StoredContinuation) SubstrateNewHybridInstanceNode.allocate(StoredContinuation.class, byte.class, payloadSize); assert readPayloadSize(f) == payloadSize; return f; } - // validator functions - - @Uninterruptible(reason = "read StoredContinuation") - private static boolean checkOffset(StoredContinuation f, int offset) { - assert offset % 4 == 0; - if (offset >= 0) { - assert offset < readSize(f); - } else { - assert offset >= VALID_OFFSET_START; - } - return true; - } - @Uninterruptible(reason = "read StoredContinuation") private static boolean checkPayloadOffset(StoredContinuation f, int offset) { assert offset % 4 == 0; assert offset >= 0; - assert offset < readSize(f); + assert offset < readPayloadSize(f); return true; } - // read/write functions - @Uninterruptible(reason = "read StoredContinuation") private static int readPayloadInt(StoredContinuation f, int offset) { - assert checkOffset(f, offset); + assert checkPayloadOffset(f, offset); return payloadLocation(f).readInt(offset); } @@ -147,56 +114,45 @@ private static void writePayloadInt(StoredContinuation f, int offset, int value) payloadLocation(f).writeInt(offset, value); } - // size of payload - @Uninterruptible(reason = "read StoredContinuation") - private static int readPayloadSize(StoredContinuation f) { - return readPayloadInt(f, SIZE_OFFSET_TO_PAYLOAD); - } + @NodeIntrinsic(ArrayLengthNode.class) + private static native int readPayloadSize(StoredContinuation f); // size of raw frame @Uninterruptible(reason = "read StoredContinuation") public static int readAllFrameSize(StoredContinuation f) { - return readPayloadSize(f) - readFrameMetaSize(f); - } - - // size of object - @Uninterruptible(reason = "read StoredContinuation") - public static int readSize(StoredContinuation f) { - return readPayloadSize(f) + HEADER_SIZE; + return readPayloadSize(f) - readFrameMetasSize(f); } @Uninterruptible(reason = "read StoredContinuation") public static int readFrameCount(StoredContinuation f) { - return readPayloadInt(f, FRAME_COUNT_OFFSET_TO_PAYLOAD); + return readPayloadInt(f, FRAME_COUNT_OFFSET); } - // read frame meta-data - @Uninterruptible(reason = "read StoredContinuation") - private static int readFrameMetaSize(StoredContinuation f) { - return FRAME_META_START_OFFSET_TO_PAYLOAD + readFrameCount(f) * FRAME_META_SIZE; + private static int readFrameMetasSize(StoredContinuation f) { + return FRAME_METAS_START_OFFSET + readFrameCount(f) * FRAME_META_SIZE; } @Uninterruptible(reason = "read StoredContinuation") public static int readFrameSize(StoredContinuation f, int frameIndex) { - return payloadLocation(f).readInt(FRAME_META_START_OFFSET_TO_PAYLOAD + frameIndex * FRAME_META_SIZE + SIZE_OFFSET_IN_FRAME_META); + return readPayloadInt(f, FRAME_METAS_START_OFFSET + frameIndex * FRAME_META_SIZE + SIZE_OFFSET_IN_FRAME_META); } @Uninterruptible(reason = "read StoredContinuation") private static int readReferenceMapIndex(StoredContinuation f, int frameIndex) { - return payloadLocation(f).readInt(FRAME_META_START_OFFSET_TO_PAYLOAD + frameIndex * FRAME_META_SIZE + REFERENCE_MAP_INDEX_OFFSET_IN_FRAME_META); + return readPayloadInt(f, FRAME_METAS_START_OFFSET + frameIndex * FRAME_META_SIZE + REFERENCE_MAP_INDEX_OFFSET_IN_FRAME_META); } - // Pointers - @Uninterruptible(reason = "read/write StoredContinuation") private static Pointer payloadLocation(StoredContinuation f) { - return Word.objectToUntrackedPointer(f).add(PAYLOAD_OFFSET); + int layout = KnownIntrinsics.readHub(f).getLayoutEncoding(); + UnsignedWord baseOffset = LayoutEncoding.getArrayBaseOffset(layout); + return Word.objectToUntrackedPointer(f).add(baseOffset); } @Uninterruptible(reason = "read/write StoredContinuation") public static Pointer payloadFrameStart(StoredContinuation f) { - return payloadLocation(f).add(readFrameMetaSize(f)); + return payloadLocation(f).add(readFrameMetasSize(f)); } public static int allocateFromCurrentStack(Continuation cont, Pointer rootSp, Pointer leafSp, CodePointer leafIp) { @@ -235,7 +191,7 @@ private static int allocateFromStack(Continuation cont, Pointer rootSp, Pointer VMError.guarantee(resultLeafSP.isNonNull()); int frameCount = visitor.frameSizeReferenceMapIndex.size(); - int payloadSize = FRAME_META_SIZE * frameCount + UnsignedUtils.safeToInt(rootSp.subtract(resultLeafSP)); + int payloadSize = FRAME_METAS_START_OFFSET + FRAME_META_SIZE * frameCount + UnsignedUtils.safeToInt(rootSp.subtract(resultLeafSP)); cont.stored = allocate(payloadSize); /* @@ -246,9 +202,9 @@ private static int allocateFromStack(Continuation cont, Pointer rootSp, Pointer int allFrameSize = 0; for (int i = 0; i < frameCount; i++) { Pair frameSizeRefMapInxPair = visitor.frameSizeReferenceMapIndex.get(i); - writePayloadInt(cont.stored, FRAME_META_START_OFFSET_TO_PAYLOAD + i * FRAME_META_SIZE + SIZE_OFFSET_IN_FRAME_META, + writePayloadInt(cont.stored, FRAME_METAS_START_OFFSET + i * FRAME_META_SIZE + SIZE_OFFSET_IN_FRAME_META, frameSizeRefMapInxPair.getLeft()); - writePayloadInt(cont.stored, FRAME_META_START_OFFSET_TO_PAYLOAD + i * FRAME_META_SIZE + REFERENCE_MAP_INDEX_OFFSET_IN_FRAME_META, + writePayloadInt(cont.stored, FRAME_METAS_START_OFFSET + i * FRAME_META_SIZE + REFERENCE_MAP_INDEX_OFFSET_IN_FRAME_META, frameSizeRefMapInxPair.getRight()); allFrameSize += frameSizeRefMapInxPair.getLeft(); } @@ -259,7 +215,7 @@ private static int allocateFromStack(Continuation cont, Pointer rootSp, Pointer @Uninterruptible(reason = "Prevent modifications to the stack while copying.") private static void fillUninterruptibly(StoredContinuation stored, Pointer sp, int size, int frameCount) { Pointer p = payloadLocation(stored); - p.writeInt(FRAME_COUNT_OFFSET_TO_PAYLOAD, frameCount); + p.writeInt(FRAME_COUNT_OFFSET, frameCount); VMError.guarantee(size == readAllFrameSize(stored)); Pointer frameStart = payloadFrameStart(stored); @@ -286,13 +242,9 @@ private static void fillUninterruptibly(StoredContinuation stored, Pointer sp, i @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static boolean walkStoredContinuationFromPointer(Pointer baseAddress, RawFrameVisitor frameVisitor, ObjectReferenceVisitor refVisitor, Object holderObject) { StoredContinuation f = (StoredContinuation) holderObject; - - Pointer payloadStart = StoredContinuationImpl.payloadLocation(f); - assert payloadStart.subtract(baseAddress).equal(StoredContinuationImpl.HEADER_SIZE) : "base address not pointing to frame instance"; + assert baseAddress.equal(Word.objectToUntrackedPointer(holderObject)); int frameCount = StoredContinuationImpl.readFrameCount(f); - int size = StoredContinuationImpl.readPayloadSize(f); - Pointer curFrame = StoredContinuationImpl.payloadFrameStart(f); int frameIndex = 0; @@ -314,7 +266,7 @@ public static boolean walkStoredContinuationFromPointer(Pointer baseAddress, Raw assert frameIndex == frameCount; // NOTE: frameCount can be 0 if the object has just been allocated but not filled yet - assert frameCount == 0 || curFrame.subtract(payloadStart).equal(size); + assert frameCount == 0 || curFrame.subtract(payloadLocation(f)).equal(readPayloadSize(f)); return true; } 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 7b9843f3a2a9..a82da86a4888 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 @@ -98,7 +98,7 @@ import sun.reflect.generics.factory.GenericsFactory; import sun.reflect.generics.repository.ClassRepository; -@Hybrid(canHybridFieldsBeDuplicated = false) +@Hybrid @Substitute @TargetClass(java.lang.Class.class) @SuppressWarnings({"static-method", "serial"}) @@ -384,10 +384,10 @@ public void setClassInitializationInfo(ClassInitializationInfo classInitializati } @Platforms(Platform.HOSTED_ONLY.class) - public void setData(int layoutEncoding, int typeID, int monitorOffset, - short typeCheckStart, short typeCheckRange, short typeCheckSlot, short[] typeCheckSlots, - CFunctionPointer[] vtable, long referenceMapIndex, boolean isInstantiated) { + public void setData(int layoutEncoding, int typeID, int monitorOffset, short typeCheckStart, short typeCheckRange, short typeCheckSlot, short[] typeCheckSlots, + CFunctionPointer[] vtable, long referenceMapIndex, boolean isInstantiated, boolean canInstantiateAsInstance) { assert this.vtable == null : "Initialization must be called only once"; + assert !(!isInstantiated && canInstantiateAsInstance); this.layoutEncoding = layoutEncoding; this.typeID = typeID; @@ -402,8 +402,7 @@ public void setData(int layoutEncoding, int typeID, int monitorOffset, throw VMError.shouldNotReachHere("Reference map index not within integer range, need to switch field from int to long"); } this.referenceMapIndex = (int) referenceMapIndex; - this.instantiationFlags = NumUtil.safeToUByte(makeFlag(IS_INSTANTIATED_BIT, isInstantiated) | - makeFlag(CAN_INSTANTIATE_AS_INSTANCE_BIT, isInstantiated && isInstanceClass() && !LayoutEncoding.isSpecial(layoutEncoding))); + this.instantiationFlags = NumUtil.safeToUByte(makeFlag(IS_INSTANTIATED_BIT, isInstantiated) | makeFlag(CAN_INSTANTIATE_AS_INSTANCE_BIT, canInstantiateAsInstance)); } @Platforms(Platform.HOSTED_ONLY.class) @@ -621,8 +620,8 @@ public boolean isInstanceClass() { } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public boolean isStoredContinuationClass() { - return HubType.isStoredContinuation(hubType); + public boolean isPodInstanceClass() { + return HubType.isPodInstance(hubType); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/HubType.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/HubType.java index 54da8de209b4..ca902e0e42a7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/HubType.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/HubType.java @@ -34,7 +34,7 @@ public enum HubType { InstanceReference(1), // special hubs - StoredContinuation(2), + PodInstance(2), Other(3), // array hubs @@ -54,7 +54,7 @@ public int getValue() { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static boolean isInstance(int hubType) { - return hubType <= StoredContinuation.getValue(); + return hubType <= PodInstance.getValue(); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @@ -63,8 +63,8 @@ public static boolean isReferenceInstance(int hubType) { } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static boolean isStoredContinuation(int hubType) { - return hubType == StoredContinuation.getValue(); + public static boolean isPodInstance(int hubType) { + return hubType == PodInstance.getValue(); } @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 9522479008f7..0d4ae77e5d2f 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 @@ -36,6 +36,8 @@ import com.oracle.svm.core.heap.InstanceReferenceMapDecoder; import com.oracle.svm.core.heap.ObjectHeader; import com.oracle.svm.core.heap.ObjectReferenceVisitor; +import com.oracle.svm.core.heap.Pod; +import com.oracle.svm.core.heap.PodReferenceMapDecoder; import com.oracle.svm.core.heap.ReferenceAccess; /** @@ -65,7 +67,7 @@ public static boolean walkObjectInline(final Object obj, final ObjectReferenceVi final Pointer objPointer = Word.objectToUntrackedPointer(obj); // Visit each Object reference in the array part of the Object. - if (LayoutEncoding.isObjectArray(layoutEncoding)) { + if (LayoutEncoding.isArrayLikeWithObjectElements(layoutEncoding)) { int length = ArrayLengthNode.arrayLength(obj); int referenceSize = ConfigurationValues.getObjectLayout().getReferenceSize(); boolean isCompressed = ReferenceAccess.singleton().haveCompressedReferences(); @@ -79,6 +81,10 @@ public static boolean walkObjectInline(final Object obj, final ObjectReferenceVi } pos = pos.add(referenceSize); } + } else if (Pod.RuntimeSupport.isPresent() && objHub.isPodInstanceClass()) { + if (!PodReferenceMapDecoder.walkOffsetsFromPointer(objPointer, layoutEncoding, visitor, obj)) { + return false; + } } NonmovableArray referenceMapEncoding = DynamicHubSupport.getReferenceMapEncoding(); 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 5a9f895397e1..1a73be6c8d1d 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,11 +24,9 @@ */ package com.oracle.svm.core.hub; -import org.graalvm.compiler.api.directives.GraalDirectives; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.calc.UnsignedMath; import org.graalvm.compiler.nodes.java.ArrayLengthNode; -import org.graalvm.compiler.replacements.ReplacementsUtil; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; @@ -42,10 +40,7 @@ import com.oracle.svm.core.annotate.Hybrid; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.config.ObjectLayout; -import com.oracle.svm.core.heap.StoredContinuation; -import com.oracle.svm.core.heap.StoredContinuationImpl; import com.oracle.svm.core.snippets.KnownIntrinsics; -import com.oracle.svm.core.thread.Continuation; import com.oracle.svm.core.util.VMError; import jdk.vm.ci.meta.ResolvedJavaType; @@ -60,16 +55,16 @@ *
  • Array objects: the layout encoding for arrays is a negative number with the following * format:
    * - * [tag:2, free:10, base:12, indexShift:8] + * [tag:3, unused:9, base:12, indexShift:8] *
      - *
    • tag: 0x80 if the array is an object array, 0xC0 if it is a primitive array
    • - *
    • free: currently unused bits
    • + *
    • tag: determines element type and whether the object is a true array or a {@link Hybrid}.
    • + *
    • unused: bits currently not used for any purpose
    • *
    • base: the array base offset
    • *
    • indexShift: the array index shift for accessing array elements or for computing the array * size based on the array length
    • *
    * - * {@link Hybrid} objects are also encoded as arrays but are treated like instance objects in other + * {@link Hybrid} objects are encoded like arrays but are treated like instance objects in other * places (e.g. {@link HubType}). Another difference to arrays is that hybrid objects need a * reference map because they have fields.
  • * @@ -81,17 +76,21 @@ public class LayoutEncoding { private static final int PRIMITIVE_VALUE = NEUTRAL_VALUE + 1; private static final int INTERFACE_VALUE = PRIMITIVE_VALUE + 1; private static final int ABSTRACT_VALUE = INTERFACE_VALUE + 1; - private static final int STORED_CONTINUATION_VALUE = ABSTRACT_VALUE + 1; - private static final int LAST_SPECIAL_VALUE = STORED_CONTINUATION_VALUE; + private static final int LAST_SPECIAL_VALUE = ABSTRACT_VALUE; private static final int ARRAY_INDEX_SHIFT_SHIFT = 0; private static final int ARRAY_INDEX_SHIFT_MASK = 0xff; private static final int ARRAY_BASE_SHIFT = 8 + ARRAY_INDEX_SHIFT_SHIFT; private static final int ARRAY_BASE_MASK = 0xfff; - private static final int ARRAY_TAG_BITS = 2; + private static final int ARRAY_TAG_BITS = 3; private static final int ARRAY_TAG_SHIFT = Integer.SIZE - ARRAY_TAG_BITS; - private static final int ARRAY_TAG_PRIMITIVE_VALUE = ~0x00; - private static final int ARRAY_TAG_OBJECT_VALUE = ~0x01; + private static final int ARRAY_TAG_IDENTITY_BIT = 0b100; + private static final int ARRAY_TAG_PRIMITIVE_BIT = 0b010; + private static final int ARRAY_TAG_PURE_BIT = 0b001; // means non-hybrid + private static final int ARRAY_TAG_PRIMITIVE_VALUE = ARRAY_TAG_IDENTITY_BIT | ARRAY_TAG_PRIMITIVE_BIT | ARRAY_TAG_PURE_BIT; // 0b111 + private static final int ARRAY_TAG_HYBRID_PRIMITIVE_VALUE = ARRAY_TAG_IDENTITY_BIT | ARRAY_TAG_PRIMITIVE_BIT; // 0b110 + private static final int ARRAY_TAG_OBJECT_VALUE = ARRAY_TAG_IDENTITY_BIT | ARRAY_TAG_PURE_BIT; // 0b101 + private static final int ARRAY_TAG_HYBRID_OBJECT_VALUE = ARRAY_TAG_IDENTITY_BIT; // 0b100 public static int forPrimitive() { return PRIMITIVE_VALUE; @@ -105,14 +104,11 @@ public static int forAbstract() { return ABSTRACT_VALUE; } - public static int forStoredContinuation() { - return STORED_CONTINUATION_VALUE; - } - @Platforms(Platform.HOSTED_ONLY.class) - private static void guaranteeEncoding(ResolvedJavaType type, boolean condition, String description) { - if (!condition) { - VMError.shouldNotReachHere(description + ". This error is caused by an incorrect compact encoding of a type " + + private static void guaranteeEncoding(ResolvedJavaType type, boolean expected, boolean actual, String description) { + if (actual != expected) { + throw VMError.shouldNotReachHere(description + ": expected to be " + expected + ". " + + "This error is caused by an incorrect compact encoding of a type " + "(a class, array or a primitive). The error occurred with the following type, but also could be caused " + "by characteristics of the overall type hierarchy: " + type + ". Please report this problem and the " + "conditions in which it occurs and include any noteworthy characteristics of the type hierarchy and " + @@ -121,31 +117,47 @@ private static void guaranteeEncoding(ResolvedJavaType type, boolean condition, } @Platforms(Platform.HOSTED_ONLY.class) - public static int forInstance(ResolvedJavaType type, int size) { - guaranteeEncoding(type, size > LAST_SPECIAL_VALUE, "Instance type size must be above special values for encoding: " + size); + public static int forPureInstance(ResolvedJavaType type, int size) { + guaranteeEncoding(type, true, size > LAST_SPECIAL_VALUE, "Instance type size must be above special values for encoding: " + size); int encoding = size; - guaranteeEncoding(type, isInstance(encoding), "Instance type encoding must denote an instance"); - guaranteeEncoding(type, !Continuation.isSupported() || !isStoredContinuation(encoding), "Instance type encoding must not denote a stored continuation"); - guaranteeEncoding(type, !isArray(encoding), "Instance type encoding must not denote an array"); - guaranteeEncoding(type, !isObjectArray(encoding), "Instance type encoding must not denote an object array"); - guaranteeEncoding(type, !isPrimitiveArray(encoding), "Instance type encoding must not denote a primitive array"); - guaranteeEncoding(type, getInstanceSize(encoding).equal(WordFactory.unsigned(size)), "Instance type encoding size must match type size"); + guaranteeEncoding(type, true, isPureInstance(encoding), "Instance type encoding denotes an instance"); + guaranteeEncoding(type, false, isArray(encoding) || isArrayLike(encoding), "Instance type encoding denotes an array-like object"); + guaranteeEncoding(type, false, isHybrid(encoding), "Instance type encoding denotes a hybrid"); + guaranteeEncoding(type, false, isObjectArray(encoding) || isArrayLikeWithObjectElements(encoding), "Instance type encoding denotes an object array"); + guaranteeEncoding(type, false, isPrimitiveArray(encoding) || isArrayLikeWithPrimitiveElements(encoding), "Instance type encoding denotes a primitive array"); + guaranteeEncoding(type, true, getPureInstanceSize(encoding).equal(WordFactory.unsigned(size)), "Instance type encoding size matches type size"); return encoding; } @Platforms(Platform.HOSTED_ONLY.class) - public static int forArray(ResolvedJavaType type, boolean isObject, int arrayBaseOffset, int arrayIndexShift) { - int tag = isObject ? ARRAY_TAG_OBJECT_VALUE : ARRAY_TAG_PRIMITIVE_VALUE; + public static int forArray(ResolvedJavaType type, boolean objectElements, int arrayBaseOffset, int arrayIndexShift) { + return forArrayLike(type, false, objectElements, arrayBaseOffset, arrayIndexShift); + } + + @Platforms(Platform.HOSTED_ONLY.class) + public static int forHybrid(ResolvedJavaType type, boolean objectElements, int arrayBaseOffset, int arrayIndexShift) { + return forArrayLike(type, true, objectElements, arrayBaseOffset, arrayIndexShift); + } + + @Platforms(Platform.HOSTED_ONLY.class) + private static int forArrayLike(ResolvedJavaType type, boolean isHybrid, boolean objectElements, int arrayBaseOffset, int arrayIndexShift) { + assert isHybrid != type.isArray(); + int tag = isHybrid ? (objectElements ? ARRAY_TAG_HYBRID_OBJECT_VALUE : ARRAY_TAG_HYBRID_PRIMITIVE_VALUE) + : (objectElements ? ARRAY_TAG_OBJECT_VALUE : ARRAY_TAG_PRIMITIVE_VALUE); int encoding = (tag << ARRAY_TAG_SHIFT) | (arrayBaseOffset << ARRAY_BASE_SHIFT) | (arrayIndexShift << ARRAY_INDEX_SHIFT_SHIFT); - guaranteeEncoding(type, isArray(encoding), "Array encoding must denote an array"); - guaranteeEncoding(type, !isInstance(encoding), "Array encoding must not denote an instance type"); - guaranteeEncoding(type, isObjectArray(encoding) == isObject, "Expected isObjectArray(encoding) == " + isObject); - guaranteeEncoding(type, isPrimitiveArray(encoding) != isObject, "Expected isPrimitiveArray(encoding) != " + isObject); - guaranteeEncoding(type, getArrayBaseOffset(encoding).equal(WordFactory.unsigned(arrayBaseOffset)), - "Expected array base offset of " + arrayBaseOffset + ", but encoding gives " + getArrayBaseOffset(encoding)); - guaranteeEncoding(type, getArrayIndexShift(encoding) == arrayIndexShift, - "Expected array index shift of " + arrayIndexShift + ", but encoding gives " + getArrayIndexShift(encoding)); + guaranteeEncoding(type, true, isArrayLike(encoding), "Array-like object encoding denotes an array-like object"); + guaranteeEncoding(type, !isHybrid, isArray(encoding), "Encoding denotes an array"); + guaranteeEncoding(type, isHybrid, isHybrid(encoding), "Encoding denotes a hybrid"); + guaranteeEncoding(type, false, isPureInstance(encoding), "Array-like object encoding denotes an instance type"); + guaranteeEncoding(type, objectElements, isArrayLikeWithObjectElements(encoding), "Encoding denotes an array-like object with object elements"); + guaranteeEncoding(type, !objectElements, isArrayLikeWithPrimitiveElements(encoding), "Encoding denotes an array-like object with primitive elements"); + guaranteeEncoding(type, !isHybrid && objectElements, isObjectArray(encoding), "Encoding denotes an object array"); + guaranteeEncoding(type, !isHybrid && !objectElements, isPrimitiveArray(encoding), "Encoding denotes a primitive array"); + guaranteeEncoding(type, true, getArrayBaseOffset(encoding).equal(WordFactory.unsigned(arrayBaseOffset)), + "Encoding denotes a base offset of " + arrayBaseOffset + " (actual value: " + getArrayBaseOffset(encoding) + ')'); + guaranteeEncoding(type, true, getArrayIndexShift(encoding) == arrayIndexShift, + "Encoding denotes an index shift of " + arrayIndexShift + " (actual value: " + getArrayIndexShift(encoding) + ')'); return encoding; } @@ -161,37 +173,41 @@ public static boolean isAbstract(int encoding) { return encoding == ABSTRACT_VALUE; } + /** Note that this method does not consider hybrids special. */ public static boolean isSpecial(int encoding) { return encoding >= NEUTRAL_VALUE && encoding <= LAST_SPECIAL_VALUE; } + /** Tests if an encoding denotes a pure instance object, i.e. not a hybrid object or array. */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static boolean isInstance(int encoding) { + public static boolean isPureInstance(int encoding) { return encoding > LAST_SPECIAL_VALUE; } + /** Determines the size of a pure instance object (i.e. not a hybrid object or array). */ @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static UnsignedWord getInstanceSize(int encoding) { - boolean isStoredContinuation = Continuation.isSupported() && isStoredContinuation(encoding); - if (GraalDirectives.inIntrinsic()) { - ReplacementsUtil.dynamicAssert(!isStoredContinuation, "size cannot be determined from encoding"); - } else { - assert !isStoredContinuation : "size cannot be determined from encoding"; - } + public static UnsignedWord getPureInstanceSize(int encoding) { return WordFactory.unsigned(encoding); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static boolean isStoredContinuation(int encoding) { - return encoding == STORED_CONTINUATION_VALUE; + public static boolean isArray(int encoding) { + int mask = (ARRAY_TAG_IDENTITY_BIT | ARRAY_TAG_PURE_BIT) << ARRAY_TAG_SHIFT; + return (encoding & mask) == mask; } - // May be inlined because it does not deal in Pointers. @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - public static boolean isArray(int encoding) { + public static boolean isArrayLike(int encoding) { return encoding < NEUTRAL_VALUE; } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static boolean isHybrid(int encoding) { + int setBits = ARRAY_TAG_IDENTITY_BIT << ARRAY_TAG_SHIFT; + int mask = setBits | (ARRAY_TAG_PURE_BIT << ARRAY_TAG_SHIFT); + return (encoding & mask) == setBits; + } + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static boolean isPrimitiveArray(int encoding) { return UnsignedMath.aboveOrEqual(encoding, ARRAY_TAG_PRIMITIVE_VALUE << ARRAY_TAG_SHIFT); @@ -199,7 +215,17 @@ public static boolean isPrimitiveArray(int encoding) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static boolean isObjectArray(int encoding) { - return encoding < (ARRAY_TAG_PRIMITIVE_VALUE << ARRAY_TAG_SHIFT); + return (encoding >>> ARRAY_TAG_SHIFT) == ARRAY_TAG_OBJECT_VALUE; + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static boolean isArrayLikeWithPrimitiveElements(int encoding) { + return UnsignedMath.aboveOrEqual(encoding, ARRAY_TAG_HYBRID_PRIMITIVE_VALUE << ARRAY_TAG_SHIFT); + } + + @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) + public static boolean isArrayLikeWithObjectElements(int encoding) { + return encoding <= ~(~ARRAY_TAG_OBJECT_VALUE << ARRAY_TAG_SHIFT); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) @@ -243,12 +269,10 @@ public static UnsignedWord getSizeFromObject(Object obj) { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static UnsignedWord getSizeFromObjectInline(Object obj) { int encoding = KnownIntrinsics.readHub(obj).getLayoutEncoding(); - if (isArray(encoding)) { + if (isArrayLike(encoding)) { return getArraySize(encoding, ArrayLengthNode.arrayLength(obj)); - } else if (Continuation.isSupported() && isStoredContinuation(encoding)) { - return WordFactory.unsigned(StoredContinuationImpl.readSize((StoredContinuation) obj)); } else { - return getInstanceSize(encoding); + return getPureInstanceSize(encoding); } } @@ -269,9 +293,9 @@ public static boolean isArray(Object obj) { return isArray(encoding); } - public static boolean isInstance(Object obj) { + public static boolean isArrayLike(Object obj) { final int encoding = KnownIntrinsics.readHub(obj).getLayoutEncoding(); - return isInstance(encoding); + return isArrayLike(encoding); } @Fold diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java index fcc81b591faa..060e0036d472 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java @@ -35,12 +35,10 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.heap.StoredContinuation; import com.oracle.svm.core.heap.StoredContinuationImpl; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.UserError; -import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ReflectionUtil; @AutomaticFeature @@ -74,11 +72,8 @@ public void afterRegistration(AfterRegistrationAccess access) { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { if (Continuation.isSupported()) { - access.registerAsInHeap(StoredContinuation.class); - VMError.guarantee(ConfigurationValues.getObjectLayout().getFirstFieldOffset() == StoredContinuationImpl.OBJECT_MONITOR_OFFSET, - "Unexpected monitor field offset. Continuation support currently needs option " + - SubstrateOptionsParser.commandArgument(SubstrateOptions.SpawnIsolates, "+") + - " and compressed 32-bit reference support."); + access.registerReachabilityHandler(a -> access.registerAsInHeap(StoredContinuation.class), + ReflectionUtil.lookupMethod(StoredContinuationImpl.class, "allocate", int.class)); if (LoomSupport.isEnabled()) { RuntimeReflection.register(ReflectionUtil.lookupMethod(ForkJoinPool.class, "compensatedBlock", ForkJoinPool.ManagedBlocker.class)); 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 abe408c14ace..b6e9351573a4 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 @@ -1668,17 +1668,13 @@ private void printTypes() { System.out.print("interface "); } else if (LayoutEncoding.isAbstract(le)) { System.out.print("abstract "); - } else if (LayoutEncoding.isInstance(le)) { - System.out.format("instance size %d ", LayoutEncoding.getInstanceSize(le).rawValue()); - } else if (LayoutEncoding.isObjectArray(le)) { - System.out.format("object array base %d shift %d scale %d ", LayoutEncoding.getArrayBaseOffset(le).rawValue(), LayoutEncoding.getArrayIndexShift(le), - LayoutEncoding.getArrayIndexScale(le)); - } else if (LayoutEncoding.isPrimitiveArray(le)) { - System.out.format("primitive array base %d shift %d scale %d ", LayoutEncoding.getArrayBaseOffset(le).rawValue(), LayoutEncoding.getArrayIndexShift(le), - LayoutEncoding.getArrayIndexScale(le)); - } else if (LayoutEncoding.isStoredContinuation(le)) { - // can exist as HostedType even if unreachable and continuations are disabled - System.out.print("stored continuation "); + } else if (LayoutEncoding.isPureInstance(le)) { + System.out.format("instance size %d ", LayoutEncoding.getPureInstanceSize(le).rawValue()); + } else if (LayoutEncoding.isArrayLike(le)) { + String arrayType = LayoutEncoding.isHybrid(le) ? "hybrid" : "array"; + String elements = LayoutEncoding.isArrayLikeWithPrimitiveElements(le) ? "primitives" : "objects"; + System.out.format("%s containing %s, base %d shift %d scale %d ", arrayType, elements, LayoutEncoding.getArrayBaseOffset(le).rawValue(), + LayoutEncoding.getArrayIndexShift(le), LayoutEncoding.getArrayIndexScale(le)); } else { throw VMError.shouldNotReachHere(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java index 1ed77df6a936..6f90526de4ba 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java @@ -51,7 +51,6 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.impl.ImageSingletonsSupport; -import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -75,6 +74,7 @@ import com.oracle.svm.hosted.code.CompileQueue.CompileTask; import com.oracle.svm.hosted.image.AbstractImage.NativeImageKind; import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; +import com.oracle.svm.hosted.meta.InternalRuntimeReflectionSupport; import com.oracle.svm.util.ImageBuildStatistics; import com.oracle.svm.util.ReflectionUtil; @@ -310,7 +310,7 @@ private void printAnalysisStatistics(AnalysisUniverse universe) { .a(" methods included for ").doclink("runtime compilation", "#glossary-runtime-methods").println(); } String classesFieldsMethodFormat = "%,8d classes, %,5d fields, and %,5d methods "; - RuntimeReflectionSupport rs = ImageSingletons.lookup(RuntimeReflectionSupport.class); + InternalRuntimeReflectionSupport rs = ImageSingletons.lookup(InternalRuntimeReflectionSupport.class); l().a(classesFieldsMethodFormat, rs.getReflectionClassesCount(), rs.getReflectionFieldsCount(), rs.getReflectionMethodsCount()) .doclink("registered for reflection", "#glossary-reflection-registrations").println(); if (numJNIClasses > 0) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index e205b4b72248..c93d3ca864c7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -92,7 +92,6 @@ import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; import com.oracle.svm.core.graal.stackvalue.StackValueNode; import com.oracle.svm.core.graal.thread.VMThreadLocalAccess; -import com.oracle.svm.core.heap.StoredContinuation; import com.oracle.svm.core.heap.Target_java_lang_ref_Reference; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.HubType; @@ -107,6 +106,7 @@ import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import com.oracle.svm.hosted.code.InliningUtilities; import com.oracle.svm.hosted.code.UninterruptibleAnnotationChecker; +import com.oracle.svm.hosted.heap.PodSupport; import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.phases.AnalysisGraphBuilderPhase; import com.oracle.svm.hosted.phases.ImplicitAssertionsPhase; @@ -463,8 +463,8 @@ private static HubType computeHubType(AnalysisType type) { } else if (type.isInstanceClass()) { if (Reference.class.isAssignableFrom(type.getJavaClass())) { return HubType.InstanceReference; - } else if (type.getJavaClass().equals(StoredContinuation.class)) { - return HubType.StoredContinuation; + } else if (PodSupport.isPresent() && PodSupport.singleton().isPodClass(type.getJavaClass())) { + return HubType.PodInstance; } assert !Target_java_lang_ref_Reference.class.isAssignableFrom(type.getJavaClass()) : "should not see substitution type here"; return HubType.Instance; 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 94262cb89309..0ab31379f4a8 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 @@ -34,15 +34,13 @@ import com.oracle.svm.hosted.meta.HostedType; import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaType; /** - * Defines the layout for a hybrid class. + * Provides sizes and offsets of a hybrid class. * * @see Hybrid - * - * @param The class which has a layout in hybrid form. It must be annotated with the - * {@link Hybrid} annotation. */ public class HybridLayout { @@ -58,26 +56,35 @@ public static boolean canHybridFieldsBeDuplicated(HostedType clazz) { return HybridLayoutSupport.singleton().canHybridFieldsBeDuplicated(clazz); } + public static boolean canInstantiateAsInstance(HostedType clazz) { + return HybridLayoutSupport.singleton().canInstantiateAsInstance(clazz); + } + private final ObjectLayout layout; + private final HostedType arrayType; private final HostedField arrayField; private final HostedField typeIDSlotsField; private final int arrayBaseOffset; public HybridLayout(Class hybridClass, ObjectLayout layout, HostedMetaAccess metaAccess) { - this((HostedInstanceClass) metaAccess.lookupJavaType(hybridClass), layout); + this((HostedInstanceClass) metaAccess.lookupJavaType(hybridClass), layout, metaAccess); } - public HybridLayout(HostedInstanceClass hybridClass, ObjectLayout layout) { + public HybridLayout(HostedInstanceClass hybridClass, ObjectLayout layout, MetaAccessProvider metaAccess) { this.layout = layout; - HybridLayoutSupport utils = HybridLayoutSupport.singleton(); - HybridLayoutSupport.HybridFields hybridFields = utils.findHybridFields(hybridClass); - arrayField = hybridFields.arrayField; - typeIDSlotsField = hybridFields.typeIDSlotsField; - arrayBaseOffset = NumUtil.roundUp(hybridClass.getAfterFieldsOffset(), layout.sizeInBytes(getArrayElementStorageKind())); + HybridLayoutSupport.HybridInfo hybridInfo = HybridLayoutSupport.singleton().inspectHybrid(hybridClass, metaAccess); + this.arrayType = hybridInfo.arrayType; + this.arrayField = hybridInfo.arrayField; + this.typeIDSlotsField = hybridInfo.typeIDSlotsField; + this.arrayBaseOffset = NumUtil.roundUp(hybridClass.getAfterFieldsOffset(), layout.sizeInBytes(getArrayElementStorageKind())); + } + + public HostedType getArrayType() { + return arrayType; } public JavaKind getArrayElementStorageKind() { - return arrayField.getType().getComponentType().getStorageKind(); + return arrayType.getComponentType().getStorageKind(); } public int getArrayBaseOffset() { @@ -100,9 +107,6 @@ public HostedField getTypeIDSlotsField() { return typeIDSlotsField; } - /** - * In a given build, only the bit field or the type id slot array field will exist. - */ public static int getTypeIDSlotsFieldOffset(ObjectLayout layout) { return layout.getArrayLengthOffset() + layout.sizeInBytes(JavaKind.Int); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/HybridLayoutSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/HybridLayoutSupport.java index 0c36b60d62f7..33b0c14401ff 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/HybridLayoutSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/HybridLayoutSupport.java @@ -26,15 +26,16 @@ import java.lang.reflect.Modifier; -import org.graalvm.collections.Pair; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.annotate.Hybrid; +import com.oracle.svm.hosted.meta.HostedArrayClass; import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedInstanceClass; import com.oracle.svm.hosted.meta.HostedType; +import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaType; public class HybridLayoutSupport { @@ -56,23 +57,22 @@ public boolean canHybridFieldsBeDuplicated(HostedType clazz) { return clazz.getAnnotation(Hybrid.class).canHybridFieldsBeDuplicated(); } - /** - * Finds the hybrid array and bitset fields of a class annotated with {@link Hybrid}. - * - * @param hybridClass A class annotated with {@link Hybrid} - * @return A {@link Pair} containing the (non-null) hybrid array field in the left position, and - * the (nullable) hybrid bitset field in the right position. - */ - public HybridFields findHybridFields(HostedInstanceClass hybridClass) { - assert hybridClass.getAnnotation(Hybrid.class) != null; + public boolean canInstantiateAsInstance(HostedType clazz) { + assert isHybrid(clazz) : "Can only be called on hybrid types"; + return false; + } + + /** Determines characteristics of a hybrid class. */ + protected HybridInfo inspectHybrid(HostedInstanceClass hybridClass, MetaAccessProvider metaAccess) { + Hybrid annotation = hybridClass.getAnnotation(Hybrid.class); + assert annotation != null; assert Modifier.isFinal(hybridClass.getModifiers()); HostedField foundArrayField = null; HostedField foundTypeIDSlotsField = null; for (HostedField field : hybridClass.getInstanceFields(true)) { if (field.getAnnotation(Hybrid.Array.class) != null) { - assert foundArrayField == null : "must have exactly one hybrid array field"; - assert field.getType().isArray(); + assert foundArrayField == null : "must have at most one hybrid array field"; foundArrayField = field; } if (field.getAnnotation(Hybrid.TypeIDSlots.class) != null) { @@ -81,15 +81,29 @@ public HybridFields findHybridFields(HostedInstanceClass hybridClass) { foundTypeIDSlotsField = field; } } - assert foundArrayField != null : "must have exactly one hybrid array field"; - return new HybridFields(foundArrayField, foundTypeIDSlotsField); + + HostedType arrayType; + boolean arrayTypeIsSet = (annotation.componentType() != void.class); + if (foundArrayField != null) { + arrayType = foundArrayField.getType(); + + assert !arrayTypeIsSet || arrayType.equals(metaAccess.lookupJavaType(annotation.componentType()).getArrayClass()) : // + "@Hybrid.componentType must match the type of a @Hybrid.Array field when both are present"; + } else { + assert arrayTypeIsSet : "@Hybrid.componentType must be set when no @Hybrid.Array field is present (if present, ensure it is reachable)"; + arrayType = (HostedArrayClass) metaAccess.lookupJavaType(annotation.componentType()).getArrayClass(); + } + assert arrayType.isArray(); + return new HybridInfo(arrayType, foundArrayField, foundTypeIDSlotsField); } - public static class HybridFields { + public static class HybridInfo { + public final HostedType arrayType; public final HostedField arrayField; public final HostedField typeIDSlotsField; - public HybridFields(HostedField arrayField, HostedField typeIDSlotsField) { + public HybridInfo(HostedType arrayType, HostedField arrayField, HostedField typeIDSlotsField) { + this.arrayType = arrayType; this.arrayField = arrayField; this.typeIDSlotsField = typeIDSlotsField; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodFactorySubstitutionMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodFactorySubstitutionMethod.java new file mode 100644 index 000000000000..13190c463868 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodFactorySubstitutionMethod.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.heap; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.graph.NodeSourcePosition; +import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.FixedNode; +import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.InvokeWithExceptionNode; +import org.graalvm.compiler.nodes.PiNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.UnreachableBeginNode; +import org.graalvm.compiler.nodes.UnwindNode; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.java.ExceptionObjectNode; + +import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; +import com.oracle.graal.pointsto.meta.HostedProviders; +import com.oracle.svm.core.annotate.DeoptTest; +import com.oracle.svm.core.graal.nodes.DeoptEntryBeginNode; +import com.oracle.svm.core.graal.nodes.DeoptEntryNode; +import com.oracle.svm.core.graal.nodes.LoweredDeadEndNode; +import com.oracle.svm.core.graal.nodes.NewPodInstanceNode; +import com.oracle.svm.core.graal.nodes.TestDeoptimizeNode; +import com.oracle.svm.core.heap.Pod; +import com.oracle.svm.core.heap.Pod.RuntimeSupport.PodFactory; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.hosted.annotation.CustomSubstitutionMethod; +import com.oracle.svm.hosted.nodes.DeoptProxyNode; +import com.oracle.svm.hosted.phases.HostedGraphKit; + +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; + +final class PodFactorySubstitutionProcessor extends SubstitutionProcessor { + private final ConcurrentMap substitutions = new ConcurrentHashMap<>(); + + @Override + public ResolvedJavaMethod lookup(ResolvedJavaMethod method) { + if (method.isSynthetic() && method.getDeclaringClass().isAnnotationPresent(PodFactory.class) && !method.isConstructor()) { + assert !(method instanceof CustomSubstitutionMethod); + return substitutions.computeIfAbsent(method, PodFactorySubstitutionMethod::new); + } + return method; + } + + @Override + public ResolvedJavaMethod resolve(ResolvedJavaMethod method) { + if (method instanceof PodFactorySubstitutionMethod) { + return ((PodFactorySubstitutionMethod) method).getOriginal(); + } + return method; + } +} + +final class PodFactorySubstitutionMethod extends CustomSubstitutionMethod { + PodFactorySubstitutionMethod(ResolvedJavaMethod original) { + super(original); + } + + @Override + public boolean allowRuntimeCompilation() { + return true; + } + + @Override + public int getModifiers() { + return super.getModifiers() & ~Modifier.NATIVE; + } + + @Override + public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method, HostedProviders providers, Purpose purpose) { + // Needed to match type flows to invokes so invoked methods can be inlined in runtime + // compilations, see GraalFeature.processMethod() and MethodTypeFlowBuilder.uniqueKey() + boolean trackNodeSourcePosition = (purpose == Purpose.ANALYSIS); + + HostedGraphKit kit = new HostedGraphKit(debug, providers, method, trackNodeSourcePosition); + boolean isDeoptTarget = (method instanceof SharedMethod) && ((SharedMethod) method).isDeoptTarget(); + + ResolvedJavaType factoryType = method.getDeclaringClass(); + PodFactory annotation = factoryType.getAnnotation(PodFactory.class); + ResolvedJavaType podConcreteType = kit.getMetaAccess().lookupJavaType(annotation.podClass()); + ResolvedJavaMethod targetCtor = null; + for (ResolvedJavaMethod ctor : podConcreteType.getSuperclass().getDeclaredConstructors()) { + if (parameterTypesMatch(method, ctor)) { + targetCtor = ctor; + break; + } + } + GraalError.guarantee(targetCtor != null, "Matching constructor not found: %s", getSignature()); + + /* + * The graph must be safe for runtime compilation and so for compilation as a deoptimization + * target. We must be careful to use values only from the frame state, so we keep the + * allocated instance in a local and load it after each step during which a deopt can occur. + */ + int instanceLocal = kit.getFrameState().localsSize() - 1; // reserved when generating class + int nextDeoptIndex = startMethod(kit, isDeoptTarget, 0); + instantiatePod(kit, providers, factoryType, podConcreteType, instanceLocal); + if (isAnnotationPresent(DeoptTest.class)) { + kit.append(new TestDeoptimizeNode()); + } + nextDeoptIndex = invokeConstructor(kit, method, isDeoptTarget, nextDeoptIndex, targetCtor, instanceLocal); + + kit.createReturn(kit.loadLocal(instanceLocal, JavaKind.Object), JavaKind.Object); + return kit.finalizeGraph(); + } + + private static int startMethod(HostedGraphKit kit, boolean isDeoptTarget, int nextDeoptIndex) { + if (!isDeoptTarget) { + return nextDeoptIndex; + } + FrameState initialState = kit.getGraph().start().stateAfter(); + return appendDeoptWithExceptionUnwind(kit, initialState, initialState.bci, nextDeoptIndex); + } + + private static void instantiatePod(HostedGraphKit kit, HostedProviders providers, ResolvedJavaType factoryType, ResolvedJavaType podConcreteType, int instanceLocal) { + ResolvedJavaType podType = kit.getMetaAccess().lookupJavaType(Pod.class); + ValueNode receiver = kit.loadLocal(0, JavaKind.Object); + ValueNode pod = loadNonNullField(kit, receiver, findField(factoryType, "pod")); + ValueNode arrayLength = kit.createLoadField(pod, findField(podType, "arrayLength")); + ValueNode refMap = loadNonNullField(kit, pod, findField(podType, "referenceMap")); + ConstantNode hub = kit.createConstant(providers.getConstantReflection().asObjectHub(podConcreteType), JavaKind.Object); + ValueNode instance = kit.append(new NewPodInstanceNode(podConcreteType, hub, arrayLength, refMap)); + kit.storeLocal(instanceLocal, JavaKind.Object, instance); + } + + private static ValueNode loadNonNullField(HostedGraphKit kit, ValueNode object, ResolvedJavaField field) { + return kit.append(PiNode.create(kit.createLoadField(object, field), StampFactory.objectNonNull())); + } + + private static int invokeConstructor(HostedGraphKit kit, ResolvedJavaMethod method, boolean isDeoptTarget, int nextDeoptIndex, ResolvedJavaMethod targetCtor, int instanceLocal) { + ValueNode instance = kit.loadLocal(instanceLocal, JavaKind.Object); + ValueNode[] originalArgs = kit.loadArguments(method.toParameterTypes()).toArray(ValueNode.EMPTY_ARRAY); + ValueNode[] invokeArgs = Arrays.copyOf(originalArgs, originalArgs.length); + invokeArgs[0] = instance; + return invokeWithDeoptAndExceptionUnwind(kit, isDeoptTarget, nextDeoptIndex, targetCtor, InvokeKind.Special, invokeArgs); + } + + /** @see com.oracle.svm.hosted.phases.HostedGraphBuilderPhase */ + private static int invokeWithDeoptAndExceptionUnwind(HostedGraphKit kit, boolean isDeoptTarget, int initialNextDeoptIndex, ResolvedJavaMethod target, InvokeKind invokeKind, ValueNode... args) { + int bci = kit.bci(); + InvokeWithExceptionNode invoke = kit.startInvokeWithException(target, invokeKind, kit.getFrameState(), bci, args); + invoke.setNodeSourcePosition(NodeSourcePosition.placeholder(kit.getGraph().method(), bci)); + kit.exceptionPart(); + ExceptionObjectNode exception = kit.exceptionObject(); + + if (!isDeoptTarget) { + kit.append(new UnwindNode(exception)); + kit.endInvokeWithException(); + return initialNextDeoptIndex; + } + + int nextDeoptIndex = initialNextDeoptIndex; + + // Exception during invoke + + var exceptionDeopt = kit.add(new DeoptEntryNode()); + exceptionDeopt.setStateAfter(exception.stateAfter().duplicate()); + var exceptionDeoptBegin = kit.add(new DeoptEntryBeginNode()); + int exceptionDeoptIndex = nextDeoptIndex++; + ValueNode exceptionProxy = createDeoptProxy(kit, exceptionDeoptIndex, exceptionDeopt, exception); + var unwind = kit.append(new UnwindNode(exceptionProxy)); + exception.setNext(exceptionDeopt); + exceptionDeopt.setNext(exceptionDeoptBegin); + exceptionDeoptBegin.setNext(unwind); + + var exceptionDeoptExceptionEdge = kit.add(new UnreachableBeginNode()); + exceptionDeoptExceptionEdge.setNext(kit.add(new LoweredDeadEndNode())); + exceptionDeopt.setExceptionEdge(exceptionDeoptExceptionEdge); + + // Deopt entry after invoke without exception + + kit.noExceptionPart(); + nextDeoptIndex = appendDeoptWithExceptionUnwind(kit, invoke.stateAfter(), invoke.stateAfter().bci, nextDeoptIndex); + kit.endInvokeWithException(); + + return nextDeoptIndex; + } + + /** @see com.oracle.svm.hosted.phases.HostedGraphBuilderPhase */ + private static int appendDeoptWithExceptionUnwind(HostedGraphKit kit, FrameState state, int exceptionBci, int nextDeoptIndex) { + var entry = kit.add(new DeoptEntryNode()); + entry.setStateAfter(state.duplicate()); + var begin = kit.append(new DeoptEntryBeginNode()); + ((FixedWithNextNode) begin.predecessor()).setNext(entry); + entry.setNext(begin); + + ExceptionObjectNode exception = kit.add(new ExceptionObjectNode(kit.getMetaAccess())); + entry.setExceptionEdge(exception); + var exState = kit.getFrameState().copy(); + exState.clearStack(); + exState.push(JavaKind.Object, exception); + exState.setRethrowException(true); + exception.setStateAfter(exState.create(exceptionBci, exception)); + exception.setNext(kit.add(new UnwindNode(exception))); + + // Ensure later nodes see values from potential deoptimization + kit.getFrameState().insertProxies(value -> createDeoptProxy(kit, nextDeoptIndex, entry, value)); + return nextDeoptIndex + 1; + } + + private static ValueNode createDeoptProxy(HostedGraphKit kit, int nextDeoptIndex, FixedNode deoptTarget, ValueNode value) { + return kit.getGraph().addOrUniqueWithInputs(DeoptProxyNode.create(value, deoptTarget, nextDeoptIndex)); + } + + private static boolean parameterTypesMatch(ResolvedJavaMethod method, ResolvedJavaMethod ctor) { + int paramsCount = method.getSignature().getParameterCount(false); + if (paramsCount != ctor.getSignature().getParameterCount(false)) { + return false; + } + for (int i = 0; i < paramsCount; i++) { + if (!ctor.getSignature().getParameterType(i, ctor.getDeclaringClass()) + .equals(method.getSignature().getParameterType(i, method.getDeclaringClass()))) { + return false; + } + } + return true; + } + + private static ResolvedJavaField findField(ResolvedJavaType type, String name) { + for (ResolvedJavaField field : type.getInstanceFields(false)) { + if (field.getName().equals(name)) { + return field; + } + } + throw GraalError.shouldNotReachHere("Required field " + name + " not found in " + type); + } +} 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 new file mode 100644 index 000000000000..d51641233e4e --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodSupport.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.heap; + +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SYNTHETIC; +import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL; +import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; +import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; +import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD; +import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; +import static jdk.internal.org.objectweb.asm.Opcodes.V11; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.annotate.DeoptTest; +import com.oracle.svm.core.annotate.Hybrid; +import com.oracle.svm.core.heap.Pod; +import com.oracle.svm.core.heap.Pod.Builder; +import com.oracle.svm.core.heap.Pod.RuntimeSupport.PodFactory; +import com.oracle.svm.core.heap.Pod.RuntimeSupport.PodInfo; +import com.oracle.svm.core.heap.Pod.RuntimeSupport.PodSpec; +import com.oracle.svm.core.hub.LayoutEncoding; +import com.oracle.svm.core.util.UserError; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; +import com.oracle.svm.hosted.NativeImageSystemClassLoader; +import com.oracle.svm.util.ReflectionUtil; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Type; +import jdk.vm.ci.meta.JavaKind; + +/** Support for preparing the creation of {@link Pod} objects during the image build. */ +public interface PodSupport { + static boolean isPresent() { + return ImageSingletons.contains(PodSupport.class); + } + + static PodSupport singleton() { + if (!ImageSingletons.contains(PodSupport.class)) { + throw UserError.abort("Pods are not available in this native image build. Only SerialGC currently supports pods."); + } + return ImageSingletons.lookup(PodSupport.class); + } + + /** + * Registers a superclass and factory interface so that they are available to build {@link Pod}s + * at runtime via {@link Builder#createExtending(Class, Class)}. The provided superclass must be + * non-abstract, non-final and accessible (usually public and in an exported package). The + * factory interface consist solely of methods with a return type to which the superclass + * {@linkplain Class#isAssignableFrom is assignable to} and their parameter list musts match + * exactly the parameter list of one of the constructors of the superclass. + *

    + * This method can be called multiple times for the same superclass and different factory + * interfaces, and for the same factory interface and different superclasses. + */ + void registerSuperclass(Class superClass, Class factoryInterface); + + boolean isPodClass(Class clazz); + + boolean mustReserveLengthField(Class clazz); +} + +@AutomaticFeature +final class PodFeature implements PodSupport, Feature { + private static final AtomicInteger GENERATED_COUNTER = new AtomicInteger(); + + private final Map pods = new ConcurrentHashMap<>(); + + /** + * These classes are ancestors of pod classes which directly inherit from {@link Object}, and so + * they must reserve space for a length field so subclasses (or the class itself) don't place + * 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 BeforeAnalysisAccess analysisAccess; + private volatile boolean instantiated = false; + private boolean sealed = false; + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return SubstrateOptions.UseSerialGC.getValue(); + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(PodSupport.class, this); + ImageSingletons.add(Pod.RuntimeSupport.class, new Pod.RuntimeSupport()); + + registerSuperclass(Object.class, Supplier.class); + } + + @Override + public void duringSetup(DuringSetupAccess access) { + ((DuringSetupAccessImpl) access).registerSubstitutionProcessor(new PodFactorySubstitutionProcessor()); + } + + @Override + public void beforeAnalysis(Feature.BeforeAnalysisAccess access) { + this.analysisAccess = access; + access.registerReachabilityHandler(this::registerAsInstantiated, (Object[]) Builder.class.getDeclaredConstructors()); + } + + private void registerAsInstantiated(DuringAnalysisAccess access) { + instantiated = true; + pods.forEach((spec, podInfo) -> registerPodAsInstantiated(access, spec, podInfo)); + } + + private static void registerPodAsInstantiated(BeforeAnalysisAccess access, PodSpec spec, PodInfo info) { + access.registerAsInHeap(info.podClass); + Pod.RuntimeSupport.singleton().registerPod(spec, info); + } + + @Override + public void registerSuperclass(Class superClass, Class factoryInterface) { + if (sealed) { + throw UserError.abort("Pod superclasses can not be registered after analysis has finished"); + } + if (superClass == null || factoryInterface == null) { + throw new NullPointerException(); + } + var spec = new PodSpec(superClass, factoryInterface); + if (pods.containsKey(spec)) { + return; + } + if (!factoryInterface.isInterface()) { + throw new IllegalArgumentException("Factory is not an interface: " + factoryInterface); + } + for (Method method : factoryInterface.getMethods()) { + if (!method.getReturnType().isAssignableFrom(superClass)) { + throw new IllegalArgumentException("The return type of " + method + " is not assignable from " + superClass); + } + try { + superClass.getDeclaredConstructor(method.getParameterTypes()); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("Method " + method + " does not match any constructor in '" + superClass, e); + } + } + Class podClass = generatePodClass(superClass); + Constructor factoryCtor = generatePodFactory(podClass, factoryInterface); + + var info = new PodInfo(podClass, factoryCtor); + if (pods.putIfAbsent(spec, info) != null) { + return; // lost a race with another thread + } + + if (instantiated) { + registerPodAsInstantiated(analysisAccess, spec, info); + } + Class sup = podClass; + while (sup.getSuperclass() != Object.class) { + sup = sup.getSuperclass(); + } + classesNeedingLengthFields.add(sup); + } + + /** + * For pods, we need a designated class that (1) is a {@link Hybrid} class and so must never be + * allocated as an instance class because its {@link LayoutEncoding} describes it as an array + * and (2) is not subclassed since fields in a subclass would overlap with the array part. + */ + private static Class generatePodClass(Class superClass) { + String className = Pod.class.getName() + "$$Generated" + GENERATED_COUNTER.incrementAndGet(); + + ClassWriter writer = new ClassWriter(0); + int access = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC | ACC_FINAL; + writer.visit(V11, access, className.replace('.', '/'), null, Type.getInternalName(superClass), null); + var annotation = writer.visitAnnotation(Type.getDescriptor(Hybrid.class), true); + annotation.visit("componentType", Type.getType(byte.class)); + annotation.visitEnd(); + writer.visitEnd(); + byte[] data = writer.toByteArray(); + + Class podClass = NativeImageSystemClassLoader.singleton().predefineClass(className, data, 0, data.length); + assert podClass.getSuperclass() == superClass; + return podClass; + } + + /** + * Generate a concrete subclass of the provided factory interface with dummy methods that are + * subsequently substituted with {@link PodFactorySubstitutionMethod} to allocate pod instances + * and invoke the matching superclass constructor on them. + */ + private static Constructor generatePodFactory(Class podClass, Class factoryInterface) { + String name = Pod.class.getName() + "$$GeneratedFactory" + GENERATED_COUNTER.incrementAndGet(); + String internalName = name.replace('.', '/'); + String podDescriptor = Type.getDescriptor(Pod.class); + + ClassWriter writer = new ClassWriter(0); + int access = ACC_PUBLIC | ACC_SUPER | ACC_SYNTHETIC | ACC_FINAL; + writer.visit(V11, access, internalName, null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(factoryInterface)}); + var annotation = writer.visitAnnotation(Type.getDescriptor(PodFactory.class), true); + annotation.visit("podClass", Type.getType(podClass)); + annotation.visitEnd(); + writer.visitField(ACC_PRIVATE | ACC_FINAL | ACC_SYNTHETIC, "pod", podDescriptor, null, null).visitEnd(); + var init = writer.visitMethod(ACC_PUBLIC | ACC_SYNTHETIC, "", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(podDescriptor)), null, null); + init.visitCode(); + init.visitVarInsn(ALOAD, 0); + init.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V", false); + init.visitVarInsn(ALOAD, 0); + init.visitVarInsn(ALOAD, 1); + init.visitFieldInsn(PUTFIELD, internalName, "pod", podDescriptor); + init.visitInsn(RETURN); + init.visitMaxs(2, 2); + init.visitEnd(); + for (Method method : factoryInterface.getMethods()) { + int methodAccess = ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC; + var impl = writer.visitMethod(methodAccess, method.getName(), Type.getMethodDescriptor(method), null, null); + if (method.isAnnotationPresent(DeoptTest.class)) { + impl.visitAnnotation(Type.getDescriptor(DeoptTest.class), true).visitEnd(); + } + impl.visitCode(); // substituted with custom graph + impl.visitInsn(ACONST_NULL); + impl.visitInsn(ARETURN); + int localSlots = 1; // spare slot needed by generated graph + for (Class clazz : method.getParameterTypes()) { + localSlots += JavaKind.fromJavaClass(clazz).getSlotCount(); + } + impl.visitMaxs(1, localSlots); + impl.visitEnd(); + } + writer.visitEnd(); + byte[] data = writer.toByteArray(); + + Class factoryClass = NativeImageSystemClassLoader.singleton().predefineClass(name, data, 0, data.length); + assert factoryInterface.isAssignableFrom(factoryClass); + return ReflectionUtil.lookupConstructor(factoryClass, Pod.class); + } + + @Override + public boolean isPodClass(Class clazz) { + if ((clazz.getModifiers() & ACC_SYNTHETIC) != 0 && clazz.isAnnotationPresent(Hybrid.class)) { + for (PodInfo info : pods.values()) { + if (info.podClass == clazz) { + return true; + } + } + } + return false; + } + + @Override + public boolean mustReserveLengthField(Class clazz) { + return classesNeedingLengthFields.contains(clazz); + } + + @Override + public void afterAnalysis(AfterAnalysisAccess access) { + analysisAccess = null; + sealed = true; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java index 2e5273c7502e..94625fb2cd9f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java @@ -32,7 +32,6 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import com.oracle.graal.pointsto.ObjectScanner.ScanReason; import com.oracle.graal.pointsto.ObjectScanningObserver; @@ -50,6 +49,7 @@ import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider; import com.oracle.svm.hosted.meta.HostedMetaAccess; +import com.oracle.svm.hosted.meta.InternalRuntimeReflectionSupport; import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.ConstantReflectionProvider; @@ -62,7 +62,7 @@ public class SVMImageHeapScanner extends ImageHeapScanner { private final Class economicMapImpl; private final Field economicMapImplEntriesField; private final Field economicMapImplHashArrayField; - private final RuntimeReflectionSupport reflectionSupport; + private final InternalRuntimeReflectionSupport reflectionSupport; public SVMImageHeapScanner(ImageHeap imageHeap, ImageClassLoader loader, AnalysisMetaAccess metaAccess, SnippetReflectionProvider snippetReflection, ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) { @@ -72,7 +72,7 @@ public SVMImageHeapScanner(ImageHeap imageHeap, ImageClassLoader loader, Analysi economicMapImplEntriesField = ReflectionUtil.lookupField(economicMapImpl, "entries"); economicMapImplHashArrayField = ReflectionUtil.lookupField(economicMapImpl, "hashArray"); ImageSingletons.add(ImageHeapScanner.class, this); - reflectionSupport = ImageSingletons.lookup(RuntimeReflectionSupport.class); + reflectionSupport = ImageSingletons.lookup(InternalRuntimeReflectionSupport.class); } public static ImageHeapScanner instance() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapVerifier.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapVerifier.java index 839b78cf2037..b85c4a0a2c0c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapVerifier.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapVerifier.java @@ -25,7 +25,6 @@ package com.oracle.svm.hosted.heap; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.ObjectScanner; @@ -37,6 +36,7 @@ import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.analysis.NativeImagePointsToAnalysis; +import com.oracle.svm.hosted.meta.InternalRuntimeReflectionSupport; import jdk.vm.ci.meta.JavaConstant; @@ -63,7 +63,7 @@ public boolean requireAnalysisIteration(CompletionExecutor executor) throws Inte * */ private static boolean imageStateModified() { - return ImageSingletons.lookup(RuntimeReflectionSupport.class).requiresProcessing() || + return ImageSingletons.lookup(InternalRuntimeReflectionSupport.class).requiresProcessing() || ImageSingletons.lookup(ImageHeapMapFeature.class).imageHeapMapNeedsUpdate(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java index 115c72ff56b4..e67f561fbaf4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java @@ -54,7 +54,6 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.c.function.CFunctionPointer; -import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -93,6 +92,7 @@ import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.meta.HostedUniverse; +import com.oracle.svm.hosted.meta.InternalRuntimeReflectionSupport; import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.site.Call; @@ -245,7 +245,7 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code } ReflectionMetadataEncoder reflectionMetadataEncoder = ImageSingletons.lookup(ReflectionMetadataEncoderFactory.class).create(encoders); - RuntimeReflectionSupport reflectionSupport = ImageSingletons.lookup(RuntimeReflectionSupport.class); + InternalRuntimeReflectionSupport reflectionSupport = ImageSingletons.lookup(InternalRuntimeReflectionSupport.class); HostedUniverse hUniverse = imageHeap.getUniverse(); HostedMetaAccess hMetaAccess = imageHeap.getMetaAccess(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java index ef5e14f0e7bc..64342c48756a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java @@ -332,7 +332,7 @@ private boolean assertFillerObjectSizes() { assert minArraySize == objectLayout.getArraySize(JavaKind.Int, 0); HostedType filler = metaAccess.lookupJavaType(FillerObject.class); - UnsignedWord fillerSize = LayoutEncoding.getInstanceSize(filler.getHub().getLayoutEncoding()); + UnsignedWord fillerSize = LayoutEncoding.getPureInstanceSize(filler.getHub().getLayoutEncoding()); assert fillerSize.equal(minInstanceSize); assert minInstanceSize * 2 >= minArraySize : "otherwise, we might need more than one non-array object"; @@ -392,7 +392,7 @@ private void addObjectToImageHeap(final Object object, boolean immutableFromPare if (HybridLayout.isHybrid(clazz)) { HybridLayout hybridLayout = hybridLayouts.get(clazz); if (hybridLayout == null) { - hybridLayout = new HybridLayout<>(clazz, objectLayout); + hybridLayout = new HybridLayout<>(clazz, objectLayout, metaAccess); hybridLayouts.put(clazz, hybridLayout); } @@ -421,7 +421,7 @@ private void addObjectToImageHeap(final Object object, boolean immutableFromPare assert hybridArray != null : "Cannot read value for field " + hybridArrayField.format("%H.%n"); size = hybridLayout.getTotalSize(Array.getLength(hybridArray)); } else { - size = LayoutEncoding.getInstanceSize(hub.getLayoutEncoding()).rawValue(); + size = LayoutEncoding.getPureInstanceSize(hub.getLayoutEncoding()).rawValue(); } info = addToImageHeap(object, clazz, size, identityHashCode, reason); @@ -561,7 +561,7 @@ private long getSize(Object object, HostedType type) { if (type.isInstanceClass()) { HostedInstanceClass clazz = (HostedInstanceClass) type; assert !HybridLayout.isHybrid(clazz); - return LayoutEncoding.getInstanceSize(clazz.getHub().getLayoutEncoding()).rawValue(); + return LayoutEncoding.getPureInstanceSize(clazz.getHub().getLayoutEncoding()).rawValue(); } else if (type.isArray()) { return objectLayout.getArraySize(type.getComponentType().getStorageKind(), Array.getLength(object)); } else { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/InternalRuntimeReflectionSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/InternalRuntimeReflectionSupport.java new file mode 100644 index 000000000000..8aaaf8722e6d --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/InternalRuntimeReflectionSupport.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.meta; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Executable; +import java.lang.reflect.Field; +import java.util.Map; +import java.util.Set; + +import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; + +public interface InternalRuntimeReflectionSupport extends RuntimeReflectionSupport { + Map, Set>> getReflectionInnerClasses(); + + Set getReflectionFields(); + + Set getReflectionExecutables(); + + Object getAccessor(Executable method); + + /* + * Returns the methods and fields that shadow a superclass element registered for reflection, to + * be excluded from reflection queries. + */ + Set getHidingReflectionFields(); + + Set getHidingReflectionMethods(); + + Object[] getRecordComponents(Class type); + + void registerHeapDynamicHub(Object hub); + + Set getHeapDynamicHubs(); + + void registerHeapReflectionObject(AccessibleObject object); + + Set getHeapReflectionObjects(); + + int getReflectionClassesCount(); + + int getReflectionMethodsCount(); + + int getReflectionFieldsCount(); + + boolean requiresProcessing(); +} 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 5c32a040e76b..70398bc3ff5b 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 @@ -91,6 +91,7 @@ import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.hosted.annotation.CustomSubstitutionMethod; import com.oracle.svm.hosted.config.HybridLayout; +import com.oracle.svm.hosted.heap.PodSupport; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.hosted.substitute.ComputedValueField; import com.oracle.svm.hosted.substitute.DeletedMethod; @@ -400,6 +401,18 @@ private void layoutInstanceFields() { layoutInstanceFields(hUniverse.getObjectClass(), ConfigurationValues.getObjectLayout().getFirstFieldOffset(), new HostedField[0]); } + private static boolean mustReserveLengthField(HostedInstanceClass clazz) { + if (PodSupport.isPresent() && PodSupport.singleton().mustReserveLengthField(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. + return !PodSupport.isPresent() || !PodSupport.singleton().isPodClass(clazz.getJavaClass()); + } + return false; + } + private void layoutInstanceFields(HostedInstanceClass clazz, int superSize, HostedField[] superFields) { ArrayList rawFields = new ArrayList<>(); ArrayList orderedFields = new ArrayList<>(); @@ -413,15 +426,14 @@ private void layoutInstanceFields(HostedInstanceClass clazz, int superSize, Host startSize = DeoptimizedFrame.getScratchSpaceOffset() + layout.getDeoptScratchSpace(); } - if (HybridLayout.isHybrid(clazz)) { - /* Set start after array length field */ - assert startSize == layout.getArrayLengthOffset(); + if (mustReserveLengthField(clazz)) { + VMError.guarantee(startSize == layout.getArrayLengthOffset()); int fieldSize = layout.sizeInBytes(JavaKind.Int); startSize += fieldSize; /* * Set start after typecheck slots field, if the hybrid class has one. For now, only - * DynamicHubs can this field(s). + * DynamicHubs can have such field(s). */ if (clazz.equals(hMetaAccess.lookupJavaType(DynamicHub.class))) { /* Each type check id slot is 2 bytes. */ @@ -890,20 +902,21 @@ private void buildHubs() { hUniverse.bb.getHeartbeatCallback().run(); int layoutHelper; + boolean canInstantiateAsInstance = false; int monitorOffset = 0; if (type.isInstanceClass()) { HostedInstanceClass instanceClass = (HostedInstanceClass) type; if (instanceClass.isAbstract()) { layoutHelper = LayoutEncoding.forAbstract(); } else if (HybridLayout.isHybrid(type)) { - HybridLayout hybridLayout = new HybridLayout<>(instanceClass, ol); + HybridLayout hybridLayout = new HybridLayout<>(instanceClass, ol, hMetaAccess); JavaKind storageKind = hybridLayout.getArrayElementStorageKind(); boolean isObject = (storageKind == JavaKind.Object); - layoutHelper = LayoutEncoding.forArray(type, isObject, hybridLayout.getArrayBaseOffset(), ol.getArrayIndexShift(storageKind)); - } else if (instanceClass.getJavaClass().equals(StoredContinuation.class)) { - layoutHelper = LayoutEncoding.forStoredContinuation(); + layoutHelper = LayoutEncoding.forHybrid(type, isObject, hybridLayout.getArrayBaseOffset(), ol.getArrayIndexShift(storageKind)); + canInstantiateAsInstance = type.isInstantiated() && HybridLayout.canInstantiateAsInstance(type); } else { - layoutHelper = LayoutEncoding.forInstance(type, ConfigurationValues.getObjectLayout().alignUp(instanceClass.getInstanceSize())); + layoutHelper = LayoutEncoding.forPureInstance(type, ConfigurationValues.getObjectLayout().alignUp(instanceClass.getInstanceSize())); + canInstantiateAsInstance = type.isInstantiated(); } monitorOffset = instanceClass.getMonitorFieldOffset(); } else if (type.isArray()) { @@ -943,8 +956,8 @@ private void buildHubs() { } DynamicHub hub = type.getHub(); - hub.setData(layoutHelper, type.getTypeID(), monitorOffset, type.getTypeCheckStart(), type.getTypeCheckRange(), type.getTypeCheckSlot(), type.getTypeCheckSlots(), - vtable, referenceMapIndex, type.isInstantiated()); + hub.setData(layoutHelper, type.getTypeID(), monitorOffset, type.getTypeCheckStart(), type.getTypeCheckRange(), type.getTypeCheckSlot(), + type.getTypeCheckSlots(), vtable, referenceMapIndex, type.isInstantiated(), canInstantiateAsInstance); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphKit.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphKit.java index 7d0a227d21b6..daf7f50aaa9a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphKit.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/HostedGraphKit.java @@ -70,7 +70,11 @@ public class HostedGraphKit extends SubstrateGraphKit { public HostedGraphKit(DebugContext debug, HostedProviders providers, ResolvedJavaMethod method) { - super(debug, method, providers, providers.getWordTypes(), providers.getGraphBuilderPlugins(), new SubstrateCompilationIdentifier()); + this(debug, providers, method, false); + } + + public HostedGraphKit(DebugContext debug, HostedProviders providers, ResolvedJavaMethod method, boolean forceTrackNodeSourcePosition) { + super(debug, method, providers, providers.getWordTypes(), providers.getGraphBuilderPlugins(), new SubstrateCompilationIdentifier(), forceTrackNodeSourcePosition); } @Override diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIThreadLocalPinnedObjects.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIThreadLocalPinnedObjects.java index b064820a41e9..72957b3e41e6 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIThreadLocalPinnedObjects.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIThreadLocalPinnedObjects.java @@ -87,7 +87,7 @@ public static boolean unpinObject(Object object) { } public static boolean unpinArrayByAddress(PointerBase address) { - return unpinFirst(n -> LayoutEncoding.isArray(n.object.getObject()) && n.object.addressOfArrayElement(0) == address); + return unpinFirst(n -> LayoutEncoding.isArrayLike(n.object.getObject()) && n.object.addressOfArrayElement(0) == address); } static int pinnedObjectCount() { diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleField.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleField.java index b85e7756af29..d93da05f3056 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleField.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleField.java @@ -111,8 +111,9 @@ void finishBeforeCompilation(CompilationAccessImpl access) { int offset; if (HybridLayout.isHybridField(field)) { assert !field.hasLocation(); - HybridLayout hybridLayout = new HybridLayout<>((HostedInstanceClass) field.getDeclaringClass(), ImageSingletons.lookup(ObjectLayout.class)); - assert field.equals(hybridLayout.getArrayField()) : "JNI access to hybrid bitset field is not implemented"; + HybridLayout hybridLayout = new HybridLayout<>((HostedInstanceClass) field.getDeclaringClass(), + ImageSingletons.lookup(ObjectLayout.class), access.getMetaAccess()); + assert field.equals(hybridLayout.getArrayField()) : "JNI access to hybrid objects is implemented only for the array field"; offset = hybridLayout.getArrayBaseOffset(); } else { assert field.hasLocation(); diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java index 0416d4056273..23b00f48c568 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java @@ -58,7 +58,6 @@ import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.ConfigurationCondition; -import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; @@ -78,6 +77,7 @@ import com.oracle.svm.hosted.ConditionalConfigurationRegistry; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.hosted.annotation.AnnotationSubstitutionType; +import com.oracle.svm.hosted.meta.InternalRuntimeReflectionSupport; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; import com.oracle.svm.util.ModuleSupport; @@ -90,7 +90,7 @@ import sun.reflect.annotation.TypeAnnotation; import sun.reflect.annotation.TypeNotPresentExceptionProxy; -public class ReflectionDataBuilder extends ConditionalConfigurationRegistry implements RuntimeReflectionSupport { +public class ReflectionDataBuilder extends ConditionalConfigurationRegistry implements InternalRuntimeReflectionSupport { private final Set> modifiedClasses = ConcurrentHashMap.newKeySet(); private boolean sealed; diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java index f8a374c615c6..25bf875b5413 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java @@ -66,6 +66,7 @@ import com.oracle.svm.hosted.analysis.Inflation; import com.oracle.svm.hosted.code.FactoryMethodSupport; import com.oracle.svm.hosted.config.ConfigurationParserUtils; +import com.oracle.svm.hosted.meta.InternalRuntimeReflectionSupport; import com.oracle.svm.hosted.snippets.ReflectionPlugins; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.util.ModuleSupport; @@ -203,6 +204,7 @@ public void afterRegistration(AfterRegistrationAccess access) { reflectionData = new ReflectionDataBuilder(); ImageSingletons.add(RuntimeReflectionSupport.class, reflectionData); + ImageSingletons.add(InternalRuntimeReflectionSupport.class, reflectionData); } @Override diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java index d31f195df871..b291b008d8f6 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java @@ -60,7 +60,6 @@ import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -79,6 +78,7 @@ import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.hosted.meta.InternalRuntimeReflectionSupport; import com.oracle.svm.hosted.substitute.DeletedElementException; import com.oracle.svm.reflect.hosted.ReflectionMetadata.AccessibleObjectMetadata; import com.oracle.svm.reflect.hosted.ReflectionMetadata.ClassMetadata; @@ -656,7 +656,7 @@ private static ReflectParameterMetadata[] getReflectParameters(Executable reflec } private RecordComponentMetadata[] getRecordComponents(MetaAccessProvider metaAccess, HostedType declaringType, Class clazz) { - Object[] recordComponents = ImageSingletons.lookup(RuntimeReflectionSupport.class).getRecordComponents(clazz); + Object[] recordComponents = ImageSingletons.lookup(InternalRuntimeReflectionSupport.class).getRecordComponents(clazz); if (recordComponents == null) { return null; }