Skip to content

Commit

Permalink
[GR-38182] Reduce analysis memory footprint.
Browse files Browse the repository at this point in the history
PullRequest: graal/11640
  • Loading branch information
cstancu committed Apr 22, 2022
2 parents 25dbe69 + 104a3f5 commit 15c747c
Show file tree
Hide file tree
Showing 7 changed files with 513 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import com.oracle.graal.pointsto.util.ConcurrentLightHashSet;
import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;

import com.oracle.graal.pointsto.PointsToAnalysis;
Expand All @@ -41,20 +43,24 @@ public abstract class AnalysisElement {
* Contains reachability handlers that are notified when the element is marked as reachable.
* Each handler is notified only once, and then it is removed from the set.
*/
private final Set<ElementReachableNotification> elementReachableNotifications = ConcurrentHashMap.newKeySet();

private static final AtomicReferenceFieldUpdater<AnalysisElement, Object> reachableNotificationsUpdater = AtomicReferenceFieldUpdater
.newUpdater(AnalysisElement.class, Object.class, "elementReachableNotifications");

@SuppressWarnings("unused") private volatile Object elementReachableNotifications;

public void registerReachabilityNotification(ElementReachableNotification notification) {
elementReachableNotifications.add(notification);
ConcurrentLightHashSet.addElement(this, reachableNotificationsUpdater, notification);
}

public void notifyReachabilityCallback(AnalysisUniverse universe, ElementReachableNotification notification) {
notification.notifyCallback(universe, this);
elementReachableNotifications.remove(notification);
ConcurrentLightHashSet.removeElement(this, reachableNotificationsUpdater, notification);
}

protected void notifyReachabilityCallbacks(AnalysisUniverse universe) {
elementReachableNotifications.forEach(c -> c.notifyCallback(universe, this));
elementReachableNotifications.removeIf(ElementReachableNotification::isNotified);
ConcurrentLightHashSet.forEach(this, reachableNotificationsUpdater, (ElementReachableNotification c) -> c.notifyCallback(universe, this));
ConcurrentLightHashSet.removeElementIf(this, reachableNotificationsUpdater, ElementReachableNotification::isNotified);
}

public abstract boolean isReachable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

import org.graalvm.compiler.debug.GraalError;
Expand Down Expand Up @@ -58,6 +58,24 @@ public abstract class AnalysisField extends AnalysisElement implements ResolvedJ
private static final AtomicReferenceFieldUpdater<AnalysisField, Object> OBSERVERS_UPDATER = //
AtomicReferenceFieldUpdater.newUpdater(AnalysisField.class, Object.class, "observers");

private static final AtomicIntegerFieldUpdater<AnalysisField> isAccessedUpdater = AtomicIntegerFieldUpdater
.newUpdater(AnalysisField.class, "isAccessed");

private static final AtomicIntegerFieldUpdater<AnalysisField> isReadUpdater = AtomicIntegerFieldUpdater
.newUpdater(AnalysisField.class, "isRead");

private static final AtomicIntegerFieldUpdater<AnalysisField> isWrittenUpdater = AtomicIntegerFieldUpdater
.newUpdater(AnalysisField.class, "isWritten");

private static final AtomicIntegerFieldUpdater<AnalysisField> isFoldedUpdater = AtomicIntegerFieldUpdater
.newUpdater(AnalysisField.class, "isFolded");

private static final AtomicIntegerFieldUpdater<AnalysisField> isUnsafeAccessedUpdater = AtomicIntegerFieldUpdater
.newUpdater(AnalysisField.class, "isUnsafeAccessed");

private static final AtomicIntegerFieldUpdater<AnalysisField> unsafeFrozenTypeStateUpdater = AtomicIntegerFieldUpdater
.newUpdater(AnalysisField.class, "unsafeFrozenTypeState");

private final int id;

public final ResolvedJavaField wrapped;
Expand All @@ -74,15 +92,15 @@ public abstract class AnalysisField extends AnalysisElement implements ResolvedJ
*/
private ContextInsensitiveFieldTypeFlow instanceFieldFlow;

private AtomicBoolean isAccessed = new AtomicBoolean();
private AtomicBoolean isRead = new AtomicBoolean();
private AtomicBoolean isWritten = new AtomicBoolean();
private AtomicBoolean isFolded = new AtomicBoolean();
@SuppressWarnings("unused") private volatile int isAccessed;
@SuppressWarnings("unused") private volatile int isRead;
@SuppressWarnings("unused") private volatile int isWritten;
@SuppressWarnings("unused") private volatile int isFolded;

private boolean isJNIAccessed;
private boolean isUsedInComparison;
private AtomicBoolean isUnsafeAccessed;
private AtomicBoolean unsafeFrozenTypeState;
@SuppressWarnings("unused") private volatile int isUnsafeAccessed;
@SuppressWarnings("unused") private volatile int unsafeFrozenTypeState;
@SuppressWarnings("unused") private volatile Object observers;

/**
Expand All @@ -106,8 +124,6 @@ public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField)
assert !wrappedField.isInternal();

this.position = -1;
this.isUnsafeAccessed = new AtomicBoolean();
this.unsafeFrozenTypeState = new AtomicBoolean();

this.wrapped = wrappedField;
this.id = universe.nextFieldId.getAndIncrement();
Expand Down Expand Up @@ -153,30 +169,30 @@ private static AnalysisType getDeclaredType(AnalysisUniverse universe, ResolvedJ
}

public void copyAccessInfos(AnalysisField other) {
this.isAccessed = new AtomicBoolean(other.isAccessed.get());
this.isUnsafeAccessed = other.isUnsafeAccessed;
isAccessedUpdater.set(this, other.isAccessed);
isUnsafeAccessedUpdater.set(this, other.isUnsafeAccessed);
this.canBeNull = other.canBeNull;
this.isWritten = new AtomicBoolean(other.isWritten.get());
this.isFolded = new AtomicBoolean(other.isFolded.get());
this.isRead = new AtomicBoolean(other.isRead.get());
isWrittenUpdater.set(this, other.isWritten);
isFoldedUpdater.set(this, other.isFolded);
isReadUpdater.set(this, other.isRead);
notifyUpdateAccessInfo();
}

public void intersectAccessInfos(AnalysisField other) {
this.isAccessed = new AtomicBoolean(this.isAccessed.get() && other.isAccessed.get());
isAccessedUpdater.set(this, this.isAccessed & other.isAccessed);
this.canBeNull = this.canBeNull && other.canBeNull;
this.isWritten = new AtomicBoolean(this.isWritten.get() && other.isWritten.get());
this.isFolded = new AtomicBoolean(this.isFolded.get() && other.isFolded.get());
this.isRead = new AtomicBoolean(this.isRead.get() && other.isRead.get());
isWrittenUpdater.set(this, this.isWritten & other.isWritten);
isFoldedUpdater.set(this, this.isFolded & other.isFolded);
isReadUpdater.set(this, this.isRead & other.isRead);
notifyUpdateAccessInfo();
}

public void clearAccessInfos() {
this.isAccessed.set(false);
isAccessedUpdater.set(this, 0);
this.canBeNull = true;
this.isWritten.set(false);
this.isFolded.set(false);
this.isRead.set(false);
isWrittenUpdater.set(this, 0);
isFoldedUpdater.set(this, 0);
isReadUpdater.set(this, 0);
notifyUpdateAccessInfo();
}

Expand Down Expand Up @@ -239,7 +255,7 @@ public void cleanupAfterAnalysis() {
}

public boolean registerAsAccessed() {
boolean firstAttempt = AtomicUtils.atomicMark(isAccessed);
boolean firstAttempt = AtomicUtils.atomicMark(this, isAccessedUpdater);
notifyUpdateAccessInfo();
if (firstAttempt) {
onReachable();
Expand All @@ -250,7 +266,7 @@ public boolean registerAsAccessed() {
}

public boolean registerAsRead(MethodTypeFlow method) {
boolean firstAttempt = AtomicUtils.atomicMark(isRead);
boolean firstAttempt = AtomicUtils.atomicMark(this, isReadUpdater);
notifyUpdateAccessInfo();
if (readBy != null && method != null) {
readBy.put(method, Boolean.TRUE);
Expand All @@ -270,7 +286,7 @@ public boolean registerAsRead(MethodTypeFlow method) {
* for an unsafe accessed field.
*/
public boolean registerAsWritten(MethodTypeFlow method) {
boolean firstAttempt = AtomicUtils.atomicMark(isWritten);
boolean firstAttempt = AtomicUtils.atomicMark(this, isWrittenUpdater);
notifyUpdateAccessInfo();
if (writtenBy != null && method != null) {
writtenBy.put(method, Boolean.TRUE);
Expand All @@ -285,7 +301,7 @@ public boolean registerAsWritten(MethodTypeFlow method) {
}

public void markFolded() {
if (AtomicUtils.atomicMark(isFolded)) {
if (AtomicUtils.atomicMark(this, isFoldedUpdater)) {
getDeclaringClass().registerAsReachable();
onReachable();
}
Expand All @@ -305,9 +321,9 @@ public void registerAsUnsafeAccessed(UnsafePartitionKind partitionKind) {
* only register fields as unsafe accessed with their declaring type once.
*/

if (!isUnsafeAccessed.getAndSet(true)) {
if (isUnsafeAccessedUpdater.getAndSet(this, 1) != 1) {
/*
* The atomic boolean ensures that the field is registered as unsafe accessed with its
* The atomic updater ensures that the field is registered as unsafe accessed with its
* declaring class only once. However, at the end of this call the registration might
* still be in progress. The first thread that calls this methods enters the if and
* starts the registration, the next threads return right away, while the registration
Expand All @@ -329,7 +345,7 @@ public void registerAsUnsafeAccessed(UnsafePartitionKind partitionKind) {
}

public boolean isUnsafeAccessed() {
return isUnsafeAccessed.get();
return AtomicUtils.isSet(this, isUnsafeAccessedUpdater);
}

public void registerAsJNIAccessed() {
Expand All @@ -341,11 +357,11 @@ public boolean isJNIAccessed() {
}

public void setUnsafeFrozenTypeState(boolean value) {
unsafeFrozenTypeState.getAndSet(value);
unsafeFrozenTypeStateUpdater.set(this, value ? 1 : 0);
}

public boolean hasUnsafeFrozenTypeState() {
return unsafeFrozenTypeState.get();
return AtomicUtils.isSet(this, unsafeFrozenTypeStateUpdater);
}

public Set<MethodTypeFlow> getReadBy() {
Expand Down Expand Up @@ -376,24 +392,26 @@ public Set<MethodTypeFlow> getWrittenBy() {
* DirectByteBuffer remains reachable.
*/
public boolean isAccessed() {
return isAccessed.get() || isRead.get() || (isWritten.get() && (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object));
return AtomicUtils.isSet(this, isAccessedUpdater) || AtomicUtils.isSet(this, isReadUpdater) ||
(AtomicUtils.isSet(this, isWrittenUpdater) && (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object));
}

public boolean isRead() {
return isAccessed.get() || isRead.get();
return AtomicUtils.isSet(this, isAccessedUpdater) || AtomicUtils.isSet(this, isReadUpdater);
}

public boolean isWritten() {
return isAccessed.get() || isWritten.get();
return AtomicUtils.isSet(this, isAccessedUpdater) || AtomicUtils.isSet(this, isWrittenUpdater);
}

public boolean isFolded() {
return isFolded.get();
return AtomicUtils.isSet(this, isFoldedUpdater);
}

@Override
public boolean isReachable() {
return isAccessed.get() || isRead.get() || isWritten.get() || isFolded.get();
return AtomicUtils.isSet(this, isAccessedUpdater) || AtomicUtils.isSet(this, isReadUpdater) ||
AtomicUtils.isSet(this, isWrittenUpdater) || AtomicUtils.isSet(this, isFoldedUpdater);
}

@Override
Expand Down
Loading

0 comments on commit 15c747c

Please sign in to comment.