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