Skip to content

Commit

Permalink
[GR-38180] Encode analysis graphs to reduce memory footprint of the i…
Browse files Browse the repository at this point in the history
…mage generator.

PullRequest: graal/11629
  • Loading branch information
Christian Wimmer committed Apr 19, 2022
2 parents 55ecc1d + 47d4bc5 commit eed8557
Show file tree
Hide file tree
Showing 21 changed files with 503 additions and 207 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
package org.graalvm.compiler.nodes;

import java.util.List;
import java.util.Objects;

import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeClass;

import jdk.vm.ci.meta.Assumptions;
Expand All @@ -38,6 +41,46 @@
*/
public class EncodedGraph {

/**
* A marker for a node in an encoded graph. The reason to encode graphs is usually to reduce the
* memory footprint, because encoded graphs are much smaller than non-encoded graphs. But the
* encoding/decoding cycle does not preserve the identity of node objects: all nodes are newly
* allocated during decoding, and keeping a single node of a graph alive would keep the whole
* graph alive. So it is not possible to keep a direct reference to a node in an encoded graph.
* This class serves as a marker instead: All markers passed into
* {@link GraphEncoder#encode(StructuredGraph, Iterable)} and
* {@link GraphDecoder#decode(EncodedGraph, Iterable)} are properly updated, so that after
* decoding the {@link #getNode()} method returns the new node of the just decoded graph.
*/
public static class EncodedNodeReference {
static final int DECODED = -1;
static final int CLEARED = -2;

Node node;
int orderId;

public EncodedNodeReference(Node node) {
this.node = Objects.requireNonNull(node);
this.orderId = DECODED;
}

public Node getNode() {
if (orderId != DECODED) {
throw GraalError.shouldNotReachHere("Cannot access node while graph is encoded");
}
return node;
}

/**
* Clears the reference, all future encode, decode, or access operations will throw an
* exception.
*/
public void clear() {
node = null;
orderId = CLEARED;
}
}

private final byte[] encoding;
private final int startOffset;
protected final Object[] objects;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import org.graalvm.compiler.graph.NodeSuccessorList;
import org.graalvm.compiler.nodeinfo.InputType;
import org.graalvm.compiler.nodeinfo.NodeInfo;
import org.graalvm.compiler.nodes.EncodedGraph.EncodedNodeReference;
import org.graalvm.compiler.nodes.GraphDecoder.MethodScope;
import org.graalvm.compiler.nodes.GraphDecoder.ProxyPlaceholder;
import org.graalvm.compiler.nodes.ProfileData.SwitchProbabilityData;
Expand Down Expand Up @@ -482,13 +483,31 @@ public GraphDecoder(Architecture architecture, StructuredGraph graph) {
reusableFloatingNodes = EconomicMap.create(Equivalence.IDENTITY);
}

@SuppressWarnings("try")
public final void decode(EncodedGraph encodedGraph) {
decode(encodedGraph, null);
}

@SuppressWarnings("try")
public final void decode(EncodedGraph encodedGraph, Iterable<EncodedNodeReference> nodeReferences) {
try (DebugContext.Scope scope = debug.scope("GraphDecoder", graph)) {
MethodScope methodScope = new MethodScope(null, graph, encodedGraph, LoopExplosionKind.NONE);
decode(createInitialLoopScope(methodScope, null));
LoopScope loopScope = createInitialLoopScope(methodScope, null);
decode(loopScope);
cleanupGraph(methodScope);
assert graph.verify();

if (nodeReferences != null) {
for (var nodeReference : nodeReferences) {
if (nodeReference.orderId < 0) {
throw GraalError.shouldNotReachHere("EncodeNodeReference is not in 'encoded' state");
}
nodeReference.node = loopScope.createdNodes[nodeReference.orderId];
if (nodeReference.node == null || !nodeReference.node.isAlive()) {
throw GraalError.shouldNotReachHere("Could not decode the EncodedNodeReference");
}
nodeReference.orderId = EncodedNodeReference.DECODED;
}
}
} catch (Throwable ex) {
debug.handle(ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@
import org.graalvm.compiler.core.common.util.TypeWriter;
import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Edges;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeClass;
import org.graalvm.compiler.graph.NodeList;
import org.graalvm.compiler.graph.NodeMap;
import org.graalvm.compiler.graph.iterators.NodeIterable;
import org.graalvm.compiler.nodes.EncodedGraph.EncodedNodeReference;
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
import org.graalvm.compiler.nodes.java.ExceptionObjectNode;

Expand Down Expand Up @@ -166,10 +168,17 @@ public class GraphEncoder {
* Utility method that does everything necessary to encode a single graph.
*/
public static EncodedGraph encodeSingleGraph(StructuredGraph graph, Architecture architecture) {
return encodeSingleGraph(graph, architecture, null);
}

/**
* Utility method that does everything necessary to encode a single graph.
*/
public static EncodedGraph encodeSingleGraph(StructuredGraph graph, Architecture architecture, Iterable<EncodedNodeReference> nodeReferences) {
GraphEncoder encoder = new GraphEncoder(architecture);
encoder.prepare(graph);
encoder.finishPrepare();
int startOffset = encoder.encode(graph);
int startOffset = encoder.encode(graph, nodeReferences);
return new EncodedGraph(encoder.getEncoding(), startOffset, encoder.getObjects(), encoder.getNodeClasses(), graph);
}

Expand Down Expand Up @@ -229,13 +238,27 @@ public NodeClass<?>[] getNodeClasses() {
* @param graph The graph to encode
*/
public int encode(StructuredGraph graph) {
return encode(graph, null);
}

protected int encode(StructuredGraph graph, Iterable<EncodedNodeReference> nodeReferences) {
assert objectsArray != null && nodeClassesArray != null : "finishPrepare() must be called before encode()";

NodeOrder nodeOrder = new NodeOrder(graph);
int nodeCount = nodeOrder.nextOrderId;
assert nodeOrder.orderIds.get(graph.start()) == START_NODE_ORDER_ID;
assert nodeOrder.orderIds.get(graph.start().next()) == FIRST_NODE_ORDER_ID;

if (nodeReferences != null) {
for (var nodeReference : nodeReferences) {
if (nodeReference.orderId != EncodedNodeReference.DECODED) {
throw GraalError.shouldNotReachHere("EncodedNodeReference is not in 'decoded' state");
}
nodeReference.orderId = nodeOrder.orderIds.get(nodeReference.node);
nodeReference.node = null;
}
}

long[] nodeStartOffsets = new long[nodeCount];
UnmodifiableMapCursor<Node, Integer> cursor = nodeOrder.orderIds.getEntries();
while (cursor.advance()) {
Expand Down
2 changes: 1 addition & 1 deletion graal-common.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"README": "This file contains definitions that are useful for the hocon and jsonnet CI files of the graal and graal-enterprise repositories.",
"ci": {
"overlay": "2fb56d2621f3b269485587d232a94182b1aeb795"
"overlay": "53a70b677c1edb30ac0b6c6e824bf4aad3a3ef0c"
},

"COMMENT.mx": [
Expand Down
1 change: 1 addition & 0 deletions sdk/src/org.graalvm.collections/snapshot.sigtest
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ meth public static <%0 extends java.lang.Object, %1 extends java.lang.Object> or
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)
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}>)
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}>)
meth public static <%0 extends java.lang.Object, %1 extends java.lang.Object> org.graalvm.collections.EconomicMap<{%%0},{%%1}> emptyMap()
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}>)
meth public static <%0 extends java.lang.Object, %1 extends java.lang.Object> org.graalvm.collections.MapCursor<{%%0},{%%1}> emptyCursor()
meth public void putAll(org.graalvm.collections.EconomicMap<{org.graalvm.collections.EconomicMap%0},{org.graalvm.collections.EconomicMap%1}>)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,31 +236,18 @@ static <K, V> EconomicMap<K, V> wrapMap(Map<K, V> map) {
*
* @since 22.0
*/
@SuppressWarnings("unchecked")
static <K, V> MapCursor<K, V> emptyCursor() {
return new MapCursor<>() {
@Override
public void remove() {
}

@Override
public boolean advance() {
return false;
}

@Override
public K getKey() {
return null;
}

@Override
public V getValue() {
return null;
}
return (MapCursor<K, V>) EmptyMap.EMPTY_CURSOR;
}

@Override
public V setValue(V newValue) {
return null;
}
};
/**
* Return an empty, unmodifiable {@link EconomicMap}.
*
* @since 22.2
*/
@SuppressWarnings("unchecked")
static <K, V> EconomicMap<K, V> emptyMap() {
return (EconomicMap<K, V>) EmptyMap.EMPTY_MAP;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.graalvm.collections;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.BiFunction;

/**
* Singleton instances for empty maps and the corresponding iterators and cursors.
*/
class EmptyMap {

static final MapCursor<Object, Object> EMPTY_CURSOR = new MapCursor<>() {
@Override
public void remove() {
throw new NoSuchElementException("Empty cursor does not have elements");
}

@Override
public boolean advance() {
return false;
}

@Override
public Object getKey() {
throw new NoSuchElementException("Empty cursor does not have elements");
}

@Override
public Object getValue() {
throw new NoSuchElementException("Empty cursor does not have elements");
}

@Override
public Object setValue(Object newValue) {
throw new NoSuchElementException("Empty cursor does not have elements");
}
};

static final Iterator<Object> EMPTY_ITERATOR = new Iterator<>() {
@Override
public boolean hasNext() {
return false;
}

@Override
public Object next() {
throw new NoSuchElementException("Empty iterator does not have elements");
}
};

static final Iterable<Object> EMPTY_ITERABLE = new Iterable<>() {
@Override
public Iterator<Object> iterator() {
return EMPTY_ITERATOR;
}
};

static final EconomicMap<Object, Object> EMPTY_MAP = new EconomicMap<>() {
@Override
public Object put(Object key, Object value) {
throw new IllegalArgumentException("Cannot modify the always-empty map");
}

@Override
public void clear() {
throw new IllegalArgumentException("Cannot modify the always-empty map");
}

@Override
public Object removeKey(Object key) {
throw new IllegalArgumentException("Cannot modify the always-empty map");
}

@Override
public Object get(Object key) {
return null;
}

@Override
public boolean containsKey(Object key) {
return false;
}

@Override
public int size() {
return 0;
}

@Override
public boolean isEmpty() {
return true;
}

@Override
public Iterable<Object> getValues() {
return EMPTY_ITERABLE;
}

@Override
public Iterable<Object> getKeys() {
return EMPTY_ITERABLE;
}

@Override
public MapCursor<Object, Object> getEntries() {
return EMPTY_CURSOR;
}

@Override
public void replaceAll(BiFunction<? super Object, ? super Object, ?> function) {
throw new IllegalArgumentException("Cannot modify the always-empty map");
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,8 @@ public void onObservedSaturated(PointsToAnalysis bb, TypeFlow<?> observed) {
}
}
/* Remove the link between the formal and the actual return, if present. */
if (actualReturn != null && calleeFlows.getResult() != null) {
calleeFlows.getResult().removeUse(actualReturn);
if (actualReturn != null && calleeFlows.getReturnFlow() != null) {
calleeFlows.getReturnFlow().removeUse(actualReturn);
}
}

Expand Down
Loading

0 comments on commit eed8557

Please sign in to comment.