Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,10 @@ public boolean isClosedTypeWorld() {
return true;
}

public boolean enableTrackAcrossLayers() {
return false;
}

/**
* Helpers to determine what analysis actions should be taken for a given Multi-Method version.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,6 @@ public void persistImageHeapSize(long imageHeapSize) {
jsonMap.put(IMAGE_HEAP_SIZE_TAG, String.valueOf(imageHeapSize));
}

protected boolean shouldPersistMethod(AnalysisMethod method) {
return method.isReachable();
}

public void persistAnalysisInfo() {
persistHook();

Expand All @@ -289,18 +285,18 @@ public void persistAnalysisInfo() {
* removed after a mechanism for determining which types have to be persisted is added, or
* if a stable name is implemented for them.
*/
for (AnalysisType type : aUniverse.getTypes().stream().filter(AnalysisType::isReachable).toList()) {
for (AnalysisType type : aUniverse.getTypes().stream().filter(AnalysisType::isTrackedAcrossLayers).toList()) {
checkTypeStability(type);
persistType(type);
}
jsonMap.put(TYPES_TAG, typesMap);

for (AnalysisMethod method : aUniverse.getMethods().stream().filter(this::shouldPersistMethod).toList()) {
for (AnalysisMethod method : aUniverse.getMethods().stream().filter(AnalysisMethod::isTrackedAcrossLayers).toList()) {
persistMethod(method);
}
jsonMap.put(METHODS_TAG, methodsMap);

for (AnalysisField field : aUniverse.getFields().stream().filter(AnalysisField::isReachable).toList()) {
for (AnalysisField field : aUniverse.getFields().stream().filter(AnalysisField::isTrackedAcrossLayers).toList()) {
persistField(field);
}
jsonMap.put(FIELDS_TAG, fieldsMap);
Expand Down Expand Up @@ -448,7 +444,7 @@ public boolean isMethodPersisted(AnalysisMethod method) {

public void persistMethodGraphs() {
for (AnalysisMethod method : aUniverse.getMethods()) {
if (method.isReachable()) {
if (method.isTrackedAcrossLayers()) {
persistAnalysisParsedGraph(method);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import jdk.graal.compiler.debug.GraalError;
import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;

import com.oracle.graal.pointsto.BigBang;
Expand All @@ -49,6 +48,7 @@
import com.oracle.graal.pointsto.util.AnalysisFuture;
import com.oracle.graal.pointsto.util.ConcurrentLightHashSet;

import jdk.graal.compiler.debug.GraalError;
import jdk.vm.ci.code.BytecodePosition;
import jdk.vm.ci.meta.ModifiersProvider;
import jdk.vm.ci.meta.ResolvedJavaField;
Expand Down Expand Up @@ -112,7 +112,12 @@ protected void notifyReachabilityCallbacks(AnalysisUniverse universe, List<Analy

public abstract boolean isReachable();

protected abstract void onReachable();
protected abstract void onReachable(Object reason);

/**
* Indicates we need this information to be saved in the layer archive file.
*/
public abstract boolean isTrackedAcrossLayers();

/** Return true if reachability handlers should be executed for this element. */
public boolean isTriggered() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ public abstract class AnalysisField extends AnalysisElement implements WrappedJa
private static final AtomicReferenceFieldUpdater<AnalysisField, Object> isUnsafeAccessedUpdater = AtomicReferenceFieldUpdater
.newUpdater(AnalysisField.class, Object.class, "isUnsafeAccessed");

private static final AtomicReferenceFieldUpdater<AnalysisField, Object> trackAcrossLayersUpdater = AtomicReferenceFieldUpdater
.newUpdater(AnalysisField.class, Object.class, "trackAcrossLayers");

private final int id;
/** Marks a field loaded from a base layer. */
private final boolean isInBaseLayer;
Expand All @@ -90,6 +93,12 @@ public abstract class AnalysisField extends AnalysisElement implements WrappedJa
@SuppressWarnings("unused") private volatile Object isFolded;
@SuppressWarnings("unused") private volatile Object isUnsafeAccessed;

/**
* See {@link AnalysisElement#isTrackedAcrossLayers} for explanation.
*/
@SuppressWarnings("unused") private volatile Object trackAcrossLayers;
private final boolean enableTrackAcrossLayers;

private ConcurrentMap<Object, Boolean> readBy;
private ConcurrentMap<Object, Boolean> writtenBy;

Expand Down Expand Up @@ -150,6 +159,8 @@ public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField)
id = universe.computeNextFieldId();
isInBaseLayer = false;
}

this.enableTrackAcrossLayers = universe.hostVM.enableTrackAcrossLayers();
}

@Override
Expand Down Expand Up @@ -220,13 +231,11 @@ public boolean registerAsAccessed(Object reason) {
getDeclaringClass().registerAsReachable(this);

assert isValidReason(reason) : "Registering a field as accessed needs to provide a valid reason.";
boolean firstAttempt = AtomicUtils.atomicSet(this, reason, isAccessedUpdater);
if (firstAttempt) {
onReachable();
return AtomicUtils.atomicSetAndRun(this, reason, isAccessedUpdater, () -> {
onReachable(reason);
getUniverse().onFieldAccessed(this);
getUniverse().getHeapScanner().onFieldRead(this);
}
return firstAttempt;
});
}

/**
Expand All @@ -236,16 +245,14 @@ public boolean registerAsRead(Object reason) {
getDeclaringClass().registerAsReachable(this);

assert isValidReason(reason) : "Registering a field as read needs to provide a valid reason.";
boolean firstAttempt = AtomicUtils.atomicSet(this, reason, isReadUpdater);
if (readBy != null) {
readBy.put(reason, Boolean.TRUE);
}
if (firstAttempt) {
onReachable();
return AtomicUtils.atomicSetAndRun(this, reason, isReadUpdater, () -> {
onReachable(reason);
getUniverse().onFieldAccessed(this);
getUniverse().getHeapScanner().onFieldRead(this);
}
return firstAttempt;
});
}

/**
Expand All @@ -257,27 +264,25 @@ public boolean registerAsWritten(Object reason) {
getDeclaringClass().registerAsReachable(this);

assert isValidReason(reason) : "Registering a field as written needs to provide a valid reason.";
boolean firstAttempt = AtomicUtils.atomicSet(this, reason, isWrittenUpdater);
if (writtenBy != null && reason != null) {
if (writtenBy != null) {
writtenBy.put(reason, Boolean.TRUE);
}
if (firstAttempt) {
onReachable();
return AtomicUtils.atomicSetAndRun(this, reason, isWrittenUpdater, () -> {
onReachable(reason);
if (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object) {
getUniverse().onFieldAccessed(this);
}
}
return firstAttempt;
});
}

public void registerAsFolded(Object reason) {
getDeclaringClass().registerAsReachable(this);

assert isValidReason(reason) : "Registering a field as folded needs to provide a valid reason.";
if (AtomicUtils.atomicSet(this, reason, isFoldedUpdater)) {
AtomicUtils.atomicSetAndRun(this, reason, isFoldedUpdater, () -> {
assert getDeclaringClass().isReachable() : this;
onReachable();
}
onReachable(reason);
});
}

public boolean registerAsUnsafeAccessed(Object reason) {
Expand All @@ -292,7 +297,7 @@ public boolean registerAsUnsafeAccessed(Object reason) {
* only register fields as unsafe accessed with their declaring type once.
*/

if (AtomicUtils.atomicSet(this, reason, isUnsafeAccessedUpdater)) {
return AtomicUtils.atomicSetAndRun(this, reason, isUnsafeAccessedUpdater, () -> {
/*
* 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
Expand All @@ -311,9 +316,7 @@ public boolean registerAsUnsafeAccessed(Object reason) {
AnalysisType declaringType = getDeclaringClass();
declaringType.registerUnsafeAccessedField(this);
}
return true;
}
return false;
});
}

public boolean isUnsafeAccessed() {
Expand Down Expand Up @@ -379,10 +382,18 @@ public boolean isReachable() {
}

@Override
public void onReachable() {
public void onReachable(Object reason) {
if (enableTrackAcrossLayers) {
AtomicUtils.atomicSet(this, reason, trackAcrossLayersUpdater);
}
notifyReachabilityCallbacks(declaringClass.getUniverse(), new ArrayList<>());
}

@Override
public boolean isTrackedAcrossLayers() {
return AtomicUtils.isSet(this, trackAcrossLayersUpdater);
}

public Object getFieldValueInterceptor() {
return fieldValueInterceptor;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ public abstract class AnalysisMethod extends AnalysisElement implements WrappedJ
static final AtomicReferenceFieldUpdater<AnalysisMethod, Object> allImplementationsUpdater = AtomicReferenceFieldUpdater
.newUpdater(AnalysisMethod.class, Object.class, "allImplementations");

private static final AtomicReferenceFieldUpdater<AnalysisMethod, Object> trackAcrossLayersUpdater = AtomicReferenceFieldUpdater
.newUpdater(AnalysisMethod.class, Object.class, "trackAcrossLayers");

public record Signature(String name, AnalysisType[] parameterTypes) {
}

Expand Down Expand Up @@ -181,6 +184,12 @@ public record Signature(String name, AnalysisType[] parameterTypes) {
*/
@SuppressWarnings("unused") private volatile Object allImplementations;

/**
* See {@link AnalysisElement#isTrackedAcrossLayers} for explanation.
*/
@SuppressWarnings("unused") private volatile Object trackAcrossLayers;
private final boolean enableTrackAcrossLayers;

/**
* Indicates that this method has opaque return. This is necessary when there are control flows
* present which cannot be tracked by analysis, which happens for continuation support.
Expand Down Expand Up @@ -265,6 +274,8 @@ protected AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped,
startTrackInvocations();
}
parsingContextMaxDepth = PointstoOptions.ParsingContextMaxDepth.getValue(declaringClass.universe.hostVM.options());

this.enableTrackAcrossLayers = universe.hostVM.enableTrackAcrossLayers();
}

@SuppressWarnings("this-escape")
Expand Down Expand Up @@ -292,6 +303,8 @@ protected AnalysisMethod(AnalysisMethod original, MultiMethodKey multiMethodKey)
if (PointstoOptions.TrackAccessChain.getValue(declaringClass.universe.hostVM().options())) {
startTrackInvocations();
}

this.enableTrackAcrossLayers = original.enableTrackAcrossLayers;
}

private static String createName(ResolvedJavaMethod wrapped, MultiMethodKey multiMethodKey) {
Expand Down Expand Up @@ -464,7 +477,7 @@ public boolean analyzedInPriorLayer() {
*/
public void registerAsIntrinsicMethod(Object reason) {
assert isValidReason(reason) : "Registering a method as intrinsic needs to provide a valid reason, found: " + reason;
AtomicUtils.atomicSetAndRun(this, reason, isIntrinsicMethodUpdater, this::onImplementationInvoked);
AtomicUtils.atomicSetAndRun(this, reason, isIntrinsicMethodUpdater, () -> onImplementationInvoked(reason));
}

public void registerAsEntryPoint(Object newEntryPointData) {
Expand Down Expand Up @@ -495,12 +508,12 @@ public boolean registerAsImplementationInvoked(Object reason) {
* return before the class gets marked as reachable.
*/
getDeclaringClass().registerAsReachable("declared method " + qualifiedName + " is registered as implementation invoked");
return AtomicUtils.atomicSetAndRun(this, reason, isImplementationInvokedUpdater, this::onImplementationInvoked);
return AtomicUtils.atomicSetAndRun(this, reason, isImplementationInvokedUpdater, () -> onImplementationInvoked(reason));
}

public void registerAsInlined(Object reason) {
assert reason instanceof NodeSourcePosition || reason instanceof ResolvedJavaMethod : "Registering a method as inlined needs to provide the inline location as reason, found: " + reason;
AtomicUtils.atomicSetAndRun(this, reason, isInlinedUpdater, this::onReachable);
AtomicUtils.atomicSetAndRun(this, reason, isInlinedUpdater, () -> onReachable(reason));
}

public void registerImplementationInvokedCallback(Consumer<DuringAnalysisAccess> callback) {
Expand Down Expand Up @@ -636,6 +649,11 @@ public boolean isReachable() {
return isImplementationInvoked() || isInlined();
}

@Override
public boolean isTrackedAcrossLayers() {
return AtomicUtils.isSet(this, trackAcrossLayersUpdater);
}

@Override
public boolean isTriggered() {
if (isReachable()) {
Expand All @@ -644,13 +662,16 @@ public boolean isTriggered() {
return isClassInitializer() && getDeclaringClass().isInitialized();
}

public void onImplementationInvoked() {
onReachable();
public void onImplementationInvoked(Object reason) {
onReachable(reason);
notifyImplementationInvokedCallbacks();
}

@Override
public void onReachable() {
public void onReachable(Object reason) {
if (enableTrackAcrossLayers) {
AtomicUtils.atomicSet(this, reason, trackAcrossLayersUpdater);
}
notifyReachabilityCallbacks(declaringClass.getUniverse(), new ArrayList<>());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ public abstract class AnalysisType extends AnalysisElement implements WrappedJav
static final AtomicReferenceFieldUpdater<AnalysisType, Object> overrideableMethodsUpdater = AtomicReferenceFieldUpdater
.newUpdater(AnalysisType.class, Object.class, "overrideableMethods");

private static final AtomicReferenceFieldUpdater<AnalysisType, Object> trackAcrossLayersUpdater = AtomicReferenceFieldUpdater
.newUpdater(AnalysisType.class, Object.class, "trackAcrossLayers");

protected final AnalysisUniverse universe;
private final ResolvedJavaType wrapped;
private final String qualifiedName;
Expand Down Expand Up @@ -218,6 +221,12 @@ public abstract class AnalysisType extends AnalysisElement implements WrappedJav
*/
@SuppressWarnings("unused") private volatile Object overrideableMethods;

/**
* See {@link AnalysisElement#isTrackedAcrossLayers} for explanation.
*/
@SuppressWarnings("unused") private volatile Object trackAcrossLayers;
private final boolean enableTrackAcrossLayers;

@SuppressWarnings("this-escape")
public AnalysisType(AnalysisUniverse universe, ResolvedJavaType javaType, JavaKind storageKind, AnalysisType objectType, AnalysisType cloneableType) {
this.universe = universe;
Expand Down Expand Up @@ -343,6 +352,8 @@ public AnalysisType(AnalysisUniverse universe, ResolvedJavaType javaType, JavaKi
AnalysisError.guarantee(universe.getHeapScanner() != null, "Heap scanner is not available.");
return universe.getHeapScanner().computeTypeData(this);
});

this.enableTrackAcrossLayers = universe.hostVM.enableTrackAcrossLayers();
}

private AnalysisType[] convertTypes(ResolvedJavaType[] originalTypes) {
Expand Down Expand Up @@ -592,14 +603,18 @@ public boolean registerAsReachable(Object reason) {
* that the onReachable hook for all supertypes is already finished, because they can
* still be running in another thread.
*/
AtomicUtils.atomicSetAndRun(this, reason, isReachableUpdater, this::onReachable);
AtomicUtils.atomicSetAndRun(this, reason, isReachableUpdater, () -> onReachable(reason));
return true;
}
return false;
}

@Override
protected void onReachable() {
protected void onReachable(Object reason) {
if (enableTrackAcrossLayers) {
AtomicUtils.atomicSet(this, reason, trackAcrossLayersUpdater);
}

List<AnalysisFuture<Void>> futures = new ArrayList<>();
notifyReachabilityCallbacks(universe, futures);
forAllSuperTypes(type -> ConcurrentLightHashSet.forEach(type, subtypeReachableNotificationsUpdater,
Expand Down Expand Up @@ -860,6 +875,11 @@ public Object getReachableReason() {
return isReachable;
}

@Override
public boolean isTrackedAcrossLayers() {
return AtomicUtils.isSet(this, trackAcrossLayersUpdater);
}

/**
* The kind of the field in memory (in contrast to {@link #getJavaKind()}, which is the kind of
* the field on the Java type system level). For example {@link WordBase word types} have a
Expand Down
Loading