From d7ec8a804f3440ef44c5af9b3e27715dceb9d515 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Mon, 14 Apr 2025 23:59:39 +0200 Subject: [PATCH 01/12] add javadoc to caution use of Node.getId --- .../src/jdk/graal/compiler/graph/Node.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java index c1a2425b2d3a..d92b5e37bb47 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java @@ -58,6 +58,7 @@ import jdk.graal.compiler.nodeinfo.NodeInfo; import jdk.graal.compiler.nodeinfo.NodeSize; import jdk.graal.compiler.nodeinfo.Verbosity; +import jdk.graal.compiler.nodes.StructuredGraph; import jdk.graal.compiler.nodes.spi.Simplifiable; import jdk.graal.compiler.options.OptionValues; import jdk.graal.compiler.serviceprovider.GraalServices; @@ -1728,6 +1729,13 @@ public String toString(Verbosity verbosity) { } } + /** + * Note that this is not a stable identity. It's updated when a node is + * {@linkplain #markDeleted() deleted} or potentially when its graph is + * {@linkplain StructuredGraph#maybeCompress compressed}. + * + * @see NodeIdAccessor + */ @Deprecated public int getId() { return id; From 9389ad25f8d1de4080bbfdadbd454ce674cca6fa Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Fri, 25 Apr 2025 11:43:03 +0200 Subject: [PATCH 02/12] include option values in CompilationWrapper output --- .../src/jdk/graal/compiler/core/test/SubprocessTest.java | 2 +- .../graal/compiler/hotspot/test/CompilationWrapperTest.java | 3 ++- .../src/jdk/graal/compiler/core/CompilationWrapper.java | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/SubprocessTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/SubprocessTest.java index 19a9b9ca1a6c..ed7295503720 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/SubprocessTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/SubprocessTest.java @@ -126,7 +126,7 @@ public static SubprocessUtil.Subprocess launchSubprocess(Predicate> assert testSelector != null : "must pass the name of the current unit test"; String testName = testSelector.equals(ALL_TESTS) ? testClass.getName() : testClass.getName() + "#" + testSelector; mainClassAndArgs.add(testName); - boolean junitVerbose = getProcessCommandLine().contains("-JUnitVerbose"); + boolean junitVerbose = String.valueOf(getProcessCommandLine()).contains("-JUnitVerbose"); if (junitVerbose) { mainClassAndArgs.add("-JUnitVerbose"); } diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/CompilationWrapperTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/CompilationWrapperTest.java index cb7ed85b3f37..180d06642a9e 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/CompilationWrapperTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/CompilationWrapperTest.java @@ -26,6 +26,7 @@ import static jdk.graal.compiler.debug.StandardPathUtilitiesProvider.DIAGNOSTIC_OUTPUT_DIRECTORY_MESSAGE_FORMAT; import static jdk.graal.compiler.debug.StandardPathUtilitiesProvider.DIAGNOSTIC_OUTPUT_DIRECTORY_MESSAGE_REGEXP; +import static jdk.graal.compiler.test.SubprocessUtil.getProcessCommandLine; import static jdk.graal.compiler.test.SubprocessUtil.getVMCommandLine; import static jdk.graal.compiler.test.SubprocessUtil.withoutDebuggerArguments; @@ -232,7 +233,7 @@ public void testTruffleCompilation2() throws IOException, InterruptedException { SLCompileASTTestSuite.class.getName(), "test"); } - private static final boolean VERBOSE = Boolean.getBoolean("CompilationWrapperTest.verbose"); + private static final boolean VERBOSE = Boolean.getBoolean("CompilationWrapperTest.verbose") || String.valueOf(getProcessCommandLine()).contains("-JUnitVerbose"); public static void testHelper(List initialProbes, List extraVmArgs, diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/CompilationWrapper.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/CompilationWrapper.java index 3f9e20b9708b..515244ef23f6 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/CompilationWrapper.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/CompilationWrapper.java @@ -288,6 +288,7 @@ protected T handleFailure(DebugContext initialDebug, Throwable cause) { try (PrintStream ps = new PrintStream(baos)) { ps.printf("%s: Compilation of %s failed: ", Thread.currentThread(), this); cause.printStackTrace(ps); + ps.println("Options: " + initialOptions); printCompilationFailureActionAlternatives(ps, ExceptionAction.Silent, ExceptionAction.Diagnose); } TTY.print(baos.toString()); @@ -322,6 +323,7 @@ protected T handleFailure(DebugContext initialDebug, Throwable cause) { ps.printf("%s: Compilation of %s failed:%n", Thread.currentThread(), this); cause.printStackTrace(ps); + ps.println("Options: " + initialOptions); printCompilationFailureActionAlternatives(ps, ExceptionAction.Silent, ExceptionAction.Print); if (dumpPath != null) { ps.println("Retrying compilation of " + this); From 463266040cf70936db422e2d61db9abb12fd0996 Mon Sep 17 00:00:00 2001 From: Carlo Refice Date: Thu, 10 Apr 2025 11:18:33 +0200 Subject: [PATCH 03/12] release Node#extraUsages array when all usages have been removed --- .../src/jdk/graal/compiler/graph/Node.java | 42 +++++-------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java index d92b5e37bb47..dbde7758b3af 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, 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 @@ -40,8 +40,6 @@ import java.util.function.Predicate; import java.util.function.Supplier; -import org.graalvm.collections.EconomicSet; - import jdk.graal.compiler.core.common.Fields; import jdk.graal.compiler.core.common.type.Stamp; import jdk.graal.compiler.core.common.util.CompilationAlarm; @@ -589,6 +587,9 @@ private void movUsageFromEndToExtraUsages(int destExtraIndex) { Node n = extraUsages[extraUsagesCount]; extraUsages[destExtraIndex] = n; extraUsages[extraUsagesCount] = null; + if (extraUsagesCount == 0) { + extraUsages = EMPTY_ARRAY; + } } private void movUsageFromEndToIndexZero() { @@ -602,6 +603,9 @@ private void movUsageFromEndToIndexZero() { } else { usage0 = null; } + if (extraUsagesCount == 0) { + extraUsages = EMPTY_ARRAY; + } } private void movUsageFromEndToIndexOne() { @@ -613,6 +617,9 @@ private void movUsageFromEndToIndexOne() { assert usage1 != null; usage1 = null; } + if (extraUsagesCount == 0) { + extraUsages = EMPTY_ARRAY; + } } /** @@ -689,35 +696,6 @@ public int removeUsageNTimes(Node node, int limit) { return removedUsages; } - /** - * Removes all nodes in the provided set from {@code this} node's usages. This is significantly - * faster than repeated execution of {@link Node#removeUsage}. - */ - public void removeUsages(EconomicSet toDelete) { - if (toDelete.size() == 0) { - return; - } else if (toDelete.size() == 1) { - removeUsage(toDelete.iterator().next()); - return; - } - - // requires iteration from back to front to check nodes prior to being moved to the front - for (int i = extraUsagesCount - 1; i >= 0; i--) { - if (toDelete.contains(extraUsages[i])) { - movUsageFromEndToExtraUsages(i); - incUsageModCount(); - } - } - if (usage1 != null && toDelete.contains(usage1)) { - movUsageFromEndToIndexOne(); - incUsageModCount(); - } - if (usage0 != null && toDelete.contains(usage0)) { - movUsageFromEndToIndexZero(); - incUsageModCount(); - } - } - /** * Removes all dead nodes from {@code this} node's usages. This is significantly faster than * repeated execution of {@link Node#removeUsage}. From 5b8b59c97d7785fa26020b9634a35f1e78a83d79 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Mon, 14 Apr 2025 23:33:45 +0200 Subject: [PATCH 04/12] reduce extraUsages expansion from 2x down to 1.5x --- .../jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java index dbde7758b3af..7da421d2a88f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java @@ -563,7 +563,9 @@ void addUsage(Node node) { if (length == 0) { extraUsages = new Node[4]; } else if (extraUsagesCount == length) { - Node[] newExtraUsages = new Node[length * 2 + 1]; + int growth = length >> 1; + // Grow the usages array by 1.5x + Node[] newExtraUsages = new Node[length + growth]; System.arraycopy(extraUsages, 0, newExtraUsages, 0, length); extraUsages = newExtraUsages; } From 2f177e0753187e45ca5d9ae488981eacb884a9ca Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Mon, 14 Apr 2025 23:34:15 +0200 Subject: [PATCH 05/12] move setting of extraUsages array to more precise location --- .../src/jdk/graal/compiler/graph/Node.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java index 7da421d2a88f..d9c338c7c589 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java @@ -598,30 +598,32 @@ private void movUsageFromEndToIndexZero() { if (extraUsagesCount > 0) { this.extraUsagesCount--; usage0 = extraUsages[extraUsagesCount]; - extraUsages[extraUsagesCount] = null; + if (extraUsagesCount == 0) { + extraUsages = EMPTY_ARRAY; + } else { + extraUsages[extraUsagesCount] = null; + } } else if (usage1 != null) { usage0 = usage1; usage1 = null; } else { usage0 = null; } - if (extraUsagesCount == 0) { - extraUsages = EMPTY_ARRAY; - } } private void movUsageFromEndToIndexOne() { if (extraUsagesCount > 0) { this.extraUsagesCount--; usage1 = extraUsages[extraUsagesCount]; - extraUsages[extraUsagesCount] = null; + if (extraUsagesCount == 0) { + extraUsages = EMPTY_ARRAY; + } else { + extraUsages[extraUsagesCount] = null; + } } else { assert usage1 != null; usage1 = null; } - if (extraUsagesCount == 0) { - extraUsages = EMPTY_ARRAY; - } } /** From 4257df51665caf683a44003472a8cfecb0528a76 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Wed, 16 Apr 2025 12:21:19 +0200 Subject: [PATCH 06/12] added ObjectCopier.NotExternalValue annotation --- .../jdk/graal/compiler/util/ObjectCopier.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index c1074ce79202..58e57d5ab3fd 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -28,6 +28,10 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -1007,6 +1011,23 @@ public static Field getField(Class declaredClass, String fieldName) { } } + /** + * Denotes a field that should not be treated as an external value. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface NotExternalValue { + /** + * Documents the reason why the annotated field is not an external value. + */ + String reason(); + } + + /** + * Gets the set of static, final fields whose values are not serialized. + * + * @see NotExternalValue + */ public static List getExternalValueFields() throws IOException { List externalValues = new ArrayList<>(); addImmutableCollectionsFields(externalValues); @@ -1051,12 +1072,16 @@ public static List getStaticFinalObjectFields(Class declaringClass) { for (Field field : declaringClass.getDeclaredFields()) { int fieldModifiers = field.getModifiers(); int fieldMask = Modifier.STATIC | Modifier.FINAL; - if ((fieldModifiers & fieldMask) != fieldMask) { + boolean isStaticAndFinal = (fieldModifiers & fieldMask) == fieldMask; + if (!isStaticAndFinal) { continue; } if (field.getType().isPrimitive()) { continue; } + if (field.getAnnotation(NotExternalValue.class) != null) { + continue; + } field.setAccessible(true); fields.add(field); } From c4f350b354bee8ab3e564a30b51c2a8296c433fe Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Wed, 16 Apr 2025 12:22:06 +0200 Subject: [PATCH 07/12] added support to ObjectCopier for box values --- .../compiler/util/test/ObjectCopierTest.java | 37 ++++++++++++++++--- .../jdk/graal/compiler/util/ObjectCopier.java | 29 +++++++++++++++ 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java index 706305ef9591..161668fe460a 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java @@ -89,6 +89,11 @@ static class TestObject extends BaseClass { int intField3 = Integer.MIN_VALUE; int intField4 = Integer.MAX_VALUE; int intField5 = -1; + long longField1 = 0; + long longField2 = 42L; + long longField3 = Long.MIN_VALUE; + long longField4 = Long.MAX_VALUE; + long longField5 = -1L; float floatField1 = -1.4F; float floatField2 = Float.MIN_NORMAL; float floatField3 = Float.MIN_VALUE; @@ -120,13 +125,18 @@ public String toString() { } }; - private static List fieldValues(Object obj) { - List values = new ArrayList<>(); + @ObjectCopier.NotExternalValue(reason = "testing NotExternalValue annotation") // + static final String[] TEST_OBJECT_COPIABLE = { + "this", "value", "should", "be", "copied" + }; + + private static Map fieldValues(Object obj) { + Map values = new HashMap<>(); Class c = obj.getClass(); while (c != Object.class) { for (Field f : c.getDeclaredFields()) { try { - values.add(f.get(obj)); + values.put(f, f.get(obj)); } catch (IllegalAccessException e) { throw new AssertionError(e); } @@ -138,7 +148,7 @@ private static List fieldValues(Object obj) { @Override public String toString() { - return fieldValues(this).stream().map(String::valueOf).collect(Collectors.joining(", ")); + return fieldValues(this).values().stream().map(String::valueOf).collect(Collectors.joining(", ")); } } @@ -150,10 +160,16 @@ public void testIt() { ClassLoader loader = getClass().getClassLoader(); TestObject testObject = new TestObject(); + EconomicMap fieldMap = EconomicMap.create(); + for (var e : TestObject.fieldValues(testObject).entrySet()) { + fieldMap.put(e.getKey().getName(), e.getValue()); + } + List timeUnits = List.of(TimeUnit.MICROSECONDS, TimeUnit.DAYS, TimeUnit.SECONDS); EconomicMap emap = EconomicMap.create(); emap.put(42, Map.of("1", 1, "2", 2)); emap.put(-12345, testObject); + emap.put(-6789, fieldMap); Map hmap = new HashMap<>(Map.of("1000", "one thousand")); Map idMap = new IdentityHashMap<>(Map.of(new Object(), "some obj")); @@ -172,9 +188,13 @@ public void testIt() { root.put("6", new ArrayList<>(timeUnits)); root.put("singleton2", TestObject.TEST_OBJECT_SINGLETON); root.put("singleton2_2", TestObject.TEST_OBJECT_SINGLETON); + root.put("copiable", TestObject.TEST_OBJECT_COPIABLE); + + List externalValueFields = new ArrayList<>(); + externalValueFields.addAll(ObjectCopier.getStaticFinalObjectFields(BaseClass.class)); + externalValueFields.addAll(ObjectCopier.getStaticFinalObjectFields(TestObject.class)); - List externalValueFields = List.of(ObjectCopier.getField(BaseClass.class, "BASE_SINGLETON"), - ObjectCopier.getField(TestObject.class, "TEST_OBJECT_SINGLETON")); + Assert.assertFalse(externalValueFields.contains(ObjectCopier.getField(TestObject.class, "TEST_OBJECT_COPIABLE"))); byte[] encoded = encode(externalValueFields, root, "encoded"); Object decoded = ObjectCopier.decode(encoded, loader); @@ -187,13 +207,18 @@ public void testIt() { Map root2 = (Map) ObjectCopier.decode(reencoded, loader); + Assert.assertSame(BaseClass.BASE_SINGLETON, root2.get("singleton1")); Assert.assertSame(root.get("singleton1"), root2.get("singleton1")); Assert.assertSame(root.get("singleton1_2"), root2.get("singleton1_2")); Assert.assertSame(root2.get("singleton1"), root2.get("singleton1_2")); + Assert.assertSame(TestObject.TEST_OBJECT_SINGLETON, root.get("singleton2")); Assert.assertSame(root.get("singleton2"), root2.get("singleton2")); Assert.assertSame(root.get("singleton2_2"), root2.get("singleton2_2")); Assert.assertSame(root2.get("singleton2"), root2.get("singleton2_2")); + + Assert.assertNotSame(TestObject.TEST_OBJECT_COPIABLE, root2.get("copiable")); + Assert.assertNotSame(root.get("copiable"), root2.get("copiable")); } private static byte[] encode(List externalValueFields, Object root, String debugLabel) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index 58e57d5ab3fd..a9fb15beecdd 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -54,10 +54,12 @@ import java.util.TreeMap; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.vm.ci.meta.JavaKind; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicMapWrap; import org.graalvm.collections.Equivalence; @@ -186,6 +188,32 @@ protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInpu } } + /** + * Builtin for handling boxed primitives. + */ + static final class BoxBuiltin extends Builtin { + + private static final Map, JavaKind> CONCRETE_CLASSES = // + Stream.of(JavaKind.values())// + .filter(k -> k.isPrimitive() && k != JavaKind.Void)// + .collect(Collectors.toMap(JavaKind::toBoxedJavaClass, Function.identity())); + + BoxBuiltin() { + super(Number.class, CONCRETE_CLASSES.keySet().toArray(Class[]::new)); + } + + @Override + protected void encode(Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { + stream.writeUntypedValue(obj); + } + + @Override + protected Object decode(Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + char typeCh = CONCRETE_CLASSES.get(concreteType).getTypeChar(); + return stream.readUntypedValue(typeCh); + } + } + /** * Builtin for handling {@link String} values. */ @@ -432,6 +460,7 @@ final void addBuiltin(Builtin builtin, Class clazz) { public ObjectCopier() { addBuiltin(new ClassBuiltin()); + addBuiltin(new BoxBuiltin()); addBuiltin(new EconomicMapBuiltin()); addBuiltin(new EnumBuiltin()); From 02c88ee2f58902676406b930c0fc916c0fcfdc1a Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Fri, 11 Apr 2025 18:21:37 +0200 Subject: [PATCH 08/12] add NodeClassMap and remove EncodedGraph.types --- .../compiler/libgraal/LibGraalFeature.java | 12 ++- .../jdk/graal/compiler/graph/NodeClass.java | 19 ++-- .../compiler/hotspot/CompilerConfig.java | 6 ++ .../compiler/hotspot/EncodedSnippets.java | 12 +-- .../hotspot/SymbolicSnippetEncoder.java | 8 +- .../graal/compiler/nodes/EncodedGraph.java | 20 ++-- .../graal/compiler/nodes/GraphDecoder.java | 8 +- .../graal/compiler/nodes/GraphEncoder.java | 83 ++++++++------- .../graal/compiler/nodes/NodeClassMap.java | 100 ++++++++++++++++++ .../graal/meta/SubstrateReplacements.java | 8 +- .../TruffleRuntimeCompilationSupport.java | 14 +-- .../RuntimeCompilationFeature.java | 3 +- .../RuntimeCompiledMethodSupport.java | 4 +- 13 files changed, 208 insertions(+), 89 deletions(-) create mode 100644 compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/NodeClassMap.java diff --git a/compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java b/compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java index bae1a46163ba..ade911301785 100644 --- a/compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java +++ b/compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java @@ -31,7 +31,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; @@ -199,7 +198,7 @@ public void duringSetup(DuringSetupAccess access) { // (see jdk.graal.compiler.graph.NodeClass.allocateInstance). access.registerObjectReachabilityHandler(nodeClass -> { Class clazz = nodeClass.getClazz(); - if (!Modifier.isAbstract(clazz.getModifiers())) { + if (!nodeClass.isAbstract()) { /* Support for NodeClass.allocateInstance. */ beforeAnalysisAccess.registerAsUnsafeAllocated(clazz); } @@ -375,9 +374,12 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { EncodedSnippets encodedSnippets = (EncodedSnippets) libgraalObjects.get("encodedSnippets"); checkNodeClasses(encodedSnippets, (String) libgraalObjects.get("snippetNodeClasses")); - // Mark all the Node classes as allocated so they are available during graph decoding. + // Mark all non-abstract Node classes as allocated so they + // are available during graph decoding. for (NodeClass nodeClass : encodedSnippets.getSnippetNodeClasses()) { - access.registerAsInHeap(nodeClass.getClazz()); + if (!nodeClass.isAbstract()) { + access.registerAsInHeap(nodeClass.getClazz()); + } } HotSpotReplacementsImpl.setEncodedSnippets(encodedSnippets); @@ -389,7 +391,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { private static void checkNodeClasses(EncodedSnippets encodedSnippets, String actual) { String expect = CompilerConfig.snippetNodeClassesToJSON(encodedSnippets); - GraalError.guarantee(actual.equals(expect), "%s != %s", actual, expect); + GraalError.guarantee(actual.equals(expect), "%n%s%n !=%n%s", actual, expect); } /** diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeClass.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeClass.java index 2a89991a918e..db8bbe8a108f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeClass.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeClass.java @@ -149,14 +149,9 @@ public static NodeClass get(Class clazz) { private final int leafId; - @LibGraalSupport.HostedOnly - public NodeClass(Class clazz, NodeClass superNodeClass) { - this(clazz, superNodeClass, null, 0); - } - @SuppressWarnings("try") @LibGraalSupport.HostedOnly - private NodeClass(Class clazz, NodeClass superNodeClass, int[] presetIterableIds, int presetIterableId) { + public NodeClass(Class clazz, NodeClass superNodeClass) { super(clazz); DebugContext debug = DebugContext.forCurrentThread(); this.superNodeClass = superNodeClass; @@ -201,10 +196,7 @@ private NodeClass(Class clazz, NodeClass superNodeClass, int[] pre GraalError.guarantee(!allowedUsageTypes.contains(InputType.Memory) || MemoryKillMarker.class.isAssignableFrom(clazz), "Node of type %s with allowedUsageType of memory must inherit from MemoryKill", clazz); - if (presetIterableIds != null) { - this.iterableIds = presetIterableIds; - this.iterableId = presetIterableId; - } else if (IterableNodeType.class.isAssignableFrom(clazz)) { + if (IterableNodeType.class.isAssignableFrom(clazz)) { ITERABLE_NODE_TYPES.increment(debug); this.iterableId = nextIterableId.getAndIncrement(); @@ -332,6 +324,13 @@ public boolean valueNumberable() { return canGVN; } + /** + * Determines if this node type is abstract. + */ + public boolean isAbstract() { + return Modifier.isAbstract(this.getClazz().getModifiers()); + } + /** * Determines if this node type is {@link CanonicalizableMarker canonicalizable}. */ diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/CompilerConfig.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/CompilerConfig.java index ffef728cb93f..bea5d76c6f58 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/CompilerConfig.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/CompilerConfig.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.Locale; +import jdk.graal.compiler.nodes.GraphEncoder; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; @@ -81,6 +82,11 @@ public static void main(String[] args) throws Exception { EncodedSnippets encodedSnippets = getEncodedSnippets(replacements, options); List externalValueFields = ObjectCopier.getExternalValueFields(); + // The NodeClassMap value read from GraphEncoder.GLOBAL_NODE_CLASS_MAP + // must not be treated as an external value when being serialized + // across processes. + externalValueFields.remove(ObjectCopier.getField(GraphEncoder.class, "GLOBAL_NODE_CLASS_MAP")); + EconomicMap encodedObjects = EconomicMap.create(); encodedObjects.put("encodedSnippets", encodedSnippets); encodedObjects.put("snippetNodeClasses", snippetNodeClassesToJSON(encodedSnippets)); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/EncodedSnippets.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/EncodedSnippets.java index 926d33f01eaa..f6f1862f75b2 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/EncodedSnippets.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/EncodedSnippets.java @@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap; import jdk.graal.compiler.core.common.LibGraalSupport; +import jdk.graal.compiler.nodes.NodeClassMap; import org.graalvm.collections.UnmodifiableEconomicMap; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; @@ -42,7 +43,6 @@ import jdk.graal.compiler.core.common.type.SymbolicJVMCIReference; import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.debug.GraalError; -import jdk.graal.compiler.graph.NodeClass; import jdk.graal.compiler.nodeinfo.Verbosity; import jdk.graal.compiler.nodes.ConstantNode; import jdk.graal.compiler.nodes.EncodedGraph; @@ -170,11 +170,11 @@ int getStartOffset(Class aClass) { private final byte[] snippetEncoding; private final Object[] snippetObjects; - private final NodeClass[] snippetNodeClasses; + private final NodeClassMap snippetNodeClasses; private final UnmodifiableEconomicMap graphDatas; private final UnmodifiableEconomicMap, SnippetResolvedJavaType> snippetTypes; - EncodedSnippets(byte[] snippetEncoding, Object[] snippetObjects, NodeClass[] snippetNodeClasses, UnmodifiableEconomicMap graphDatas, + EncodedSnippets(byte[] snippetEncoding, Object[] snippetObjects, NodeClassMap snippetNodeClasses, UnmodifiableEconomicMap graphDatas, UnmodifiableEconomicMap, SnippetResolvedJavaType> snippetTypes) { this.snippetEncoding = snippetEncoding; this.snippetObjects = snippetObjects; @@ -183,7 +183,7 @@ int getStartOffset(Class aClass) { this.snippetTypes = snippetTypes; } - public NodeClass[] getSnippetNodeClasses() { + public NodeClassMap getSnippetNodeClasses() { return snippetNodeClasses; } @@ -398,8 +398,8 @@ static class SymbolicEncodedGraph extends EncodedGraph { private final ResolvedJavaType[] accessingClasses; private final String originalMethod; - SymbolicEncodedGraph(byte[] encoding, int startOffset, Object[] objects, NodeClass[] types, String originalMethod, ResolvedJavaType... accessingClasses) { - super(encoding, startOffset, objects, types, null, null, false, false); + SymbolicEncodedGraph(byte[] encoding, int startOffset, Object[] objects, NodeClassMap nodeClasses, String originalMethod, ResolvedJavaType... accessingClasses) { + super(encoding, startOffset, objects, nodeClasses, null, null, false, false); this.accessingClasses = accessingClasses; this.originalMethod = originalMethod; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java index 4747595ba2a3..34a6269c3cb7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java @@ -513,7 +513,7 @@ private boolean verifySingle(DebugContext debug, StructuredGraph graph) { } private synchronized EncodedSnippets encodeSnippets(DebugContext debug, EconomicMap preparedSnippetGraphs) { - GraphEncoder encoder = new GraphEncoder(HotSpotJVMCIRuntime.runtime().getHostJVMCIBackend().getTarget().arch, debug); + GraphEncoder encoder = new GraphEncoder(HotSpotJVMCIRuntime.runtime().getHostJVMCIBackend().getTarget().arch, debug, null); for (StructuredGraph graph : preparedSnippetGraphs.getValues()) { graph.resetDebug(debug); assert verifySingle(debug, graph); @@ -549,6 +549,7 @@ private synchronized EncodedSnippets encodeSnippets(DebugContext debug, Economic debug.log("snippetObjects[%d] = %s -> %s", i, o != null ? o.getClass().getSimpleName() : null, o); snippetObjects[i] = o; } + debug.log("Encoded %d snippet preparedSnippetGraphs using %d bytes with %d objects", graphDatas.size(), snippetEncoding.length, snippetObjects.length); return new EncodedSnippets(snippetEncoding, snippetObjects, encoder.getNodeClasses(), graphDatas, snippetTypes); } @@ -816,8 +817,7 @@ private Object filterStamp(DebugContext debug, Stamp stamp) { if (cached != null) { return cached; } - if (stamp instanceof AbstractObjectStamp) { - AbstractObjectStamp objectStamp = (AbstractObjectStamp) stamp; + if (stamp instanceof AbstractObjectStamp objectStamp) { ResolvedJavaType type = objectStamp.type(); if (type == null) { return stamp; @@ -959,7 +959,7 @@ private static String getCanonicalGraphString(StructuredGraph graph, boolean exc constantsLinesResult.append('\n'); } - return constantsLinesResult.toString() + result.toString(); + return constantsLinesResult + result.toString(); } private static int filteredUsageCount(Node node) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/EncodedGraph.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/EncodedGraph.java index 1a728903eb6a..785d50752abd 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/EncodedGraph.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/EncodedGraph.java @@ -83,7 +83,7 @@ public void clear() { private final byte[] encoding; private final int startOffset; protected final Object[] objects; - private final NodeClass[] types; + private final NodeClassMap nodeClasses; private final Assumptions assumptions; private final List inlinedMethods; private final boolean trackNodeSourcePosition; @@ -95,11 +95,11 @@ public void clear() { */ protected int[] nodeStartOffsets; - public EncodedGraph(byte[] encoding, int startOffset, Object[] objects, NodeClass[] types, StructuredGraph sourceGraph) { + public EncodedGraph(byte[] encoding, int startOffset, Object[] objects, NodeClassMap nodeClasses, StructuredGraph sourceGraph) { this(encoding, startOffset, objects, - types, + nodeClasses, sourceGraph.getAssumptions(), sourceGraph.isRecordingInlinedMethods() ? sourceGraph.getMethods() : null, sourceGraph.hasUnsafeAccess(), @@ -110,7 +110,7 @@ public EncodedGraph(EncodedGraph original) { this(original.encoding, original.startOffset, original.objects, - original.types, + original.nodeClasses, original.assumptions, original.inlinedMethods, original.hasUnsafeAccess, @@ -122,12 +122,12 @@ public EncodedGraph(EncodedGraph original) { this.nodeStartOffsets = null; } - public EncodedGraph(byte[] encoding, int startOffset, Object[] objects, NodeClass[] types, Assumptions assumptions, List inlinedMethods, + public EncodedGraph(byte[] encoding, int startOffset, Object[] objects, NodeClassMap nodeClasses, Assumptions assumptions, List inlinedMethods, boolean hasUnsafeAccess, boolean trackNodeSourcePosition) { this.encoding = encoding; this.startOffset = startOffset; this.objects = objects; - this.types = types; + this.nodeClasses = nodeClasses; this.assumptions = assumptions; this.inlinedMethods = inlinedMethods; this.trackNodeSourcePosition = trackNodeSourcePosition; @@ -158,8 +158,12 @@ public void setObject(int i, Object object) { objects[i] = object; } - public NodeClass[] getNodeClasses() { - return types; + public NodeClassMap getNodeClasses() { + return nodeClasses; + } + + public NodeClass getNodeClass(int id) { + return nodeClasses.get(id); } public Assumptions getAssumptions() { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphDecoder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphDecoder.java index b3244ea63723..7d7bba52ee85 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphDecoder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphDecoder.java @@ -797,7 +797,7 @@ protected LoopScope processNextNode(MethodScope methodScope, LoopScope loopScope methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]); int typeId = methodScope.reader.getUVInt(); - assert node.getNodeClass() == methodScope.encodedGraph.getNodeClasses()[typeId] : Assertions.errorMessage(node, methodScope.encodedGraph.getNodeClasses()[typeId]); + assert node.getNodeClass() == methodScope.encodedGraph.getNodeClass(typeId) : Assertions.errorMessage(node, methodScope.encodedGraph.getNodeClass(typeId)); makeFixedNodeInputs(methodScope, loopScope, node); readProperties(methodScope, node); @@ -1597,7 +1597,7 @@ protected Node decodeFloatingNode(MethodScope methodScope, LoopScope loopScope, long readerByteIndex = methodScope.reader.getByteIndex(); methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]); - NodeClass nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()]; + NodeClass nodeClass = methodScope.encodedGraph.getNodeClass(methodScope.reader.getUVInt()); Node node = allocateFloatingNode(nodeClass); if (node instanceof FixedNode) { /* @@ -1715,7 +1715,7 @@ protected NodeClass getNodeClass(MethodScope methodScope, LoopScope loopScope long readerByteIndex = methodScope.reader.getByteIndex(); methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]); - NodeClass nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()]; + NodeClass nodeClass = methodScope.encodedGraph.getNodeClass(methodScope.reader.getUVInt()); methodScope.reader.setByteIndex(readerByteIndex); return nodeClass; } @@ -1731,7 +1731,7 @@ protected FixedNode makeStubNode(MethodScope methodScope, LoopScope loopScope, i long readerByteIndex = methodScope.reader.getByteIndex(); methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]); - NodeClass nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()]; + NodeClass nodeClass = methodScope.encodedGraph.getNodeClass(methodScope.reader.getUVInt()); Node stubNode = nodeClass.allocateInstance(); if (graph.trackNodeSourcePosition()) { stubNode.setNodeSourcePosition(NodeSourcePosition.placeholder(graph.method())); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java index 8f602733995e..dcd1ac77e056 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java @@ -58,18 +58,14 @@ * * One encoder instance can be used to encode multiple graphs. This requires that {@link #prepare} * is called for all graphs first, followed by one call to {@link #finishPrepare}. Then - * {@link #encode} can be called for all graphs. The {@link #getObjects() objects} and - * {@link #getNodeClasses() node classes} arrays do not change anymore after preparation. + * {@link #encode} can be called for all graphs. The {@link #getObjects() objects} array does not + * change anymore after preparation. * * Multiple encoded graphs share the Object[] array, and elements of the Object[] array are * de-duplicated using {@link Object#equals Object equality}. This uses the assumption and good * coding practice that data objects are immutable if {@link Object#equals} is implemented. * Unfortunately, this cannot be enforced. * - * The Graal {@link NodeClass} does not have a unique id that allows class lookup from an id. - * Therefore, the encoded graph contains a {@link NodeClass}[] array for lookup, and type ids are - * encoding-local. - * * The encoded graph has the following structure: First, all nodes and their edges are serialized. * The start offset of every node is then known. The raw node data is followed by metadata, i.e., * the maximum fixed node order id and a "table of contents" that lists the start offset for every @@ -77,13 +73,13 @@ * * The beginning of this metadata is the return value of {@link #encode} and stored in * {@link EncodedGraph#getStartOffset()}. The order of nodes in the table of contents is the - * {@link NodeOrder#orderIds orderId} of a node. Note that the orderId is not the regular node id - * that every Graal graph node gets assigned. The orderId is computed and used just for encoding and - * decoding. The orderId of fixed nodes is assigned in reverse postorder. The decoder processes - * nodes using that order, which ensures that all predecessors of a node (including all - * {@link EndNode predecessors} of a {@link AbstractBeginNode block}) are decoded before the node. - * The order id of floating node does not matter during decoding, so floating nodes get order ids - * after all fixed nodes. The order id is used to encode edges between nodes + * {@link NodeOrder#orderIds orderId} of a node. Note that the orderId is not the same as + * {@code Node.getId()}. The orderId is computed and used just for encoding and decoding. The + * orderId of fixed nodes is assigned in reverse postorder. The decoder processes nodes using that + * order, which ensures that all predecessors of a node (including all {@link EndNode predecessors} + * of a {@link AbstractBeginNode block}) are decoded before the node. The orderId of a floating node + * does not matter during decoding, so floating nodes get orderIds after all fixed nodes. The + * orderId is used to encode edges between nodes * * Structure of an encoded node: * @@ -149,21 +145,23 @@ public class GraphEncoder { * used objects have the small indices. */ protected final FrequencyEncoder objects; - /** - * Collects all node classes referenced in graphs. This is necessary because {@link NodeClass} - * currently does not have a unique id. - */ - protected final FrequencyEncoder> nodeClasses; /** The writer for the encoded graphs. */ protected final UnsafeArrayTypeWriter writer; + protected final NodeClassMap nodeClasses; + /** The last snapshot of {@link #objects} that was retrieved. */ protected Object[] objectsArray; - /** The last snapshot of {@link #nodeClasses} that was retrieved. */ - protected NodeClass[] nodeClassesArray; protected DebugContext debug; + /** + * Global map used in HotSpot JIT compilation as well as Native Image AOT compilation. Encoding + * graphs for Native Image runtime compilation must not use this map as it will contain + * hosted-only types. + */ + private static final NodeClassMap GLOBAL_NODE_CLASS_MAP = new NodeClassMap(); + private final InliningLogCodec inliningLogCodec; private final OptimizationLogCodec optimizationLogCodec; @@ -187,17 +185,17 @@ public static EncodedGraph encodeSingleGraph(StructuredGraph graph, Architecture } public GraphEncoder(Architecture architecture) { - this(architecture, null); + this(architecture, null, GLOBAL_NODE_CLASS_MAP); } - public GraphEncoder(Architecture architecture, DebugContext debug) { + public GraphEncoder(Architecture architecture, DebugContext debug, NodeClassMap nodeClasses) { this.architecture = architecture; this.debug = debug; - objects = FrequencyEncoder.createEqualityEncoder(); - nodeClasses = FrequencyEncoder.createIdentityEncoder(); - writer = UnsafeArrayTypeWriter.create(architecture.supportsUnalignedMemoryAccess()); - inliningLogCodec = new InliningLogCodec(); - optimizationLogCodec = new OptimizationLogCodec(); + this.objects = FrequencyEncoder.createEqualityEncoder(); + this.nodeClasses = nodeClasses == null ? GLOBAL_NODE_CLASS_MAP : nodeClasses; + this.writer = UnsafeArrayTypeWriter.create(architecture.supportsUnalignedMemoryAccess()); + this.inliningLogCodec = new InliningLogCodec(); + this.optimizationLogCodec = new OptimizationLogCodec(); } /** @@ -210,7 +208,10 @@ public void prepare(StructuredGraph graph) { optimizationLogCodec.prepare(graph, this::addObject); for (Node node : graph.getNodes()) { NodeClass nodeClass = node.getNodeClass(); - nodeClasses.addObject(nodeClass); + + // Create encoding id for the node class + nodeClasses.getId(nodeClass); + addObject(node.getNodeSourcePosition()); for (int i = 0; i < nodeClass.getData().getCount(); i++) { if (!nodeClass.getData().getType(i).isPrimitive()) { @@ -233,15 +234,19 @@ protected void addObject(Object object) { public void finishPrepare() { objectsArray = objects.encodeAll(new Object[objects.getLength()]); - nodeClassesArray = nodeClasses.encodeAll(new NodeClass[nodeClasses.getLength()]); } public Object[] getObjects() { return objectsArray; } - public NodeClass[] getNodeClasses() { - return nodeClassesArray; + /** + * Gets the map used to assign ids to {@link NodeClass} objects encoded by this encoder. Note + * that there may be entries for {@link NodeClass} objects not encoded by this encoder as maps + * can be shared between encoders. + */ + public NodeClassMap getNodeClasses() { + return nodeClasses; } /** @@ -263,7 +268,7 @@ public int encode(StructuredGraph graph) { * @return the offset of the encoded graph */ protected int encode(StructuredGraph graph, Iterable nodeReferences) { - assert objectsArray != null && nodeClassesArray != null : "finishPrepare() must be called before encode()"; + assert objectsArray != null : "finishPrepare() must be called before encode()"; NodeOrder nodeOrder = new NodeOrder(graph); int nodeCount = nodeOrder.nextOrderId; @@ -294,14 +299,13 @@ protected int encode(StructuredGraph graph, Iterable nodeClass = node.getNodeClass(); - writer.putUV(nodeClasses.getIndex(nodeClass)); + writer.putUV(getNodeClasses().getId(nodeClass)); writeEdges(node, nodeClass.getEdges(Edges.Type.Inputs), nodeOrder); writeProperties(node, nodeClass.getData()); writeEdges(node, nodeClass.getEdges(Edges.Type.Successors), nodeOrder); /* Special handling for some nodes that require additional information for decoding. */ - if (node instanceof AbstractEndNode) { - AbstractEndNode end = (AbstractEndNode) node; + if (node instanceof AbstractEndNode end) { AbstractMergeNode merge = end.merge(); /* * Write the orderId of the merge. The merge is not a successor in the Graal graph @@ -310,8 +314,8 @@ protected int encode(StructuredGraph graph, Iterable> { + + /** + * The approximate number of instances created when building libgraal. + */ + private static final int INITIAL_CONCRETE_CAPACITY = 400; + + /** + * All non-null values are at index {@code [0 .. size - 1]}. All other values are null. + */ + private NodeClass[] values; + + private final EconomicMap, Integer> valueToId = EconomicMap.create(); + + /** + * Current number of non-null elements in {@link #values}. + */ + int size; + + public NodeClassMap() { + this.values = new NodeClass[INITIAL_CONCRETE_CAPACITY]; + } + + /** + * Gets an id for {@code nc}, creating it first if necessary. + */ + public synchronized int getId(NodeClass nc) { + Integer id = valueToId.get(nc); + if (id == null) { + if (size == values.length) { + int growth = (size + 1) >> 1; + values = Arrays.copyOf(values, size + growth); + } + id = size++; + valueToId.put(nc, id); + values[id] = nc; + } + return id; + } + + /** + * Gets the entry for {@code id}. + */ + public NodeClass get(int id) { + Objects.checkIndex(id, size); + return values[id]; + } + + /** + * Gets the number of entries in this map. + */ + public int size() { + return size; + } + + /** + * Gets an iterator view over the node classes in this map. Entries added after this method + * returns are not included in the iteration. + */ + @Override + public Iterator> iterator() { + return Arrays.asList(values).subList(0, size).iterator(); + } +} 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 9ff1158d8556..9e3f1d4689b3 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 @@ -42,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import jdk.graal.compiler.nodes.NodeClassMap; import org.graalvm.nativeimage.AnnotationAccess; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -62,7 +63,6 @@ import jdk.graal.compiler.core.common.type.TypeReference; import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.debug.DebugOptions; -import jdk.graal.compiler.graph.NodeClass; import jdk.graal.compiler.graph.NodeSourcePosition; import jdk.graal.compiler.nodes.EncodedGraph; import jdk.graal.compiler.nodes.GraphEncoder; @@ -148,7 +148,7 @@ public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod m private InvocationPlugins snippetInvocationPlugins; private byte[] snippetEncoding; private Object[] snippetObjects; - private NodeClass[] snippetNodeClasses; + private NodeClassMap snippetNodeClasses; private Map snippetStartOffsets; @Platforms(Platform.HOSTED_ONLY.class) @@ -185,7 +185,7 @@ public Collection getSnippetGraphs(boolean trackNodeSourcePosit } @Platforms(Platform.HOSTED_ONLY.class) - public NodeClass[] getSnippetNodeClasses() { + public NodeClassMap getSnippetNodeClasses() { return snippetNodeClasses; } @@ -346,7 +346,7 @@ protected void copyFrom(SubstrateReplacements copyFrom, Function snippetInvocationPlugins = makeInvocationPlugins(getGraphBuilderPlugins(), copyFrom.builder, objectReplacer); snippetEncoding = Arrays.copyOf(copyFrom.snippetEncoding, copyFrom.snippetEncoding.length); - snippetNodeClasses = Arrays.copyOf(copyFrom.snippetNodeClasses, copyFrom.snippetNodeClasses.length); + snippetNodeClasses = copyFrom.snippetNodeClasses; snippetObjects = new Object[copyFrom.snippetObjects.length]; for (int i = 0; i < snippetObjects.length; i++) { snippetObjects[i] = objectReplacer.apply(copyFrom.snippetObjects[i]); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/TruffleRuntimeCompilationSupport.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/TruffleRuntimeCompilationSupport.java index e25f41fcd0d7..5f4e2933895b 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/TruffleRuntimeCompilationSupport.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/TruffleRuntimeCompilationSupport.java @@ -30,6 +30,8 @@ import java.util.Map; import java.util.function.Function; +import com.oracle.svm.core.heap.UnknownPrimitiveField; +import jdk.graal.compiler.nodes.NodeClassMap; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -54,7 +56,6 @@ import jdk.graal.compiler.debug.DebugContext.Description; import jdk.graal.compiler.debug.DiagnosticsOutputDirectory; import jdk.graal.compiler.debug.GlobalMetrics; -import jdk.graal.compiler.graph.NodeClass; import jdk.graal.compiler.lir.phases.LIRSuites; import jdk.graal.compiler.nodes.EncodedGraph; import jdk.graal.compiler.nodes.GraphDecoder; @@ -84,13 +85,14 @@ public class TruffleRuntimeCompilationSupport { /* * The following four fields are set late in the image build process. To ensure their values are - * not prematurely constant folded we must mark them as unknown object fields. + * not prematurely constant folded we must mark them as unknown fields. */ @UnknownObjectField private SubstrateMethod[] methodsToCompile; @UnknownObjectField private byte[] graphEncoding; @UnknownObjectField private Object[] graphObjects; - @UnknownObjectField private NodeClass[] graphNodeTypes; + @UnknownObjectField private NodeClassMap graphNodeTypes; + @UnknownPrimitiveField private int graphNodeTypesSize; protected Function runtimeBackendProvider; @@ -184,11 +186,10 @@ public static Suites getMatchingSuitesForGraph(StructuredGraph graph) { } @Platforms(Platform.HOSTED_ONLY.class) - public static boolean setGraphEncoding(FeatureAccess a, byte[] graphEncoding, Object[] graphObjects, NodeClass[] graphNodeTypes) { + public static boolean setGraphEncoding(FeatureAccess a, byte[] graphEncoding, Object[] graphObjects, NodeClassMap graphNodeTypes) { TruffleRuntimeCompilationSupport support = get(); if (support.graphObjects == null && graphObjects.length == 0) { assert graphEncoding.length == 0; - assert graphNodeTypes.length == 0; return false; } boolean result = false; @@ -201,7 +202,8 @@ public static boolean setGraphEncoding(FeatureAccess a, byte[] graphEncoding, Ob TruffleRuntimeCompilationSupport.rescan(a, graphObjects); result = true; } - if (!Arrays.equals(support.graphNodeTypes, graphNodeTypes)) { + if (support.graphNodeTypesSize != graphNodeTypes.size() || support.graphNodeTypes != graphNodeTypes) { + support.graphNodeTypesSize = graphNodeTypes.size(); support.graphNodeTypes = graphNodeTypes; TruffleRuntimeCompilationSupport.rescan(a, graphNodeTypes); result = true; diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java index ba21dd662f55..990b6555af1e 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java @@ -44,6 +44,7 @@ import java.util.function.Supplier; import java.util.stream.Stream; +import jdk.graal.compiler.nodes.NodeClassMap; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; @@ -483,7 +484,7 @@ public void duringAnalysis(Feature.DuringAnalysisAccess c) { graphEncoder.finishPrepare(); AnalysisMetaAccess metaAccess = config.getMetaAccess(); - NodeClass[] nodeClasses = graphEncoder.getNodeClasses(); + NodeClassMap nodeClasses = graphEncoder.getNodeClasses(); for (NodeClass nodeClass : nodeClasses) { metaAccess.lookupJavaType(nodeClass.getClazz()).registerAsInstantiated("All " + NodeClass.class.getName() + " classes are marked as instantiated eagerly."); } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java index f7d6983baf61..fa6ee71f17c8 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java @@ -35,6 +35,7 @@ import java.util.function.Function; import java.util.stream.Stream; +import jdk.graal.compiler.nodes.NodeClassMap; import org.graalvm.collections.EconomicMap; import org.graalvm.word.LocationIdentity; @@ -377,6 +378,7 @@ public JavaConstant readFieldValue(ResolvedJavaField field, JavaConstant receive */ @SuppressWarnings("javadoc") public static class RuntimeCompilationGraphEncoder extends GraphEncoder { + public static final NodeClassMap RUNTIME_NODE_CLASS_MAP = new NodeClassMap(); private final ImageHeapScanner heapScanner; /** @@ -386,7 +388,7 @@ public static class RuntimeCompilationGraphEncoder extends GraphEncoder { private final Map locationIdentityCache; public RuntimeCompilationGraphEncoder(Architecture architecture, ImageHeapScanner heapScanner) { - super(architecture); + super(architecture, null, RUNTIME_NODE_CLASS_MAP); this.heapScanner = heapScanner; this.locationIdentityCache = new ConcurrentHashMap<>(); } From 90cf54acbe4324f955395aa299bea55ccaf4a0bd Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Thu, 17 Apr 2025 10:46:22 +0200 Subject: [PATCH 09/12] add cache to avoid synchronization --- .../jdk/graal/compiler/graph/NodeClass.java | 34 +++++++++++++++++++ .../graal/compiler/nodes/NodeClassMap.java | 32 ++++++++++++----- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeClass.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeClass.java index db8bbe8a108f..c16b702e0825 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeClass.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeClass.java @@ -44,6 +44,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; +import jdk.graal.compiler.nodes.NodeClassMap; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.Equivalence; @@ -1487,4 +1488,37 @@ private static void unregisterAtInputsAsUsageHelperMany(Node node, NodeList[INITIAL_CONCRETE_CAPACITY]; } + /** + * An object that is a proxy for this {@link NodeClassMap} in a cache. Using a proxy prevents a + * reference from a {@link NodeClass} to all other entries in a {@link NodeClassMap}. This is + * important in the context of Native Image as some {@link NodeClassMap}s can contain + * hosted-only node classes. + */ + private final Object cacheToken = new Object(); + /** * Gets an id for {@code nc}, creating it first if necessary. */ - public synchronized int getId(NodeClass nc) { - Integer id = valueToId.get(nc); + public int getId(NodeClass nc) { + // Using a cache entry in `nc` mostly avoids going + // into the synchronized block below. + Integer id = nc.getCachedId(cacheToken); if (id == null) { - if (size == values.length) { - int growth = (size + 1) >> 1; - values = Arrays.copyOf(values, size + growth); + synchronized (this) { + id = valueToId.get(nc); + if (id == null) { + if (size == values.length) { + int growth = (size + 1) >> 1; + values = Arrays.copyOf(values, size + growth); + } + id = size++; + valueToId.put(nc, id); + values[id] = nc; + } } - id = size++; - valueToId.put(nc, id); - values[id] = nc; + nc.setCachedId(cacheToken, id); } return id; } From 351aafce8a45e6e4cb4516c2f5cf8b7c6d92a5bb Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Fri, 25 Apr 2025 18:01:07 +0200 Subject: [PATCH 10/12] Persist and reload the location for the GLOBAL_NODE_CLASS_MAP --- .../graal/compiler/nodes/GraphEncoder.java | 2 + .../SharedLayerSnapshotCapnProtoSchema.capnp | 1 + .../svm/hosted/NativeImageGenerator.java | 3 + .../imagelayer/SVMImageLayerLoader.java | 17 +++-- .../imagelayer/SVMImageLayerSnapshotUtil.java | 62 ++++++++++++++++--- .../imagelayer/SVMImageLayerWriter.java | 9 ++- ...redLayerSnapshotCapnProtoSchemaHolder.java | 24 ++++++- 7 files changed, 103 insertions(+), 15 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java index dcd1ac77e056..b40f71009a2f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java @@ -49,6 +49,7 @@ import jdk.graal.compiler.graph.iterators.NodeIterable; import jdk.graal.compiler.nodes.java.ExceptionObjectNode; import jdk.graal.compiler.replacements.nodes.MethodHandleWithExceptionNode; +import jdk.graal.compiler.util.ObjectCopier; import jdk.vm.ci.code.Architecture; /** @@ -160,6 +161,7 @@ public class GraphEncoder { * graphs for Native Image runtime compilation must not use this map as it will contain * hosted-only types. */ + @ObjectCopier.NotExternalValue(reason = "Needs to be persisted separately") private static final NodeClassMap GLOBAL_NODE_CLASS_MAP = new NodeClassMap(); private final InliningLogCodec inliningLogCodec; diff --git a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp index de5e607d35f7..623cb74a6e2d 100644 --- a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp +++ b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp @@ -277,6 +277,7 @@ struct SharedLayerSnapshot { layeredRuntimeMetadataSingleton @17 :LayeredRuntimeMetadataSingleton; dynamicHubInfos @18 :List(DynamicHubInfo); hostedMethods @19 :List(PersistedHostedMethod); + globalNodeClassMapLocation @20 :Text; } struct StaticFinalFieldFoldingSingleton { 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 71e8b882582e..e0933e76015a 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 @@ -1040,6 +1040,9 @@ protected void setupNativeImage(String imageName, OptionValues options, Map getRelinkedFields(AnalysisType type, Set type return typeRelinkedFieldsSet.stream().map(metaAccess::lookupJavaField).map(AnalysisField::getPosition).collect(Collectors.toSet()); } - public SVMGraphEncoder getGraphEncoder() { - return new SVMGraphEncoder(externalValues); + public SVMGraphEncoder getGraphEncoder(boolean graph) { + return new SVMGraphEncoder(externalValues, graph); } public AbstractSVMGraphDecoder getGraphHostedToAnalysisElementsDecoder(SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider) { return new SVMGraphHostedToAnalysisElementsDecoder(EncodedGraph.class.getClassLoader(), imageLayerLoader, analysisMethod, snippetReflectionProvider); } - public AbstractSVMGraphDecoder getGraphDecoder(SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider) { - return new SVMGraphDecoder(EncodedGraph.class.getClassLoader(), imageLayerLoader, analysisMethod, snippetReflectionProvider); + public AbstractSVMGraphDecoder getGraphDecoder(SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider, boolean graph, NodeClassMap globalNodeClassMap) { + return new SVMGraphDecoder(EncodedGraph.class.getClassLoader(), imageLayerLoader, analysisMethod, snippetReflectionProvider, graph, globalNodeClassMap); } /** @@ -344,8 +345,10 @@ public static void forcePersistConstant(ImageHeapConstant imageHeapConstant) { } public static class SVMGraphEncoder extends ObjectCopier.Encoder { + public static final GlobalNodeClassMapBuiltin globalNodeClassMapBuiltin = new GlobalNodeClassMapBuiltin(null); + @SuppressWarnings("this-escape") - public SVMGraphEncoder(Map externalValues) { + public SVMGraphEncoder(Map externalValues, boolean graph) { super(externalValues); addBuiltin(new ImageHeapConstantBuiltIn(null)); addBuiltin(new AnalysisTypeBuiltIn(null)); @@ -359,6 +362,9 @@ public SVMGraphEncoder(Map externalValues) { addBuiltin(new CInterfaceLocationIdentityBuiltIn()); addBuiltin(new FastThreadLocalLocationIdentityBuiltIn()); addBuiltin(new VMThreadLocalInfoBuiltIn()); + if (graph) { + addBuiltin(globalNodeClassMapBuiltin); + } } @Override @@ -377,7 +383,7 @@ public abstract static class AbstractSVMGraphDecoder extends ObjectCopier.Decode private final HostedImageLayerBuildingSupport imageLayerBuildingSupport; @SuppressWarnings("this-escape") - public AbstractSVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider) { + public AbstractSVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider, boolean graph, NodeClassMap globalMap) { super(classLoader); this.imageLayerBuildingSupport = imageLayerLoader.getImageLayerBuildingSupport(); addBuiltin(new ImageHeapConstantBuiltIn(imageLayerLoader)); @@ -390,6 +396,10 @@ public AbstractSVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader imag addBuiltin(new CInterfaceLocationIdentityBuiltIn()); addBuiltin(new FastThreadLocalLocationIdentityBuiltIn()); addBuiltin(new VMThreadLocalInfoBuiltIn()); + // TODO: Read serialized NodeClassMap somehow and pass to constructor below + if (graph) { + addBuiltin(new GlobalNodeClassMapBuiltin(globalMap)); + } } @Override @@ -402,7 +412,7 @@ public static class SVMGraphHostedToAnalysisElementsDecoder extends AbstractSVMG @SuppressWarnings("this-escape") public SVMGraphHostedToAnalysisElementsDecoder(ClassLoader classLoader, SVMImageLayerLoader svmImageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider) { - super(classLoader, svmImageLayerLoader, analysisMethod, snippetReflectionProvider); + super(classLoader, svmImageLayerLoader, analysisMethod, snippetReflectionProvider, true, null); addBuiltin(new HostedToAnalysisTypeDecoderBuiltIn(svmImageLayerLoader)); addBuiltin(new HostedToAnalysisMethodDecoderBuiltIn(svmImageLayerLoader)); } @@ -410,13 +420,47 @@ public SVMGraphHostedToAnalysisElementsDecoder(ClassLoader classLoader, SVMImage public static class SVMGraphDecoder extends AbstractSVMGraphDecoder { @SuppressWarnings("this-escape") - public SVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader svmImageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider) { - super(classLoader, svmImageLayerLoader, analysisMethod, snippetReflectionProvider); + public SVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader svmImageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider, boolean graph, NodeClassMap globalMap) { + super(classLoader, svmImageLayerLoader, analysisMethod, snippetReflectionProvider, graph, globalMap); addBuiltin(new HostedTypeBuiltIn(svmImageLayerLoader)); addBuiltin(new HostedMethodBuiltIn(svmImageLayerLoader)); } } + public static class GlobalNodeClassMapBuiltin extends ObjectCopier.Builtin { + private NodeClassMap globalMap; + + protected GlobalNodeClassMapBuiltin(NodeClassMap map) { + super(NodeClassMap.class); + this.globalMap = map; + } + + @Override + public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { + if (globalMap == null) { + globalMap = (NodeClassMap) obj; + } else if (globalMap != obj) { + throw AnalysisError.shouldNotReachHere("More than one NodeClassMap instance encountered"); + } + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { + if (globalMap == null) { + throw AnalysisError.shouldNotReachHere("Global NodeClassMap not set"); + } + return globalMap; + } + + public NodeClassMap getGlobalMap() { + return globalMap; + } + + public void setGlobalMap(NodeClassMap globalMap) { + this.globalMap = globalMap; + } + } + public static class ImageHeapConstantBuiltIn extends ObjectCopier.Builtin { private final SVMImageLayerLoader imageLayerLoader; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java index 713e8b576468..61c20bcd4d01 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java @@ -169,6 +169,7 @@ import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.java.LambdaUtils; import jdk.graal.compiler.nodes.EncodedGraph; +import jdk.graal.compiler.nodes.NodeClassMap; import jdk.graal.compiler.nodes.spi.IdentityHashCodeProvider; import jdk.graal.compiler.util.ObjectCopier; import jdk.vm.ci.meta.JavaConstant; @@ -297,6 +298,12 @@ public void openGraphsOutput(Path layerGraphsPath, String fileName, String suffi } public void dumpFiles() { + SVMImageLayerSnapshotUtil.SVMGraphEncoder graphEncoder = imageLayerSnapshotUtil.getGraphEncoder(false); + NodeClassMap globalMap = SVMImageLayerSnapshotUtil.SVMGraphEncoder.globalNodeClassMapBuiltin.getGlobalMap(); + byte[] encodedGlobalMap = ObjectCopier.encode(graphEncoder, globalMap); + String location = graphsOutput.add(encodedGlobalMap); + snapshotBuilder.setGlobalNodeClassMapLocation(location); + graphsOutput.finish(); FileDumpingUtil.dumpFile(fileInfo.layerFilePath, fileInfo.fileName, fileInfo.suffix, outputStream -> { @@ -1062,7 +1069,7 @@ private String persistGraph(AnalysisMethod method, EncodedGraph analyzedGraph) { */ return null; } - byte[] encodedGraph = ObjectCopier.encode(imageLayerSnapshotUtil.getGraphEncoder(), analyzedGraph); + byte[] encodedGraph = ObjectCopier.encode(imageLayerSnapshotUtil.getGraphEncoder(true), analyzedGraph); if (contains(encodedGraph, LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING.getBytes(StandardCharsets.UTF_8))) { throw AnalysisError.shouldNotReachHere("The graph for the method %s contains a reference to a lambda type, which cannot be decoded: %s".formatted(method, encodedGraph)); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java index bd90b41b76f4..7dceb22e4fc6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java @@ -4596,7 +4596,7 @@ public final com.oracle.svm.shaded.org.capnproto.StructList.Reader { public Factory() { } @@ -4817,6 +4817,21 @@ public final void setHostedMethods(com.oracle.svm.shaded.org.capnproto.StructLis public final com.oracle.svm.shaded.org.capnproto.StructList.Builder initHostedMethods(int size) { return _initPointerField(com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.PersistedHostedMethod.listFactory, 11, size); } + public final boolean hasGlobalNodeClassMapLocation() { + return !_pointerFieldIsNull(12); + } + public final com.oracle.svm.shaded.org.capnproto.Text.Builder getGlobalNodeClassMapLocation() { + return _getPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 12, null, 0, 0); + } + public final void setGlobalNodeClassMapLocation(com.oracle.svm.shaded.org.capnproto.Text.Reader value) { + _setPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 12, value); + } + public final void setGlobalNodeClassMapLocation(String value) { + _setPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 12, new com.oracle.svm.shaded.org.capnproto.Text.Reader(value)); + } + public final com.oracle.svm.shaded.org.capnproto.Text.Builder initGlobalNodeClassMapLocation(int size) { + return _initPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 12, size); + } } public static final class Reader extends com.oracle.svm.shaded.org.capnproto.StructReader { @@ -4940,6 +4955,13 @@ public final com.oracle.svm.shaded.org.capnproto.StructList.Reader Date: Mon, 28 Apr 2025 20:22:12 +0200 Subject: [PATCH 11/12] tidy up handling of NodeClassMap for layered images --- .../graal/compiler/nodes/GraphEncoder.java | 4 +- .../SharedLayerSnapshotCapnProtoSchema.capnp | 2 +- .../svm/hosted/NativeImageGenerator.java | 2 +- .../imagelayer/SVMImageLayerLoader.java | 25 +++++-- .../imagelayer/SVMImageLayerSnapshotUtil.java | 73 +++++++++---------- .../imagelayer/SVMImageLayerWriter.java | 18 +++-- ...redLayerSnapshotCapnProtoSchemaHolder.java | 14 ++-- 7 files changed, 74 insertions(+), 64 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java index b40f71009a2f..888adc95464b 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/GraphEncoder.java @@ -161,8 +161,8 @@ public class GraphEncoder { * graphs for Native Image runtime compilation must not use this map as it will contain * hosted-only types. */ - @ObjectCopier.NotExternalValue(reason = "Needs to be persisted separately") - private static final NodeClassMap GLOBAL_NODE_CLASS_MAP = new NodeClassMap(); + @ObjectCopier.NotExternalValue(reason = "Needs to be persisted separately") // + public static final NodeClassMap GLOBAL_NODE_CLASS_MAP = new NodeClassMap(); private final InliningLogCodec inliningLogCodec; diff --git a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp index 623cb74a6e2d..b224e0851024 100644 --- a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp +++ b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp @@ -277,7 +277,7 @@ struct SharedLayerSnapshot { layeredRuntimeMetadataSingleton @17 :LayeredRuntimeMetadataSingleton; dynamicHubInfos @18 :List(DynamicHubInfo); hostedMethods @19 :List(PersistedHostedMethod); - globalNodeClassMapLocation @20 :Text; + nodeClassMapLocation @20 :Text; } struct StaticFinalFieldFoldingSingleton { 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 e0933e76015a..82ccffe0ac1f 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 @@ -1041,7 +1041,7 @@ protected void setupNativeImage(String imageName, OptionValues options, Map getRelinkedFields(AnalysisType type, Set type return typeRelinkedFieldsSet.stream().map(metaAccess::lookupJavaField).map(AnalysisField::getPosition).collect(Collectors.toSet()); } - public SVMGraphEncoder getGraphEncoder(boolean graph) { - return new SVMGraphEncoder(externalValues, graph); + public SVMGraphEncoder getGraphEncoder(NodeClassMap nodeClassMap) { + return new SVMGraphEncoder(externalValues, nodeClassMap); } - public AbstractSVMGraphDecoder getGraphHostedToAnalysisElementsDecoder(SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider) { - return new SVMGraphHostedToAnalysisElementsDecoder(EncodedGraph.class.getClassLoader(), imageLayerLoader, analysisMethod, snippetReflectionProvider); + public AbstractSVMGraphDecoder getGraphHostedToAnalysisElementsDecoder(SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider, + NodeClassMap nodeClassMap) { + + return new SVMGraphHostedToAnalysisElementsDecoder(EncodedGraph.class.getClassLoader(), imageLayerLoader, analysisMethod, snippetReflectionProvider, nodeClassMap); } - public AbstractSVMGraphDecoder getGraphDecoder(SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider, boolean graph, NodeClassMap globalNodeClassMap) { - return new SVMGraphDecoder(EncodedGraph.class.getClassLoader(), imageLayerLoader, analysisMethod, snippetReflectionProvider, graph, globalNodeClassMap); + public AbstractSVMGraphDecoder getGraphDecoder(SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, + SnippetReflectionProvider snippetReflectionProvider, NodeClassMap nodeClassMap) { + return new SVMGraphDecoder(EncodedGraph.class.getClassLoader(), imageLayerLoader, analysisMethod, snippetReflectionProvider, nodeClassMap); } /** @@ -345,10 +349,8 @@ public static void forcePersistConstant(ImageHeapConstant imageHeapConstant) { } public static class SVMGraphEncoder extends ObjectCopier.Encoder { - public static final GlobalNodeClassMapBuiltin globalNodeClassMapBuiltin = new GlobalNodeClassMapBuiltin(null); - @SuppressWarnings("this-escape") - public SVMGraphEncoder(Map externalValues, boolean graph) { + public SVMGraphEncoder(Map externalValues, NodeClassMap nodeClassMap) { super(externalValues); addBuiltin(new ImageHeapConstantBuiltIn(null)); addBuiltin(new AnalysisTypeBuiltIn(null)); @@ -362,8 +364,8 @@ public SVMGraphEncoder(Map externalValues, boolean graph) { addBuiltin(new CInterfaceLocationIdentityBuiltIn()); addBuiltin(new FastThreadLocalLocationIdentityBuiltIn()); addBuiltin(new VMThreadLocalInfoBuiltIn()); - if (graph) { - addBuiltin(globalNodeClassMapBuiltin); + if (nodeClassMap != null) { + addBuiltin(new NodeClassMapBuiltin(nodeClassMap)); } } @@ -383,7 +385,8 @@ public abstract static class AbstractSVMGraphDecoder extends ObjectCopier.Decode private final HostedImageLayerBuildingSupport imageLayerBuildingSupport; @SuppressWarnings("this-escape") - public AbstractSVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider, boolean graph, NodeClassMap globalMap) { + public AbstractSVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider, + NodeClassMap nodeClassMap) { super(classLoader); this.imageLayerBuildingSupport = imageLayerLoader.getImageLayerBuildingSupport(); addBuiltin(new ImageHeapConstantBuiltIn(imageLayerLoader)); @@ -396,9 +399,8 @@ public AbstractSVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader imag addBuiltin(new CInterfaceLocationIdentityBuiltIn()); addBuiltin(new FastThreadLocalLocationIdentityBuiltIn()); addBuiltin(new VMThreadLocalInfoBuiltIn()); - // TODO: Read serialized NodeClassMap somehow and pass to constructor below - if (graph) { - addBuiltin(new GlobalNodeClassMapBuiltin(globalMap)); + if (nodeClassMap != null) { + addBuiltin(new NodeClassMapBuiltin(nodeClassMap)); } } @@ -411,8 +413,8 @@ public Class loadClass(String className) { public static class SVMGraphHostedToAnalysisElementsDecoder extends AbstractSVMGraphDecoder { @SuppressWarnings("this-escape") public SVMGraphHostedToAnalysisElementsDecoder(ClassLoader classLoader, SVMImageLayerLoader svmImageLayerLoader, AnalysisMethod analysisMethod, - SnippetReflectionProvider snippetReflectionProvider) { - super(classLoader, svmImageLayerLoader, analysisMethod, snippetReflectionProvider, true, null); + SnippetReflectionProvider snippetReflectionProvider, NodeClassMap nodeClassMap) { + super(classLoader, svmImageLayerLoader, analysisMethod, snippetReflectionProvider, nodeClassMap); addBuiltin(new HostedToAnalysisTypeDecoderBuiltIn(svmImageLayerLoader)); addBuiltin(new HostedToAnalysisMethodDecoderBuiltIn(svmImageLayerLoader)); } @@ -420,44 +422,37 @@ public SVMGraphHostedToAnalysisElementsDecoder(ClassLoader classLoader, SVMImage public static class SVMGraphDecoder extends AbstractSVMGraphDecoder { @SuppressWarnings("this-escape") - public SVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader svmImageLayerLoader, AnalysisMethod analysisMethod, SnippetReflectionProvider snippetReflectionProvider, boolean graph, NodeClassMap globalMap) { - super(classLoader, svmImageLayerLoader, analysisMethod, snippetReflectionProvider, graph, globalMap); + public SVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader svmImageLayerLoader, AnalysisMethod analysisMethod, + SnippetReflectionProvider snippetReflectionProvider, NodeClassMap nodeClassMap) { + super(classLoader, svmImageLayerLoader, analysisMethod, snippetReflectionProvider, nodeClassMap); addBuiltin(new HostedTypeBuiltIn(svmImageLayerLoader)); addBuiltin(new HostedMethodBuiltIn(svmImageLayerLoader)); } } - public static class GlobalNodeClassMapBuiltin extends ObjectCopier.Builtin { - private NodeClassMap globalMap; + /** + * Builtin to replace a {@link NodeClassMap} during encoding with a placeholder so that a single + * map will be shared by all {@link EncodedGraph}s processed by a + * {@link jdk.graal.compiler.util.ObjectCopier.Encoder}. + */ + public static class NodeClassMapBuiltin extends ObjectCopier.Builtin { + private final NodeClassMap nodeClassMap; - protected GlobalNodeClassMapBuiltin(NodeClassMap map) { + protected NodeClassMapBuiltin(NodeClassMap nodeClassMap) { super(NodeClassMap.class); - this.globalMap = map; + this.nodeClassMap = Objects.requireNonNull(nodeClassMap); } @Override public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException { - if (globalMap == null) { - globalMap = (NodeClassMap) obj; - } else if (globalMap != obj) { - throw AnalysisError.shouldNotReachHere("More than one NodeClassMap instance encountered"); + if (nodeClassMap != obj) { + throw AnalysisError.shouldNotReachHere("Unexpected NodeClassMap instance encountered"); } } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, ObjectCopierInputStream stream) throws IOException { - if (globalMap == null) { - throw AnalysisError.shouldNotReachHere("Global NodeClassMap not set"); - } - return globalMap; - } - - public NodeClassMap getGlobalMap() { - return globalMap; - } - - public void setGlobalMap(NodeClassMap globalMap) { - this.globalMap = globalMap; + return nodeClassMap; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java index 61c20bcd4d01..ccca4e45bf69 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java @@ -67,6 +67,8 @@ import java.util.stream.IntStream; import java.util.stream.Stream; +import jdk.graal.compiler.graph.NodeClass; +import jdk.graal.compiler.nodes.GraphEncoder; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; import org.graalvm.nativeimage.AnnotationAccess; @@ -202,6 +204,11 @@ public class SVMImageLayerWriter extends ImageLayerWriter { private boolean polymorphicSignatureSealed = false; + /** + * Used to encode {@link NodeClass} ids in {@link #persistGraph}. + */ + private final NodeClassMap nodeClassMap = GraphEncoder.GLOBAL_NODE_CLASS_MAP; + private record ConstantParent(int constantId, int index) { static ConstantParent NONE = new ConstantParent(UNDEFINED_CONSTANT_ID, UNDEFINED_FIELD_INDEX); } @@ -298,11 +305,10 @@ public void openGraphsOutput(Path layerGraphsPath, String fileName, String suffi } public void dumpFiles() { - SVMImageLayerSnapshotUtil.SVMGraphEncoder graphEncoder = imageLayerSnapshotUtil.getGraphEncoder(false); - NodeClassMap globalMap = SVMImageLayerSnapshotUtil.SVMGraphEncoder.globalNodeClassMapBuiltin.getGlobalMap(); - byte[] encodedGlobalMap = ObjectCopier.encode(graphEncoder, globalMap); - String location = graphsOutput.add(encodedGlobalMap); - snapshotBuilder.setGlobalNodeClassMapLocation(location); + SVMImageLayerSnapshotUtil.SVMGraphEncoder graphEncoder = imageLayerSnapshotUtil.getGraphEncoder(null); + byte[] encodedNodeClassMap = ObjectCopier.encode(graphEncoder, nodeClassMap); + String location = graphsOutput.add(encodedNodeClassMap); + snapshotBuilder.setNodeClassMapLocation(location); graphsOutput.finish(); @@ -1069,7 +1075,7 @@ private String persistGraph(AnalysisMethod method, EncodedGraph analyzedGraph) { */ return null; } - byte[] encodedGraph = ObjectCopier.encode(imageLayerSnapshotUtil.getGraphEncoder(true), analyzedGraph); + byte[] encodedGraph = ObjectCopier.encode(imageLayerSnapshotUtil.getGraphEncoder(nodeClassMap), analyzedGraph); if (contains(encodedGraph, LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING.getBytes(StandardCharsets.UTF_8))) { throw AnalysisError.shouldNotReachHere("The graph for the method %s contains a reference to a lambda type, which cannot be decoded: %s".formatted(method, encodedGraph)); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java index 7dceb22e4fc6..7b68797756ad 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java @@ -4817,19 +4817,19 @@ public final void setHostedMethods(com.oracle.svm.shaded.org.capnproto.StructLis public final com.oracle.svm.shaded.org.capnproto.StructList.Builder initHostedMethods(int size) { return _initPointerField(com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.PersistedHostedMethod.listFactory, 11, size); } - public final boolean hasGlobalNodeClassMapLocation() { + public final boolean hasNodeClassMapLocation() { return !_pointerFieldIsNull(12); } - public final com.oracle.svm.shaded.org.capnproto.Text.Builder getGlobalNodeClassMapLocation() { + public final com.oracle.svm.shaded.org.capnproto.Text.Builder getNodeClassMapLocation() { return _getPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 12, null, 0, 0); } - public final void setGlobalNodeClassMapLocation(com.oracle.svm.shaded.org.capnproto.Text.Reader value) { + public final void setNodeClassMapLocation(com.oracle.svm.shaded.org.capnproto.Text.Reader value) { _setPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 12, value); } - public final void setGlobalNodeClassMapLocation(String value) { + public final void setNodeClassMapLocation(String value) { _setPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 12, new com.oracle.svm.shaded.org.capnproto.Text.Reader(value)); } - public final com.oracle.svm.shaded.org.capnproto.Text.Builder initGlobalNodeClassMapLocation(int size) { + public final com.oracle.svm.shaded.org.capnproto.Text.Builder initNodeClassMapLocation(int size) { return _initPointerField(com.oracle.svm.shaded.org.capnproto.Text.factory, 12, size); } } @@ -4955,10 +4955,10 @@ public final com.oracle.svm.shaded.org.capnproto.StructList.Reader Date: Mon, 28 Apr 2025 22:43:39 +0200 Subject: [PATCH 12/12] remove code redundant with use of @NotExternalValue annotation --- .../src/jdk/graal/compiler/hotspot/CompilerConfig.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/CompilerConfig.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/CompilerConfig.java index bea5d76c6f58..ffef728cb93f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/CompilerConfig.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/CompilerConfig.java @@ -34,7 +34,6 @@ import java.util.List; import java.util.Locale; -import jdk.graal.compiler.nodes.GraphEncoder; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; @@ -82,11 +81,6 @@ public static void main(String[] args) throws Exception { EncodedSnippets encodedSnippets = getEncodedSnippets(replacements, options); List externalValueFields = ObjectCopier.getExternalValueFields(); - // The NodeClassMap value read from GraphEncoder.GLOBAL_NODE_CLASS_MAP - // must not be treated as an external value when being serialized - // across processes. - externalValueFields.remove(ObjectCopier.getField(GraphEncoder.class, "GLOBAL_NODE_CLASS_MAP")); - EconomicMap encodedObjects = EconomicMap.create(); encodedObjects.put("encodedSnippets", encodedSnippets); encodedObjects.put("snippetNodeClasses", snippetNodeClassesToJSON(encodedSnippets));