Skip to content

Commit f86bce8

Browse files
committed
[GR-64069] Graal memory usage improvements.
PullRequest: graal/20538
2 parents c16f70e + 39c96b9 commit f86bce8

File tree

24 files changed

+485
-152
lines changed

24 files changed

+485
-152
lines changed

compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import java.lang.reflect.Constructor;
3232
import java.lang.reflect.Field;
3333
import java.lang.reflect.Method;
34-
import java.lang.reflect.Modifier;
3534
import java.nio.file.Path;
3635
import java.util.ArrayList;
3736
import java.util.Collections;
@@ -199,7 +198,7 @@ public void duringSetup(DuringSetupAccess access) {
199198
// (see jdk.graal.compiler.graph.NodeClass.allocateInstance).
200199
access.registerObjectReachabilityHandler(nodeClass -> {
201200
Class<?> clazz = nodeClass.getClazz();
202-
if (!Modifier.isAbstract(clazz.getModifiers())) {
201+
if (!nodeClass.isAbstract()) {
203202
/* Support for NodeClass.allocateInstance. */
204203
beforeAnalysisAccess.registerAsUnsafeAllocated(clazz);
205204
}
@@ -375,9 +374,12 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
375374
EncodedSnippets encodedSnippets = (EncodedSnippets) libgraalObjects.get("encodedSnippets");
376375
checkNodeClasses(encodedSnippets, (String) libgraalObjects.get("snippetNodeClasses"));
377376

378-
// Mark all the Node classes as allocated so they are available during graph decoding.
377+
// Mark all non-abstract Node classes as allocated so they
378+
// are available during graph decoding.
379379
for (NodeClass<?> nodeClass : encodedSnippets.getSnippetNodeClasses()) {
380-
access.registerAsInHeap(nodeClass.getClazz());
380+
if (!nodeClass.isAbstract()) {
381+
access.registerAsInHeap(nodeClass.getClazz());
382+
}
381383
}
382384
HotSpotReplacementsImpl.setEncodedSnippets(encodedSnippets);
383385

@@ -389,7 +391,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
389391

390392
private static void checkNodeClasses(EncodedSnippets encodedSnippets, String actual) {
391393
String expect = CompilerConfig.snippetNodeClassesToJSON(encodedSnippets);
392-
GraalError.guarantee(actual.equals(expect), "%s != %s", actual, expect);
394+
GraalError.guarantee(actual.equals(expect), "%n%s%n !=%n%s", actual, expect);
393395
}
394396

395397
/**

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/SubprocessTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public static SubprocessUtil.Subprocess launchSubprocess(Predicate<List<String>>
126126
assert testSelector != null : "must pass the name of the current unit test";
127127
String testName = testSelector.equals(ALL_TESTS) ? testClass.getName() : testClass.getName() + "#" + testSelector;
128128
mainClassAndArgs.add(testName);
129-
boolean junitVerbose = getProcessCommandLine().contains("-JUnitVerbose");
129+
boolean junitVerbose = String.valueOf(getProcessCommandLine()).contains("-JUnitVerbose");
130130
if (junitVerbose) {
131131
mainClassAndArgs.add("-JUnitVerbose");
132132
}

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/CompilationWrapperTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import static jdk.graal.compiler.debug.StandardPathUtilitiesProvider.DIAGNOSTIC_OUTPUT_DIRECTORY_MESSAGE_FORMAT;
2828
import static jdk.graal.compiler.debug.StandardPathUtilitiesProvider.DIAGNOSTIC_OUTPUT_DIRECTORY_MESSAGE_REGEXP;
29+
import static jdk.graal.compiler.test.SubprocessUtil.getProcessCommandLine;
2930
import static jdk.graal.compiler.test.SubprocessUtil.getVMCommandLine;
3031
import static jdk.graal.compiler.test.SubprocessUtil.withoutDebuggerArguments;
3132

@@ -232,7 +233,7 @@ public void testTruffleCompilation2() throws IOException, InterruptedException {
232233
SLCompileASTTestSuite.class.getName(), "test");
233234
}
234235

235-
private static final boolean VERBOSE = Boolean.getBoolean("CompilationWrapperTest.verbose");
236+
private static final boolean VERBOSE = Boolean.getBoolean("CompilationWrapperTest.verbose") || String.valueOf(getProcessCommandLine()).contains("-JUnitVerbose");
236237

237238
public static void testHelper(List<Probe> initialProbes,
238239
List<String> extraVmArgs,

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java

+31-6
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ static class TestObject extends BaseClass {
8989
int intField3 = Integer.MIN_VALUE;
9090
int intField4 = Integer.MAX_VALUE;
9191
int intField5 = -1;
92+
long longField1 = 0;
93+
long longField2 = 42L;
94+
long longField3 = Long.MIN_VALUE;
95+
long longField4 = Long.MAX_VALUE;
96+
long longField5 = -1L;
9297
float floatField1 = -1.4F;
9398
float floatField2 = Float.MIN_NORMAL;
9499
float floatField3 = Float.MIN_VALUE;
@@ -120,13 +125,18 @@ public String toString() {
120125
}
121126
};
122127

123-
private static List<Object> fieldValues(Object obj) {
124-
List<Object> values = new ArrayList<>();
128+
@ObjectCopier.NotExternalValue(reason = "testing NotExternalValue annotation") //
129+
static final String[] TEST_OBJECT_COPIABLE = {
130+
"this", "value", "should", "be", "copied"
131+
};
132+
133+
private static Map<Field, Object> fieldValues(Object obj) {
134+
Map<Field, Object> values = new HashMap<>();
125135
Class<?> c = obj.getClass();
126136
while (c != Object.class) {
127137
for (Field f : c.getDeclaredFields()) {
128138
try {
129-
values.add(f.get(obj));
139+
values.put(f, f.get(obj));
130140
} catch (IllegalAccessException e) {
131141
throw new AssertionError(e);
132142
}
@@ -138,7 +148,7 @@ private static List<Object> fieldValues(Object obj) {
138148

139149
@Override
140150
public String toString() {
141-
return fieldValues(this).stream().map(String::valueOf).collect(Collectors.joining(", "));
151+
return fieldValues(this).values().stream().map(String::valueOf).collect(Collectors.joining(", "));
142152
}
143153
}
144154

@@ -150,10 +160,16 @@ public void testIt() {
150160
ClassLoader loader = getClass().getClassLoader();
151161
TestObject testObject = new TestObject();
152162

163+
EconomicMap<String, Object> fieldMap = EconomicMap.create();
164+
for (var e : TestObject.fieldValues(testObject).entrySet()) {
165+
fieldMap.put(e.getKey().getName(), e.getValue());
166+
}
167+
153168
List<TimeUnit> timeUnits = List.of(TimeUnit.MICROSECONDS, TimeUnit.DAYS, TimeUnit.SECONDS);
154169
EconomicMap<Integer, Object> emap = EconomicMap.create();
155170
emap.put(42, Map.of("1", 1, "2", 2));
156171
emap.put(-12345, testObject);
172+
emap.put(-6789, fieldMap);
157173

158174
Map<String, String> hmap = new HashMap<>(Map.of("1000", "one thousand"));
159175
Map<Object, String> idMap = new IdentityHashMap<>(Map.of(new Object(), "some obj"));
@@ -172,9 +188,13 @@ public void testIt() {
172188
root.put("6", new ArrayList<>(timeUnits));
173189
root.put("singleton2", TestObject.TEST_OBJECT_SINGLETON);
174190
root.put("singleton2_2", TestObject.TEST_OBJECT_SINGLETON);
191+
root.put("copiable", TestObject.TEST_OBJECT_COPIABLE);
192+
193+
List<Field> externalValueFields = new ArrayList<>();
194+
externalValueFields.addAll(ObjectCopier.getStaticFinalObjectFields(BaseClass.class));
195+
externalValueFields.addAll(ObjectCopier.getStaticFinalObjectFields(TestObject.class));
175196

176-
List<Field> externalValueFields = List.of(ObjectCopier.getField(BaseClass.class, "BASE_SINGLETON"),
177-
ObjectCopier.getField(TestObject.class, "TEST_OBJECT_SINGLETON"));
197+
Assert.assertFalse(externalValueFields.contains(ObjectCopier.getField(TestObject.class, "TEST_OBJECT_COPIABLE")));
178198

179199
byte[] encoded = encode(externalValueFields, root, "encoded");
180200
Object decoded = ObjectCopier.decode(encoded, loader);
@@ -187,13 +207,18 @@ public void testIt() {
187207

188208
Map<String, Object> root2 = (Map<String, Object>) ObjectCopier.decode(reencoded, loader);
189209

210+
Assert.assertSame(BaseClass.BASE_SINGLETON, root2.get("singleton1"));
190211
Assert.assertSame(root.get("singleton1"), root2.get("singleton1"));
191212
Assert.assertSame(root.get("singleton1_2"), root2.get("singleton1_2"));
192213
Assert.assertSame(root2.get("singleton1"), root2.get("singleton1_2"));
193214

215+
Assert.assertSame(TestObject.TEST_OBJECT_SINGLETON, root.get("singleton2"));
194216
Assert.assertSame(root.get("singleton2"), root2.get("singleton2"));
195217
Assert.assertSame(root.get("singleton2_2"), root2.get("singleton2_2"));
196218
Assert.assertSame(root2.get("singleton2"), root2.get("singleton2_2"));
219+
220+
Assert.assertNotSame(TestObject.TEST_OBJECT_COPIABLE, root2.get("copiable"));
221+
Assert.assertNotSame(root.get("copiable"), root2.get("copiable"));
197222
}
198223

199224
private static byte[] encode(List<Field> externalValueFields, Object root, String debugLabel) {

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/CompilationWrapper.java

+2
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ protected T handleFailure(DebugContext initialDebug, Throwable cause) {
288288
try (PrintStream ps = new PrintStream(baos)) {
289289
ps.printf("%s: Compilation of %s failed: ", Thread.currentThread(), this);
290290
cause.printStackTrace(ps);
291+
ps.println("Options: " + initialOptions);
291292
printCompilationFailureActionAlternatives(ps, ExceptionAction.Silent, ExceptionAction.Diagnose);
292293
}
293294
TTY.print(baos.toString());
@@ -322,6 +323,7 @@ protected T handleFailure(DebugContext initialDebug, Throwable cause) {
322323

323324
ps.printf("%s: Compilation of %s failed:%n", Thread.currentThread(), this);
324325
cause.printStackTrace(ps);
326+
ps.println("Options: " + initialOptions);
325327
printCompilationFailureActionAlternatives(ps, ExceptionAction.Silent, ExceptionAction.Print);
326328
if (dumpPath != null) {
327329
ps.println("Retrying compilation of " + this);

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/Node.java

+25-35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -40,8 +40,6 @@
4040
import java.util.function.Predicate;
4141
import java.util.function.Supplier;
4242

43-
import org.graalvm.collections.EconomicSet;
44-
4543
import jdk.graal.compiler.core.common.Fields;
4644
import jdk.graal.compiler.core.common.type.Stamp;
4745
import jdk.graal.compiler.core.common.util.CompilationAlarm;
@@ -58,6 +56,7 @@
5856
import jdk.graal.compiler.nodeinfo.NodeInfo;
5957
import jdk.graal.compiler.nodeinfo.NodeSize;
6058
import jdk.graal.compiler.nodeinfo.Verbosity;
59+
import jdk.graal.compiler.nodes.StructuredGraph;
6160
import jdk.graal.compiler.nodes.spi.Simplifiable;
6261
import jdk.graal.compiler.options.OptionValues;
6362
import jdk.graal.compiler.serviceprovider.GraalServices;
@@ -564,7 +563,9 @@ void addUsage(Node node) {
564563
if (length == 0) {
565564
extraUsages = new Node[4];
566565
} else if (extraUsagesCount == length) {
567-
Node[] newExtraUsages = new Node[length * 2 + 1];
566+
int growth = length >> 1;
567+
// Grow the usages array by 1.5x
568+
Node[] newExtraUsages = new Node[length + growth];
568569
System.arraycopy(extraUsages, 0, newExtraUsages, 0, length);
569570
extraUsages = newExtraUsages;
570571
}
@@ -588,13 +589,20 @@ private void movUsageFromEndToExtraUsages(int destExtraIndex) {
588589
Node n = extraUsages[extraUsagesCount];
589590
extraUsages[destExtraIndex] = n;
590591
extraUsages[extraUsagesCount] = null;
592+
if (extraUsagesCount == 0) {
593+
extraUsages = EMPTY_ARRAY;
594+
}
591595
}
592596

593597
private void movUsageFromEndToIndexZero() {
594598
if (extraUsagesCount > 0) {
595599
this.extraUsagesCount--;
596600
usage0 = extraUsages[extraUsagesCount];
597-
extraUsages[extraUsagesCount] = null;
601+
if (extraUsagesCount == 0) {
602+
extraUsages = EMPTY_ARRAY;
603+
} else {
604+
extraUsages[extraUsagesCount] = null;
605+
}
598606
} else if (usage1 != null) {
599607
usage0 = usage1;
600608
usage1 = null;
@@ -607,7 +615,11 @@ private void movUsageFromEndToIndexOne() {
607615
if (extraUsagesCount > 0) {
608616
this.extraUsagesCount--;
609617
usage1 = extraUsages[extraUsagesCount];
610-
extraUsages[extraUsagesCount] = null;
618+
if (extraUsagesCount == 0) {
619+
extraUsages = EMPTY_ARRAY;
620+
} else {
621+
extraUsages[extraUsagesCount] = null;
622+
}
611623
} else {
612624
assert usage1 != null;
613625
usage1 = null;
@@ -688,35 +700,6 @@ public int removeUsageNTimes(Node node, int limit) {
688700
return removedUsages;
689701
}
690702

691-
/**
692-
* Removes all nodes in the provided set from {@code this} node's usages. This is significantly
693-
* faster than repeated execution of {@link Node#removeUsage}.
694-
*/
695-
public void removeUsages(EconomicSet<Node> toDelete) {
696-
if (toDelete.size() == 0) {
697-
return;
698-
} else if (toDelete.size() == 1) {
699-
removeUsage(toDelete.iterator().next());
700-
return;
701-
}
702-
703-
// requires iteration from back to front to check nodes prior to being moved to the front
704-
for (int i = extraUsagesCount - 1; i >= 0; i--) {
705-
if (toDelete.contains(extraUsages[i])) {
706-
movUsageFromEndToExtraUsages(i);
707-
incUsageModCount();
708-
}
709-
}
710-
if (usage1 != null && toDelete.contains(usage1)) {
711-
movUsageFromEndToIndexOne();
712-
incUsageModCount();
713-
}
714-
if (usage0 != null && toDelete.contains(usage0)) {
715-
movUsageFromEndToIndexZero();
716-
incUsageModCount();
717-
}
718-
}
719-
720703
/**
721704
* Removes all dead nodes from {@code this} node's usages. This is significantly faster than
722705
* repeated execution of {@link Node#removeUsage}.
@@ -1728,6 +1711,13 @@ public String toString(Verbosity verbosity) {
17281711
}
17291712
}
17301713

1714+
/**
1715+
* Note that this is not a stable identity. It's updated when a node is
1716+
* {@linkplain #markDeleted() deleted} or potentially when its graph is
1717+
* {@linkplain StructuredGraph#maybeCompress compressed}.
1718+
*
1719+
* @see NodeIdAccessor
1720+
*/
17311721
@Deprecated
17321722
public int getId() {
17331723
return id;

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/graph/NodeClass.java

+43-10
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.util.concurrent.atomic.AtomicInteger;
4545
import java.util.stream.Stream;
4646

47+
import jdk.graal.compiler.nodes.NodeClassMap;
4748
import org.graalvm.collections.EconomicMap;
4849
import org.graalvm.collections.Equivalence;
4950

@@ -149,14 +150,9 @@ public static <T> NodeClass<T> get(Class<T> clazz) {
149150

150151
private final int leafId;
151152

152-
@LibGraalSupport.HostedOnly
153-
public NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass) {
154-
this(clazz, superNodeClass, null, 0);
155-
}
156-
157153
@SuppressWarnings("try")
158154
@LibGraalSupport.HostedOnly
159-
private NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass, int[] presetIterableIds, int presetIterableId) {
155+
public NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass) {
160156
super(clazz);
161157
DebugContext debug = DebugContext.forCurrentThread();
162158
this.superNodeClass = superNodeClass;
@@ -201,10 +197,7 @@ private NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass, int[] pre
201197
GraalError.guarantee(!allowedUsageTypes.contains(InputType.Memory) || MemoryKillMarker.class.isAssignableFrom(clazz),
202198
"Node of type %s with allowedUsageType of memory must inherit from MemoryKill", clazz);
203199

204-
if (presetIterableIds != null) {
205-
this.iterableIds = presetIterableIds;
206-
this.iterableId = presetIterableId;
207-
} else if (IterableNodeType.class.isAssignableFrom(clazz)) {
200+
if (IterableNodeType.class.isAssignableFrom(clazz)) {
208201
ITERABLE_NODE_TYPES.increment(debug);
209202
this.iterableId = nextIterableId.getAndIncrement();
210203

@@ -332,6 +325,13 @@ public boolean valueNumberable() {
332325
return canGVN;
333326
}
334327

328+
/**
329+
* Determines if this node type is abstract.
330+
*/
331+
public boolean isAbstract() {
332+
return Modifier.isAbstract(this.getClazz().getModifiers());
333+
}
334+
335335
/**
336336
* Determines if this node type is {@link CanonicalizableMarker canonicalizable}.
337337
*/
@@ -1488,4 +1488,37 @@ private static void unregisterAtInputsAsUsageHelperMany(Node node, NodeList<Node
14881488
}
14891489
list.clearWithoutUpdate();
14901490
}
1491+
1492+
/**
1493+
* The cached id for a {@link NodeClass} object in a specific {@link NodeClassMap}.
1494+
*
1495+
* @param map an object whose identity uniquely identifies a {@link NodeClassMap}
1496+
*/
1497+
record CachedId(Object map, Integer id) {
1498+
}
1499+
1500+
private CachedId cachedId;
1501+
1502+
/**
1503+
* Sets the cache for this object's {@code id} in {@code map}.
1504+
*
1505+
* @param map an object whose identity uniquely identifies a {@link NodeClassMap}
1506+
*/
1507+
public void setCachedId(Object map, Integer id) {
1508+
cachedId = new CachedId(map, id);
1509+
}
1510+
1511+
/**
1512+
* Gets the cache for this object's id in {@code map}.
1513+
*
1514+
* @param map an object whose identity uniquely identifies a {@link NodeClassMap}
1515+
* @return null if no cached id for this object in {@code map} is available
1516+
*/
1517+
public Integer getCachedId(Object map) {
1518+
var c = cachedId;
1519+
if (c != null && c.map == map) {
1520+
return c.id;
1521+
}
1522+
return null;
1523+
}
14911524
}

0 commit comments

Comments
 (0)