Skip to content

Commit eed8557

Browse files
author
Christian Wimmer
committed
[GR-38180] Encode analysis graphs to reduce memory footprint of the image generator.
PullRequest: graal/11629
2 parents 55ecc1d + 47d4bc5 commit eed8557

File tree

21 files changed

+503
-207
lines changed

21 files changed

+503
-207
lines changed

compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/EncodedGraph.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
package org.graalvm.compiler.nodes;
2626

2727
import java.util.List;
28+
import java.util.Objects;
2829

30+
import org.graalvm.compiler.debug.GraalError;
31+
import org.graalvm.compiler.graph.Node;
2932
import org.graalvm.compiler.graph.NodeClass;
3033

3134
import jdk.vm.ci.meta.Assumptions;
@@ -38,6 +41,46 @@
3841
*/
3942
public class EncodedGraph {
4043

44+
/**
45+
* A marker for a node in an encoded graph. The reason to encode graphs is usually to reduce the
46+
* memory footprint, because encoded graphs are much smaller than non-encoded graphs. But the
47+
* encoding/decoding cycle does not preserve the identity of node objects: all nodes are newly
48+
* allocated during decoding, and keeping a single node of a graph alive would keep the whole
49+
* graph alive. So it is not possible to keep a direct reference to a node in an encoded graph.
50+
* This class serves as a marker instead: All markers passed into
51+
* {@link GraphEncoder#encode(StructuredGraph, Iterable)} and
52+
* {@link GraphDecoder#decode(EncodedGraph, Iterable)} are properly updated, so that after
53+
* decoding the {@link #getNode()} method returns the new node of the just decoded graph.
54+
*/
55+
public static class EncodedNodeReference {
56+
static final int DECODED = -1;
57+
static final int CLEARED = -2;
58+
59+
Node node;
60+
int orderId;
61+
62+
public EncodedNodeReference(Node node) {
63+
this.node = Objects.requireNonNull(node);
64+
this.orderId = DECODED;
65+
}
66+
67+
public Node getNode() {
68+
if (orderId != DECODED) {
69+
throw GraalError.shouldNotReachHere("Cannot access node while graph is encoded");
70+
}
71+
return node;
72+
}
73+
74+
/**
75+
* Clears the reference, all future encode, decode, or access operations will throw an
76+
* exception.
77+
*/
78+
public void clear() {
79+
node = null;
80+
orderId = CLEARED;
81+
}
82+
}
83+
4184
private final byte[] encoding;
4285
private final int startOffset;
4386
protected final Object[] objects;

compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import org.graalvm.compiler.graph.NodeSuccessorList;
6262
import org.graalvm.compiler.nodeinfo.InputType;
6363
import org.graalvm.compiler.nodeinfo.NodeInfo;
64+
import org.graalvm.compiler.nodes.EncodedGraph.EncodedNodeReference;
6465
import org.graalvm.compiler.nodes.GraphDecoder.MethodScope;
6566
import org.graalvm.compiler.nodes.GraphDecoder.ProxyPlaceholder;
6667
import org.graalvm.compiler.nodes.ProfileData.SwitchProbabilityData;
@@ -482,13 +483,31 @@ public GraphDecoder(Architecture architecture, StructuredGraph graph) {
482483
reusableFloatingNodes = EconomicMap.create(Equivalence.IDENTITY);
483484
}
484485

485-
@SuppressWarnings("try")
486486
public final void decode(EncodedGraph encodedGraph) {
487+
decode(encodedGraph, null);
488+
}
489+
490+
@SuppressWarnings("try")
491+
public final void decode(EncodedGraph encodedGraph, Iterable<EncodedNodeReference> nodeReferences) {
487492
try (DebugContext.Scope scope = debug.scope("GraphDecoder", graph)) {
488493
MethodScope methodScope = new MethodScope(null, graph, encodedGraph, LoopExplosionKind.NONE);
489-
decode(createInitialLoopScope(methodScope, null));
494+
LoopScope loopScope = createInitialLoopScope(methodScope, null);
495+
decode(loopScope);
490496
cleanupGraph(methodScope);
491497
assert graph.verify();
498+
499+
if (nodeReferences != null) {
500+
for (var nodeReference : nodeReferences) {
501+
if (nodeReference.orderId < 0) {
502+
throw GraalError.shouldNotReachHere("EncodeNodeReference is not in 'encoded' state");
503+
}
504+
nodeReference.node = loopScope.createdNodes[nodeReference.orderId];
505+
if (nodeReference.node == null || !nodeReference.node.isAlive()) {
506+
throw GraalError.shouldNotReachHere("Could not decode the EncodedNodeReference");
507+
}
508+
nodeReference.orderId = EncodedNodeReference.DECODED;
509+
}
510+
}
492511
} catch (Throwable ex) {
493512
debug.handle(ex);
494513
}

compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphEncoder.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@
3838
import org.graalvm.compiler.core.common.util.TypeWriter;
3939
import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter;
4040
import org.graalvm.compiler.debug.DebugContext;
41+
import org.graalvm.compiler.debug.GraalError;
4142
import org.graalvm.compiler.graph.Edges;
4243
import org.graalvm.compiler.graph.Node;
4344
import org.graalvm.compiler.graph.NodeClass;
4445
import org.graalvm.compiler.graph.NodeList;
4546
import org.graalvm.compiler.graph.NodeMap;
4647
import org.graalvm.compiler.graph.iterators.NodeIterable;
48+
import org.graalvm.compiler.nodes.EncodedGraph.EncodedNodeReference;
4749
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
4850
import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
4951

@@ -166,10 +168,17 @@ public class GraphEncoder {
166168
* Utility method that does everything necessary to encode a single graph.
167169
*/
168170
public static EncodedGraph encodeSingleGraph(StructuredGraph graph, Architecture architecture) {
171+
return encodeSingleGraph(graph, architecture, null);
172+
}
173+
174+
/**
175+
* Utility method that does everything necessary to encode a single graph.
176+
*/
177+
public static EncodedGraph encodeSingleGraph(StructuredGraph graph, Architecture architecture, Iterable<EncodedNodeReference> nodeReferences) {
169178
GraphEncoder encoder = new GraphEncoder(architecture);
170179
encoder.prepare(graph);
171180
encoder.finishPrepare();
172-
int startOffset = encoder.encode(graph);
181+
int startOffset = encoder.encode(graph, nodeReferences);
173182
return new EncodedGraph(encoder.getEncoding(), startOffset, encoder.getObjects(), encoder.getNodeClasses(), graph);
174183
}
175184

@@ -229,13 +238,27 @@ public NodeClass<?>[] getNodeClasses() {
229238
* @param graph The graph to encode
230239
*/
231240
public int encode(StructuredGraph graph) {
241+
return encode(graph, null);
242+
}
243+
244+
protected int encode(StructuredGraph graph, Iterable<EncodedNodeReference> nodeReferences) {
232245
assert objectsArray != null && nodeClassesArray != null : "finishPrepare() must be called before encode()";
233246

234247
NodeOrder nodeOrder = new NodeOrder(graph);
235248
int nodeCount = nodeOrder.nextOrderId;
236249
assert nodeOrder.orderIds.get(graph.start()) == START_NODE_ORDER_ID;
237250
assert nodeOrder.orderIds.get(graph.start().next()) == FIRST_NODE_ORDER_ID;
238251

252+
if (nodeReferences != null) {
253+
for (var nodeReference : nodeReferences) {
254+
if (nodeReference.orderId != EncodedNodeReference.DECODED) {
255+
throw GraalError.shouldNotReachHere("EncodedNodeReference is not in 'decoded' state");
256+
}
257+
nodeReference.orderId = nodeOrder.orderIds.get(nodeReference.node);
258+
nodeReference.node = null;
259+
}
260+
}
261+
239262
long[] nodeStartOffsets = new long[nodeCount];
240263
UnmodifiableMapCursor<Node, Integer> cursor = nodeOrder.orderIds.getEntries();
241264
while (cursor.advance()) {

graal-common.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"README": "This file contains definitions that are useful for the hocon and jsonnet CI files of the graal and graal-enterprise repositories.",
33
"ci": {
4-
"overlay": "2fb56d2621f3b269485587d232a94182b1aeb795"
4+
"overlay": "53a70b677c1edb30ac0b6c6e824bf4aad3a3ef0c"
55
},
66

77
"COMMENT.mx": [

sdk/src/org.graalvm.collections/snapshot.sigtest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ meth public static <%0 extends java.lang.Object, %1 extends java.lang.Object> or
3434
meth public static <%0 extends java.lang.Object, %1 extends java.lang.Object> org.graalvm.collections.EconomicMap<{%%0},{%%1}> create(org.graalvm.collections.Equivalence,int)
3535
meth public static <%0 extends java.lang.Object, %1 extends java.lang.Object> org.graalvm.collections.EconomicMap<{%%0},{%%1}> create(org.graalvm.collections.Equivalence,org.graalvm.collections.UnmodifiableEconomicMap<{%%0},{%%1}>)
3636
meth public static <%0 extends java.lang.Object, %1 extends java.lang.Object> org.graalvm.collections.EconomicMap<{%%0},{%%1}> create(org.graalvm.collections.UnmodifiableEconomicMap<{%%0},{%%1}>)
37+
meth public static <%0 extends java.lang.Object, %1 extends java.lang.Object> org.graalvm.collections.EconomicMap<{%%0},{%%1}> emptyMap()
3738
meth public static <%0 extends java.lang.Object, %1 extends java.lang.Object> org.graalvm.collections.EconomicMap<{%%0},{%%1}> wrapMap(java.util.Map<{%%0},{%%1}>)
3839
meth public static <%0 extends java.lang.Object, %1 extends java.lang.Object> org.graalvm.collections.MapCursor<{%%0},{%%1}> emptyCursor()
3940
meth public void putAll(org.graalvm.collections.EconomicMap<{org.graalvm.collections.EconomicMap%0},{org.graalvm.collections.EconomicMap%1}>)

sdk/src/org.graalvm.collections/src/org/graalvm/collections/EconomicMap.java

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -236,31 +236,18 @@ static <K, V> EconomicMap<K, V> wrapMap(Map<K, V> map) {
236236
*
237237
* @since 22.0
238238
*/
239+
@SuppressWarnings("unchecked")
239240
static <K, V> MapCursor<K, V> emptyCursor() {
240-
return new MapCursor<>() {
241-
@Override
242-
public void remove() {
243-
}
244-
245-
@Override
246-
public boolean advance() {
247-
return false;
248-
}
249-
250-
@Override
251-
public K getKey() {
252-
return null;
253-
}
254-
255-
@Override
256-
public V getValue() {
257-
return null;
258-
}
241+
return (MapCursor<K, V>) EmptyMap.EMPTY_CURSOR;
242+
}
259243

260-
@Override
261-
public V setValue(V newValue) {
262-
return null;
263-
}
264-
};
244+
/**
245+
* Return an empty, unmodifiable {@link EconomicMap}.
246+
*
247+
* @since 22.2
248+
*/
249+
@SuppressWarnings("unchecked")
250+
static <K, V> EconomicMap<K, V> emptyMap() {
251+
return (EconomicMap<K, V>) EmptyMap.EMPTY_MAP;
265252
}
266253
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package org.graalvm.collections;
42+
43+
import java.util.Iterator;
44+
import java.util.NoSuchElementException;
45+
import java.util.function.BiFunction;
46+
47+
/**
48+
* Singleton instances for empty maps and the corresponding iterators and cursors.
49+
*/
50+
class EmptyMap {
51+
52+
static final MapCursor<Object, Object> EMPTY_CURSOR = new MapCursor<>() {
53+
@Override
54+
public void remove() {
55+
throw new NoSuchElementException("Empty cursor does not have elements");
56+
}
57+
58+
@Override
59+
public boolean advance() {
60+
return false;
61+
}
62+
63+
@Override
64+
public Object getKey() {
65+
throw new NoSuchElementException("Empty cursor does not have elements");
66+
}
67+
68+
@Override
69+
public Object getValue() {
70+
throw new NoSuchElementException("Empty cursor does not have elements");
71+
}
72+
73+
@Override
74+
public Object setValue(Object newValue) {
75+
throw new NoSuchElementException("Empty cursor does not have elements");
76+
}
77+
};
78+
79+
static final Iterator<Object> EMPTY_ITERATOR = new Iterator<>() {
80+
@Override
81+
public boolean hasNext() {
82+
return false;
83+
}
84+
85+
@Override
86+
public Object next() {
87+
throw new NoSuchElementException("Empty iterator does not have elements");
88+
}
89+
};
90+
91+
static final Iterable<Object> EMPTY_ITERABLE = new Iterable<>() {
92+
@Override
93+
public Iterator<Object> iterator() {
94+
return EMPTY_ITERATOR;
95+
}
96+
};
97+
98+
static final EconomicMap<Object, Object> EMPTY_MAP = new EconomicMap<>() {
99+
@Override
100+
public Object put(Object key, Object value) {
101+
throw new IllegalArgumentException("Cannot modify the always-empty map");
102+
}
103+
104+
@Override
105+
public void clear() {
106+
throw new IllegalArgumentException("Cannot modify the always-empty map");
107+
}
108+
109+
@Override
110+
public Object removeKey(Object key) {
111+
throw new IllegalArgumentException("Cannot modify the always-empty map");
112+
}
113+
114+
@Override
115+
public Object get(Object key) {
116+
return null;
117+
}
118+
119+
@Override
120+
public boolean containsKey(Object key) {
121+
return false;
122+
}
123+
124+
@Override
125+
public int size() {
126+
return 0;
127+
}
128+
129+
@Override
130+
public boolean isEmpty() {
131+
return true;
132+
}
133+
134+
@Override
135+
public Iterable<Object> getValues() {
136+
return EMPTY_ITERABLE;
137+
}
138+
139+
@Override
140+
public Iterable<Object> getKeys() {
141+
return EMPTY_ITERABLE;
142+
}
143+
144+
@Override
145+
public MapCursor<Object, Object> getEntries() {
146+
return EMPTY_CURSOR;
147+
}
148+
149+
@Override
150+
public void replaceAll(BiFunction<? super Object, ? super Object, ?> function) {
151+
throw new IllegalArgumentException("Cannot modify the always-empty map");
152+
}
153+
};
154+
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/DefaultAnalysisPolicy.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,8 @@ public void onObservedSaturated(PointsToAnalysis bb, TypeFlow<?> observed) {
300300
}
301301
}
302302
/* Remove the link between the formal and the actual return, if present. */
303-
if (actualReturn != null && calleeFlows.getResult() != null) {
304-
calleeFlows.getResult().removeUse(actualReturn);
303+
if (actualReturn != null && calleeFlows.getReturnFlow() != null) {
304+
calleeFlows.getReturnFlow().removeUse(actualReturn);
305305
}
306306
}
307307

0 commit comments

Comments
 (0)