| // Copyright (c) 2025, 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:cfg/ir/flow_graph.dart'; |
| import 'package:cfg/ir/instructions.dart'; |
| import 'package:cfg/utils/bit_vector.dart'; |
| |
| class Loops extends Iterable<Loop> { |
| final FlowGraph graph; |
| |
| final List<Loop> _loops = []; |
| |
| /// Block preorder number -> innermost Loop. |
| final List<Loop?> _loopByBlock; |
| |
| Loops._(this.graph) |
| : _loopByBlock = List<Loop?>.filled(graph.preorder.length, null); |
| |
| Loop? operator [](Block block) => _loopByBlock[block.preorderNumber]; |
| |
| void operator []=(Block block, Loop loop) { |
| _loopByBlock[block.preorderNumber] = loop; |
| } |
| |
| @override |
| Iterator<Loop> get iterator => _loops.iterator; |
| } |
| |
| /// Natural loop with one entrance to the loop header and |
| /// one or more back-edges. |
| class Loop { |
| final Block header; |
| final BitVector _body; |
| final List<Block> backEdges = []; |
| Loop? enclosingLoop; |
| late final int depth = _computeDepth(); |
| |
| Loop._(this.header) : _body = BitVector(header.graph.preorder.length); |
| |
| bool contains(Block block) => _body[block.preorderNumber]; |
| |
| void add(Block block) { |
| _body.add(block.preorderNumber); |
| } |
| |
| /// Add all blocks between [header] and [backEdge]. |
| void addBody(Block backEdge) { |
| final workList = <Block>[]; |
| add(header); |
| if (backEdge != header) { |
| add(backEdge); |
| workList.add(backEdge); |
| } |
| while (workList.isNotEmpty) { |
| final block = workList.removeLast(); |
| assert(block.isDominatedBy(header)); |
| for (final pred in block.predecessors) { |
| if (!contains(pred)) { |
| add(pred); |
| workList.add(pred); |
| } |
| } |
| } |
| } |
| |
| Iterable<Block> get body => _LoopBodyIterable(this); |
| |
| int _computeDepth() { |
| var depth = 0; |
| for (Loop? loop = this; loop != null; loop = loop.enclosingLoop) { |
| ++depth; |
| } |
| return depth; |
| } |
| } |
| |
| class _LoopBodyIterable extends Iterable<Block> { |
| final Loop loop; |
| _LoopBodyIterable(this.loop); |
| |
| @override |
| Iterator<Block> get iterator => |
| _LoopBodyIterator(loop.header.graph, loop._body.elements.iterator); |
| } |
| |
| class _LoopBodyIterator implements Iterator<Block> { |
| final FlowGraph _graph; |
| final Iterator<int> _bodyIterator; |
| |
| _LoopBodyIterator(this._graph, this._bodyIterator); |
| |
| @override |
| bool moveNext() => _bodyIterator.moveNext(); |
| |
| @override |
| Block get current => _graph.preorder[_bodyIterator.current]; |
| } |
| |
| /// Compute loops. |
| Loops computeLoops(FlowGraph graph) { |
| final loops = Loops._(graph); |
| |
| for (final block in graph.postorder) { |
| if (block is JoinBlock && block.predecessors.length > 1) { |
| for (final pred in block.predecessors) { |
| if (pred.isDominatedBy(block)) { |
| Loop? loop = loops[block]; |
| if (loop == null) { |
| loops[block] = loop = Loop._(block); |
| loops._loops.add(loop); |
| } |
| loop.addBody(pred); |
| loop.backEdges.add(pred); |
| } |
| } |
| } |
| } |
| |
| for (final loop in loops) { |
| for (final block in loop.body) { |
| final innerLoop = loops[block]; |
| if (innerLoop == null) { |
| loops[block] = loop; |
| } else { |
| assert(loop.contains(innerLoop.header)); |
| } |
| } |
| } |
| |
| for (final loop in loops) { |
| final domLoop = loops[loop.header.dominator!]; |
| if (domLoop != null && domLoop.contains(loop.header)) { |
| loop.enclosingLoop = domLoop; |
| } |
| } |
| |
| return loops; |
| } |