Version 2.17.0-65.0.dev

Merge commit 'f6ebe80d26883d41498d978198ab75649ee97a0d' into 'dev'
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index ea924c5..560c08b 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -109,7 +109,7 @@
 <body>
 <h1>Analysis Server API Specification</h1>
 <h1 style="color:#999999">Version
-  1.32.8
+  1.32.9
 </h1>
 <p>
   This document contains a specification of the API provided by the
@@ -4392,7 +4392,7 @@
   <dl><dt class="value">ANNOTATION</dt><dt class="value">BUILT_IN</dt><dt class="value">CLASS</dt><dt class="value">COMMENT_BLOCK</dt><dt class="value">COMMENT_DOCUMENTATION</dt><dt class="value">COMMENT_END_OF_LINE</dt><dt class="value">CONSTRUCTOR</dt><dt class="value">CONSTRUCTOR_TEAR_OFF</dt><dt class="value">DIRECTIVE</dt><dt class="value">DYNAMIC_TYPE</dt><dd>
         
         <p>Deprecated - no longer sent.</p>
-      </dd><dt class="value">DYNAMIC_LOCAL_VARIABLE_DECLARATION</dt><dt class="value">DYNAMIC_LOCAL_VARIABLE_REFERENCE</dt><dt class="value">DYNAMIC_PARAMETER_DECLARATION</dt><dt class="value">DYNAMIC_PARAMETER_REFERENCE</dt><dt class="value">ENUM</dt><dt class="value">ENUM_CONSTANT</dt><dt class="value">FIELD</dt><dd>
+      </dd><dt class="value">DYNAMIC_LOCAL_VARIABLE_DECLARATION</dt><dt class="value">DYNAMIC_LOCAL_VARIABLE_REFERENCE</dt><dt class="value">DYNAMIC_PARAMETER_DECLARATION</dt><dt class="value">DYNAMIC_PARAMETER_REFERENCE</dt><dt class="value">ENUM</dt><dt class="value">ENUM_CONSTANT</dt><dt class="value">EXTENSION</dt><dt class="value">FIELD</dt><dd>
         
         <p>Deprecated - no longer sent.</p>
       </dd><dt class="value">FIELD_STATIC</dt><dd>
diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart
index ade68e1..3baa946 100644
--- a/pkg/analysis_server/lib/protocol/protocol_constants.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart
@@ -6,7 +6,7 @@
 // To regenerate the file, use the script
 // "pkg/analysis_server/tool/spec/generate_files".
 
-const String PROTOCOL_VERSION = '1.32.8';
+const String PROTOCOL_VERSION = '1.32.9';
 
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
index 8881116..df18a65 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -242,20 +242,9 @@
       return false;
     }
 
-    // TODO(dantup): Right now there is no highlight type for extension, so
-    // bail out and do the default thing (which will be to return
-    // IDENTIFIER_DEFAULT). Adding EXTENSION requires coordination with
-    // IntelliJ + bumping protocol version.
-    if (!_computeSemanticTokens) {
-      return false;
-    }
-
     return _addRegion_node(
       node,
-      // TODO(dantup): Change this to EXTENSION and add to LSP mapping when
-      // we have it, but for now use CLASS (which is probably what we'll map it
-      // to for LSP semantic tokens anyway).
-      HighlightRegionType.CLASS,
+      HighlightRegionType.EXTENSION,
     );
   }
 
diff --git a/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart b/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
index 7143991..68e43fd 100644
--- a/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/semantic_tokens/mapping.dart
@@ -123,6 +123,7 @@
   HighlightRegionType.DYNAMIC_PARAMETER_REFERENCE: SemanticTokenTypes.parameter,
   HighlightRegionType.ENUM: SemanticTokenTypes.enum_,
   HighlightRegionType.ENUM_CONSTANT: SemanticTokenTypes.enumMember,
+  HighlightRegionType.EXTENSION: SemanticTokenTypes.class_,
   HighlightRegionType.FUNCTION_TYPE_ALIAS: SemanticTokenTypes.type,
   HighlightRegionType.IDENTIFIER_DEFAULT: CustomSemanticTokenTypes.source,
   HighlightRegionType.IMPORT_PREFIX: SemanticTokenTypes.variable,
diff --git a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
index c97ddd3..ed4a472 100644
--- a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
@@ -681,6 +681,20 @@
     assertHasRegion(HighlightRegionType.ENUM_CONSTANT, 'BBB);');
   }
 
+  Future<void> test_EXTENSION() async {
+    addTestFile('''
+extension E on int {
+  void foo() {}
+}
+void f() {
+  E(0).foo();
+}
+''');
+    await prepareHighlights();
+    assertHasRegion(HighlightRegionType.EXTENSION, 'E on int');
+    assertHasRegion(HighlightRegionType.EXTENSION, 'E(0)');
+  }
+
   Future<void> test_FUNCTION_TYPE_ALIAS() async {
     addTestFile('''
 typedef A();
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index 4b5a4ce..1e7cde4 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -794,6 +794,7 @@
 ///   DYNAMIC_PARAMETER_REFERENCE
 ///   ENUM
 ///   ENUM_CONSTANT
+///   EXTENSION
 ///   FIELD
 ///   FIELD_STATIC
 ///   FUNCTION
@@ -875,6 +876,7 @@
   'DYNAMIC_PARAMETER_REFERENCE',
   'ENUM',
   'ENUM_CONSTANT',
+  'EXTENSION',
   'FIELD',
   'FIELD_STATIC',
   'FUNCTION',
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/HighlightRegionType.java b/pkg/analysis_server/tool/spec/generated/java/types/HighlightRegionType.java
index fe567c5..4c4e17f 100644
--- a/pkg/analysis_server/tool/spec/generated/java/types/HighlightRegionType.java
+++ b/pkg/analysis_server/tool/spec/generated/java/types/HighlightRegionType.java
@@ -50,6 +50,8 @@
 
   public static final String ENUM_CONSTANT = "ENUM_CONSTANT";
 
+  public static final String EXTENSION = "EXTENSION";
+
   /**
    * Deprecated - no longer sent.
    */
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index 3c5c0ad..51f35a4 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -7,7 +7,7 @@
 <body>
 <h1>Analysis Server API Specification</h1>
 <h1 style="color:#999999">Version
-  <version>1.32.8</version>
+  <version>1.32.9</version>
 </h1>
 <p>
   This document contains a specification of the API provided by the
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
index 2223244..c1a528b 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
@@ -1882,6 +1882,7 @@
 ///   DYNAMIC_PARAMETER_REFERENCE
 ///   ENUM
 ///   ENUM_CONSTANT
+///   EXTENSION
 ///   FIELD
 ///   FIELD_STATIC
 ///   FUNCTION
@@ -1995,6 +1996,9 @@
   static const HighlightRegionType ENUM_CONSTANT =
       HighlightRegionType._('ENUM_CONSTANT');
 
+  static const HighlightRegionType EXTENSION =
+      HighlightRegionType._('EXTENSION');
+
   /// Deprecated - no longer sent.
   static const HighlightRegionType FIELD = HighlightRegionType._('FIELD');
 
@@ -2210,6 +2214,7 @@
     DYNAMIC_PARAMETER_REFERENCE,
     ENUM,
     ENUM_CONSTANT,
+    EXTENSION,
     FIELD,
     FIELD_STATIC,
     FUNCTION,
@@ -2314,6 +2319,8 @@
         return ENUM;
       case 'ENUM_CONSTANT':
         return ENUM_CONSTANT;
+      case 'EXTENSION':
+        return EXTENSION;
       case 'FIELD':
         return FIELD;
       case 'FIELD_STATIC':
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
index ade68e1..3baa946 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
@@ -6,7 +6,7 @@
 // To regenerate the file, use the script
 // "pkg/analysis_server/tool/spec/generate_files".
 
-const String PROTOCOL_VERSION = '1.32.8';
+const String PROTOCOL_VERSION = '1.32.9';
 
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES = 'analysis.analyzedFiles';
 const String ANALYSIS_NOTIFICATION_ANALYZED_FILES_DIRECTORIES = 'directories';
diff --git a/pkg/analyzer_plugin/doc/api.html b/pkg/analyzer_plugin/doc/api.html
index cba223d..a1c3097 100644
--- a/pkg/analyzer_plugin/doc/api.html
+++ b/pkg/analyzer_plugin/doc/api.html
@@ -1388,7 +1388,7 @@
   <dl><dt class="value">ANNOTATION</dt><dt class="value">BUILT_IN</dt><dt class="value">CLASS</dt><dt class="value">COMMENT_BLOCK</dt><dt class="value">COMMENT_DOCUMENTATION</dt><dt class="value">COMMENT_END_OF_LINE</dt><dt class="value">CONSTRUCTOR</dt><dt class="value">CONSTRUCTOR_TEAR_OFF</dt><dt class="value">DIRECTIVE</dt><dt class="value">DYNAMIC_TYPE</dt><dd>
         
         <p>Deprecated - no longer sent.</p>
-      </dd><dt class="value">DYNAMIC_LOCAL_VARIABLE_DECLARATION</dt><dt class="value">DYNAMIC_LOCAL_VARIABLE_REFERENCE</dt><dt class="value">DYNAMIC_PARAMETER_DECLARATION</dt><dt class="value">DYNAMIC_PARAMETER_REFERENCE</dt><dt class="value">ENUM</dt><dt class="value">ENUM_CONSTANT</dt><dt class="value">FIELD</dt><dd>
+      </dd><dt class="value">DYNAMIC_LOCAL_VARIABLE_DECLARATION</dt><dt class="value">DYNAMIC_LOCAL_VARIABLE_REFERENCE</dt><dt class="value">DYNAMIC_PARAMETER_DECLARATION</dt><dt class="value">DYNAMIC_PARAMETER_REFERENCE</dt><dt class="value">ENUM</dt><dt class="value">ENUM_CONSTANT</dt><dt class="value">EXTENSION</dt><dt class="value">FIELD</dt><dd>
         
         <p>Deprecated - no longer sent.</p>
       </dd><dt class="value">FIELD_STATIC</dt><dd>
diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
index dd7d2a2..61b9776 100644
--- a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
+++ b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
@@ -1882,6 +1882,7 @@
 ///   DYNAMIC_PARAMETER_REFERENCE
 ///   ENUM
 ///   ENUM_CONSTANT
+///   EXTENSION
 ///   FIELD
 ///   FIELD_STATIC
 ///   FUNCTION
@@ -1995,6 +1996,9 @@
   static const HighlightRegionType ENUM_CONSTANT =
       HighlightRegionType._('ENUM_CONSTANT');
 
+  static const HighlightRegionType EXTENSION =
+      HighlightRegionType._('EXTENSION');
+
   /// Deprecated - no longer sent.
   static const HighlightRegionType FIELD = HighlightRegionType._('FIELD');
 
@@ -2210,6 +2214,7 @@
     DYNAMIC_PARAMETER_REFERENCE,
     ENUM,
     ENUM_CONSTANT,
+    EXTENSION,
     FIELD,
     FIELD_STATIC,
     FUNCTION,
@@ -2314,6 +2319,8 @@
         return ENUM;
       case 'ENUM_CONSTANT':
         return ENUM_CONSTANT;
+      case 'EXTENSION':
+        return EXTENSION;
       case 'FIELD':
         return FIELD;
       case 'FIELD_STATIC':
diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
index 164ea38..cdb3f12 100644
--- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
+++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
@@ -379,6 +379,7 @@
 ///   DYNAMIC_PARAMETER_REFERENCE
 ///   ENUM
 ///   ENUM_CONSTANT
+///   EXTENSION
 ///   FIELD
 ///   FIELD_STATIC
 ///   FUNCTION
@@ -460,6 +461,7 @@
   'DYNAMIC_PARAMETER_REFERENCE',
   'ENUM',
   'ENUM_CONSTANT',
+  'EXTENSION',
   'FIELD',
   'FIELD_STATIC',
   'FUNCTION',
diff --git a/pkg/analyzer_plugin/tool/spec/common_types_spec.html b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
index ce3a5e9..969a0ee 100644
--- a/pkg/analyzer_plugin/tool/spec/common_types_spec.html
+++ b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
@@ -719,6 +719,7 @@
       </value>
       <value><code>ENUM</code></value>
       <value><code>ENUM_CONSTANT</code></value>
+      <value><code>EXTENSION</code></value>
       <value>
         <code>FIELD</code>
         <p>Deprecated - no longer sent.</p>
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) {
diff --git a/pkg/dart_internal/CHANGELOG.md b/pkg/dart_internal/CHANGELOG.md
index 8894fed..868e075 100644
--- a/pkg/dart_internal/CHANGELOG.md
+++ b/pkg/dart_internal/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.4
+
+- Support the latest Dart SDK.
+
 ## 0.2.3
 
 - Support the latest Dart SDK.
diff --git a/pkg/front_end/lib/src/fasta/kernel/macro.dart b/pkg/front_end/lib/src/fasta/kernel/macro.dart
index 3c8f11a..7a54280 100644
--- a/pkg/front_end/lib/src/fasta/kernel/macro.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/macro.dart
@@ -32,6 +32,7 @@
   bool macrosAreAvailable = false;
   Map<Uri, List<String>> macroDeclarations = {};
   List<List<Uri>>? compilationSequence;
+  List<Map<Uri, Map<String, List<String>>>> neededPrecompilations = [];
 }
 
 class MacroClass {
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 3e21692..08c9e9b 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -1345,18 +1345,24 @@
     ticker.logMs("Resolved $typeCount types");
   }
 
-  void computeMacroDeclarations() {
-    if (!enableMacros) return;
+  /// Computes which macro declarations that needs to be precompiled in order
+  /// to support macro application during compilation.
+  ///
+  /// If no macros need precompilation, `null` is returned. Otherwise a map
+  /// from library uris to macro class names and the names of constructor their
+  /// constructors is returned.
+  Map<Uri, Map<String, List<String>>>? computeMacroDeclarations() {
+    if (!enableMacros) return null;
 
     LibraryBuilder? macroLibraryBuilder = lookupLibraryBuilder(macroLibraryUri);
-    if (macroLibraryBuilder == null) return;
+    if (macroLibraryBuilder == null) return null;
 
     Builder? macroClassBuilder =
         macroLibraryBuilder.lookupLocalMember(macroClassName);
     if (macroClassBuilder is! ClassBuilder) {
       // TODO(johnniwinther): Report this when the actual macro builder package
       // exists. It should at least be a warning.
-      return;
+      return null;
     }
 
     _macroClassBuilder = macroClassBuilder;
@@ -1364,10 +1370,9 @@
       dataForTesting!.macroDeclarationData.macrosAreAvailable = true;
     }
 
-    Set<ClassBuilder> macroClasses = {};
-
-    /// Libraries containing macros that need compilation.
-    Set<Uri> macroLibraries = {};
+    /// Libraries containing macros that need compilation mapped to the
+    /// [ClassBuilder]s for the macro classes.
+    Map<Uri, List<ClassBuilder>> macroLibraries = {};
 
     /// Libraries containing precompiled macro classes.
     Set<Uri> precompiledMacroLibraries = {};
@@ -1383,8 +1388,7 @@
           Uri libraryUri = builder.library.importUri;
           MacroClass macroClass = new MacroClass(libraryUri, builder.name);
           if (!precompiledMacroUris.containsKey(macroClass)) {
-            macroClasses.add(builder);
-            macroLibraries.add(libraryUri);
+            (macroLibraries[libraryUri] ??= []).add(builder);
             if (retainDataForTesting) {
               (dataForTesting!.macroDeclarationData
                       .macroDeclarations[libraryUri] ??= [])
@@ -1397,8 +1401,8 @@
       }
     }
 
-    if (macroClasses.isEmpty) {
-      return;
+    if (macroLibraries.isEmpty) {
+      return null;
     }
 
     List<List<Uri>> computeCompilationSequence(Graph<Uri> libraryGraph,
@@ -1417,7 +1421,7 @@
         for (List<Uri> component in layer) {
           for (Uri uri in component) {
             if (filter(uri)) continue;
-            if (macroLibraries.contains(uri)) {
+            if (macroLibraries.containsKey(uri)) {
               declaresMacro = true;
             }
             currentLayer.add(uri);
@@ -1456,7 +1460,7 @@
         addPrecompiledLibrary(builder.importUri);
       } else if (precompiledMacroLibraries.contains(builder.importUri)) {
         assert(
-            !macroLibraries.contains(builder.importUri),
+            !macroLibraries.containsKey(builder.importUri),
             "Macro library ${builder.importUri} is only partially "
             "precompiled.");
         addPrecompiledLibrary(builder.importUri);
@@ -1471,6 +1475,52 @@
       dataForTesting!.macroDeclarationData.compilationSequence =
           compilationSteps;
     }
+
+    if (compilationSteps.length > 1) {
+      // We have at least 1 layer of macros that need to be precompiled before
+      // we can compile the program itself.
+      Map<Uri, Map<String, List<String>>> neededPrecompilations = {};
+      for (int i = 0; i < compilationSteps.length - 1; i++) {
+        List<Uri> compilationStep = compilationSteps[i];
+        for (Uri uri in compilationStep) {
+          List<ClassBuilder>? macroClasses = macroLibraries[uri];
+          // [uri] might not itself declare any macros but instead a part of the
+          // libraries that macros depend upon.
+          if (macroClasses != null) {
+            Map<String, List<String>>? constructorMap;
+            for (ClassBuilder macroClass in macroClasses) {
+              List<String> constructors =
+                  macroClass.constructors.local.keys.toList();
+              if (constructors.isNotEmpty) {
+                // TODO(johnniwinther): If there is no constructor here, it
+                // means the macro had no _explicit_ constructors. Since macro
+                // constructor are required to be const, this would be an error
+                // case. We need to handle that precompilation could result in
+                // errors like this. For this case we should probably add 'new'
+                // in case of [constructors] being empty in expectation of
+                // triggering the error during precompilation.
+                (constructorMap ??= {})[macroClass.name] = constructors;
+              }
+            }
+            if (constructorMap != null) {
+              neededPrecompilations[uri] = constructorMap;
+            }
+          }
+        }
+        if (neededPrecompilations.isNotEmpty) {
+          if (retainDataForTesting) {
+            dataForTesting!.macroDeclarationData.neededPrecompilations
+                .add(neededPrecompilations);
+          }
+          // We have found the first needed layer of precompilation. There might
+          // be more layers but we'll compute these at the next attempt at
+          // compilation, when this layer has been precompiled.
+          // TODO(johnniwinther): Use this to trigger a precompile step.
+          return neededPrecompilations;
+        }
+      }
+    }
+    return null;
   }
 
   Future<MacroApplications?> computeMacroApplications() async {
diff --git a/pkg/front_end/test/macros/data/tests/applications.dart b/pkg/front_end/test/macros/data/tests/applications.dart
index 06bf716..6adb8dc 100644
--- a/pkg/front_end/test/macros/data/tests/applications.dart
+++ b/pkg/front_end/test/macros/data/tests/applications.dart
@@ -24,7 +24,8 @@
   package:macro/macro.dart/Macro3/named(),
   package:macro/macro.dart/Macro3/named()],
  macrosAreApplied,
- macrosAreAvailable
+ macrosAreAvailable,
+ neededPrecompilations=[package:macro/macro.dart=Macro1(named/new)|Macro2(named/new)|Macro3(named/new)]
 */
 
 import 'package:macro/macro.dart';
diff --git a/pkg/front_end/test/macros/data/tests/declare_vs_apply/main.dart b/pkg/front_end/test/macros/data/tests/declare_vs_apply/main.dart
index 69ea910..09e50a9 100644
--- a/pkg/front_end/test/macros/data/tests/declare_vs_apply/main.dart
+++ b/pkg/front_end/test/macros/data/tests/declare_vs_apply/main.dart
@@ -8,7 +8,8 @@
   apply_lib.dart|main.dart],
  macroClassIds=[macro_lib.dart/Macro1],
  macroInstanceIds=[macro_lib.dart/Macro1/()],
- macrosAreAvailable
+ macrosAreAvailable,
+ neededPrecompilations=[macro_lib.dart=Macro1(new)]
 */
 
 import 'apply_lib.dart';
diff --git a/pkg/front_end/test/macros/data/tests/import_macro_package.dart b/pkg/front_end/test/macros/data/tests/import_macro_package.dart
index 9619bb1..64d7efc 100644
--- a/pkg/front_end/test/macros/data/tests/import_macro_package.dart
+++ b/pkg/front_end/test/macros/data/tests/import_macro_package.dart
@@ -6,7 +6,8 @@
  compilationSequence=[
   package:_fe_analyzer_shared/src/macros/api.dart|package:macro/macro.dart,
   main.dart],
- macrosAreAvailable
+ macrosAreAvailable,
+ neededPrecompilations=[package:macro/macro.dart=Macro1(named/new)|Macro2(named/new)|Macro3(named/new)]
 */
 
 // ignore: unused_import
diff --git a/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart b/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart
index 15c660c..d94aeaa 100644
--- a/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart
+++ b/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart
@@ -6,7 +6,8 @@
  compilationSequence=[
   macro_lib.dart|package:_fe_analyzer_shared/src/macros/api.dart,
   main.dart],
- macrosAreAvailable
+ macrosAreAvailable,
+ neededPrecompilations=[macro_lib.dart=Macro1(new)]
 */
 
 // ignore: unused_import
diff --git a/pkg/front_end/test/macros/data/tests/multiple_macros/main.dart b/pkg/front_end/test/macros/data/tests/multiple_macros/main.dart
index 873d202..103c1ed 100644
--- a/pkg/front_end/test/macros/data/tests/multiple_macros/main.dart
+++ b/pkg/front_end/test/macros/data/tests/multiple_macros/main.dart
@@ -17,7 +17,8 @@
   macro_lib2a.dart/Macro2a/(),
   macro_lib2b.dart/Macro2b/()],
  macrosAreApplied,
- macrosAreAvailable
+ macrosAreAvailable,
+ neededPrecompilations=[macro_lib1.dart=Macro1(new)macro_lib2a.dart=Macro2a(new)]
 */
 
 import 'macro_lib1.dart';
diff --git a/pkg/front_end/test/macros/data/tests/precompiled.dart b/pkg/front_end/test/macros/data/tests/precompiled.dart
index 5b29de2..16171a8 100644
--- a/pkg/front_end/test/macros/data/tests/precompiled.dart
+++ b/pkg/front_end/test/macros/data/tests/precompiled.dart
@@ -13,7 +13,8 @@
   package:macro/macro.dart/Macro1/(),
   package:precompiled_macro/precompiled_macro.dart/PrecompiledMacro/()],
  macrosAreApplied,
- macrosAreAvailable
+ macrosAreAvailable,
+ neededPrecompilations=[package:macro/macro.dart=Macro1(named/new)|Macro2(named/new)|Macro3(named/new)]
 */
 
 import 'package:precompiled_macro/precompiled_macro.dart';
diff --git a/pkg/front_end/test/macros/data/tests/use_macro_package.dart b/pkg/front_end/test/macros/data/tests/use_macro_package.dart
index c3b123e..9fea835 100644
--- a/pkg/front_end/test/macros/data/tests/use_macro_package.dart
+++ b/pkg/front_end/test/macros/data/tests/use_macro_package.dart
@@ -19,7 +19,8 @@
   package:macro/macro.dart/Macro3/(),
   package:macro/macro.dart/Macro3/()],
  macrosAreApplied,
- macrosAreAvailable
+ macrosAreAvailable,
+ neededPrecompilations=[package:macro/macro.dart=Macro1(named/new)|Macro2(named/new)|Macro3(named/new)]
 */
 library use_macro_package;
 
diff --git a/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart b/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart
index eff50a1..475694d 100644
--- a/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart
+++ b/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart
@@ -16,7 +16,8 @@
   macro_lib.dart/Macro2/(),
   macro_lib.dart/Macro2/()],
  macrosAreApplied,
- macrosAreAvailable
+ macrosAreAvailable,
+ neededPrecompilations=[macro_lib.dart=Macro1(new)|Macro2(new)]
 */
 
 import 'macro_lib.dart';
diff --git a/pkg/front_end/test/macros/macro_test.dart b/pkg/front_end/test/macros/macro_test.dart
index 26aef58..e6ff78d 100644
--- a/pkg/front_end/test/macros/macro_test.dart
+++ b/pkg/front_end/test/macros/macro_test.dart
@@ -86,12 +86,17 @@
   static const String macrosAreAvailable = 'macrosAreAvailable';
   static const String macrosAreApplied = 'macrosAreApplied';
   static const String compilationSequence = 'compilationSequence';
+  static const String neededPrecompilations = 'neededPrecompilations';
   static const String declaredMacros = 'declaredMacros';
   static const String appliedMacros = 'appliedMacros';
   static const String macroClassIds = 'macroClassIds';
   static const String macroInstanceIds = 'macroInstanceIds';
 }
 
+String constructorNameToString(String constructorName) {
+  return constructorName == '' ? 'new' : constructorName;
+}
+
 String importUriToString(Uri importUri) {
   if (importUri.scheme == 'package') {
     return importUri.toString();
@@ -183,9 +188,8 @@
     if (macroApplications != null) {
       for (MacroApplication application in macroApplications) {
         String className = application.classBuilder.name;
-        String constructorName = application.constructorName == ''
-            ? 'new'
-            : application.constructorName;
+        String constructorName =
+            constructorNameToString(application.constructorName);
         features.addElement(
             Tags.appliedMacros, '${className}.${constructorName}');
       }
@@ -216,6 +220,32 @@
               Tags.compilationSequence, strongComponentToString(component));
         }
       }
+      for (Map<Uri, Map<String, List<String>>> precompilation
+          in macroDeclarationData.neededPrecompilations) {
+        Map<String, Map<String, List<String>>> converted =
+            new Map.fromIterables(precompilation.keys.map(importUriToString),
+                precompilation.values);
+        List<String> uris = converted.keys.toList()..sort();
+        StringBuffer sb = new StringBuffer();
+        for (String uri in uris) {
+          sb.write(uri);
+          sb.write('=');
+          Map<String, List<String>> macros = converted[uri]!;
+          List<String> classes = macros.keys.toList()..sort();
+          String delimiter = '';
+          for (String cls in classes) {
+            List<String> constructorNames =
+                macros[cls]!.map(constructorNameToString).toList()..sort();
+            sb.write(delimiter);
+            sb.write(cls);
+            sb.write('(');
+            sb.write(constructorNames.join('/'));
+            sb.write(')');
+            delimiter = '|';
+          }
+        }
+        features.addElement(Tags.neededPrecompilations, sb.toString());
+      }
       for (_MacroClassIdentifier id in macroExecutor.macroClasses) {
         features.addElement(Tags.macroClassIds, id.toText());
       }
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 7ec217f..22b6b5a 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -980,6 +980,9 @@
 pre
 prebuild
 prebuilt
+precompilation
+precompilations
+precompile
 precompiled
 preexisted
 preexisting
@@ -1391,6 +1394,7 @@
 traverse
 trees
 tricky
+triggering
 trips
 trivially
 ts
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 4e51302..1da0854 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -774,6 +774,8 @@
 population
 portions
 pp
+precompilation
+precompilations
 preliminary
 prematurely
 press
diff --git a/sdk/lib/_internal/vm/lib/hash_factories.dart b/sdk/lib/_internal/vm/lib/hash_factories.dart
new file mode 100644
index 0000000..9e45e8d
--- /dev/null
+++ b/sdk/lib/_internal/vm/lib/hash_factories.dart
@@ -0,0 +1,7 @@
+// 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.
+
+// This is a placeholder file which will shortly contain the LinkedhashMap and
+// LinkedHashSet patches from collection_patch.dart. The change is done in two
+// steps to ease rolling it into Flutter.
diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json
index b62cc71..8573ca4 100644
--- a/sdk/lib/libraries.json
+++ b/sdk/lib/libraries.json
@@ -32,7 +32,8 @@
         "uri": "collection/collection.dart",
         "patches": [
           "_internal/vm/lib/collection_patch.dart",
-          "_internal/vm/lib/compact_hash.dart"
+          "_internal/vm/lib/compact_hash.dart",
+          "_internal/vm/lib/hash_factories.dart"
         ]
       },
       "convert": {
diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml
index 4aab70c..dcf81c0 100644
--- a/sdk/lib/libraries.yaml
+++ b/sdk/lib/libraries.yaml
@@ -41,6 +41,7 @@
       patches:
         - "_internal/vm/lib/collection_patch.dart"
         - "_internal/vm/lib/compact_hash.dart"
+        - "_internal/vm/lib/hash_factories.dart"
 
     convert:
       uri: "convert/convert.dart"
diff --git a/tools/VERSION b/tools/VERSION
index 9d9cc54..633bc51 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 64
+PRERELEASE 65
 PRERELEASE_PATCH 0
\ No newline at end of file