Skip to content

Commit 15c747c

Browse files
committed
[GR-38182] Reduce analysis memory footprint.
PullRequest: graal/11640
2 parents 25dbe69 + 104a3f5 commit 15c747c

File tree

7 files changed

+513
-87
lines changed

7 files changed

+513
-87
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisElement.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
import java.util.Set;
2929
import java.util.concurrent.ConcurrentHashMap;
3030
import java.util.concurrent.atomic.AtomicBoolean;
31+
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
3132
import java.util.function.BiConsumer;
3233
import java.util.function.Consumer;
3334

35+
import com.oracle.graal.pointsto.util.ConcurrentLightHashSet;
3436
import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;
3537

3638
import com.oracle.graal.pointsto.PointsToAnalysis;
@@ -41,20 +43,24 @@ public abstract class AnalysisElement {
4143
* Contains reachability handlers that are notified when the element is marked as reachable.
4244
* Each handler is notified only once, and then it is removed from the set.
4345
*/
44-
private final Set<ElementReachableNotification> elementReachableNotifications = ConcurrentHashMap.newKeySet();
46+
47+
private static final AtomicReferenceFieldUpdater<AnalysisElement, Object> reachableNotificationsUpdater = AtomicReferenceFieldUpdater
48+
.newUpdater(AnalysisElement.class, Object.class, "elementReachableNotifications");
49+
50+
@SuppressWarnings("unused") private volatile Object elementReachableNotifications;
4551

4652
public void registerReachabilityNotification(ElementReachableNotification notification) {
47-
elementReachableNotifications.add(notification);
53+
ConcurrentLightHashSet.addElement(this, reachableNotificationsUpdater, notification);
4854
}
4955

5056
public void notifyReachabilityCallback(AnalysisUniverse universe, ElementReachableNotification notification) {
5157
notification.notifyCallback(universe, this);
52-
elementReachableNotifications.remove(notification);
58+
ConcurrentLightHashSet.removeElement(this, reachableNotificationsUpdater, notification);
5359
}
5460

5561
protected void notifyReachabilityCallbacks(AnalysisUniverse universe) {
56-
elementReachableNotifications.forEach(c -> c.notifyCallback(universe, this));
57-
elementReachableNotifications.removeIf(ElementReachableNotification::isNotified);
62+
ConcurrentLightHashSet.forEach(this, reachableNotificationsUpdater, (ElementReachableNotification c) -> c.notifyCallback(universe, this));
63+
ConcurrentLightHashSet.removeElementIf(this, reachableNotificationsUpdater, ElementReachableNotification::isNotified);
5864
}
5965

6066
public abstract boolean isReachable();

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import java.util.Set;
3131
import java.util.concurrent.ConcurrentHashMap;
3232
import java.util.concurrent.ConcurrentMap;
33-
import java.util.concurrent.atomic.AtomicBoolean;
33+
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
3434
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
3535

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

61+
private static final AtomicIntegerFieldUpdater<AnalysisField> isAccessedUpdater = AtomicIntegerFieldUpdater
62+
.newUpdater(AnalysisField.class, "isAccessed");
63+
64+
private static final AtomicIntegerFieldUpdater<AnalysisField> isReadUpdater = AtomicIntegerFieldUpdater
65+
.newUpdater(AnalysisField.class, "isRead");
66+
67+
private static final AtomicIntegerFieldUpdater<AnalysisField> isWrittenUpdater = AtomicIntegerFieldUpdater
68+
.newUpdater(AnalysisField.class, "isWritten");
69+
70+
private static final AtomicIntegerFieldUpdater<AnalysisField> isFoldedUpdater = AtomicIntegerFieldUpdater
71+
.newUpdater(AnalysisField.class, "isFolded");
72+
73+
private static final AtomicIntegerFieldUpdater<AnalysisField> isUnsafeAccessedUpdater = AtomicIntegerFieldUpdater
74+
.newUpdater(AnalysisField.class, "isUnsafeAccessed");
75+
76+
private static final AtomicIntegerFieldUpdater<AnalysisField> unsafeFrozenTypeStateUpdater = AtomicIntegerFieldUpdater
77+
.newUpdater(AnalysisField.class, "unsafeFrozenTypeState");
78+
6179
private final int id;
6280

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

77-
private AtomicBoolean isAccessed = new AtomicBoolean();
78-
private AtomicBoolean isRead = new AtomicBoolean();
79-
private AtomicBoolean isWritten = new AtomicBoolean();
80-
private AtomicBoolean isFolded = new AtomicBoolean();
95+
@SuppressWarnings("unused") private volatile int isAccessed;
96+
@SuppressWarnings("unused") private volatile int isRead;
97+
@SuppressWarnings("unused") private volatile int isWritten;
98+
@SuppressWarnings("unused") private volatile int isFolded;
8199

82100
private boolean isJNIAccessed;
83101
private boolean isUsedInComparison;
84-
private AtomicBoolean isUnsafeAccessed;
85-
private AtomicBoolean unsafeFrozenTypeState;
102+
@SuppressWarnings("unused") private volatile int isUnsafeAccessed;
103+
@SuppressWarnings("unused") private volatile int unsafeFrozenTypeState;
86104
@SuppressWarnings("unused") private volatile Object observers;
87105

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

108126
this.position = -1;
109-
this.isUnsafeAccessed = new AtomicBoolean();
110-
this.unsafeFrozenTypeState = new AtomicBoolean();
111127

112128
this.wrapped = wrappedField;
113129
this.id = universe.nextFieldId.getAndIncrement();
@@ -153,30 +169,30 @@ private static AnalysisType getDeclaredType(AnalysisUniverse universe, ResolvedJ
153169
}
154170

155171
public void copyAccessInfos(AnalysisField other) {
156-
this.isAccessed = new AtomicBoolean(other.isAccessed.get());
157-
this.isUnsafeAccessed = other.isUnsafeAccessed;
172+
isAccessedUpdater.set(this, other.isAccessed);
173+
isUnsafeAccessedUpdater.set(this, other.isUnsafeAccessed);
158174
this.canBeNull = other.canBeNull;
159-
this.isWritten = new AtomicBoolean(other.isWritten.get());
160-
this.isFolded = new AtomicBoolean(other.isFolded.get());
161-
this.isRead = new AtomicBoolean(other.isRead.get());
175+
isWrittenUpdater.set(this, other.isWritten);
176+
isFoldedUpdater.set(this, other.isFolded);
177+
isReadUpdater.set(this, other.isRead);
162178
notifyUpdateAccessInfo();
163179
}
164180

165181
public void intersectAccessInfos(AnalysisField other) {
166-
this.isAccessed = new AtomicBoolean(this.isAccessed.get() && other.isAccessed.get());
182+
isAccessedUpdater.set(this, this.isAccessed & other.isAccessed);
167183
this.canBeNull = this.canBeNull && other.canBeNull;
168-
this.isWritten = new AtomicBoolean(this.isWritten.get() && other.isWritten.get());
169-
this.isFolded = new AtomicBoolean(this.isFolded.get() && other.isFolded.get());
170-
this.isRead = new AtomicBoolean(this.isRead.get() && other.isRead.get());
184+
isWrittenUpdater.set(this, this.isWritten & other.isWritten);
185+
isFoldedUpdater.set(this, this.isFolded & other.isFolded);
186+
isReadUpdater.set(this, this.isRead & other.isRead);
171187
notifyUpdateAccessInfo();
172188
}
173189

174190
public void clearAccessInfos() {
175-
this.isAccessed.set(false);
191+
isAccessedUpdater.set(this, 0);
176192
this.canBeNull = true;
177-
this.isWritten.set(false);
178-
this.isFolded.set(false);
179-
this.isRead.set(false);
193+
isWrittenUpdater.set(this, 0);
194+
isFoldedUpdater.set(this, 0);
195+
isReadUpdater.set(this, 0);
180196
notifyUpdateAccessInfo();
181197
}
182198

@@ -239,7 +255,7 @@ public void cleanupAfterAnalysis() {
239255
}
240256

241257
public boolean registerAsAccessed() {
242-
boolean firstAttempt = AtomicUtils.atomicMark(isAccessed);
258+
boolean firstAttempt = AtomicUtils.atomicMark(this, isAccessedUpdater);
243259
notifyUpdateAccessInfo();
244260
if (firstAttempt) {
245261
onReachable();
@@ -250,7 +266,7 @@ public boolean registerAsAccessed() {
250266
}
251267

252268
public boolean registerAsRead(MethodTypeFlow method) {
253-
boolean firstAttempt = AtomicUtils.atomicMark(isRead);
269+
boolean firstAttempt = AtomicUtils.atomicMark(this, isReadUpdater);
254270
notifyUpdateAccessInfo();
255271
if (readBy != null && method != null) {
256272
readBy.put(method, Boolean.TRUE);
@@ -270,7 +286,7 @@ public boolean registerAsRead(MethodTypeFlow method) {
270286
* for an unsafe accessed field.
271287
*/
272288
public boolean registerAsWritten(MethodTypeFlow method) {
273-
boolean firstAttempt = AtomicUtils.atomicMark(isWritten);
289+
boolean firstAttempt = AtomicUtils.atomicMark(this, isWrittenUpdater);
274290
notifyUpdateAccessInfo();
275291
if (writtenBy != null && method != null) {
276292
writtenBy.put(method, Boolean.TRUE);
@@ -285,7 +301,7 @@ public boolean registerAsWritten(MethodTypeFlow method) {
285301
}
286302

287303
public void markFolded() {
288-
if (AtomicUtils.atomicMark(isFolded)) {
304+
if (AtomicUtils.atomicMark(this, isFoldedUpdater)) {
289305
getDeclaringClass().registerAsReachable();
290306
onReachable();
291307
}
@@ -305,9 +321,9 @@ public void registerAsUnsafeAccessed(UnsafePartitionKind partitionKind) {
305321
* only register fields as unsafe accessed with their declaring type once.
306322
*/
307323

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

331347
public boolean isUnsafeAccessed() {
332-
return isUnsafeAccessed.get();
348+
return AtomicUtils.isSet(this, isUnsafeAccessedUpdater);
333349
}
334350

335351
public void registerAsJNIAccessed() {
@@ -341,11 +357,11 @@ public boolean isJNIAccessed() {
341357
}
342358

343359
public void setUnsafeFrozenTypeState(boolean value) {
344-
unsafeFrozenTypeState.getAndSet(value);
360+
unsafeFrozenTypeStateUpdater.set(this, value ? 1 : 0);
345361
}
346362

347363
public boolean hasUnsafeFrozenTypeState() {
348-
return unsafeFrozenTypeState.get();
364+
return AtomicUtils.isSet(this, unsafeFrozenTypeStateUpdater);
349365
}
350366

351367
public Set<MethodTypeFlow> getReadBy() {
@@ -376,24 +392,26 @@ public Set<MethodTypeFlow> getWrittenBy() {
376392
* DirectByteBuffer remains reachable.
377393
*/
378394
public boolean isAccessed() {
379-
return isAccessed.get() || isRead.get() || (isWritten.get() && (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object));
395+
return AtomicUtils.isSet(this, isAccessedUpdater) || AtomicUtils.isSet(this, isReadUpdater) ||
396+
(AtomicUtils.isSet(this, isWrittenUpdater) && (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object));
380397
}
381398

382399
public boolean isRead() {
383-
return isAccessed.get() || isRead.get();
400+
return AtomicUtils.isSet(this, isAccessedUpdater) || AtomicUtils.isSet(this, isReadUpdater);
384401
}
385402

386403
public boolean isWritten() {
387-
return isAccessed.get() || isWritten.get();
404+
return AtomicUtils.isSet(this, isAccessedUpdater) || AtomicUtils.isSet(this, isWrittenUpdater);
388405
}
389406

390407
public boolean isFolded() {
391-
return isFolded.get();
408+
return AtomicUtils.isSet(this, isFoldedUpdater);
392409
}
393410

394411
@Override
395412
public boolean isReachable() {
396-
return isAccessed.get() || isRead.get() || isWritten.get() || isFolded.get();
413+
return AtomicUtils.isSet(this, isAccessedUpdater) || AtomicUtils.isSet(this, isReadUpdater) ||
414+
AtomicUtils.isSet(this, isWrittenUpdater) || AtomicUtils.isSet(this, isFoldedUpdater);
397415
}
398416

399417
@Override

0 commit comments

Comments
 (0)