From fa4df6cfdef9abfaacd6fbdc0608c245ba3739ab Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Fri, 25 Jun 2021 12:29:23 -0700 Subject: [PATCH 01/10] Improve performance of method resolution --- .../hotspot/SnippetResolvedJavaType.java | 5 ++ .../graal/pointsto/meta/AnalysisType.java | 26 +++++---- ...stantAnnotationMarkerSubstitutionType.java | 5 ++ .../oracle/svm/hosted/meta/HostedType.java | 53 ++++++------------- 4 files changed, 42 insertions(+), 47 deletions(-) diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/SnippetResolvedJavaType.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/SnippetResolvedJavaType.java index 9020656454e6..31052deb36c6 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/SnippetResolvedJavaType.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/SnippetResolvedJavaType.java @@ -246,6 +246,11 @@ public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaT throw new UnsupportedOperationException(); } + @Override + public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { + throw new UnsupportedOperationException(); + } + @Override public Assumptions.AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) { throw new UnsupportedOperationException(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index 765374a38ffb..de9a4020dde5 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -925,6 +925,16 @@ public boolean hasSubTypes() { @Override public AnalysisMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { + /* + * Not needed on Substrate VM for now. We also do not have the necessary information + * available to implement it for JIT compilation at image run time. So we want to make sure + * that Graal is not using this method, and only resolveConcreteMethod instead. + */ + throw GraalError.unimplemented(); + } + + @Override + public AnalysisMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { Object resolvedMethod = resolvedMethods.get(method); if (resolvedMethod == null) { ResolvedJavaMethod substMethod = universe.substitutions.resolve(((AnalysisMethod) method).wrapped); @@ -934,7 +944,7 @@ public AnalysisMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType */ ResolvedJavaType substCallerType = substMethod.getDeclaringClass(); - Object newResolvedMethod = universe.lookup(wrapped.resolveMethod(substMethod, substCallerType)); + Object newResolvedMethod = universe.lookup(wrapped.resolveConcreteMethod(substMethod, substCallerType)); if (newResolvedMethod == null) { newResolvedMethod = NULL_METHOD; } @@ -945,18 +955,12 @@ public AnalysisMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType } /** - * Wrapper for resolveConcreteMethod() that ignores the callerType parameter. The method that - * does the resolution, resolveMethod() above, ignores the callerType parameter and uses - * substMethod.getDeclaringClass() instead since we don't want any access checks in the - * analysis. + * Wrapper for resolveConcreteMethod() without the callerType parameter. We ignore the + * callerType parameter and use substMethod.getDeclaringClass() instead since we don't want any + * access checks in the analysis. */ public AnalysisMethod resolveConcreteMethod(ResolvedJavaMethod method) { - return (AnalysisMethod) WrappedJavaType.super.resolveConcreteMethod(method, null); - } - - @Override - public AnalysisMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { - return (AnalysisMethod) WrappedJavaType.super.resolveConcreteMethod(method, callerType); + return resolveConcreteMethod(method, null); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/ConstantAnnotationMarkerSubstitutionType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/ConstantAnnotationMarkerSubstitutionType.java index 27ba47a8b9f8..873e46aa8ea8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/ConstantAnnotationMarkerSubstitutionType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/ConstantAnnotationMarkerSubstitutionType.java @@ -188,6 +188,11 @@ public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaT return original.resolveMethod(method, callerType); } + @Override + public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { + return original.resolveConcreteMethod(method, callerType); + } + @Override public Assumptions.AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) { return original.findUniqueConcreteMethod(method); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java index 376d57ac97bb..7eb6fcfe75d8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java @@ -27,7 +27,6 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHere; import java.lang.annotation.Annotation; -import java.util.Arrays; import org.graalvm.word.WordBase; @@ -310,49 +309,31 @@ public final ResolvedJavaType findLeastCommonAncestor(ResolvedJavaType otherType } @Override - public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod m, ResolvedJavaType ct) { + public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod m, ResolvedJavaType callerType) { HostedMethod method = (HostedMethod) m; - HostedType callerType = (HostedType) ct; - if (isWordType()) { + AnalysisMethod aResult = wrapped.resolveConcreteMethod(method.wrapped); + HostedMethod hResult; + if (aResult == null) { + hResult = null; + } else if (!aResult.isImplementationInvoked() && !isWordType()) { /* - * We do not keep any method information on word types on our own, so ask the hosting VM - * for the answer. + * Filter out methods that are not seen as invoked by the static analysis, e.g., because + * the declaring type is not instantiated. Word types are an exception, because methods + * of word types are never marked as invoked (they are always intrinsified). */ - return wrappedResolveMethod(method, callerType); + hResult = null; + } else { + hResult = universe.lookup(aResult); } - /* Use the same algorithm that is also used for SubstrateType during runtime compilation. */ - ResolvedJavaMethod found = SharedType.super.resolveConcreteMethod(method, callerType); - /* Check that our algorithm returns the same result as the hosting VM. */ - - /* - * For abstract classes, our result can be different than the result from HotSpot. It is - * unclear what concrete method resolution on an abstract class means. - */ - assert isAbstract() || (found == null || checkWrappedResolveMethod(method, found, callerType)); - - return found; - } - - private boolean checkWrappedResolveMethod(HostedMethod method, ResolvedJavaMethod found, HostedType callerType) { - /* - * The static analysis can determine that the resolved wrapped method is not reachable, case - * in which wrappedResolveMethod returns null. + /** + * Check that the SharedType implementation, which is used for JIT compilation, gives the + * same result as the hosted implementation. */ - ResolvedJavaMethod wrappedMethod = wrappedResolveMethod(method, callerType); - return wrappedMethod == null || found.equals(wrappedMethod); - } + assert hResult == null || isWordType() || hResult.equals(SharedType.super.resolveConcreteMethod(method, callerType)); - private ResolvedJavaMethod wrappedResolveMethod(HostedMethod method, HostedType callerType) { - AnalysisMethod orig = wrapped.resolveConcreteMethod(method.wrapped, callerType.wrapped); - ResolvedJavaMethod result = orig == null ? null : universe.lookup(orig); - - if (result != null && !isWordType() && !Arrays.asList(method.getImplementations()).contains(result)) { - /* Our static analysis found out that this method is not reachable. */ - result = null; - } - return result; + return hResult; } @Override From cd6b9cb5c9b574d059905e0402fd4cd809e9abe1 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Fri, 25 Jun 2021 19:52:08 -0700 Subject: [PATCH 02/10] Improve performance of CompileTask scheduling --- .../svm/hosted/code/CompilationInfo.java | 5 +++++ .../oracle/svm/hosted/code/CompileQueue.java | 21 ++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompilationInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompilationInfo.java index 350adb0936d0..e95c6cfc981d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompilationInfo.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompilationInfo.java @@ -40,6 +40,11 @@ public class CompilationInfo { protected final HostedMethod method; protected final AtomicBoolean inParseQueue = new AtomicBoolean(false); + /** + * No need for this flag to be atomic, because {@link CompileQueue#compilations} is used to + * ensure each method is compiled only once. + */ + protected boolean inCompileQueue; protected volatile StructuredGraph graph; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index b96f3be1c73d..5d559996077c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -199,6 +199,8 @@ public interface CompileFunction { private volatile boolean inliningProgress; + private final boolean printMethodHistogram = NativeImageOptions.PrintMethodHistogram.getValue(); + public abstract static class CompileReason { /** * For debugging only: chaining of the compile reason, so that you can track the compilation @@ -266,7 +268,7 @@ public class CompileTask implements DebugContextRunnable { public CompileTask(HostedMethod method, CompileReason reason) { this.method = method; this.reason = reason; - if (NativeImageOptions.PrintMethodHistogram.getValue()) { + if (printMethodHistogram) { this.allReasons = Collections.synchronizedList(new ArrayList()); this.allReasons.add(reason); } else { @@ -386,7 +388,7 @@ public void finish(DebugContext debug) { } catch (InterruptedException ie) { throw new InterruptImageBuilding(); } - if (NativeImageOptions.PrintMethodHistogram.getValue()) { + if (printMethodHistogram) { printMethodHistogram(); } } @@ -1108,15 +1110,28 @@ private static void handleSpecialization(final HostedMethod method, CallTargetNo } protected void ensureCompiled(HostedMethod method, CompileReason reason) { + /* + * Fast non-atomic check if method is already scheduled for compilation, to avoid frequent + * access of the ConcurrentHashMap. + */ + if (method.compilationInfo.inCompileQueue) { + if (printMethodHistogram) { + compilations.get(method).allReasons.add(reason); + } + return; + } + CompileTask task = new CompileTask(method, reason); CompileTask oldTask = compilations.putIfAbsent(method, task); if (oldTask != null) { // Method is already scheduled for compilation. - if (oldTask.allReasons != null) { + if (printMethodHistogram) { oldTask.allReasons.add(reason); } return; } + method.compilationInfo.inCompileQueue = true; + if (method.compilationInfo.specializedArguments != null) { // Do the specialization: replace the argument locals with the constant arguments. StructuredGraph graph = method.compilationInfo.graph; From 427b078ce114e688fa45ecb9c497897d41ac99e1 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Fri, 25 Jun 2021 20:49:59 -0700 Subject: [PATCH 03/10] Cache frequently accessed values --- .../svm/core/genscavenge/HostedImageHeapChunkWriter.java | 5 +++-- .../core/genscavenge/remset/AlignedChunkRememberedSet.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HostedImageHeapChunkWriter.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HostedImageHeapChunkWriter.java index d8eeb425afa6..3fca7eddd401 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HostedImageHeapChunkWriter.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HostedImageHeapChunkWriter.java @@ -51,6 +51,7 @@ final class HostedImageHeapChunkWriter implements ImageHeapChunkWriter { private final int spaceOffsetAt; private final int offsetToPreviousChunkAt; private final int offsetToNextChunkAt; + private final RememberedSet rememberedSet = RememberedSet.get(); HostedImageHeapChunkWriter(ByteBuffer heapBuffer, long layoutToBufferOffsetAddend) { buffer = heapBuffer; @@ -94,13 +95,13 @@ private void writeHeader(int chunkOffset, long topOffset, long endOffset, long o @Override public void enableRememberedSetForAlignedChunk(int chunkPosition, List objects) { int chunkOffset = getChunkOffsetInBuffer(chunkPosition); - RememberedSet.get().enableRememberedSetForAlignedChunk(new HostedByteBufferPointer(buffer, chunkOffset), chunkPosition, objects); + rememberedSet.enableRememberedSetForAlignedChunk(new HostedByteBufferPointer(buffer, chunkOffset), chunkPosition, objects); } @Override public void enableRememberedSetForUnalignedChunk(int chunkPosition) { int chunkOffset = getChunkOffsetInBuffer(chunkPosition); - RememberedSet.get().enableRememberedSetForUnalignedChunk(new HostedByteBufferPointer(buffer, chunkOffset)); + rememberedSet.enableRememberedSetForUnalignedChunk(new HostedByteBufferPointer(buffer, chunkOffset)); } static void putObjectReference(ByteBuffer buffer, int offset, long value) { diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/AlignedChunkRememberedSet.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/AlignedChunkRememberedSet.java index 3f2222e0cb08..f6a8f2be9dc0 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/AlignedChunkRememberedSet.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/remset/AlignedChunkRememberedSet.java @@ -68,11 +68,12 @@ public static void enableRememberedSet(HostedByteBufferPointer chunk, int chunkP FirstObjectTable.initializeTable(getFirstObjectTableStart(chunk), getFirstObjectTableSize()); Pointer fotStart = getFirstObjectTableStart(chunk); + UnsignedWord objectsStartOffset = AlignedHeapChunk.getObjectsStartOffset(); for (ImageHeapObject obj : objects) { long offsetWithinChunk = obj.getOffset() - chunkPosition; - assert offsetWithinChunk > 0 && WordFactory.unsigned(offsetWithinChunk).aboveOrEqual(AlignedHeapChunk.getObjectsStartOffset()); + assert offsetWithinChunk > 0 && WordFactory.unsigned(offsetWithinChunk).aboveOrEqual(objectsStartOffset); - UnsignedWord startOffset = WordFactory.unsigned(offsetWithinChunk).subtract(AlignedHeapChunk.getObjectsStartOffset()); + UnsignedWord startOffset = WordFactory.unsigned(offsetWithinChunk).subtract(objectsStartOffset); UnsignedWord endOffset = startOffset.add(WordFactory.unsigned(obj.getSize())); FirstObjectTable.setTableForObject(fotStart, startOffset, endOffset); // The remembered set bit in the header will be set by the code that writes the objects. From 13e7f16e5815e58a1b597114295351758bacdbe7 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Fri, 25 Jun 2021 20:50:18 -0700 Subject: [PATCH 04/10] Cache uniqueShortName --- .../oracle/svm/hosted/image/NativeImage.java | 21 +++---------------- .../oracle/svm/hosted/meta/HostedMethod.java | 8 +++++++ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java index 2e1c2033181e..7437c7c6ee00 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java @@ -688,21 +688,6 @@ private void markDataRelocationSiteFromText(RelocatableBuffer buffer, final Prog } } - /** - * Given a java.lang.reflect.Method, compute the symbol name of its start address (if any) in - * the image. The symbol name returned is the one that would be used for local references (e.g. - * for relocation), so is guaranteed to exist if the method is in the image. However, it is not - * necessarily visible for linking from other objects. - * - * @param m a java.lang.reflect.Method - * @return its symbol name as it would appear in the image (regardless of whether it actually - * does) - */ - public static String localSymbolNameForMethod(java.lang.reflect.Method m) { - /* We don't mangle local symbols, because they never need be referenced by an assembler. */ - return SubstrateUtil.uniqueShortName(m); - } - /** * Given a {@link ResolvedJavaMethod}, compute what symbol name of its start address (if any) in * the image. The symbol name returned is the one that would be used for local references (e.g. @@ -715,7 +700,7 @@ public static String localSymbolNameForMethod(java.lang.reflect.Method m) { */ public static String localSymbolNameForMethod(ResolvedJavaMethod sm) { /* We don't mangle local symbols, because they never need be referenced by an assembler. */ - return SubstrateOptions.ImageSymbolsPrefix.getValue() + SubstrateUtil.uniqueShortName(sm); + return SubstrateOptions.ImageSymbolsPrefix.getValue() + (sm instanceof HostedMethod ? ((HostedMethod) sm).getUniqueShortName() : SubstrateUtil.uniqueShortName(sm)); } /** @@ -745,7 +730,7 @@ public static String globalSymbolNameForMethod(java.lang.reflect.Method m) { * does) */ public static String globalSymbolNameForMethod(ResolvedJavaMethod sm) { - return mangleName(SubstrateUtil.uniqueShortName(sm)); + return mangleName((sm instanceof HostedMethod ? ((HostedMethod) sm).getUniqueShortName() : SubstrateUtil.uniqueShortName(sm))); } @Override @@ -892,7 +877,7 @@ protected void writeTextSection(DebugContext debug, final Section textSection, f // 1. fq with return type for (Map.Entry ent : codeCache.getCompilations().entrySet()) { final String symName = localSymbolNameForMethod(ent.getKey()); - final String signatureString = SubstrateUtil.uniqueShortName(ent.getKey()); + final String signatureString = ent.getKey().getUniqueShortName(); final HostedMethod existing = methodsBySignature.get(signatureString); HostedMethod current = ent.getKey(); if (existing != null) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java index 0982e43663cc..6f5d24334322 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java @@ -42,6 +42,7 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.results.StaticAnalysisResults; +import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.AlwaysInline; import com.oracle.svm.core.annotate.StubCallingConvention; import com.oracle.svm.core.deopt.Deoptimizer; @@ -92,6 +93,8 @@ public class HostedMethod implements SharedMethod, WrappedJavaMethod, GraphProvi public final CompilationInfo compilationInfo; private final LocalVariableTable localVariableTable; + private final String uniqueShortName; + public HostedMethod(HostedUniverse universe, AnalysisMethod wrapped, HostedType holder, Signature signature, ConstantPool constantPool, ExceptionHandler[] handlers) { this.wrapped = wrapped; this.holder = holder; @@ -99,6 +102,7 @@ public HostedMethod(HostedUniverse universe, AnalysisMethod wrapped, HostedType this.constantPool = constantPool; this.handlers = handlers; this.compilationInfo = new CompilationInfo(this); + this.uniqueShortName = SubstrateUtil.uniqueShortName(this); LocalVariableTable newLocalVariableTable = null; if (wrapped.getLocalVariableTable() != null) { @@ -160,6 +164,10 @@ public boolean isCompiled() { return compiled; } + public String getUniqueShortName() { + return uniqueShortName; + } + /* * Release compilation related information. */ From 399a33e6c0a57235a6f0424b4e1c58904bb35c64 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Sun, 27 Jun 2021 20:01:21 -0700 Subject: [PATCH 05/10] Cache frequently accessed values --- .../src/com/oracle/svm/hosted/image/NativeImageHeap.java | 6 ++++-- .../com/oracle/svm/hosted/image/NativeImageHeapWriter.java | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) 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 dc0b48ce3545..22afc4b3ebf1 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 @@ -611,7 +611,9 @@ static class AddObjectData { final Object reason; } - public static final class ObjectInfo implements ImageHeapObject { + private final int imageHeapOffsetInAddressSpace = Heap.getHeap().getImageHeapOffsetInAddressSpace(); + + public final class ObjectInfo implements ImageHeapObject { private final Object object; private final HostedClass clazz; private final long size; @@ -689,7 +691,7 @@ public long getAddress() { * the beginning of the heap. So, all heap-base-relative addresses must be adjusted by * that offset. */ - return Heap.getHeap().getImageHeapOffsetInAddressSpace() + getOffset(); + return imageHeapOffsetInAddressSpace + getOffset(); } /** diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java index 5c63e2c324ad..d9b1407afc16 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java @@ -157,14 +157,16 @@ private void write(RelocatableBuffer buffer, int index, JavaConstant con, Object } } + private final boolean useHeapBase = NativeImageHeap.useHeapBase(); + private final CompressEncoding compressEncoding = ImageSingletons.lookup(CompressEncoding.class); + void writeReference(RelocatableBuffer buffer, int index, Object target, Object reason) { assert !(target instanceof WordBase) : "word values are not references"; mustBeReferenceAligned(index); if (target != null) { ObjectInfo targetInfo = heap.getObjectInfo(target); verifyTargetDidNotChange(target, reason, targetInfo); - if (NativeImageHeap.useHeapBase()) { - CompressEncoding compressEncoding = ImageSingletons.lookup(CompressEncoding.class); + if (useHeapBase) { int shift = compressEncoding.getShift(); writeReferenceValue(buffer, index, targetInfo.getAddress() >>> shift); } else { From 06fb2e629fb8ae511e31ea4ea538850a435124d1 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Sun, 27 Jun 2021 20:01:40 -0700 Subject: [PATCH 06/10] Avoid expensive conversion from internal class name to Java name --- .../svm/hosted/ameta/AnalysisConstantReflectionProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java index 13516ccce6ca..b67be844f287 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java @@ -129,7 +129,7 @@ public JavaConstant readValue(MetaAccessProvider suppliedMetaAccess, AnalysisFie private static JavaConstant readUninitializedStaticValue(AnalysisField field) { JavaKind kind = field.getJavaKind(); - boolean canHaveConstantValueAttribute = kind.isPrimitive() || field.getType().toJavaName(true).equals("java.lang.String"); + boolean canHaveConstantValueAttribute = kind.isPrimitive() || field.getType().getName().equals("Ljava/lang/String;"); if (!canHaveConstantValueAttribute || !field.isFinal()) { return JavaConstant.defaultForKind(kind); } From 78b73da86d4aa9d1f3300f4a5305da05e46e1942 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Mon, 28 Jun 2021 10:36:15 -0700 Subject: [PATCH 07/10] Use bulk-copy to copy primitive arrays into the image heap --- .../svm/hosted/image/NativeImageHeapWriter.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java index d9b1407afc16..71c4684c9081 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.Indent; +import org.graalvm.compiler.serviceprovider.GraalUnsafeAccess; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.nativeimage.c.function.RelocatedPointer; @@ -61,11 +62,15 @@ import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaMethod; +import sun.misc.Unsafe; /** * Writes the native image heap into one or multiple {@link RelocatableBuffer}s. */ public final class NativeImageHeapWriter { + + private static final Unsafe UNSAFE = GraalUnsafeAccess.getUnsafe(); + private final NativeImageHeap heap; private final ImageHeapLayoutInfo heapLayout; private long sectionOffsetOfARelocatablePointer; @@ -382,11 +387,10 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { writeConstant(buffer, elementIndex, kind, element, info); } } else { - for (int i = 0; i < length; i++) { - final int elementIndex = info.getIndexInBuffer(objectLayout.getArrayElementOffset(kind, i)); - final Object element = Array.get(array, i); - writeConstant(buffer, elementIndex, kind, element, info); - } + int elementIndex = info.getIndexInBuffer(objectLayout.getArrayElementOffset(kind, 0)); + int elementTypeSize = UNSAFE.arrayIndexScale(array.getClass()); + assert elementTypeSize == kind.getByteCount(); + UNSAFE.copyMemory(array, UNSAFE.arrayBaseOffset(array.getClass()), buffer.getBackingArray(), Unsafe.ARRAY_BYTE_BASE_OFFSET + elementIndex, length * elementTypeSize); } } else { From 6eb7b94231b92e9a061237d8beea66b8763683ce Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Mon, 28 Jun 2021 09:36:22 -0700 Subject: [PATCH 08/10] Make TypeFlow objects unreachable after static analysis --- .../src/com/oracle/graal/pointsto/meta/AnalysisMethod.java | 1 + 1 file changed, 1 insertion(+) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java index a0dfa20a2b01..2b3b68039363 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java @@ -182,6 +182,7 @@ public void cleanupAfterAnalysis() { typeFlow = null; invokedBy = null; implementationInvokedBy = null; + contextInsensitiveInvoke.set(null); if (parsedGraphCacheState.get() instanceof AnalysisParsedGraph) { parsedGraphCacheState.set(GRAPH_CACHE_CLEARED); } From 25c3155281af8b6872e83220577681bcc3308932 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Mon, 28 Jun 2021 09:39:18 -0700 Subject: [PATCH 09/10] Allow GC of more Graal graphs that are no longer needed --- .../svm/core/graal/meta/SubstrateReplacements.java | 3 +++ .../src/com/oracle/svm/hosted/code/CompileQueue.java | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateReplacements.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateReplacements.java index ae1439052142..9704776347dd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateReplacements.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateReplacements.java @@ -282,6 +282,9 @@ public void encodeSnippets() { snippetNodeClasses = encoder.getNodeClasses(); snippetInvocationPlugins = makeInvocationPlugins(getGraphBuilderPlugins(), builder, Function.identity()); + + /* Original graphs are no longer necessary, release memory. */ + builder.graphs.clear(); } @Platforms(Platform.HOSTED_ONLY.class) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index 5d559996077c..41f12a2b6bd7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -374,6 +374,15 @@ public void finish(DebugContext debug) { // timer. RestrictHeapAccessAnnotationChecker.check(debug, universe, universe.getMethods()); + /* + * The graph in the analysis universe is no longer necessary. This clears the graph for + * methods that were not "parsed", i.e., method that were reached by the static analysis + * but are no longer reachable now. + */ + for (HostedMethod method : universe.getMethods()) { + method.wrapped.setAnalyzedGraph(null); + } + if (SubstrateOptions.AOTInline.getValue() && SubstrateOptions.AOTTrivialInline.getValue()) { try (StopTimer ignored = new Timer(imageName, "(inline)").start()) { inlineTrivialMethods(debug); From cd6ba194c79d882f3089da727befd0b74a07cfa4 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Wed, 30 Jun 2021 11:32:26 -0700 Subject: [PATCH 10/10] Make -H:+PrintMethodHistogram usable for large images --- .../svm/hosted/code/CompilationInfo.java | 6 +++ .../oracle/svm/hosted/code/CompileQueue.java | 45 ++++++++----------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompilationInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompilationInfo.java index e95c6cfc981d..3d3938dec910 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompilationInfo.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompilationInfo.java @@ -25,6 +25,7 @@ package com.oracle.svm.hosted.code; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -77,6 +78,11 @@ public class CompilationInfo { protected long numDeoptEntryPoints; protected long numDuringCallEntryPoints; + /* Statistics collected when method is put into compile queue. */ + protected final AtomicLong numDirectCalls = new AtomicLong(); + protected final AtomicLong numVirtualCalls = new AtomicLong(); + protected final AtomicLong numEntryPointCalls = new AtomicLong(); + public CompilationInfo(HostedMethod method) { this.method = method; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index 41f12a2b6bd7..fd9868c3c448 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -29,7 +29,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; @@ -261,19 +260,12 @@ public class CompileTask implements DebugContextRunnable { public final HostedMethod method; protected final CompileReason reason; - protected final List allReasons; public CompilationResult result; public final CompilationIdentifier compilationIdentifier; public CompileTask(HostedMethod method, CompileReason reason) { this.method = method; this.reason = reason; - if (printMethodHistogram) { - this.allReasons = Collections.synchronizedList(new ArrayList()); - this.allReasons.add(reason); - } else { - this.allReasons = null; - } compilationIdentifier = new SubstrateHostedCompilationIdentifier(method); } @@ -482,14 +474,10 @@ private void printMethodHistogram() { } else { sizeNonDeoptMethods += result.getTargetCodeSize(); numberOfNonDeopt += 1; - System.out.format(" ; %6d; %5d; %5d; %4d; %4d;", 0, 0, 0, 0, 0); + System.out.format(" ; %6d; %5d; %5d; %5d; %4d; %4d;", 0, 0, 0, 0, 0, 0); } - System.out.format(" %4d; %4d; %4d; %s\n", - task.allReasons.stream().filter(t -> t instanceof EntryPointReason).count(), - task.allReasons.stream().filter(t -> t instanceof DirectCallReason).count(), - task.allReasons.stream().filter(t -> t instanceof VirtualCallReason).count(), - method.format("%H.%n(%p) %r")); + System.out.format(" %4d; %4d; %4d; %s%n", ci.numEntryPointCalls.get(), ci.numDirectCalls.get(), ci.numVirtualCalls.get(), method.format("%H.%n(%p) %r")); } } System.out.println(); @@ -1119,34 +1107,39 @@ private static void handleSpecialization(final HostedMethod method, CallTargetNo } protected void ensureCompiled(HostedMethod method, CompileReason reason) { + CompilationInfo compilationInfo = method.compilationInfo; + + if (printMethodHistogram) { + if (reason instanceof DirectCallReason) { + compilationInfo.numDirectCalls.incrementAndGet(); + } else if (reason instanceof VirtualCallReason) { + compilationInfo.numVirtualCalls.incrementAndGet(); + } else if (reason instanceof EntryPointReason) { + compilationInfo.numEntryPointCalls.incrementAndGet(); + } + } + /* * Fast non-atomic check if method is already scheduled for compilation, to avoid frequent * access of the ConcurrentHashMap. */ - if (method.compilationInfo.inCompileQueue) { - if (printMethodHistogram) { - compilations.get(method).allReasons.add(reason); - } + if (compilationInfo.inCompileQueue) { return; } CompileTask task = new CompileTask(method, reason); CompileTask oldTask = compilations.putIfAbsent(method, task); if (oldTask != null) { - // Method is already scheduled for compilation. - if (printMethodHistogram) { - oldTask.allReasons.add(reason); - } return; } - method.compilationInfo.inCompileQueue = true; + compilationInfo.inCompileQueue = true; - if (method.compilationInfo.specializedArguments != null) { + if (compilationInfo.specializedArguments != null) { // Do the specialization: replace the argument locals with the constant arguments. - StructuredGraph graph = method.compilationInfo.graph; + StructuredGraph graph = compilationInfo.graph; int idx = 0; - for (ConstantNode argument : method.compilationInfo.specializedArguments) { + for (ConstantNode argument : compilationInfo.specializedArguments) { ParameterNode local = graph.getParameter(idx++); if (local != null) { local.replaceAndDelete(ConstantNode.forConstant(argument.asJavaConstant(), runtimeConfig.getProviders().getMetaAccess(), graph));