[dart2js] Refactor program split constraint's Fuse node.
This cl makes Fuse an unnamed ordering node, and allows ors / ands
within the fuse. For examples of the benefits of this new Fuse, please
see:
pkg/compiler/test/custom_split/data/fuse_with_and/main.dart
and
pkg/compiler/test/custom_split/data/fuse_with_or/main.dart
Change-Id: I5076abcd617d138b03dc8944737c61645e42e038
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/228685
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Joshua Litt <joshualitt@google.com>
diff --git a/pkg/compiler/lib/src/deferred_load/program_split_constraints/builder.dart b/pkg/compiler/lib/src/deferred_load/program_split_constraints/builder.dart
index 2520547..2428f3e 100644
--- a/pkg/compiler/lib/src/deferred_load/program_split_constraints/builder.dart
+++ b/pkg/compiler/lib/src/deferred_load/program_split_constraints/builder.dart
@@ -28,12 +28,10 @@
/// Imports which load before [import].
final Set<Constraint> predecessors = {};
- /// Whether or not this [ConstraintNode] should always apply transitions as
+ /// Whether or not this [Constraint] should always apply transitions as
/// opposed to conditionally applying transitions.
bool get alwaysApplyTransitions {
- return combinerType == null ||
- combinerType == CombinerType.and ||
- combinerType == CombinerType.fuse;
+ return combinerType == null || combinerType == CombinerType.and;
}
Constraint(this.name, this.imports, this.combinerType) {
@@ -122,19 +120,36 @@
// 3) Build a graph of [Constraint]s by processing user constraints and
// intializing each [Constraint]'s predecessor / successor members.
- for (var constraint in nodes.ordered) {
- var successor = nodeToConstraintMap[constraint.successor];
- var predecessor = nodeToConstraintMap[constraint.predecessor];
+ void createEdge(NamedNode successorNode, NamedNode predecessorNode) {
+ var successor = nodeToConstraintMap[successorNode];
+ var predecessor = nodeToConstraintMap[predecessorNode];
successor.predecessors.add(predecessor);
predecessor.successors.add(successor);
}
+ for (var constraint in nodes.ordered) {
+ if (constraint is RelativeOrderNode) {
+ createEdge(constraint.successor, constraint.predecessor);
+ } else if (constraint is FuseNode) {
+ // Fuse nodes are just syntactic sugar for generating cycles in the
+ // ordering graph.
+ for (var node1 in constraint.nodes) {
+ for (var node2 in constraint.nodes) {
+ if (node1 != node2) {
+ createEdge(node1, node2);
+ }
+ }
+ }
+ }
+ }
+
// 4) Compute the transitive closure of constraints. This gives us a map of
// transitiveTransitions, where each key is a parent [ImportEntity] and each
// value represents the transitive set of child [ImportEntity]s which are
// always loaded after the parent.
Map<ImportEntity, Set<ImportEntity>> singletonTransitions = {};
Map<Constraint, SetTransition> setTransitions = {};
+ Map<Constraint, Set<ImportEntity>> processed = {};
Queue<_WorkItem> queue = Queue.from(nodeToConstraintMap.values
.where((node) => node.successors.isEmpty)
.map((node) => _WorkItem(node)));
@@ -154,14 +169,6 @@
for (var import in imports) {
// We insert an implicit 'self' transition for every import.
var transitions = singletonTransitions[import] ??= {import};
-
- // In the case of [CombinerType.fuse], the nodes in the
- // [Constraint] form a strongly connected component,
- // i.e. [ImportEntity]s that are always part of a
- // single [ImportSet].
- if (constraint.combinerType == CombinerType.fuse) {
- transitions.addAll(imports);
- }
transitions.addAll(transitiveChildren);
}
} else {
@@ -177,6 +184,14 @@
...transitiveChildren,
};
for (var predecessor in constraint.predecessors) {
+ // We allow cycles in the constraint graph, so we need to support
+ // reprocessing constraints when we need to consider new transitive
+ // children.
+ if (processed.containsKey(predecessor) &&
+ processed[predecessor].containsAll(predecessorTransitiveChildren)) {
+ continue;
+ }
+ (processed[predecessor] ??= {}).addAll(predecessorTransitiveChildren);
queue.add(_WorkItem(predecessor,
transitiveChildren: predecessorTransitiveChildren));
}
diff --git a/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart b/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart
index 92068f6..17a6904 100644
--- a/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart
+++ b/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart
@@ -71,13 +71,11 @@
/// A [CombinerType] defines how to combine multiple [ReferenceNode]s in a
/// single step.
-enum CombinerType { fuse, and, or }
+enum CombinerType { and, or }
CombinerType parseCombinerType(Map<String, dynamic> nodeJson) {
String type = nodeJson['type'];
switch (type) {
- case 'fuse':
- return CombinerType.fuse;
case 'and':
return CombinerType.and;
case 'or':
@@ -89,8 +87,6 @@
String combinerTypeToString(CombinerType type) {
switch (type) {
- case CombinerType.fuse:
- return 'fuse';
case CombinerType.and:
return 'and';
case CombinerType.or:
@@ -151,9 +147,13 @@
}
}
+/// An [OrderNode] is a [Node] without a name that indicates a temporal
+/// constraint.
+abstract class OrderNode extends Node {}
+
/// A [RelativeOrderNode] is an unnamed [Node] which defines a relative
/// load order between two [NamedNode]s.
-class RelativeOrderNode extends Node {
+class RelativeOrderNode extends OrderNode {
final NamedNode predecessor;
final NamedNode successor;
@@ -165,7 +165,7 @@
@override
Map<String, dynamic> toJson() {
return {
- 'type': 'order',
+ 'type': 'relative_order',
'predecessor': predecessor.name,
'successor': successor.name
};
@@ -185,6 +185,35 @@
}
}
+/// A [FuseNode] is an [OrderNode] with a list of [NamedNode] children.
+/// A [FuseNode] joins its children into a strongly connected component.
+class FuseNode extends OrderNode {
+ final Set<NamedNode> nodes;
+
+ FuseNode(this.nodes);
+
+ @override
+ Map<String, dynamic> toJson() {
+ return {'type': 'fuse', 'nodes': nodes.map((node) => node.name).toList()};
+ }
+
+ static FuseNode fromJson(
+ Map<String, dynamic> nodeJson, Map<String, NamedNode> nameMap) {
+ List<dynamic> referencesJson = _jsonLookup(nodeJson, 'nodes');
+ Set<NamedNode> nodes = {};
+ for (String reference in referencesJson) {
+ nodes.add(nameMap[reference]);
+ }
+ return FuseNode(nodes);
+ }
+
+ @override
+ String toString() {
+ var nodeNames = nodes.map((node) => node.name).join(', ');
+ return 'FuseNode(nodes=$nodeNames)';
+ }
+}
+
/// A builder class for constructing constraint nodes.
typedef ReferenceNodeNamer = String Function(UriAndPrefix);
@@ -221,6 +250,14 @@
return namedNodes[nodeName];
}
+ ReferenceNode _lookupReferenceNode(String nodeName) {
+ var node = _lookupNamedNode(nodeName);
+ if (node is! ReferenceNode) {
+ throw 'node $nodeName is not a ReferenceNode.';
+ }
+ return node as ReferenceNode;
+ }
+
/// Returns a [ReferenceNode] referencing [importUriAndPrefix].
/// [ReferenceNode]s are typically created in bulk, by mapping over a list of
/// strings of imports in the form 'uri#prefix'. In further builder calls,
@@ -242,16 +279,8 @@
/// Creates a [CombinerNode] which can be referenced by [name] in further
/// calls to the builder.
CombinerNode combinerNode(String name, Set<String> nodes, CombinerType type) {
- ReferenceNode _lookup(String nodeName) {
- var node = _lookupNamedNode(nodeName);
- if (node is! ReferenceNode) {
- // TODO(joshualitt): Implement nested combiners.
- throw '$name references node $nodeName which is not a ReferenceNode.';
- }
- return node as ReferenceNode;
- }
-
- return _addNamedNode(CombinerNode(name, type, nodes.map(_lookup).toSet()));
+ return _addNamedNode(
+ CombinerNode(name, type, nodes.map(_lookupReferenceNode).toSet()));
}
/// Creates an 'and' [CombinerNode] which can be referenced by [name] in
@@ -260,10 +289,10 @@
return combinerNode(name, nodes, CombinerType.and);
}
- /// Creates a 'fuse' [CombinerNode] which can be referenced by [name] in
- /// further calls to the builder.
- CombinerNode fuseNode(String name, Set<String> nodes) {
- return combinerNode(name, nodes, CombinerType.fuse);
+ /// Creates a [FuseNode], which is a type of [OrderNode] indicating a
+ /// [Set<String>] nodes can always be loaded together.
+ FuseNode fuseNode(Set<String> nodes) {
+ return FuseNode(nodes.map(_lookupNamedNode).toSet());
}
/// Creates an 'or' [CombinerNode] which can be referenced by [name] in
@@ -277,7 +306,7 @@
/// program split constraints.
class ConstraintData {
final List<NamedNode> named;
- final List<RelativeOrderNode> ordered;
+ final List<OrderNode> ordered;
ConstraintData(this.named, this.ordered);
}
diff --git a/pkg/compiler/lib/src/deferred_load/program_split_constraints/parser.dart b/pkg/compiler/lib/src/deferred_load/program_split_constraints/parser.dart
index 968b07b..e60c182 100644
--- a/pkg/compiler/lib/src/deferred_load/program_split_constraints/parser.dart
+++ b/pkg/compiler/lib/src/deferred_load/program_split_constraints/parser.dart
@@ -10,7 +10,7 @@
/// [ConstraintData] object.
class Parser {
final Map<String, NamedNode> nameMap = {};
- final List<RelativeOrderNode> orderedNodes = [];
+ final List<OrderNode> orderedNodes = [];
void parseReference(Map<String, dynamic> nodeJson) {
var reference = ReferenceNode.fromJson(nodeJson);
@@ -22,29 +22,36 @@
nameMap[combinerNode.name] = combinerNode;
}
- void parseOrder(Map<String, dynamic> nodeJson) {
+ void parseRelativeOrder(Map<String, dynamic> nodeJson) {
orderedNodes.add(RelativeOrderNode.fromJson(nodeJson, nameMap));
}
+ void parseFuse(Map<String, dynamic> nodeJson) {
+ orderedNodes.add(FuseNode.fromJson(nodeJson, nameMap));
+ }
+
/// Reads a program split constraints json file string and returns a [Nodes]
/// object reflecting the parsed constraints.
ConstraintData read(String programSplitJson) {
List<dynamic> doc = json.decode(programSplitJson);
List<Map<String, dynamic>> referenceConstraints = [];
List<Map<String, dynamic>> combinerConstraints = [];
- List<Map<String, dynamic>> orderConstraints = [];
+ List<Map<String, dynamic>> fuseConstraints = [];
+ List<Map<String, dynamic>> relativeOrderConstraints = [];
for (Map<String, dynamic> constraint in doc) {
switch (constraint['type']) {
case 'reference':
referenceConstraints.add(constraint);
break;
case 'and':
- case 'fuse':
case 'or':
combinerConstraints.add(constraint);
break;
- case 'order':
- orderConstraints.add(constraint);
+ case 'fuse':
+ fuseConstraints.add(constraint);
+ break;
+ case 'relative_order':
+ relativeOrderConstraints.add(constraint);
break;
default:
throw 'Unrecognized constraint type in $constraint';
@@ -54,7 +61,8 @@
// Parse references, than combiners, than finally sequences.
referenceConstraints.forEach(parseReference);
combinerConstraints.forEach(parseCombiner);
- orderConstraints.forEach(parseOrder);
+ fuseConstraints.forEach(parseFuse);
+ relativeOrderConstraints.forEach(parseRelativeOrder);
return ConstraintData(nameMap.values.toList(), orderedNodes);
}
}
diff --git a/pkg/compiler/test/custom_split/custom_split_test.dart b/pkg/compiler/test/custom_split/custom_split_test.dart
index f1e4fd4..7f4c4dc 100644
--- a/pkg/compiler/test/custom_split/custom_split_test.dart
+++ b/pkg/compiler/test/custom_split/custom_split_test.dart
@@ -24,6 +24,8 @@
'diamond_and',
'diamond_fuse',
'diamond_or',
+ 'fuse_with_and',
+ 'fuse_with_or',
'two_step',
'two_branch',
];
diff --git a/pkg/compiler/test/custom_split/data/diamond/constraints.json b/pkg/compiler/test/custom_split/data/diamond/constraints.json
index 21b8afa..537cf0b 100644
--- a/pkg/compiler/test/custom_split/data/diamond/constraints.json
+++ b/pkg/compiler/test/custom_split/data/diamond/constraints.json
@@ -20,22 +20,22 @@
"import": "memory:sdk/tests/web/native/main.dart#step3"
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step1",
"successor": "memory:sdk/tests/web/native/main.dart#step2a"
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step1",
"successor": "memory:sdk/tests/web/native/main.dart#step2b"
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step2a",
"successor": "memory:sdk/tests/web/native/main.dart#step3"
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step2b",
"successor": "memory:sdk/tests/web/native/main.dart#step3"
}
diff --git a/pkg/compiler/test/custom_split/data/diamond_and/constraints.json b/pkg/compiler/test/custom_split/data/diamond_and/constraints.json
index 85344e7..bf91a12 100644
--- a/pkg/compiler/test/custom_split/data/diamond_and/constraints.json
+++ b/pkg/compiler/test/custom_split/data/diamond_and/constraints.json
@@ -28,12 +28,12 @@
]
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step1",
"successor": "step2"
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "step2",
"successor": "memory:sdk/tests/web/native/main.dart#step3"
}
diff --git a/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.dart b/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.dart
index 145cbd1..a0fe64f 100644
--- a/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.dart
+++ b/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.dart
@@ -19,8 +19,8 @@
var builder = ProgramSplitBuilder();
return [
...imports.map(builder.referenceNode),
- builder.fuseNode('step2', {step2a, step2b}),
- builder.orderNode(step1, 'step2'),
- builder.orderNode('step2', step3),
+ builder.fuseNode({step2a, step2b}),
+ builder.orderNode(step1, step2a),
+ builder.orderNode(step2b, step3),
];
}
diff --git a/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.json b/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.json
index dccfe20..835bebe 100644
--- a/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.json
+++ b/pkg/compiler/test/custom_split/data/diamond_fuse/constraints.json
@@ -21,20 +21,19 @@
},
{
"type": "fuse",
- "name": "step2",
"nodes": [
"memory:sdk/tests/web/native/main.dart#step2a",
"memory:sdk/tests/web/native/main.dart#step2b"
]
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step1",
- "successor": "step2"
+ "successor": "memory:sdk/tests/web/native/main.dart#step2a"
},
{
- "type": "order",
- "predecessor": "step2",
+ "type": "relative_order",
+ "predecessor": "memory:sdk/tests/web/native/main.dart#step2b",
"successor": "memory:sdk/tests/web/native/main.dart#step3"
}
]
\ No newline at end of file
diff --git a/pkg/compiler/test/custom_split/data/diamond_or/constraints.json b/pkg/compiler/test/custom_split/data/diamond_or/constraints.json
index c4091f4..f4ea01b 100644
--- a/pkg/compiler/test/custom_split/data/diamond_or/constraints.json
+++ b/pkg/compiler/test/custom_split/data/diamond_or/constraints.json
@@ -28,12 +28,12 @@
]
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step1",
"successor": "step2"
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "step2",
"successor": "memory:sdk/tests/web/native/main.dart#step3"
}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/constraints.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/constraints.dart
new file mode 100644
index 0000000..12a80ee
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/constraints.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:isolate';
+
+import 'package:compiler/src/deferred_load/program_split_constraints/nodes.dart';
+import '../../constraint_harness.dart';
+
+void main(List<String> args, SendPort sendPort) {
+ waitForImportsAndInvoke(sendPort, processDeferredImports);
+}
+
+List<Node> processDeferredImports(List<String> imports) {
+ var lib1 = 'memory:sdk/tests/web/native/lib1.dart#b1';
+ var lib2 = 'memory:sdk/tests/web/native/lib2.dart#b2';
+ var lib3 = 'memory:sdk/tests/web/native/lib3.dart#b3';
+ var lib4 = 'memory:sdk/tests/web/native/lib4.dart#b4';
+ var builder = ProgramSplitBuilder();
+ return [
+ ...imports.map(builder.referenceNode),
+ builder.andNode('lib1_and_lib2', {lib1, lib2}),
+ builder.fuseNode({'lib1_and_lib2', lib3}),
+ builder.orderNode(lib3, lib4),
+ ];
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/constraints.json b/pkg/compiler/test/custom_split/data/fuse_with_and/constraints.json
new file mode 100644
index 0000000..d95cc50
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/constraints.json
@@ -0,0 +1,42 @@
+[
+ {
+ "type": "reference",
+ "name": "memory:sdk/tests/web/native/lib1.dart#b1",
+ "import": "memory:sdk/tests/web/native/lib1.dart#b1"
+ },
+ {
+ "type": "reference",
+ "name": "memory:sdk/tests/web/native/lib2.dart#b2",
+ "import": "memory:sdk/tests/web/native/lib2.dart#b2"
+ },
+ {
+ "type": "reference",
+ "name": "memory:sdk/tests/web/native/lib3.dart#b3",
+ "import": "memory:sdk/tests/web/native/lib3.dart#b3"
+ },
+ {
+ "type": "reference",
+ "name": "memory:sdk/tests/web/native/lib4.dart#b4",
+ "import": "memory:sdk/tests/web/native/lib4.dart#b4"
+ },
+ {
+ "type": "and",
+ "name": "lib1_and_lib2",
+ "nodes": [
+ "memory:sdk/tests/web/native/lib1.dart#b1",
+ "memory:sdk/tests/web/native/lib2.dart#b2"
+ ]
+ },
+ {
+ "type": "fuse",
+ "nodes": [
+ "lib1_and_lib2",
+ "memory:sdk/tests/web/native/lib3.dart#b3"
+ ]
+ },
+ {
+ "type": "relative_order",
+ "predecessor": "memory:sdk/tests/web/native/lib3.dart#b3",
+ "successor": "memory:sdk/tests/web/native/lib4.dart#b4"
+ }
+]
\ No newline at end of file
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/lib1.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/lib1.dart
new file mode 100644
index 0000000..33e2c05
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/lib1.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import 'lib_100_0.dart' deferred as b1;
+
+/*member: entryLib1:member_unit=main{}*/
+entryLib1() async {
+ await b1.loadLibrary();
+ b1.g_100_0();
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/lib2.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/lib2.dart
new file mode 100644
index 0000000..1ad0cd3
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/lib2.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import 'lib_010_0.dart' deferred as b2;
+
+/*member: entryLib2:member_unit=main{}*/
+entryLib2() async {
+ await b2.loadLibrary();
+ b2.g_010_0();
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/lib3.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/lib3.dart
new file mode 100644
index 0000000..1d2b9d5
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/lib3.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import 'lib_001_0.dart' deferred as b3;
+
+/*member: entryLib3:member_unit=main{}*/
+entryLib3() async {
+ await b3.loadLibrary();
+ b3.g_001_0();
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/lib4.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/lib4.dart
new file mode 100644
index 0000000..1eedd87
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/lib4.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import 'lib_000_1.dart' deferred as b4;
+
+/*member: entryLib4:member_unit=main{}*/
+entryLib4() async {
+ await b4.loadLibrary();
+ b4.g_000_1();
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/libImport.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/libImport.dart
new file mode 100644
index 0000000..d55f632
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/libImport.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import "package:expect/expect.dart";
+
+/*member: v:member_unit=1{b1, b2, b3, b4}*/
+void v(Set<String> u, String name, int bit) {
+ Expect.isTrue(u.add(name));
+ Expect.equals(name[bit], '1');
+}
+
+@pragma('dart2js:noInline')
+/*member: f_100_0:member_unit=1{b1, b2, b3, b4}*/
+f_100_0(Set<String> u, int b) => v(u, '1000', b);
+@pragma('dart2js:noInline')
+/*member: f_100_1:member_unit=1{b1, b2, b3, b4}*/
+f_100_1(Set<String> u, int b) => v(u, '1001', b);
+@pragma('dart2js:noInline')
+/*member: f_101_0:member_unit=1{b1, b2, b3, b4}*/
+f_101_0(Set<String> u, int b) => v(u, '1010', b);
+@pragma('dart2js:noInline')
+/*member: f_101_1:member_unit=1{b1, b2, b3, b4}*/
+f_101_1(Set<String> u, int b) => v(u, '1011', b);
+@pragma('dart2js:noInline')
+/*member: f_110_0:member_unit=1{b1, b2, b3, b4}*/
+f_110_0(Set<String> u, int b) => v(u, '1100', b);
+@pragma('dart2js:noInline')
+/*member: f_110_1:member_unit=1{b1, b2, b3, b4}*/
+f_110_1(Set<String> u, int b) => v(u, '1101', b);
+@pragma('dart2js:noInline')
+/*member: f_111_0:member_unit=1{b1, b2, b3, b4}*/
+f_111_0(Set<String> u, int b) => v(u, '1110', b);
+@pragma('dart2js:noInline')
+/*member: f_111_1:member_unit=1{b1, b2, b3, b4}*/
+f_111_1(Set<String> u, int b) => v(u, '1111', b);
+@pragma('dart2js:noInline')
+/*member: f_010_0:member_unit=1{b1, b2, b3, b4}*/
+f_010_0(Set<String> u, int b) => v(u, '0100', b);
+@pragma('dart2js:noInline')
+/*member: f_010_1:member_unit=1{b1, b2, b3, b4}*/
+f_010_1(Set<String> u, int b) => v(u, '0101', b);
+@pragma('dart2js:noInline')
+/*member: f_011_0:member_unit=1{b1, b2, b3, b4}*/
+f_011_0(Set<String> u, int b) => v(u, '0110', b);
+@pragma('dart2js:noInline')
+/*member: f_011_1:member_unit=1{b1, b2, b3, b4}*/
+f_011_1(Set<String> u, int b) => v(u, '0111', b);
+@pragma('dart2js:noInline')
+/*member: f_001_0:member_unit=1{b1, b2, b3, b4}*/
+f_001_0(Set<String> u, int b) => v(u, '0010', b);
+@pragma('dart2js:noInline')
+/*member: f_001_1:member_unit=1{b1, b2, b3, b4}*/
+f_001_1(Set<String> u, int b) => v(u, '0011', b);
+@pragma('dart2js:noInline')
+/*member: f_000_1:member_unit=2{b4}*/
+f_000_1(Set<String> u, int b) => v(u, '0001', b);
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/lib_000_1.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/lib_000_1.dart
new file mode 100644
index 0000000..091a013
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/lib_000_1.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+/*member: g_000_1:member_unit=2{b4}*/
+g_000_1() {
+ Set<String> uniques = {};
+
+ // f_***_1;
+ f_000_1(uniques, 3);
+ f_001_1(uniques, 3);
+ f_010_1(uniques, 3);
+ f_011_1(uniques, 3);
+ f_100_1(uniques, 3);
+ f_101_1(uniques, 3);
+ f_110_1(uniques, 3);
+ f_111_1(uniques, 3);
+ Expect.equals(8, uniques.length);
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/lib_001_0.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/lib_001_0.dart
new file mode 100644
index 0000000..19906dd
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/lib_001_0.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+/*member: g_001_0:member_unit=1{b1, b2, b3, b4}*/
+g_001_0() {
+ Set<String> uniques = {};
+
+ // f_**1_*;
+ f_001_0(uniques, 2);
+ f_001_1(uniques, 2);
+ f_011_0(uniques, 2);
+ f_011_1(uniques, 2);
+ f_101_0(uniques, 2);
+ f_101_1(uniques, 2);
+ f_111_0(uniques, 2);
+ f_111_1(uniques, 2);
+ Expect.equals(8, uniques.length);
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/lib_010_0.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/lib_010_0.dart
new file mode 100644
index 0000000..22b05ac
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/lib_010_0.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+/*member: g_010_0:member_unit=1{b1, b2, b3, b4}*/
+g_010_0() {
+ Set<String> uniques = {};
+
+ // f_*1*_*;
+ f_010_0(uniques, 1);
+ f_010_1(uniques, 1);
+ f_011_0(uniques, 1);
+ f_011_1(uniques, 1);
+ f_110_0(uniques, 1);
+ f_110_1(uniques, 1);
+ f_111_0(uniques, 1);
+ f_111_1(uniques, 1);
+ Expect.equals(8, uniques.length);
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/lib_100_0.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/lib_100_0.dart
new file mode 100644
index 0000000..d39141b
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/lib_100_0.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+/*member: g_100_0:member_unit=1{b1, b2, b3, b4}*/
+g_100_0() {
+ Set<String> uniques = {};
+
+ // f_1**_*;
+ f_100_0(uniques, 0);
+ f_100_1(uniques, 0);
+ f_101_0(uniques, 0);
+ f_101_1(uniques, 0);
+ f_110_0(uniques, 0);
+ f_110_1(uniques, 0);
+ f_111_0(uniques, 0);
+ f_111_1(uniques, 0);
+ Expect.equals(8, uniques.length);
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_and/main.dart b/pkg/compiler/test/custom_split/data/fuse_with_and/main.dart
new file mode 100644
index 0000000..f07ccf9
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_and/main.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/*library:
+ a_pre_fragments=[
+ p1: {units: [2{b4}], usedBy: [], needs: []},
+ p2: {units: [1{b1, b2, b3, b4}], usedBy: [], needs: []}],
+ b_finalized_fragments=[
+ f1: [2{b4}],
+ f2: [1{b1, b2, b3, b4}]],
+ c_steps=[
+ b1=(f2),
+ b2=(f2),
+ b3=(f2),
+ b4=(f2, f1)]
+*/
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import 'lib1.dart';
+import 'lib2.dart';
+import 'lib3.dart';
+import 'lib4.dart';
+
+/*member: main:member_unit=main{}*/
+main() {
+ entryLib1();
+ entryLib2();
+ entryLib3();
+ entryLib4();
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/constraints.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/constraints.dart
new file mode 100644
index 0000000..370ca10
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/constraints.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:isolate';
+
+import 'package:compiler/src/deferred_load/program_split_constraints/nodes.dart';
+import '../../constraint_harness.dart';
+
+void main(List<String> args, SendPort sendPort) {
+ waitForImportsAndInvoke(sendPort, processDeferredImports);
+}
+
+List<Node> processDeferredImports(List<String> imports) {
+ var lib1 = 'memory:sdk/tests/web/native/lib1.dart#b1';
+ var lib2 = 'memory:sdk/tests/web/native/lib2.dart#b2';
+ var lib3 = 'memory:sdk/tests/web/native/lib3.dart#b3';
+ var lib4 = 'memory:sdk/tests/web/native/lib4.dart#b4';
+ var builder = ProgramSplitBuilder();
+ return [
+ ...imports.map(builder.referenceNode),
+ builder.orNode('lib1_or_lib2', {lib1, lib2}),
+ builder.fuseNode({'lib1_or_lib2', lib3}),
+ builder.orderNode(lib3, lib4),
+ ];
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/constraints.json b/pkg/compiler/test/custom_split/data/fuse_with_or/constraints.json
new file mode 100644
index 0000000..6d44688
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/constraints.json
@@ -0,0 +1,42 @@
+[
+ {
+ "type": "reference",
+ "name": "memory:sdk/tests/web/native/lib1.dart#b1",
+ "import": "memory:sdk/tests/web/native/lib1.dart#b1"
+ },
+ {
+ "type": "reference",
+ "name": "memory:sdk/tests/web/native/lib2.dart#b2",
+ "import": "memory:sdk/tests/web/native/lib2.dart#b2"
+ },
+ {
+ "type": "reference",
+ "name": "memory:sdk/tests/web/native/lib3.dart#b3",
+ "import": "memory:sdk/tests/web/native/lib3.dart#b3"
+ },
+ {
+ "type": "reference",
+ "name": "memory:sdk/tests/web/native/lib4.dart#b4",
+ "import": "memory:sdk/tests/web/native/lib4.dart#b4"
+ },
+ {
+ "type": "or",
+ "name": "lib1_or_lib2",
+ "nodes": [
+ "memory:sdk/tests/web/native/lib1.dart#b1",
+ "memory:sdk/tests/web/native/lib2.dart#b2"
+ ]
+ },
+ {
+ "type": "fuse",
+ "nodes": [
+ "lib1_or_lib2",
+ "memory:sdk/tests/web/native/lib3.dart#b3"
+ ]
+ },
+ {
+ "type": "relative_order",
+ "predecessor": "memory:sdk/tests/web/native/lib3.dart#b3",
+ "successor": "memory:sdk/tests/web/native/lib4.dart#b4"
+ }
+]
\ No newline at end of file
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/lib1.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/lib1.dart
new file mode 100644
index 0000000..33e2c05
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/lib1.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import 'lib_100_0.dart' deferred as b1;
+
+/*member: entryLib1:member_unit=main{}*/
+entryLib1() async {
+ await b1.loadLibrary();
+ b1.g_100_0();
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/lib2.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/lib2.dart
new file mode 100644
index 0000000..1ad0cd3
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/lib2.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import 'lib_010_0.dart' deferred as b2;
+
+/*member: entryLib2:member_unit=main{}*/
+entryLib2() async {
+ await b2.loadLibrary();
+ b2.g_010_0();
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/lib3.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/lib3.dart
new file mode 100644
index 0000000..1d2b9d5
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/lib3.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import 'lib_001_0.dart' deferred as b3;
+
+/*member: entryLib3:member_unit=main{}*/
+entryLib3() async {
+ await b3.loadLibrary();
+ b3.g_001_0();
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/lib4.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/lib4.dart
new file mode 100644
index 0000000..1eedd87
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/lib4.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import 'lib_000_1.dart' deferred as b4;
+
+/*member: entryLib4:member_unit=main{}*/
+entryLib4() async {
+ await b4.loadLibrary();
+ b4.g_000_1();
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/libImport.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/libImport.dart
new file mode 100644
index 0000000..dd4364a
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/libImport.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import "package:expect/expect.dart";
+
+/*member: v:member_unit=2{b1, b2, b3, b4}*/
+void v(Set<String> u, String name, int bit) {
+ Expect.isTrue(u.add(name));
+ Expect.equals(name[bit], '1');
+}
+
+@pragma('dart2js:noInline')
+/*member: f_100_0:member_unit=1{b1}*/
+f_100_0(Set<String> u, int b) => v(u, '1000', b);
+@pragma('dart2js:noInline')
+/*member: f_100_1:member_unit=3{b1, b4}*/
+f_100_1(Set<String> u, int b) => v(u, '1001', b);
+@pragma('dart2js:noInline')
+/*member: f_101_0:member_unit=2{b1, b2, b3, b4}*/
+f_101_0(Set<String> u, int b) => v(u, '1010', b);
+@pragma('dart2js:noInline')
+/*member: f_101_1:member_unit=2{b1, b2, b3, b4}*/
+f_101_1(Set<String> u, int b) => v(u, '1011', b);
+@pragma('dart2js:noInline')
+/*member: f_110_0:member_unit=2{b1, b2, b3, b4}*/
+f_110_0(Set<String> u, int b) => v(u, '1100', b);
+@pragma('dart2js:noInline')
+/*member: f_110_1:member_unit=2{b1, b2, b3, b4}*/
+f_110_1(Set<String> u, int b) => v(u, '1101', b);
+@pragma('dart2js:noInline')
+/*member: f_111_0:member_unit=2{b1, b2, b3, b4}*/
+f_111_0(Set<String> u, int b) => v(u, '1110', b);
+@pragma('dart2js:noInline')
+/*member: f_111_1:member_unit=2{b1, b2, b3, b4}*/
+f_111_1(Set<String> u, int b) => v(u, '1111', b);
+@pragma('dart2js:noInline')
+/*member: f_010_0:member_unit=4{b2}*/
+f_010_0(Set<String> u, int b) => v(u, '0100', b);
+@pragma('dart2js:noInline')
+/*member: f_010_1:member_unit=5{b2, b4}*/
+f_010_1(Set<String> u, int b) => v(u, '0101', b);
+@pragma('dart2js:noInline')
+/*member: f_011_0:member_unit=2{b1, b2, b3, b4}*/
+f_011_0(Set<String> u, int b) => v(u, '0110', b);
+@pragma('dart2js:noInline')
+/*member: f_011_1:member_unit=2{b1, b2, b3, b4}*/
+f_011_1(Set<String> u, int b) => v(u, '0111', b);
+@pragma('dart2js:noInline')
+/*member: f_001_0:member_unit=2{b1, b2, b3, b4}*/
+f_001_0(Set<String> u, int b) => v(u, '0010', b);
+@pragma('dart2js:noInline')
+/*member: f_001_1:member_unit=2{b1, b2, b3, b4}*/
+f_001_1(Set<String> u, int b) => v(u, '0011', b);
+@pragma('dart2js:noInline')
+/*member: f_000_1:member_unit=6{b4}*/
+f_000_1(Set<String> u, int b) => v(u, '0001', b);
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/lib_000_1.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/lib_000_1.dart
new file mode 100644
index 0000000..5407d70
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/lib_000_1.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+/*member: g_000_1:member_unit=6{b4}*/
+g_000_1() {
+ Set<String> uniques = {};
+
+ // f_***_1;
+ f_000_1(uniques, 3);
+ f_001_1(uniques, 3);
+ f_010_1(uniques, 3);
+ f_011_1(uniques, 3);
+ f_100_1(uniques, 3);
+ f_101_1(uniques, 3);
+ f_110_1(uniques, 3);
+ f_111_1(uniques, 3);
+ Expect.equals(8, uniques.length);
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/lib_001_0.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/lib_001_0.dart
new file mode 100644
index 0000000..1614e60
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/lib_001_0.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+/*member: g_001_0:member_unit=2{b1, b2, b3, b4}*/
+g_001_0() {
+ Set<String> uniques = {};
+
+ // f_**1_*;
+ f_001_0(uniques, 2);
+ f_001_1(uniques, 2);
+ f_011_0(uniques, 2);
+ f_011_1(uniques, 2);
+ f_101_0(uniques, 2);
+ f_101_1(uniques, 2);
+ f_111_0(uniques, 2);
+ f_111_1(uniques, 2);
+ Expect.equals(8, uniques.length);
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/lib_010_0.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/lib_010_0.dart
new file mode 100644
index 0000000..4cf27c5
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/lib_010_0.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+/*member: g_010_0:member_unit=4{b2}*/
+g_010_0() {
+ Set<String> uniques = {};
+
+ // f_*1*_*;
+ f_010_0(uniques, 1);
+ f_010_1(uniques, 1);
+ f_011_0(uniques, 1);
+ f_011_1(uniques, 1);
+ f_110_0(uniques, 1);
+ f_110_1(uniques, 1);
+ f_111_0(uniques, 1);
+ f_111_1(uniques, 1);
+ Expect.equals(8, uniques.length);
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/lib_100_0.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/lib_100_0.dart
new file mode 100644
index 0000000..0fe7274
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/lib_100_0.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+/*member: g_100_0:member_unit=1{b1}*/
+g_100_0() {
+ Set<String> uniques = {};
+
+ // f_1**_*;
+ f_100_0(uniques, 0);
+ f_100_1(uniques, 0);
+ f_101_0(uniques, 0);
+ f_101_1(uniques, 0);
+ f_110_0(uniques, 0);
+ f_110_1(uniques, 0);
+ f_111_0(uniques, 0);
+ f_111_1(uniques, 0);
+ Expect.equals(8, uniques.length);
+}
diff --git a/pkg/compiler/test/custom_split/data/fuse_with_or/main.dart b/pkg/compiler/test/custom_split/data/fuse_with_or/main.dart
new file mode 100644
index 0000000..a2531cb
--- /dev/null
+++ b/pkg/compiler/test/custom_split/data/fuse_with_or/main.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/*library:
+ a_pre_fragments=[
+ p1: {units: [6{b4}], usedBy: [], needs: []},
+ p2: {units: [4{b2}], usedBy: [], needs: []},
+ p3: {units: [1{b1}], usedBy: [], needs: []},
+ p4: {units: [5{b2, b4}], usedBy: [], needs: []},
+ p5: {units: [3{b1, b4}], usedBy: [], needs: []},
+ p6: {units: [2{b1, b2, b3, b4}], usedBy: [], needs: []}],
+ b_finalized_fragments=[
+ f1: [6{b4}],
+ f2: [4{b2}],
+ f3: [1{b1}],
+ f4: [5{b2, b4}],
+ f5: [3{b1, b4}],
+ f6: [2{b1, b2, b3, b4}]],
+ c_steps=[
+ b1=(f6, f5, f3),
+ b2=(f6, f4, f2),
+ b3=(f6),
+ b4=(f6, f5, f4, f1)]
+*/
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+import 'lib1.dart';
+import 'lib2.dart';
+import 'lib3.dart';
+import 'lib4.dart';
+
+/*member: main:member_unit=main{}*/
+main() {
+ entryLib1();
+ entryLib2();
+ entryLib3();
+ entryLib4();
+}
diff --git a/pkg/compiler/test/custom_split/data/two_branch/constraints.json b/pkg/compiler/test/custom_split/data/two_branch/constraints.json
index 1a29d7b..9ab73ee 100644
--- a/pkg/compiler/test/custom_split/data/two_branch/constraints.json
+++ b/pkg/compiler/test/custom_split/data/two_branch/constraints.json
@@ -15,12 +15,12 @@
"import": "memory:sdk/tests/web/native/main.dart#step2b"
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step1",
"successor": "memory:sdk/tests/web/native/main.dart#step2a"
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step1",
"successor": "memory:sdk/tests/web/native/main.dart#step2b"
}
diff --git a/pkg/compiler/test/custom_split/data/two_step/constraints.json b/pkg/compiler/test/custom_split/data/two_step/constraints.json
index 77a02de..a0e82d6 100644
--- a/pkg/compiler/test/custom_split/data/two_step/constraints.json
+++ b/pkg/compiler/test/custom_split/data/two_step/constraints.json
@@ -15,12 +15,12 @@
"import": "memory:sdk/tests/web/native/main.dart#step3"
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step1",
"successor": "memory:sdk/tests/web/native/main.dart#step2"
},
{
- "type": "order",
+ "type": "relative_order",
"predecessor": "memory:sdk/tests/web/native/main.dart#step2",
"successor": "memory:sdk/tests/web/native/main.dart#step3"
}
diff --git a/pkg/compiler/tool/graph_isomorphizer.dart b/pkg/compiler/tool/graph_isomorphizer.dart
index 49894bd..74489b8 100644
--- a/pkg/compiler/tool/graph_isomorphizer.dart
+++ b/pkg/compiler/tool/graph_isomorphizer.dart
@@ -142,8 +142,11 @@
/// A bool to omit the comment block.
final bool skipCopyright;
+ // A bool to generate simple code within test files.
+ final bool simple;
+
GraphIsomorphizer(this.names, this.maxBit,
- {this.outDirectory: '.', this.skipCopyright: false});
+ {this.outDirectory: '.', this.skipCopyright: false, this.simple: false});
void noInlineDecorator(StringBuffer out) {
out.write("@pragma('dart2js:noInline')\n");
@@ -188,90 +191,92 @@
var nameKeys = names.keys.toList();
nameKeys.sort();
- // Generate the 'base' classes, mixins, and types which will be combined to
- // generate hierarchies. Also generate a const instance per class and a closure
- // to invoke.
Set<String> uniques = {};
- for (var bitPosition in nameKeys) {
- var bitsList = names[bitPosition];
- for (var bits in bitsList) {
- var name = generateBitString(bits);
- if (!uniques.add(name)) continue;
- String className = 'C$name';
- String mixinName = 'M$name';
- String typeName = 'T$name';
- (classNames[bitPosition] ??= []).add(className);
- (mixinNames[bitPosition] ??= []).add(mixinName);
- (typeNames[bitPosition] ??= []).add(typeName);
- (mixerClassNames[bitPosition] ??= []).add(className);
- (mixerTypeNames[bitPosition] ??= []).add(typeName);
- out.write('class $className { const $className(); }\n');
- out.write('class $mixinName {}\n');
- out.write('class $typeName {}\n');
- out.write('const $className i$className = const $className();\n');
- out.write('closure$className(foo) => ($className unused) ');
- out.write('=> i$className.toString() == foo.toString();\n');
+ if (!simple) {
+ // Generate the 'base' classes, mixins, and types which will be combined to
+ // generate hierarchies. Also generate a const instance per class and a closure
+ // to invoke.
+ for (var bitPosition in nameKeys) {
+ var bitsList = names[bitPosition];
+ for (var bits in bitsList) {
+ var name = generateBitString(bits);
+ if (!uniques.add(name)) continue;
+ String className = 'C$name';
+ String mixinName = 'M$name';
+ String typeName = 'T$name';
+ (classNames[bitPosition] ??= []).add(className);
+ (mixinNames[bitPosition] ??= []).add(mixinName);
+ (typeNames[bitPosition] ??= []).add(typeName);
+ (mixerClassNames[bitPosition] ??= []).add(className);
+ (mixerTypeNames[bitPosition] ??= []).add(typeName);
+ out.write('class $className { const $className(); }\n');
+ out.write('class $mixinName {}\n');
+ out.write('class $typeName {}\n');
+ out.write('const $className i$className = const $className();\n');
+ out.write('closure$className(foo) => ($className unused) ');
+ out.write('=> i$className.toString() == foo.toString();\n');
+ }
}
- }
- // Generate combined classes and types, as well as const instances and
- // closures.
- newline(out);
- uniques = {};
- for (var bitPosition in nameKeys) {
- var bitsList = names[bitPosition];
- for (var bits in bitsList) {
- var name = generateBitString(bits);
- var bitCount = bits.reduce((a, b) => a + b);
- var baseName = 'C$name';
- if (!uniques.add(baseName)) continue;
- if (bitCount > 1) {
- List<String> classes = [];
- List<String> mixins = [];
- List<String> types = [];
- for (int i = 0; i < bits.length; i++) {
- if (bits[i] == 1) {
- classes.addAll(classNames[i]);
- mixins.addAll(mixinNames[i]);
- types.addAll(typeNames[i]);
- }
- }
- String mixinString = mixins.join(', ');
- int count = 1;
- assert(classes.length == types.length);
- for (int i = 0; i < classes.length; i++) {
- var cls = classes[i];
- var type = types[i];
- List<String> classImpls = [];
- List<String> typeImpls = [];
- if (i > 0) {
- classImpls.addAll(classes.sublist(0, i));
- typeImpls.addAll(types.sublist(0, i));
- }
- if (i < classes.length - 1) {
- classImpls.addAll(classes.sublist(i + 1));
- typeImpls.addAll(types.sublist(i + 1));
- }
- var classImplementsString = classImpls.join(', ');
- String className = '${baseName}_class_${count}';
- out.write('class $className extends $cls with $mixinString ');
- out.write(
- 'implements $classImplementsString { const $className(); }\n');
- out.write('const $className i$className = const $className();\n');
- out.write('closure$className(foo) => ($className unused) ');
- out.write('=> i$className.toString() == foo.toString();\n');
-
- var typeImplementsString = typeImpls.join(', ');
- String typeName = 'T${name}_type__${count}';
- out.write('class $typeName extends $type with $mixinString ');
- out.write('implements $typeImplementsString {}\n');
+ // Generate combined classes and types, as well as const instances and
+ // closures.
+ newline(out);
+ uniques = {};
+ for (var bitPosition in nameKeys) {
+ var bitsList = names[bitPosition];
+ for (var bits in bitsList) {
+ var name = generateBitString(bits);
+ var bitCount = bits.reduce((a, b) => a + b);
+ var baseName = 'C$name';
+ if (!uniques.add(baseName)) continue;
+ if (bitCount > 1) {
+ List<String> classes = [];
+ List<String> mixins = [];
+ List<String> types = [];
for (int i = 0; i < bits.length; i++) {
if (bits[i] == 1) {
- mixerClassNames[i].add(className);
- mixerTypeNames[i].add(typeName);
+ classes.addAll(classNames[i]);
+ mixins.addAll(mixinNames[i]);
+ types.addAll(typeNames[i]);
}
}
- count++;
+ String mixinString = mixins.join(', ');
+ int count = 1;
+ assert(classes.length == types.length);
+ for (int i = 0; i < classes.length; i++) {
+ var cls = classes[i];
+ var type = types[i];
+ List<String> classImpls = [];
+ List<String> typeImpls = [];
+ if (i > 0) {
+ classImpls.addAll(classes.sublist(0, i));
+ typeImpls.addAll(types.sublist(0, i));
+ }
+ if (i < classes.length - 1) {
+ classImpls.addAll(classes.sublist(i + 1));
+ typeImpls.addAll(types.sublist(i + 1));
+ }
+ var classImplementsString = classImpls.join(', ');
+ String className = '${baseName}_class_${count}';
+ out.write('class $className extends $cls with $mixinString ');
+ out.write(
+ 'implements $classImplementsString { const $className(); }\n');
+ out.write('const $className i$className = const $className();\n');
+ out.write('closure$className(foo) => ($className unused) ');
+ out.write('=> i$className.toString() == foo.toString();\n');
+
+ var typeImplementsString = typeImpls.join(', ');
+ String typeName = 'T${name}_type__${count}';
+ out.write('class $typeName extends $type with $mixinString ');
+ out.write('implements $typeImplementsString {}\n');
+ for (int i = 0; i < bits.length; i++) {
+ if (bits[i] == 1) {
+ mixerClassNames[i].add(className);
+ mixerTypeNames[i].add(typeName);
+ }
+ }
+ count++;
+ }
}
}
}
@@ -301,35 +306,41 @@
importExpect(out);
out.write("import '$import';\n\n");
- // create type test.
- noInlineDecorator(out);
- out.write('typeTest(dynamic t) {\n');
- for (var type in mixerTypeNames[bit]) {
- out.write(' if (t is $type) { return true; }\n');
+ if (!simple) {
+ // create type test.
+ noInlineDecorator(out);
+ out.write('typeTest(dynamic t) {\n');
+ for (var type in mixerTypeNames[bit]) {
+ out.write(' if (t is $type) { return true; }\n');
+ }
+ out.write(' return false;\n');
+ out.write('}\n\n');
}
- out.write(' return false;\n');
- out.write('}\n\n');
noInlineDecorator(out);
out.write('g$name() {\n');
- out.write(' // C${generateCommentName(bits, bit)};\n');
- // Construct new instances of each class and pass them to the typeTest
- for (var cls in mixerClassNames[bit]) {
- out.write(' Expect.isFalse(typeTest($cls()));\n');
- }
- newline(out);
+ if (!simple) {
+ out.write(' // C${generateCommentName(bits, bit)};\n');
- // Invoke the test closure for each class.
- for (var cls in mixerClassNames[bit]) {
- out.write(' Expect.isTrue(closure$cls($cls())($cls()));\n');
- }
- newline(out);
+ // Construct new instances of each class and pass them to the typeTest
+ for (var cls in mixerClassNames[bit]) {
+ out.write(' Expect.isFalse(typeTest($cls()));\n');
+ }
+ newline(out);
- // Verify the runtimeTypes of the closures haven't been mangled.
- for (var cls in mixerClassNames[bit]) {
- out.write(' Expect.equals(closure$cls($cls()).runtimeType.toString(), ');
- out.write("'($cls) => bool');\n");
+ // Invoke the test closure for each class.
+ for (var cls in mixerClassNames[bit]) {
+ out.write(' Expect.isTrue(closure$cls($cls())($cls()));\n');
+ }
+ newline(out);
+
+ // Verify the runtimeTypes of the closures haven't been mangled.
+ for (var cls in mixerClassNames[bit]) {
+ out.write(
+ ' Expect.equals(closure$cls($cls()).runtimeType.toString(), ');
+ out.write("'($cls) => bool');\n");
+ }
}
newline(out);
@@ -466,6 +477,7 @@
/// Creates a GraphIsomorphizer based on the provided args.
GraphIsomorphizer createGraphIsomorphizer(List<String> args) {
+ bool simple = true;
int maxBit = 0;
String graphFile = '';
String outDirectory = '.';
@@ -480,6 +492,9 @@
if (arg.startsWith('--out-dir')) {
outDirectory = arg.substring('--out-dir='.length);
}
+ if (arg == '--simple') {
+ simple = true;
+ }
}
// If we don't have a graphFile, then we generate all permutations of bits up
@@ -491,7 +506,8 @@
} else {
maxBit = namesFromGraphFile(graphFile, names);
}
- return GraphIsomorphizer(names, maxBit, outDirectory: outDirectory);
+ return GraphIsomorphizer(names, maxBit,
+ outDirectory: outDirectory, simple: simple);
}
void main(List<String> args) {