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 jdk.incubator.code.Body; 25 import jdk.incubator.code.dialect.core.CoreType; 26 import jdk.incubator.code.extern.OpParser; 27 import org.testng.Assert; 28 import org.testng.annotations.Test; 29 30 import jdk.incubator.code.Block; 31 import jdk.incubator.code.dialect.core.CoreOp; 32 import jdk.incubator.code.Op; 33 import jdk.incubator.code.dialect.java.JavaType; 34 35 import java.util.HashMap; 36 import java.util.HashSet; 37 import java.util.Map; 38 import java.util.Set; 39 import java.util.function.Function; 40 import java.util.stream.Collectors; 41 42 import static jdk.incubator.code.dialect.core.CoreOp.return_; 43 import static jdk.incubator.code.dialect.core.CoreOp.branch; 44 import static jdk.incubator.code.dialect.core.CoreOp.conditionalBranch; 45 import static jdk.incubator.code.dialect.core.CoreOp.constant; 46 import static jdk.incubator.code.dialect.core.CoreOp.func; 47 48 /* 49 * @test 50 * @modules jdk.incubator.code 51 * @run testng TestDominate 52 */ 53 54 public class TestDominate { 55 56 @Test 57 public void testUnmodifiableIdoms() { 58 CoreOp.FuncOp f = func("f", CoreType.FUNCTION_TYPE_VOID).body(entry -> { 59 Block.Builder ifBlock = entry.block(); 60 Block.Builder elseBlock = entry.block(); 61 Block.Builder end = entry.block(); 62 63 Op.Result p = entry.op(constant(JavaType.BOOLEAN, true)); 64 entry.op(conditionalBranch(p, ifBlock.successor(), elseBlock.successor())); 65 66 ifBlock.op(branch(end.successor())); 67 68 elseBlock.op(branch(end.successor())); 69 70 end.op(CoreOp.return_()); 71 }); 72 73 Map<Block, Block> idoms = f.body().immediateDominators(); 74 Assert.assertThrows(UnsupportedOperationException.class, 75 () -> idoms.put(f.body().entryBlock(), f.body().entryBlock())); 76 Assert.assertThrows(UnsupportedOperationException.class, 77 idoms::clear); 78 79 Map<Block, Block> ipdoms = f.body().immediatePostDominators(); 80 Assert.assertThrows(UnsupportedOperationException.class, 81 () -> ipdoms.put(f.body().entryBlock(), f.body().entryBlock())); 82 Assert.assertThrows(UnsupportedOperationException.class, 83 ipdoms::clear); 84 } 85 86 @Test 87 public void testIfElse() { 88 CoreOp.FuncOp f = func("f", CoreType.FUNCTION_TYPE_VOID).body(entry -> { 89 Block.Builder ifBlock = entry.block(); 90 Block.Builder elseBlock = entry.block(); 91 Block.Builder end = entry.block(); 92 93 Op.Result p = entry.op(constant(JavaType.BOOLEAN, true)); 94 entry.op(conditionalBranch(p, ifBlock.successor(), elseBlock.successor())); 95 96 ifBlock.op(branch(end.successor())); 97 98 elseBlock.op(branch(end.successor())); 99 100 end.op(CoreOp.return_()); 101 }); 102 103 boolean[][] bvs = new boolean[][]{ 104 {true, false, false, false}, 105 {true, true, false, false}, 106 {true, false, true, false}, 107 {true, false, false, true} 108 }; 109 110 test(f, bvs); 111 } 112 113 @Test 114 public void testForwardSuccessors() { 115 CoreOp.FuncOp f = func("f", CoreType.FUNCTION_TYPE_VOID).body(entry -> { 116 Block.Builder b1 = entry.block(); 117 Block.Builder b2 = entry.block(); 118 Block.Builder b3 = entry.block(); 119 Block.Builder b4 = entry.block(); 120 Block.Builder b5 = entry.block(); 121 122 Op.Result p = entry.op(constant(JavaType.BOOLEAN, true)); 123 entry.op(conditionalBranch(p, b4.successor(), b2.successor())); 124 125 b4.op(conditionalBranch(p, b5.successor(), b3.successor())); 126 127 b2.op(conditionalBranch(p, b5.successor(), b1.successor())); 128 129 b5.op(CoreOp.return_()); 130 131 b3.op(branch(b1.successor())); 132 133 b1.op(CoreOp.return_()); 134 }); 135 136 System.out.println(f.toText()); 137 boolean[][] bvs = new boolean[][]{ 138 {true, false, false, false, false, false}, 139 {true, true, false, false, false, false}, 140 {true, true, true, false, false, false}, 141 {true, false, false, true, false, false}, 142 {true, false, false, false, true, false}, 143 {true, false, false, false, false, true}, 144 }; 145 146 test(f, bvs); 147 } 148 149 @Test 150 public void testBackbranch() { 151 CoreOp.FuncOp f = func("f", CoreType.FUNCTION_TYPE_VOID).body(entry -> { 152 Block.Builder cond = entry.block(); 153 Block.Builder body = entry.block(); 154 Block.Builder update = entry.block(); 155 Block.Builder end = entry.block(); 156 157 Op.Result p = entry.op(constant(JavaType.BOOLEAN, true)); 158 entry.op(branch(cond.successor())); 159 160 cond.op(conditionalBranch(p, body.successor(), end.successor())); 161 162 body.op(branch(update.successor())); 163 164 update.op(branch(cond.successor())); 165 166 end.op(CoreOp.return_()); 167 168 }); 169 170 boolean[][] bvs = new boolean[][]{ 171 {true, false, false, false, false}, 172 {true, true, false, false, false}, 173 {true, true, true, false, false}, 174 {true, true, true, true, false}, 175 {true, true, false, false, true}, 176 }; 177 test(f, bvs); 178 } 179 180 static void test(CoreOp.FuncOp f, boolean[][] bvs) { 181 Block[] bs = f.body().blocks().toArray(Block[]::new); 182 for (int i = 0; i < bs.length; i++) { 183 for (int j = 0; j < bs.length; j++) { 184 Block x = bs[i]; 185 Block y = bs[j]; 186 Assert.assertEquals(y.isDominatedBy(x), bvs[j][i]); 187 } 188 } 189 } 190 191 192 @Test 193 public void testImmediateDominators() { 194 CoreOp.FuncOp f = func("f", CoreType.FUNCTION_TYPE_VOID).body(entry -> { 195 Block.Builder b6 = entry.block(); 196 Block.Builder b5 = entry.block(); 197 Block.Builder b4 = entry.block(); 198 Block.Builder b3 = entry.block(); 199 Block.Builder b2 = entry.block(); 200 Block.Builder b1 = entry.block(); 201 202 Op.Result p = entry.op(constant(JavaType.BOOLEAN, true)); 203 entry.op(branch(b6.successor())); 204 205 b6.op(conditionalBranch(p, b5.successor(), b4.successor())); 206 207 b5.op(branch(b1.successor())); 208 209 b4.op(conditionalBranch(p, b2.successor(), b3.successor())); 210 211 b1.op(branch(b2.successor())); 212 213 b2.op(conditionalBranch(p, b1.successor(), b3.successor())); 214 215 b3.op(branch(b2.successor())); 216 }); 217 System.out.println(f.toText()); 218 Map<Block, Block> idoms = f.body().immediateDominators(); 219 System.out.println(idoms); 220 221 Block entry = f.body().entryBlock(); 222 Block b6 = entry.successors().get(0).targetBlock(); 223 224 for (Block b : f.body().blocks()) { 225 if (b == entry || b == b6) { 226 Assert.assertEquals(idoms.get(b), entry); 227 } else { 228 Assert.assertEquals(idoms.get(b), b6); 229 } 230 } 231 } 232 233 @Test 234 public void testCytronExample() { 235 CoreOp.FuncOp f = func("f", CoreType.FUNCTION_TYPE_VOID).body(entry -> { 236 Block.Builder exit = entry.block(); 237 Block.Builder b12 = entry.block(); 238 Block.Builder b11 = entry.block(); 239 Block.Builder b10 = entry.block(); 240 Block.Builder b9 = entry.block(); 241 Block.Builder b8 = entry.block(); 242 Block.Builder b7 = entry.block(); 243 Block.Builder b6 = entry.block(); 244 Block.Builder b5 = entry.block(); 245 Block.Builder b4 = entry.block(); 246 Block.Builder b3 = entry.block(); 247 Block.Builder b2 = entry.block(); 248 Block.Builder b1 = entry.block(); 249 250 Op.Result p = entry.op(constant(JavaType.BOOLEAN, true)); 251 252 entry.op(conditionalBranch(p, exit.successor(), b1.successor())); 253 254 b1.op(branch(b2.successor())); 255 256 b2.op(conditionalBranch(p, b3.successor(), b7.successor())); 257 258 b3.op(conditionalBranch(p, b4.successor(), b5.successor())); 259 260 b4.op(branch(b6.successor())); 261 262 b5.op(branch(b6.successor())); 263 264 b6.op(branch(b8.successor())); 265 266 b7.op(branch(b8.successor())); 267 268 b8.op(branch(b9.successor())); 269 270 b9.op(conditionalBranch(p, b10.successor(), b11.successor())); 271 272 b10.op(branch(b11.successor())); 273 274 b11.op(conditionalBranch(p, b12.successor(), b9.successor())); 275 276 b12.op(conditionalBranch(p, exit.successor(), b2.successor())); 277 278 exit.op(CoreOp.return_()); 279 }); 280 281 System.out.println(f.toText()); 282 283 Map<Block, Block> idoms = f.body().immediateDominators(); 284 Node<String> domTree = buildDomTree(f.body().entryBlock(), idoms).transform(b -> Integer.toString(b.index())); 285 Node<String> domTreeExpected = 286 node("0", 287 node("1", 288 node("2", 289 node("7"), 290 node("8", 291 node("9", 292 node("10"), 293 node("11", 294 node("12")))), 295 node("3", 296 node("4"), node("5"), node("6")))), 297 node("13")); 298 Assert.assertEquals(domTree, domTreeExpected); 299 300 301 Map<String, Set<String>> df = f.body().dominanceFrontier().entrySet().stream() 302 .map(e -> Map.entry(Integer.toString(e.getKey().index()), 303 e.getValue().stream().map(b -> Integer.toString(b.index())).collect(Collectors.toSet()))) 304 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 305 306 Map<String, Set<String>> dfExpected = Map.ofEntries( 307 Map.entry("1", Set.of("13")), 308 Map.entry("2", Set.of("13", "2")), 309 Map.entry("3", Set.of("8")), 310 Map.entry("4", Set.of("6")), 311 Map.entry("5", Set.of("6")), 312 Map.entry("6", Set.of("8")), 313 Map.entry("7", Set.of("8")), 314 Map.entry("8", Set.of("13", "2")), 315 Map.entry("9", Set.of("13", "2", "9")), 316 Map.entry("10", Set.of("11")), 317 Map.entry("11", Set.of("13", "2", "9")), 318 Map.entry("12", Set.of("13", "2")) 319 ); 320 Assert.assertEquals(df, dfExpected); 321 } 322 323 @Test 324 public void testPostDominance() { 325 String m = """ 326 func @"f" (%0 : java.type:"boolean")java.type:"void" -> { 327 %5 : java.type:"void" = branch ^A; 328 329 ^A: 330 %8 : java.type:"void" = cbranch %0 ^B ^C; 331 332 ^B: 333 %11 : java.type:"void" = branch ^D; 334 335 ^C: 336 %13 : java.type:"void" = branch ^D; 337 338 ^D: 339 %15 : java.type:"void" = branch ^E; 340 341 ^E: 342 %15 : java.type:"void" = branch ^F; 343 344 ^F: 345 %16 : java.type:"void" = cbranch %0 ^E ^END; 346 347 ^END: 348 %18 : java.type:"void" = return; 349 }; 350 """; 351 CoreOp.FuncOp f = (CoreOp.FuncOp) OpParser.fromStringOfJavaCodeModel(m); 352 353 Map<Block, Block> ipdoms = f.body().immediatePostDominators(); 354 Assert.assertFalse(ipdoms.containsKey(Body.IPDOM_EXIT)); 355 356 Block exit = ipdoms.containsKey(Body.IPDOM_EXIT) ? Body.IPDOM_EXIT : f.body().blocks().getLast(); 357 Node<String> domTree = buildDomTree(exit, ipdoms).transform(b -> Integer.toString(b.index())); 358 Node<String> domTreeExpected = 359 node("7", 360 node("6", 361 node("5", 362 node("4", 363 node("2"), 364 node("3"), 365 node("1", 366 node("0")))))); 367 Assert.assertEquals(domTree, domTreeExpected); 368 } 369 370 @Test 371 public void testPostDominanceFrontier() { 372 String m = """ 373 func @"f" (%0 : java.type:"boolean")java.type:"void" -> { 374 %5 : java.type:"void" = cbranch %0 ^B ^F; 375 376 ^B: 377 %8 : java.type:"void" = cbranch %0 ^C ^D; 378 379 ^C: 380 %11 : java.type:"void" = branch ^E; 381 382 ^D: 383 %13 : java.type:"void" = branch ^E; 384 385 ^E: 386 %15 : java.type:"void" = branch ^F; 387 388 ^F: 389 %18 : java.type:"void" = return; 390 }; 391 """; 392 CoreOp.FuncOp f = (CoreOp.FuncOp) OpParser.fromStringOfJavaCodeModel(m); 393 394 Map<Block, Block> ipdoms = f.body().immediatePostDominators(); 395 Assert.assertFalse(ipdoms.containsKey(Body.IPDOM_EXIT)); 396 397 Block exit = ipdoms.containsKey(Body.IPDOM_EXIT) ? Body.IPDOM_EXIT : f.body().blocks().getLast(); 398 Node<String> domTree = buildDomTree(exit, ipdoms).transform(b -> Integer.toString(b.index())); 399 Node<String> domTreeExpected = 400 node("5", 401 node("4", 402 node("1"), 403 node("2"), 404 node("3")), 405 node("0")); 406 Assert.assertEquals(domTree, domTreeExpected); 407 408 Map<String, Set<String>> df = f.body().postDominanceFrontier().entrySet().stream() 409 .map(e -> Map.entry(Integer.toString(e.getKey().index()), 410 e.getValue().stream().map(b -> Integer.toString(b.index())).collect(Collectors.toSet()))) 411 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 412 413 Map<String, Set<String>> dfExpected = Map.ofEntries( 414 Map.entry("1", Set.of("0")), 415 Map.entry("2", Set.of("1")), 416 Map.entry("3", Set.of("1")), 417 Map.entry("4", Set.of("0")) 418 ); 419 Assert.assertEquals(df, dfExpected); 420 } 421 422 423 static Node<Block> buildDomTree(Block entryBlock, Map<Block, Block> idoms) { 424 Map<Block, Node<Block>> m = new HashMap<>(); 425 for (Map.Entry<Block, Block> e : idoms.entrySet()) { 426 Block id = e.getValue(); 427 Block b = e.getKey(); 428 if (b == entryBlock) { 429 continue; 430 } 431 432 Node<Block> parent = m.computeIfAbsent(id, _k -> new Node<>(_k, new HashSet<>())); 433 Node<Block> child = m.computeIfAbsent(b, _k -> new Node<>(_k, new HashSet<>())); 434 parent.children.add(child); 435 } 436 return m.get(entryBlock); 437 } 438 439 @SafeVarargs 440 static <T> Node<T> node(T t, Node<T>... children) { 441 return new Node<>(t, Set.of(children)); 442 } 443 444 static <T> Node<T> node(T t, Set<Node<T>> children) { 445 return new Node<>(t, children); 446 } 447 448 record Node<T>(T t, Set<Node<T>> children) { 449 <U> Node<U> transform(Function<T, U> f) { 450 Set<Node<U>> mchildren = new HashSet<>(); 451 for (Node<T> nc : children) { 452 mchildren.add(nc.transform(f)); 453 } 454 return node(f.apply(t), mchildren); 455 } 456 } 457 }