blob: 595e8a04ca9b81a85cb7b626195541436bdfaed9 [file] [log] [blame] [edit]
// Copyright (c) 2026, 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 'package:native_compiler/back_end/back_end_state.dart';
import 'package:cfg/passes/pass.dart';
import 'package:cfg/ir/instructions.dart';
import 'package:cfg/utils/bit_vector.dart';
/// Compute block order for code generation.
///
/// It is similar to the reverse postorder except:
/// - blocks ending with Throw are considered cold and placed at the end.
/// - blocks which have only cold successors are also considered cold.
/// - blocks belonging to a loop are placed together.
final class ReorderBlocks extends Pass {
final BackEndState backEndState;
ReorderBlocks(this.backEndState) : super('ReorderBlocks');
@override
void run() {
final numBlocks = graph.preorder.length;
final visited = BitVector(numBlocks);
final cold = BitVector(numBlocks);
final postorder = <Block>[];
final coldPostorder = <Block>[];
final workList = <(Block, bool)>[];
void pushBlock(Block succ) {
if (!visited[succ.preorderNumber]) {
visited.add(succ.preorderNumber);
workList.add((succ, true));
}
}
pushBlock(graph.entryBlock);
while (workList.isNotEmpty) {
final (block, discoverSuccessors) = workList.removeLast();
final successors = block.successors;
if (discoverSuccessors) {
// Re-visit block after successors are visited.
workList.add((block, false));
final lastInstruction = block.lastInstruction;
if (lastInstruction is Branch || lastInstruction is CompareAndBranch) {
// Push successor with lower loop nesting last so
// it is processed first.
final succ0 = successors[0];
final succ1 = successors[1];
if (succ0.loopDepth < succ1.loopDepth) {
pushBlock(succ1);
pushBlock(succ0);
} else {
pushBlock(succ0);
pushBlock(succ1);
}
} else {
for (final succ in successors) {
pushBlock(succ);
}
}
} else {
// All successors have been processed.
// Figure out if block belongs to a cold section.
var isCold = false;
switch (block.lastInstruction) {
case Throw():
isCold = true;
break;
default:
if (successors.isNotEmpty) {
isCold = true;
for (final succ in successors) {
if (!cold[succ.preorderNumber]) {
isCold = false;
break;
}
}
}
}
if (isCold) {
cold.add(block.preorderNumber);
coldPostorder.add(block);
} else {
postorder.add(block);
}
}
}
backEndState.codeGenBlockOrder = <Block>[
...postorder.reversed,
...coldPostorder.reversed,
];
}
}