|
39 | 39 | import java.util.Map;
|
40 | 40 | import java.util.Objects;
|
41 | 41 | import java.util.Set;
|
| 42 | +import java.util.concurrent.ConcurrentHashMap; |
42 | 43 | import java.util.function.BiConsumer;
|
43 | 44 | import java.util.function.Consumer;
|
44 | 45 | import java.util.function.Function;
|
|
56 | 57 | import com.oracle.graal.pointsto.ObjectScanner;
|
57 | 58 | import com.oracle.graal.pointsto.api.DefaultUnsafePartition;
|
58 | 59 | import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor;
|
| 60 | +import com.oracle.graal.pointsto.meta.AnalysisElement; |
59 | 61 | import com.oracle.graal.pointsto.meta.AnalysisField;
|
60 | 62 | import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
|
61 | 63 | import com.oracle.graal.pointsto.meta.AnalysisMethod;
|
|
64 | 66 | import com.oracle.graal.pointsto.meta.ObjectReachableCallback;
|
65 | 67 | import com.oracle.svm.common.meta.MultiMethod;
|
66 | 68 | import com.oracle.svm.core.LinkerInvocation;
|
67 |
| -import com.oracle.svm.core.SubstrateOptions; |
68 | 69 | import com.oracle.svm.core.annotate.Delete;
|
69 | 70 | import com.oracle.svm.core.graal.meta.RuntimeConfiguration;
|
70 | 71 | import com.oracle.svm.core.meta.SharedField;
|
71 | 72 | import com.oracle.svm.core.meta.SharedMethod;
|
72 | 73 | import com.oracle.svm.core.meta.SharedType;
|
73 |
| -import com.oracle.svm.core.option.SubstrateOptionsParser; |
| 74 | +import com.oracle.svm.core.util.UserError; |
74 | 75 | import com.oracle.svm.core.util.VMError;
|
75 | 76 | import com.oracle.svm.hosted.ameta.FieldValueInterceptionSupport;
|
76 | 77 | import com.oracle.svm.hosted.analysis.Inflation;
|
@@ -336,14 +337,11 @@ public SVMHost getHostVM() {
|
336 | 337 | public static class BeforeAnalysisAccessImpl extends AnalysisAccessBase implements Feature.BeforeAnalysisAccess {
|
337 | 338 |
|
338 | 339 | private final NativeLibraries nativeLibraries;
|
339 |
| - private final boolean concurrentReachabilityHandlers; |
340 |
| - private final ReachabilityHandler reachabilityHandler; |
| 340 | + private final Map<Consumer<DuringAnalysisAccess>, AnalysisElement.ElementNotification> reachabilityNotifications = new ConcurrentHashMap<>(); |
341 | 341 |
|
342 | 342 | public BeforeAnalysisAccessImpl(FeatureHandler featureHandler, ImageClassLoader imageClassLoader, Inflation bb, NativeLibraries nativeLibraries, DebugContext debugContext) {
|
343 | 343 | super(featureHandler, imageClassLoader, bb, debugContext);
|
344 | 344 | this.nativeLibraries = nativeLibraries;
|
345 |
| - this.concurrentReachabilityHandlers = SubstrateOptions.RunReachabilityHandlersConcurrently.getValue(bb.getOptions()); |
346 |
| - this.reachabilityHandler = concurrentReachabilityHandlers ? ConcurrentReachabilityHandler.singleton() : ReachabilityHandlerFeature.singleton(); |
347 | 345 | }
|
348 | 346 |
|
349 | 347 | public NativeLibraries getNativeLibraries() {
|
@@ -450,26 +448,89 @@ public void registerHierarchyForReflectiveInstantiation(Class<?> c) {
|
450 | 448 |
|
451 | 449 | @Override
|
452 | 450 | public void registerReachabilityHandler(Consumer<DuringAnalysisAccess> callback, Object... elements) {
|
453 |
| - reachabilityHandler.registerReachabilityHandler(this, callback, elements); |
| 451 | + AnalysisMetaAccess metaAccess = getMetaAccess(); |
| 452 | + |
| 453 | + /* |
| 454 | + * All callback->notification pairs are tracked by the reachabilityNotifications map to |
| 455 | + * prevent registering the same callback multiple times. The notifications are also |
| 456 | + * tracked by each AnalysisElement, i.e., each trigger, and are removed as soon as they |
| 457 | + * are notified. |
| 458 | + */ |
| 459 | + AnalysisElement.ElementNotification notification = reachabilityNotifications.computeIfAbsent(callback, AnalysisElement.ElementNotification::new); |
| 460 | + |
| 461 | + if (notification.isNotified()) { |
| 462 | + /* Already notified from an earlier registration, nothing to do. */ |
| 463 | + return; |
| 464 | + } |
| 465 | + |
| 466 | + for (Object trigger : elements) { |
| 467 | + AnalysisElement analysisElement; |
| 468 | + if (trigger instanceof Class) { |
| 469 | + analysisElement = metaAccess.lookupJavaType((Class<?>) trigger); |
| 470 | + } else if (trigger instanceof Field) { |
| 471 | + analysisElement = metaAccess.lookupJavaField((Field) trigger); |
| 472 | + } else if (trigger instanceof Executable) { |
| 473 | + analysisElement = metaAccess.lookupJavaMethod((Executable) trigger); |
| 474 | + } else { |
| 475 | + throw UserError.abort("'registerReachabilityHandler' called with an element that is not a Class, Field, or Executable: %s", trigger.getClass().getTypeName()); |
| 476 | + } |
| 477 | + |
| 478 | + analysisElement.registerReachabilityNotification(notification); |
| 479 | + if (analysisElement.isTriggered()) { |
| 480 | + /* |
| 481 | + * Element already triggered, just notify the callback. At this point we could |
| 482 | + * just notify the callback and bail out, but, for debugging, it may be useful |
| 483 | + * to execute the notification for each trigger. Note that although the |
| 484 | + * notification can be shared between multiple triggers the notification |
| 485 | + * mechanism ensures that the callback itself is only executed once. |
| 486 | + */ |
| 487 | + analysisElement.notifyReachabilityCallback(getUniverse(), notification); |
| 488 | + } |
| 489 | + } |
454 | 490 | }
|
455 | 491 |
|
456 | 492 | @Override
|
457 | 493 | public void registerMethodOverrideReachabilityHandler(BiConsumer<DuringAnalysisAccess, Executable> callback, Executable baseMethod) {
|
458 |
| - reachabilityHandler.registerMethodOverrideReachabilityHandler(this, callback, baseMethod); |
| 494 | + AnalysisMetaAccess metaAccess = getMetaAccess(); |
| 495 | + AnalysisMethod baseAnalysisMethod = metaAccess.lookupJavaMethod(baseMethod); |
| 496 | + |
| 497 | + AnalysisElement.MethodOverrideReachableNotification notification = new AnalysisElement.MethodOverrideReachableNotification(callback); |
| 498 | + baseAnalysisMethod.registerOverrideReachabilityNotification(notification); |
| 499 | + |
| 500 | + /* |
| 501 | + * Notify for already reachable overrides. When a new override becomes reachable all |
| 502 | + * installed reachability callbacks in the supertypes declaring the method are |
| 503 | + * triggered. |
| 504 | + */ |
| 505 | + for (AnalysisMethod override : reachableMethodOverrides(baseAnalysisMethod)) { |
| 506 | + notification.notifyCallback(metaAccess.getUniverse(), override); |
| 507 | + } |
459 | 508 | }
|
460 | 509 |
|
461 | 510 | @Override
|
462 | 511 | public void registerSubtypeReachabilityHandler(BiConsumer<DuringAnalysisAccess, Class<?>> callback, Class<?> baseClass) {
|
463 |
| - reachabilityHandler.registerSubtypeReachabilityHandler(this, callback, baseClass); |
| 512 | + AnalysisMetaAccess metaAccess = getMetaAccess(); |
| 513 | + AnalysisType baseType = metaAccess.lookupJavaType(baseClass); |
| 514 | + |
| 515 | + AnalysisElement.SubtypeReachableNotification notification = new AnalysisElement.SubtypeReachableNotification(callback); |
| 516 | + baseType.registerSubtypeReachabilityNotification(notification); |
| 517 | + |
| 518 | + /* |
| 519 | + * Notify for already reachable subtypes. When a new type becomes reachable all |
| 520 | + * installed reachability callbacks in the supertypes are triggered. |
| 521 | + */ |
| 522 | + for (AnalysisType subtype : reachableSubtypes(baseType)) { |
| 523 | + notification.notifyCallback(metaAccess.getUniverse(), subtype); |
| 524 | + } |
464 | 525 | }
|
465 | 526 |
|
466 | 527 | @Override
|
467 | 528 | public void registerClassInitializerReachabilityHandler(Consumer<DuringAnalysisAccess> callback, Class<?> clazz) {
|
468 |
| - reachabilityHandler.registerClassInitializerReachabilityHandler(this, callback, clazz); |
469 |
| - } |
470 |
| - |
471 |
| - public boolean concurrentReachabilityHandlers() { |
472 |
| - return concurrentReachabilityHandlers; |
| 529 | + /* |
| 530 | + * In our current static analysis implementations, there is no difference between the |
| 531 | + * reachability of a class and the reachability of its class initializer. |
| 532 | + */ |
| 533 | + registerReachabilityHandler(callback, clazz); |
473 | 534 | }
|
474 | 535 |
|
475 | 536 | @Override
|
@@ -516,19 +577,14 @@ public boolean getAndResetRequireAnalysisIteration() {
|
516 | 577 |
|
517 | 578 | public static class ConcurrentAnalysisAccessImpl extends DuringAnalysisAccessImpl {
|
518 | 579 |
|
519 |
| - private static final String concurrentReachabilityOption = SubstrateOptionsParser.commandArgument(SubstrateOptions.RunReachabilityHandlersConcurrently, "-"); |
520 |
| - |
521 | 580 | public ConcurrentAnalysisAccessImpl(FeatureHandler featureHandler, ImageClassLoader imageClassLoader, Inflation bb, NativeLibraries nativeLibraries, DebugContext debugContext) {
|
522 | 581 | super(featureHandler, imageClassLoader, bb, nativeLibraries, debugContext);
|
523 | 582 | }
|
524 | 583 |
|
525 | 584 | @Override
|
526 | 585 | public void requireAnalysisIteration() {
|
527 | 586 | if (bb.executorIsStarted()) {
|
528 |
| - String msg = "Calling DuringAnalysisAccessImpl.requireAnalysisIteration() is not necessary when running the reachability handlers concurrently during analysis. " + |
529 |
| - "To fallback to running the reachability handlers sequentially, i.e., from Feature.duringAnalysis(), you can add the " + concurrentReachabilityOption + |
530 |
| - " option to the native-image command. Note that the fallback option is deprecated and it will be removed in a future release."; |
531 |
| - throw VMError.shouldNotReachHere(msg); |
| 587 | + throw VMError.shouldNotReachHere("Calling DuringAnalysisAccessImpl.requireAnalysisIteration() is not necessary because reachability handlers run concurrently during analysis."); |
532 | 588 | }
|
533 | 589 | super.requireAnalysisIteration();
|
534 | 590 | }
|
|
0 commit comments