[dart:ffi] Only operate on transitive dependencies of dart:ffi
This CL does two things:
* "Reenable" the skipping of the dart:ffi transformation entirely when
the application does not use dart:ffi. This was originally added in
ad596359a676 but was logically disabled in 455811210525 when dart:core
started depending on it. Here we now ignore (real) dart:* libraries
when looking for dependencies of dart:ffi.
* Find the subset of the just compiled libraries that transitively
depend on dart:ffi and just run the dart:ffi transformation on those
libraries.
For dart2js that doesn't use dart:ffi at all we go from:
```
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v pkg/compiler/bin/dart2js.dart | grep -i "ffi"
0:00:09.003428: Transformed ffi natives in 51ms.
0:00:09.203442: Transformed ffi annotations in 199ms.
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v pkg/compiler/bin/dart2js.dart | grep -i "ffi"
0:00:09.098175: Transformed ffi natives in 52ms.
0:00:09.296847: Transformed ffi annotations in 198ms.
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v pkg/compiler/bin/dart2js.dart | grep -i "ffi"
0:00:08.854341: Transformed ffi natives in 57ms.
0:00:09.061804: Transformed ffi annotations in 207ms.
```
to
```
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v pkg/compiler/bin/dart2js.dart | grep -i "ffi"
0:00:09.138651: Skipped ffi transformation in 1ms.
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v pkg/compiler/bin/dart2js.dart | grep -i "ffi"
0:00:09.242590: Skipped ffi transformation in 1ms.
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v pkg/compiler/bin/dart2js.dart | grep -i "ffi"
0:00:09.163885: Skipped ffi transformation in 1ms.
```
When compiling the flutter gallery app that does depend on dart:ffi we go from:
```
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v --target=flutter --platform=/tmp/tmp.6KV5psVGIG/flutter_patched_sdk/platform_strong.dill /tmp/tmp.6KV5psVGIG/gallery/lib/main.dart | grep -i "ffi"
0:00:16.344955: Transformed ffi natives in 78ms.
0:00:16.777218: Transformed ffi annotations in 432ms.
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v --target=flutter --platform=/tmp/tmp.6KV5psVGIG/flutter_patched_sdk/platform_strong.dill /tmp/tmp.6KV5psVGIG/gallery/lib/main.dart | grep -i "ffi"
0:00:15.994674: Transformed ffi natives in 80ms.
0:00:16.472779: Transformed ffi annotations in 478ms.
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v --target=flutter --platform=/tmp/tmp.6KV5psVGIG/flutter_patched_sdk/platform_strong.dill /tmp/tmp.6KV5psVGIG/gallery/lib/main.dart | grep -i "ffi"
0:00:16.027488: Transformed ffi natives in 81ms.
0:00:16.491362: Transformed ffi annotations in 463ms.
```
to
```
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v --target=flutter --platform=/tmp/tmp.6KV5psVGIG/flutter_patched_sdk/platform_strong.dill /tmp/tmp.6KV5psVGIG/gallery/lib/main.dart | grep -i "ffi"
0:00:15.991628: Transformed ffi natives in 30ms.
0:00:16.226564: Transformed ffi annotations in 234ms.
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v --target=flutter --platform=/tmp/tmp.6KV5psVGIG/flutter_patched_sdk/platform_strong.dill /tmp/tmp.6KV5psVGIG/gallery/lib/main.dart | grep -i "ffi"
0:00:16.184984: Transformed ffi natives in 29ms.
0:00:16.404439: Transformed ffi annotations in 219ms.
$ out/ReleaseX64/dart pkg/front_end/tool/_fasta/compile.dart -v --target=flutter --platform=/tmp/tmp.6KV5psVGIG/flutter_patched_sdk/platform_strong.dill /tmp/tmp.6KV5psVGIG/gallery/lib/main.dart | grep -i "ffi"
0:00:15.933513: Transformed ffi natives in 27ms.
0:00:16.145640: Transformed ffi annotations in 212ms.
```
TEST=Assuming tests already exists.
Change-Id: Ie13be1924d3439cb710c782af81e1e42cda2a838
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/228001
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
diff --git a/pkg/kernel/lib/util/graph.dart b/pkg/kernel/lib/util/graph.dart
index 105b170b..eef4b6b 100644
--- a/pkg/kernel/lib/util/graph.dart
+++ b/pkg/kernel/lib/util/graph.dart
@@ -208,3 +208,37 @@
/// That is, `layers[i]` contain the list of vertices with index `i`.
final List<List<T>> layers = [];
}
+
+/// Find vertices in [graph] that either is in [vertices], has a neighbor in
+/// [vertices], has a neighbor of a neighbor (etc) in [vertices].
+Set<T> calculateTransitiveDependenciesOf<T>(Graph<T> graph, Set<T> vertices) {
+ // Compute direct dependencies for each vertex (the reverse of the
+ // edges returned by `graph.neighborsOf`).
+ Map<T, Set<T>> directDependencies = <T, Set<T>>{};
+ List<T> workList = [];
+ {
+ for (T vertex in graph.vertices) {
+ if (vertices.contains(vertex)) workList.add(vertex);
+ for (T neighbor in graph.neighborsOf(vertex)) {
+ (directDependencies[neighbor] ??= new Set<T>()).add(vertex);
+ if (vertices.contains(neighbor)) workList.add(vertex);
+ }
+ }
+ }
+
+ // Collect and remove all dependencies.
+ Set<T> left = new Set<T>.from(graph.vertices);
+ Set<T> transitive = {};
+ while (workList.isNotEmpty) {
+ T removed = workList.removeLast();
+ if (left.remove(removed)) {
+ Set<T>? s = directDependencies[removed];
+ if (s != null) {
+ // [s] is null for leaves.
+ workList.addAll(s);
+ }
+ transitive.add(removed);
+ }
+ }
+ return transitive;
+}
diff --git a/pkg/kernel/test/graph_test.dart b/pkg/kernel/test/graph_test.dart
index 83827be..cbcb0d1 100644
--- a/pkg/kernel/test/graph_test.dart
+++ b/pkg/kernel/test/graph_test.dart
@@ -556,4 +556,103 @@
[F]
]
]);
+
+ testTransitiveDependencies(graphData: {
+ A: [B],
+ B: [A],
+ }, of: {
+ A
+ }, expected: {
+ A,
+ B,
+ });
+
+ testTransitiveDependencies(graphData: {}, of: {
+ A,
+ B,
+ C,
+ D,
+ E,
+ F,
+ }, expected: {});
+
+ {
+ String MAIN = "MAIN";
+ String DARTFFI = "DARTFFI";
+ testTransitiveDependencies(graphData: {
+ MAIN: [DARTFFI],
+ }, of: {
+ DARTFFI
+ }, expected: {
+ MAIN,
+ });
+ }
+
+ testTransitiveDependencies(graphData: {
+ A: [B],
+ B: [C],
+ C: [B, D],
+ D: [C],
+ E: [A],
+ F: [B],
+ }, of: {
+ A
+ }, expected: {
+ A,
+ E
+ });
+
+ {
+ String MAIN = "MAIN";
+ String TARGET = "TARGET";
+
+ testTransitiveDependencies(graphData: {
+ MAIN: [A, E],
+ A: [B],
+ B: [A, C],
+ C: [TARGET],
+ D: [],
+ E: [D],
+ }, of: {
+ TARGET,
+ }, expected: {
+ C,
+ B,
+ A,
+ MAIN,
+ });
+
+ testTransitiveDependencies(graphData: {
+ MAIN: [A, E],
+ A: [B],
+ B: [A, C],
+ C: [],
+ D: [],
+ E: [D],
+ }, of: {
+ TARGET,
+ }, expected: {});
+
+ testTransitiveDependencies(graphData: {
+ MAIN: [A, C, E, TARGET],
+ A: [B],
+ B: [A, C],
+ C: [],
+ D: [],
+ E: [D],
+ }, of: {
+ TARGET,
+ }, expected: {
+ MAIN,
+ });
+ }
+}
+
+void testTransitiveDependencies(
+ {required Set<String> of,
+ required Set<String> expected,
+ required Map<String, List<String>> graphData}) {
+ Graph<String> graph = new TestGraph(graphData);
+ Set<String> actual = calculateTransitiveDependenciesOf(graph, of);
+ Expect.setEquals(expected, actual);
}
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index da77a9e..b1a60d8 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -18,7 +18,8 @@
import '../transformations/call_site_annotator.dart' as callSiteAnnotator;
import '../transformations/lowering.dart' as lowering
show transformLibraries, transformProcedure;
-import '../transformations/ffi/common.dart' as ffiHelper show importsFfi;
+import '../transformations/ffi/common.dart' as ffiHelper
+ show calculateTransitiveImportsOfDartFfiIfUsed;
import '../transformations/ffi/definitions.dart' as transformFfiDefinitions
show transformLibraries;
import '../transformations/ffi/native.dart' as transformFfiNative
@@ -154,7 +155,9 @@
this, coreTypes, hierarchy, libraries, referenceFromIndex);
logger?.call("Transformed mixin applications");
- if (!ffiHelper.importsFfi(component, libraries)) {
+ List<Library>? transitiveImportingDartFfi = ffiHelper
+ .calculateTransitiveImportsOfDartFfiIfUsed(component, libraries);
+ if (transitiveImportingDartFfi == null) {
logger?.call("Skipped ffi transformation");
} else {
// Transform @FfiNative(..) functions into FFI native call functions.
@@ -163,21 +166,19 @@
// Transform arguments that extend NativeFieldWrapperClass1 to Pointer if
// the native function expects Pointer (to avoid Handle overhead).
transformFfiNative.transformLibraries(component, coreTypes, hierarchy,
- libraries, diagnosticReporter, referenceFromIndex);
+ transitiveImportingDartFfi, diagnosticReporter, referenceFromIndex);
logger?.call("Transformed ffi natives");
- // TODO(jensj/dacoharkes): We can probably limit the transformations to
- // libraries that transitivley depend on dart:ffi.
transformFfiDefinitions.transformLibraries(
component,
coreTypes,
hierarchy,
- libraries,
+ transitiveImportingDartFfi,
diagnosticReporter,
referenceFromIndex,
changedStructureNotifier);
transformFfiUseSites.transformLibraries(component, coreTypes, hierarchy,
- libraries, diagnosticReporter, referenceFromIndex);
+ transitiveImportingDartFfi, diagnosticReporter, referenceFromIndex);
logger?.call("Transformed ffi annotations");
}
diff --git a/pkg/vm/lib/transformations/ffi/common.dart b/pkg/vm/lib/transformations/ffi/common.dart
index 4446159..c23bddb 100644
--- a/pkg/vm/lib/transformations/ffi/common.dart
+++ b/pkg/vm/lib/transformations/ffi/common.dart
@@ -16,6 +16,7 @@
import 'package:kernel/type_algebra.dart' show Substitution;
import 'package:kernel/type_environment.dart'
show TypeEnvironment, SubtypeCheckMode;
+import 'package:kernel/util/graph.dart' as kernelGraph;
import 'abi.dart';
import 'native_type_cfe.dart';
@@ -980,18 +981,61 @@
}
}
+/// Returns all libraries including the ones from component except for platform
+/// libraries that are only in component.
+Set<Library> _getAllRelevantLibraries(
+ Component component, List<Library> libraries) {
+ Set<Library> allLibs = {};
+ allLibs.addAll(libraries);
+ for (Library lib in component.libraries) {
+ // Skip real dart: libraries. dart:core imports dart:ffi, but that doesn't
+ // mean we have to transform anything.
+ if (lib.importUri.scheme == "dart" && !lib.isSynthetic) continue;
+ allLibs.add(lib);
+ }
+ return allLibs;
+}
+
/// Checks if any library depends on dart:ffi.
-bool importsFfi(Component component, List<Library> libraries) {
- Set<Library> allLibs = {...component.libraries, ...libraries};
+Library? importsFfi(Component component, List<Library> libraries) {
final Uri dartFfiUri = Uri.parse("dart:ffi");
+ Set<Library> allLibs = _getAllRelevantLibraries(component, libraries);
for (Library lib in allLibs) {
for (LibraryDependency dependency in lib.dependencies) {
- if (dependency.targetLibrary.importUri == dartFfiUri) {
- return true;
+ Library targetLibrary = dependency.targetLibrary;
+ if (targetLibrary.importUri == dartFfiUri) {
+ return targetLibrary;
}
}
}
- return false;
+ return null;
+}
+
+/// Calculates the libraries in [libraries] that transitively imports dart:ffi.
+///
+/// Returns null if dart:ffi is not imported.
+List<Library>? calculateTransitiveImportsOfDartFfiIfUsed(
+ Component component, List<Library> libraries) {
+ Set<Library> allLibs = _getAllRelevantLibraries(component, libraries);
+
+ final Uri dartFfiUri = Uri.parse("dart:ffi");
+ Library? dartFfi;
+ canFind:
+ for (Library lib in allLibs) {
+ for (LibraryDependency dependency in lib.dependencies) {
+ Library targetLibrary = dependency.targetLibrary;
+ if (targetLibrary.importUri == dartFfiUri) {
+ dartFfi = targetLibrary;
+ break canFind;
+ }
+ }
+ }
+ if (dartFfi == null) return null;
+
+ kernelGraph.LibraryGraph graph = new kernelGraph.LibraryGraph(allLibs);
+ Set<Library> result =
+ kernelGraph.calculateTransitiveDependenciesOf(graph, {dartFfi});
+ return (result..retainAll(libraries)).toList();
}
extension on Map<Abi, Object?> {