1 /*
  2  * Copyright (c) 2024, 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 org.testng.Assert;
 25 import org.testng.annotations.Test;
 26 
 27 import jdk.incubator.code.Block;
 28 import jdk.incubator.code.op.CoreOp;
 29 import jdk.incubator.code.Op;
 30 import jdk.incubator.code.type.FunctionType;
 31 import jdk.incubator.code.type.JavaType;
 32 import java.util.HashMap;
 33 import java.util.HashSet;
 34 import java.util.Map;
 35 import java.util.Set;
 36 import java.util.function.Function;
 37 import java.util.stream.Collectors;
 38 
 39 import static jdk.incubator.code.op.CoreOp._return;
 40 import static jdk.incubator.code.op.CoreOp.branch;
 41 import static jdk.incubator.code.op.CoreOp.conditionalBranch;
 42 import static jdk.incubator.code.op.CoreOp.constant;
 43 import static jdk.incubator.code.op.CoreOp.func;
 44 
 45 /*
 46  * @test
 47  * @modules jdk.incubator.code
 48  * @run testng TestDominate
 49  */
 50 
 51 public class TestDominate {
 52 
 53     public void testUnmodifiableIdoms() {
 54         CoreOp.FuncOp f = func("f", FunctionType.VOID).body(entry -> {
 55             Block.Builder ifBlock = entry.block();
 56             Block.Builder elseBlock = entry.block();
 57             Block.Builder end = entry.block();
 58 
 59             Op.Result p = entry.op(constant(JavaType.BOOLEAN, true));
 60             entry.op(conditionalBranch(p, ifBlock.successor(), elseBlock.successor()));
 61 
 62             ifBlock.op(branch(end.successor()));
 63 
 64             elseBlock.op(branch(end.successor()));
 65 
 66             end.op(_return());
 67         });
 68 
 69         Map<Block, Block> idoms = f.body().immediateDominators();
 70         Assert.assertThrows(UnsupportedOperationException.class,
 71                 () -> idoms.put(f.body().entryBlock(), f.body().entryBlock()));
 72         Assert.assertThrows(UnsupportedOperationException.class,
 73                 idoms::clear);
 74     }
 75 
 76     @Test
 77     public void testIfElse() {
 78         CoreOp.FuncOp f = func("f", FunctionType.VOID).body(entry -> {
 79             Block.Builder ifBlock = entry.block();
 80             Block.Builder elseBlock = entry.block();
 81             Block.Builder end = entry.block();
 82 
 83             Op.Result p = entry.op(constant(JavaType.BOOLEAN, true));
 84             entry.op(conditionalBranch(p, ifBlock.successor(), elseBlock.successor()));
 85 
 86             ifBlock.op(branch(end.successor()));
 87 
 88             elseBlock.op(branch(end.successor()));
 89 
 90             end.op(_return());
 91         });
 92 
 93         boolean[][] bvs = new boolean[][]{
 94                 {true, false, false, false},
 95                 {true, true, false, false},
 96                 {true, false, true, false},
 97                 {true, false, false, true}
 98         };
 99 
100         test(f, bvs);
101     }
102 
103     @Test
104     public void testForwardSuccessors() {
105         CoreOp.FuncOp f = func("f", FunctionType.VOID).body(entry -> {
106             Block.Builder b1 = entry.block();
107             Block.Builder b2 = entry.block();
108             Block.Builder b3 = entry.block();
109             Block.Builder b4 = entry.block();
110             Block.Builder b5 = entry.block();
111 
112             Op.Result p = entry.op(constant(JavaType.BOOLEAN, true));
113             entry.op(conditionalBranch(p, b4.successor(), b2.successor()));
114 
115             b4.op(conditionalBranch(p, b5.successor(), b3.successor()));
116 
117             b2.op(conditionalBranch(p, b5.successor(), b1.successor()));
118 
119             b5.op(_return());
120 
121             b3.op(branch(b1.successor()));
122 
123             b1.op(_return());
124         });
125 
126         f.writeTo(System.out);
127         boolean[][] bvs = new boolean[][]{
128                 {true, false, false, false, false, false},
129                 {true, true, false, false, false, false},
130                 {true, true, true, false, false, false},
131                 {true, false, false, true, false, false},
132                 {true, false, false, false, true, false},
133                 {true, false, false, false, false, true},
134         };
135 
136         test(f, bvs);
137     }
138 
139     @Test
140     public void testBackbranch() {
141         CoreOp.FuncOp f = func("f", FunctionType.VOID).body(entry -> {
142             Block.Builder cond = entry.block();
143             Block.Builder body = entry.block();
144             Block.Builder update = entry.block();
145             Block.Builder end = entry.block();
146 
147             Op.Result p = entry.op(constant(JavaType.BOOLEAN, true));
148             entry.op(branch(cond.successor()));
149 
150             cond.op(conditionalBranch(p, body.successor(), end.successor()));
151 
152             body.op(branch(update.successor()));
153 
154             update.op(branch(cond.successor()));
155 
156             end.op(_return());
157 
158         });
159 
160         boolean[][] bvs = new boolean[][]{
161                 {true, false, false, false, false},
162                 {true, true, false, false, false},
163                 {true, true, true, false, false},
164                 {true, true, true, true, false},
165                 {true, true, false, false, true},
166         };
167         test(f, bvs);
168     }
169 
170     static void test(CoreOp.FuncOp f, boolean[][] bvs) {
171         Block[] bs = f.body().blocks().toArray(Block[]::new);
172         for (int i = 0; i < bs.length; i++) {
173             for (int j = 0; j < bs.length; j++) {
174                 Block x = bs[i];
175                 Block y = bs[j];
176                 Assert.assertEquals(y.isDominatedBy(x), bvs[j][i]);
177             }
178         }
179     }
180 
181 
182     @Test
183     public void testImmediateDominators() {
184         CoreOp.FuncOp f = func("f", FunctionType.VOID).body(entry -> {
185             Block.Builder b6 = entry.block();
186             Block.Builder b5 = entry.block();
187             Block.Builder b4 = entry.block();
188             Block.Builder b3 = entry.block();
189             Block.Builder b2 = entry.block();
190             Block.Builder b1 = entry.block();
191 
192             Op.Result p = entry.op(constant(JavaType.BOOLEAN, true));
193             entry.op(branch(b6.successor()));
194 
195             b6.op(conditionalBranch(p, b5.successor(), b4.successor()));
196 
197             b5.op(branch(b1.successor()));
198 
199             b4.op(conditionalBranch(p, b2.successor(), b3.successor()));
200 
201             b1.op(branch(b2.successor()));
202 
203             b2.op(conditionalBranch(p, b1.successor(), b3.successor()));
204 
205             b3.op(branch(b2.successor()));
206         });
207         f.writeTo(System.out);
208         Map<Block, Block> idoms = f.body().immediateDominators();
209         System.out.println(idoms);
210 
211         Block entry = f.body().entryBlock();
212         Block b6 = entry.successors().get(0).targetBlock();
213 
214         for (Block b : f.body().blocks()) {
215             if (b == entry || b == b6) {
216                 Assert.assertEquals(idoms.get(b), entry);
217             } else {
218                 Assert.assertEquals(idoms.get(b), b6);
219             }
220         }
221     }
222 
223     @Test
224     public void testCytronExample() {
225         CoreOp.FuncOp f = func("f", FunctionType.VOID).body(entry -> {
226             Block.Builder exit = entry.block();
227             Block.Builder b12 = entry.block();
228             Block.Builder b11 = entry.block();
229             Block.Builder b10 = entry.block();
230             Block.Builder b9 = entry.block();
231             Block.Builder b8 = entry.block();
232             Block.Builder b7 = entry.block();
233             Block.Builder b6 = entry.block();
234             Block.Builder b5 = entry.block();
235             Block.Builder b4 = entry.block();
236             Block.Builder b3 = entry.block();
237             Block.Builder b2 = entry.block();
238             Block.Builder b1 = entry.block();
239 
240             Op.Result p = entry.op(constant(JavaType.BOOLEAN, true));
241 
242             entry.op(conditionalBranch(p, exit.successor(), b1.successor()));
243 
244             b1.op(branch(b2.successor()));
245 
246             b2.op(conditionalBranch(p, b3.successor(), b7.successor()));
247 
248             b3.op(conditionalBranch(p, b4.successor(), b5.successor()));
249 
250             b4.op(branch(b6.successor()));
251 
252             b5.op(branch(b6.successor()));
253 
254             b6.op(branch(b8.successor()));
255 
256             b7.op(branch(b8.successor()));
257 
258             b8.op(branch(b9.successor()));
259 
260             b9.op(conditionalBranch(p, b10.successor(), b11.successor()));
261 
262             b10.op(branch(b11.successor()));
263 
264             b11.op(conditionalBranch(p, b12.successor(), b9.successor()));
265 
266             b12.op(conditionalBranch(p, exit.successor(), b2.successor()));
267 
268             exit.op(_return());
269         });
270 
271         f.writeTo(System.out);
272 
273         Map<Block, Block> idoms = f.body().immediateDominators();
274         Node<String> domTree = buildDomTree(f.body().entryBlock(), idoms).transform(b -> Integer.toString(b.index()));
275         Node<String> domTreeExpected =
276                 node("0",
277                         node("1",
278                                 node("2",
279                                         node("7"),
280                                         node("8",
281                                                 node("9",
282                                                         node("10"),
283                                                         node("11",
284                                                                 node("12")))),
285                                         node("3",
286                                                 node("4"), node("5"), node("6")))),
287                         node("13"));
288         Assert.assertEquals(domTree, domTreeExpected);
289 
290 
291         Map<String, Set<String>> df = f.body().dominanceFrontier().entrySet().stream()
292                 .map(e -> Map.entry(Integer.toString(e.getKey().index()),
293                         e.getValue().stream().map(b -> Integer.toString(b.index())).collect(Collectors.toSet())))
294                 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
295 
296         Map<String, Set<String>> dfExpected = Map.ofEntries(
297                 Map.entry("1", Set.of("13")),
298                 Map.entry("2", Set.of("13", "2")),
299                 Map.entry("3", Set.of("8")),
300                 Map.entry("4", Set.of("6")),
301                 Map.entry("5", Set.of("6")),
302                 Map.entry("6", Set.of("8")),
303                 Map.entry("7", Set.of("8")),
304                 Map.entry("8", Set.of("13", "2")),
305                 Map.entry("9", Set.of("13", "2", "9")),
306                 Map.entry("10", Set.of("11")),
307                 Map.entry("11", Set.of("13", "2", "9")),
308                 Map.entry("12", Set.of("13", "2"))
309         );
310         Assert.assertEquals(df, dfExpected);
311     }
312 
313     static Node<Block> buildDomTree(Block entryBlock, Map<Block, Block> idoms) {
314         Map<Block, Node<Block>> m = new HashMap<>();
315         for (Map.Entry<Block, Block> e : idoms.entrySet()) {
316             Block id = e.getValue();
317             Block b = e.getKey();
318             if (b == entryBlock) {
319                 continue;
320             }
321 
322             Node<Block> parent = m.computeIfAbsent(id, _k -> new Node<>(_k, new HashSet<>()));
323             Node<Block> child = m.computeIfAbsent(b, _k -> new Node<>(_k, new HashSet<>()));
324             parent.children.add(child);
325         }
326         return m.get(entryBlock);
327     }
328 
329     @SafeVarargs
330     static <T> Node<T> node(T t, Node<T>... children) {
331         return new Node<>(t, Set.of(children));
332     }
333 
334     static <T> Node<T> node(T t, Set<Node<T>> children) {
335         return new Node<>(t, children);
336     }
337 
338     record Node<T>(T t, Set<Node<T>> children) {
339         <U> Node<U> transform(Function<T, U> f) {
340             Set<Node<U>> mchildren = new HashSet<>();
341             for (Node<T> nc : children) {
342                 mchildren.add(nc.transform(f));
343             }
344             return node(f.apply(t), mchildren);
345         }
346     }
347 }