1 /*
  2  * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 import jdk.incubator.code.Block;
 25 import jdk.incubator.code.Body;
 26 
 27 import java.util.*;
 28 
 29 public class LoopAnalyzer {
 30     public record Loop(Block header, List<Block> latches, Set<Block> body, List<LoopExit> exits) {
 31     }
 32 
 33     public record LoopExit(Block exit, Block target) {
 34     }
 35 
 36     public static Optional<Loop> isLoop(Block header) {
 37         List<Block> latches = header.predecessors()
 38                 .stream().filter(p -> p.isDominatedBy(header))
 39                 .toList();
 40         if (latches.isEmpty()) {
 41             return Optional.empty();
 42         }
 43 
 44         Set<Block> body = new HashSet<>();
 45         for (Block latch : latches) {
 46             loopBody(body, header, latch);
 47         }
 48         List<LoopExit> exits = loopExits(body);
 49         return Optional.of(new Loop(header, latches, body, exits));
 50     }
 51 
 52     static Set<Block> naturalLoopBody(Set<Block> loopBody, Block header, Block latch) {
 53         Deque<Block> stack = new ArrayDeque<>();
 54         stack.push(latch);
 55         Block node;
 56         // Backward depth first search from latch to header
 57         while (!stack.isEmpty() && (node = stack.pop()) != header) {
 58             if (!loopBody.add(node)) {
 59                 continue;
 60             }
 61 
 62             stack.addAll(node.predecessors().reversed());
 63         }
 64         loopBody.add(header);
 65 
 66         return loopBody;
 67     }
 68 
 69     static Set<Block> loopBody(Set<Block> loopBody, Block header, Block latch) {
 70         naturalLoopBody(loopBody, header, latch);
 71 
 72         Set<Block> extendedLoopBody = new HashSet<>();
 73         for (Block lb : loopBody) {
 74             for (Block lbs : lb.successorTargets()) {
 75                 if (!loopBody.contains(lbs) && !extendedLoopBody.contains(lbs)) {
 76                     // Find if there is path from lbs to latch that does not pass through header
 77                     Deque<Block> stack = new ArrayDeque<>();
 78                     stack.push(lbs);
 79                     Set<Block> visited = new HashSet<>();
 80                     while (!stack.isEmpty()) {
 81                         Block x = stack.pop();
 82                         if (!visited.add(x)) {
 83                             continue;
 84                         }
 85 
 86                         if (find(x, header, latch)) {
 87                             extendedLoopBody.add(x);
 88                         }
 89 
 90                         stack.addAll(x.successorTargets());
 91                     }
 92                 }
 93             }
 94         }
 95 
 96         loopBody.addAll(extendedLoopBody);
 97         return loopBody;
 98     }
 99 
100     // Determine if there is a forward path from x to y that does not pass through n
101     static boolean find(Block x, Block n, Block y) {
102         if (x == n) {
103             return false;
104         }
105         if (x == y) {
106             return true;
107         }
108 
109         boolean r = false;
110         for (Block b : x.successorTargets()) {
111             // Back branch
112             if (x.isDominatedBy(b))
113                 return false;
114             if (find(b, n, y)) {
115                 return true;
116             }
117         }
118         return false;
119     }
120 
121     static List<LoopExit> loopExits(Set<Block> loopBody) {
122         List<LoopExit> loopExits = new ArrayList<>();
123         for (Block block : loopBody) {
124             for (Block t : block.successorTargets()) {
125                 if (!loopBody.contains(t)) {
126                     loopExits.add(new LoopExit(block, t));
127                 }
128             }
129         }
130         return loopExits;
131     }
132 
133     static List<Loop> findLoops(Body body) {
134         return body.blocks().stream().flatMap(b -> isLoop(b).stream()).toList();
135     }
136 }