Skip to content

Commit 0517eee

Browse files
author
Christian Wimmer
committed
[WIP] [GR-54862] Fix scalability bottlenecks in the static analysis.
PullRequest: graal/18105
2 parents ac12d6f + 9456187 commit 0517eee

File tree

19 files changed

+218
-234
lines changed

19 files changed

+218
-234
lines changed

substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/features/StandaloneAnalysisFeatureImpl.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public Set<Executable> reachableMethodOverrides(Executable baseMethod) {
158158
}
159159

160160
Set<AnalysisMethod> reachableMethodOverrides(AnalysisMethod baseMethod) {
161-
return AnalysisUniverse.getMethodImplementations(baseMethod, true);
161+
return baseMethod.collectMethodImplementations(true);
162162
}
163163
}
164164

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -597,10 +597,12 @@ public TypeFlow<?> getTypeFlow() {
597597
@Override
598598
public boolean finish() throws InterruptedException {
599599
try (Indent indent = debug.logAndIndent("starting analysis in BigBang.finish")) {
600-
universe.setAnalysisDataValid(false);
601-
boolean didSomeWork = doTypeflow();
602-
assert executor.getPostedOperations() == 0 : executor.getPostedOperations();
603-
universe.setAnalysisDataValid(true);
600+
boolean didSomeWork = false;
601+
do {
602+
didSomeWork |= doTypeflow();
603+
assert executor.getPostedOperations() == 0 : executor.getPostedOperations();
604+
universe.runAtFixedPoint();
605+
} while (executor.getPostedOperations() > 0);
604606
return didSomeWork;
605607
}
606608
}

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

+53-81
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.ArrayList;
3535
import java.util.Collection;
3636
import java.util.Collections;
37+
import java.util.HashSet;
3738
import java.util.List;
3839
import java.util.Map;
3940
import java.util.Set;
@@ -107,6 +108,9 @@ public abstract class AnalysisMethod extends AnalysisElement implements WrappedJ
107108
private static final AtomicReferenceFieldUpdater<AnalysisMethod, Object> isInlinedUpdater = AtomicReferenceFieldUpdater
108109
.newUpdater(AnalysisMethod.class, Object.class, "isInlined");
109110

111+
static final AtomicReferenceFieldUpdater<AnalysisMethod, Object> allImplementationsUpdater = AtomicReferenceFieldUpdater
112+
.newUpdater(AnalysisMethod.class, Object.class, "allImplementations");
113+
110114
public record Signature(String name, AnalysisType[] parameterTypes) {
111115
}
112116

@@ -120,6 +124,7 @@ public record Signature(String name, AnalysisType[] parameterTypes) {
120124
private final LocalVariableTable localVariableTable;
121125
private final String name;
122126
private final String qualifiedName;
127+
private final int modifiers;
123128

124129
protected final AnalysisType declaringClass;
125130
protected final ResolvedSignature<AnalysisType> signature;
@@ -164,10 +169,12 @@ public record Signature(String name, AnalysisType[] parameterTypes) {
164169
private EncodedGraph analyzedGraph;
165170

166171
/**
167-
* All concrete methods that can actually be called when calling this method. This includes all
168-
* overridden methods in subclasses, as well as this method if it is non-abstract.
172+
* Concrete methods that could possibly be called when calling this method. This also includes
173+
* methods that are not reachable yet, i.e., this set must be filtered before it can be used. It
174+
* never includes the method itself to reduce the size. See
175+
* {@link AnalysisMethod#collectMethodImplementations} for more details.
169176
*/
170-
protected AnalysisMethod[] implementations;
177+
@SuppressWarnings("unused") private volatile Object allImplementations;
171178

172179
/**
173180
* Indicates that this method returns all instantiated types. This is necessary when there are
@@ -189,6 +196,7 @@ protected AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped,
189196

190197
name = createName(wrapped, multiMethodKey);
191198
qualifiedName = format("%H.%n(%P)");
199+
modifiers = wrapped.getModifiers();
192200

193201
if (universe.hostVM().useBaseLayer()) {
194202
int mid = universe.getImageLayerLoader().lookupHostedMethodInBaseLayer(this);
@@ -258,6 +266,7 @@ protected AnalysisMethod(AnalysisMethod original, MultiMethodKey multiMethodKey)
258266

259267
name = createName(wrapped, multiMethodKey);
260268
qualifiedName = format("%H.%n(%P)");
269+
modifiers = original.modifiers;
261270

262271
this.multiMethodKey = multiMethodKey;
263272
assert original.multiMethodMap != null;
@@ -550,81 +559,10 @@ public void onImplementationInvoked() {
550559
@Override
551560
public void onReachable() {
552561
notifyReachabilityCallbacks(declaringClass.getUniverse(), new ArrayList<>());
553-
processMethodOverrides();
554-
}
555-
556-
private void processMethodOverrides() {
557-
if (wrapped.canBeStaticallyBound() || isConstructor()) {
558-
notifyMethodOverride(this);
559-
} else if (declaringClass.isAnySubtypeInstantiated()) {
560-
/*
561-
* If neither the declaring class nor a subtype is instantiated then this method cannot
562-
* be marked as invoked, so it cannot be an override.
563-
*/
564-
declaringClass.forAllSuperTypes(superType -> {
565-
/*
566-
* Iterate all the super types (including this type itself) looking for installed
567-
* override notifications. If this method is found in a super type, and it has an
568-
* override handler installed in that type, pass this method to the callback. It
569-
* doesn't matter if the superMethod is actually reachable, only if it has any
570-
* override handlers installed. Note that ResolvedJavaType.resolveMethod() cannot be
571-
* used here because it only resolves methods declared by the type itself or if the
572-
* method's declaring class is assignable from the type.
573-
*/
574-
AnalysisMethod superMethod = findInType(superType);
575-
if (superMethod != null) {
576-
superMethod.notifyMethodOverride(AnalysisMethod.this);
577-
}
578-
});
579-
}
580-
}
581-
582-
/** Find if the type declares a method with the same name and signature as this method. */
583-
private AnalysisMethod findInType(AnalysisType type) {
584-
try {
585-
return type.findMethod(wrapped.getName(), getSignature());
586-
} catch (UnsupportedFeatureException | LinkageError e) {
587-
/* Ignore linking errors and deleted methods. */
588-
return null;
589-
}
590-
}
591-
592-
protected void notifyMethodOverride(AnalysisMethod override) {
593-
declaringClass.getOverrideReachabilityNotifications(this).forEach(n -> n.notifyCallback(getUniverse(), override));
594562
}
595563

596564
public void registerOverrideReachabilityNotification(MethodOverrideReachableNotification notification) {
597-
declaringClass.registerOverrideReachabilityNotification(this, notification);
598-
}
599-
600-
/**
601-
* Resolves this method in the provided type, but only if the type or any of its subtypes is
602-
* marked as instantiated.
603-
*/
604-
protected AnalysisMethod resolveInType(AnalysisType holder) {
605-
return resolveInType(holder, holder.isAnySubtypeInstantiated());
606-
}
607-
608-
protected AnalysisMethod resolveInType(AnalysisType holder, boolean holderOrSubtypeInstantiated) {
609-
/*
610-
* If the holder and all subtypes are not instantiated, then we do not need to resolve the
611-
* method. The method cannot be marked as invoked.
612-
*/
613-
if (holderOrSubtypeInstantiated || isIntrinsicMethod()) {
614-
AnalysisMethod resolved;
615-
try {
616-
resolved = holder.resolveConcreteMethod(this, null);
617-
} catch (UnsupportedFeatureException e) {
618-
/* An unsupported overriding method is not reachable. */
619-
resolved = null;
620-
}
621-
/*
622-
* resolved == null means that the method in the base class was called, but never with
623-
* this holder.
624-
*/
625-
return resolved;
626-
}
627-
return null;
565+
getUniverse().registerOverrideReachabilityNotification(this, notification);
628566
}
629567

630568
@Override
@@ -699,7 +637,7 @@ public Parameter[] getParameters() {
699637

700638
@Override
701639
public int getModifiers() {
702-
return wrapped.getModifiers();
640+
return modifiers;
703641
}
704642

705643
@Override
@@ -735,12 +673,46 @@ public boolean canBeStaticallyBound() {
735673

736674
}
737675

738-
public AnalysisMethod[] getImplementations() {
739-
assert getUniverse().analysisDataValid : this;
740-
if (implementations == null) {
741-
return new AnalysisMethod[0];
676+
/**
677+
* Returns all methods that override (= implement) this method. If the
678+
* {@code includeInlinedMethods} parameter is true, all reachable overrides are returned; if it
679+
* is false, only invoked methods are returned (and methods that are already inlined at all call
680+
* sites are excluded).
681+
*
682+
* In the parallel static analysis, it is difficult to have this information always available:
683+
* when a method becomes reachable or invoked, it is not known which other methods it overrides.
684+
* Therefore, we collect all possible implementations in {@link #allImplementations} without
685+
* taking reachability into account, and then filter this too-large set of methods here on
686+
* demand.
687+
*/
688+
public Set<AnalysisMethod> collectMethodImplementations(boolean includeInlinedMethods) {
689+
/*
690+
* To keep the allImplementations set as small as possible (and empty for most methods), the
691+
* set never includes this method itself. It is clear that every method is always an
692+
* implementation of itself.
693+
*/
694+
boolean includeOurselfs = (isStatic() || getDeclaringClass().isAnySubtypeInstantiated()) &&
695+
(includeInlinedMethods ? isReachable() : isImplementationInvoked());
696+
697+
int allImplementationsSize = ConcurrentLightHashSet.size(this, allImplementationsUpdater);
698+
if (allImplementationsSize == 0) {
699+
/* Fast-path that avoids allocation of a full HashSet. */
700+
return includeOurselfs ? Set.of(this) : Set.of();
701+
}
702+
703+
Set<AnalysisMethod> result = new HashSet<>(allImplementationsSize + 1);
704+
if (includeOurselfs) {
705+
result.add(this);
742706
}
743-
return implementations;
707+
ConcurrentLightHashSet.forEach(this, allImplementationsUpdater, (AnalysisMethod override) -> {
708+
if (override.getDeclaringClass().isAnySubtypeInstantiated()) {
709+
if (includeInlinedMethods ? override.isReachable() : override.isImplementationInvoked()) {
710+
result.add(override);
711+
}
712+
}
713+
});
714+
715+
return result;
744716
}
745717

746718
@Override

0 commit comments

Comments
 (0)