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 /* 25 * @test 26 * @modules jdk.incubator.code 27 * @run testng TestLiveness 28 */ 29 30 import org.testng.Assert; 31 import org.testng.annotations.Test; 32 33 import jdk.incubator.code.Block; 34 import jdk.incubator.code.CodeElement; 35 import jdk.incubator.code.Op; 36 import jdk.incubator.code.Value; 37 import jdk.incubator.code.analysis.Liveness; 38 import jdk.incubator.code.op.CoreOp; 39 import jdk.incubator.code.type.JavaType; 40 import jdk.incubator.code.parser.OpParser; 41 import java.util.HashMap; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.concurrent.atomic.AtomicInteger; 46 import java.util.stream.Collectors; 47 48 public class TestLiveness { 49 50 static final String F = """ 51 func @"f" (%0 : int, %1 : int)int -> { 52 %2 : int = add %0 %1; 53 return %2; 54 }; 55 """; 56 57 @Test 58 public void testF() { 59 Op op = OpParser.fromString(CoreOp.FACTORY, F).getFirst(); 60 61 var actual = liveness(op); 62 var expected = Map.of( 63 0, List.of(Set.of(), Set.of())); 64 Assert.assertEquals(actual, expected); 65 } 66 67 static final String IF_ELSE = """ 68 func @"ifelse" (%0 : int, %1 : int, %2 : int)int -> { 69 %3 : int = constant @"10"; 70 %4 : boolean = lt %2 %3; 71 cbranch %4 ^block_0 ^block_1; 72 73 ^block_0: 74 %5 : int = constant @"1"; 75 %6 : int = add %0 %5; 76 branch ^block_2(%6, %1); 77 78 ^block_1: 79 %7 : int = constant @"2"; 80 %8 : int = add %1 %7; 81 branch ^block_2(%0, %8); 82 83 ^block_2(%9 : int, %10 : int): 84 %11 : int = add %9 %10; 85 return %11; 86 }; 87 """; 88 89 @Test 90 public void testIfElse() { 91 Op op = OpParser.fromString(CoreOp.FACTORY, IF_ELSE).getFirst(); 92 93 var actual = liveness(op); 94 var expected = Map.of( 95 0, List.of(Set.of(), Set.of(0, 1)), 96 1, List.of(Set.of(0, 1), Set.of()), 97 2, List.of(Set.of(0, 1), Set.of()), 98 3, List.of(Set.of(), Set.of()) 99 ); 100 Assert.assertEquals(actual, expected); 101 } 102 103 static final String LOOP = """ 104 func @"loop" (%0 : int)int -> { 105 %1 : int = constant @"0"; 106 %2 : int = constant @"0"; 107 branch ^block_0(%1, %2); 108 109 ^block_0(%3 : int, %4 : int): 110 %5 : boolean = lt %4 %0; 111 cbranch %5 ^block_1 ^block_2; 112 113 ^block_1: 114 %6 : int = add %3 %4; 115 branch ^block_3; 116 117 ^block_3: 118 %7 : int = constant @"1"; 119 %8 : int = add %4 %7; 120 branch ^block_0(%6, %8); 121 122 ^block_2: 123 return %3; 124 }; 125 """; 126 127 @Test 128 public void testLoop() { 129 Op op = OpParser.fromString(CoreOp.FACTORY, LOOP).getFirst(); 130 131 var actual = liveness(op); 132 var expected = Map.of( 133 0, List.of(Set.of(), Set.of(0)), 134 1, List.of(Set.of(0), Set.of(0, 3, 4)), 135 2, List.of(Set.of(0, 3, 4), Set.of(0, 4, 6)), 136 3, List.of(Set.of(3), Set.of()), 137 4, List.of(Set.of(0, 4, 6), Set.of(0)) 138 ); 139 Assert.assertEquals(actual, expected); 140 } 141 142 static final String IF_ELSE_NESTED = """ 143 func @"ifelseNested" (%0 : int, %1 : int, %2 : int, %3 : int, %4 : int)int -> { 144 %5 : int = constant @"20"; 145 %6 : boolean = lt %4 %5; 146 cbranch %6 ^block_0 ^block_1; 147 148 ^block_0: 149 %7 : int = constant @"10"; 150 %8 : boolean = lt %4 %7; 151 cbranch %8 ^block_2 ^block_3; 152 153 ^block_2: 154 %9 : int = constant @"1"; 155 %10 : int = add %0 %9; 156 branch ^block_4(%10, %1); 157 158 ^block_3: 159 %11 : int = constant @"2"; 160 %12 : int = add %1 %11; 161 branch ^block_4(%0, %12); 162 163 ^block_4(%13 : int, %14 : int): 164 %15 : int = constant @"3"; 165 %16 : int = add %2 %15; 166 branch ^block_5(%13, %14, %16, %3); 167 168 ^block_1: 169 %17 : int = constant @"20"; 170 %18 : boolean = gt %4 %17; 171 cbranch %18 ^block_6 ^block_7; 172 173 ^block_6: 174 %19 : int = constant @"4"; 175 %20 : int = add %0 %19; 176 branch ^block_8(%20, %1); 177 178 ^block_7: 179 %21 : int = constant @"5"; 180 %22 : int = add %1 %21; 181 branch ^block_8(%0, %22); 182 183 ^block_8(%23 : int, %24 : int): 184 %25 : int = constant @"6"; 185 %26 : int = add %3 %25; 186 branch ^block_5(%23, %24, %2, %26); 187 188 ^block_5(%27 : int, %28 : int, %29 : int, %30 : int): 189 %31 : int = add %27 %28; 190 %32 : int = add %31 %29; 191 %33 : int = add %32 %30; 192 return %33; 193 }; 194 """; 195 196 @Test 197 public void testIfElseNested() { 198 Op op = OpParser.fromString(CoreOp.FACTORY, IF_ELSE_NESTED).getFirst(); 199 200 var actual = liveness(op); 201 var expected = Map.of( 202 0, List.of(Set.of(), Set.of(0, 1, 2, 3, 4)), 203 1, List.of(Set.of(0, 1, 2, 3, 4), Set.of(0, 1, 2, 3)), 204 2, List.of(Set.of(0, 1, 2, 3, 4), Set.of(0, 1, 2, 3)), 205 3, List.of(Set.of(0, 1, 2, 3), Set.of(2, 3)), 206 4, List.of(Set.of(0, 1, 2, 3), Set.of(2, 3)), 207 5, List.of(Set.of(2, 3), Set.of()), 208 6, List.of(Set.of(), Set.of()), 209 7, List.of(Set.of(0, 1, 2, 3), Set.of(2, 3)), 210 8, List.of(Set.of(0, 1, 2, 3), Set.of(2, 3)), 211 9, List.of(Set.of(2, 3), Set.of()) 212 ); 213 Assert.assertEquals(actual, expected); 214 } 215 216 static final String LOOP_NESTED = """ 217 func @"loopNested" (%0 : int)int -> { 218 %1 : int = constant @"0"; 219 %2 : int = constant @"0"; 220 branch ^block_0(%1, %2); 221 222 ^block_0(%3 : int, %4 : int): 223 %5 : boolean = lt %4 %0; 224 cbranch %5 ^block_1 ^block_2; 225 226 ^block_1: 227 %6 : int = constant @"0"; 228 branch ^block_3(%3, %6); 229 230 ^block_3(%7 : int, %8 : int): 231 %9 : boolean = lt %8 %0; 232 cbranch %9 ^block_4 ^block_5; 233 234 ^block_4: 235 %10 : int = add %7 %4; 236 %11 : int = add %10 %8; 237 branch ^block_6; 238 239 ^block_6: 240 %12 : int = constant @"1"; 241 %13 : int = add %8 %12; 242 branch ^block_3(%11, %13); 243 244 ^block_5: 245 branch ^block_7; 246 247 ^block_7: 248 %14 : int = constant @"1"; 249 %15 : int = add %4 %14; 250 branch ^block_0(%7, %15); 251 252 ^block_2: 253 return %3; 254 }; 255 """; 256 257 @Test 258 public void testLoopNested() { 259 Op op = OpParser.fromString(CoreOp.FACTORY, LOOP_NESTED).getFirst(); 260 261 var actual = liveness(op); 262 var expected = Map.of( 263 0, List.of(Set.of(), Set.of(0)), 264 1, List.of(Set.of(0), Set.of(0, 3, 4)), 265 2, List.of(Set.of(0, 3, 4), Set.of(0, 4)), 266 3, List.of(Set.of(3), Set.of()), 267 4, List.of(Set.of(0, 4), Set.of(0, 4, 7, 8)), 268 5, List.of(Set.of(0, 4, 7, 8), Set.of(0, 4, 8, 11)), 269 6, List.of(Set.of(0, 4, 7), Set.of(0, 4, 7)), 270 7, List.of(Set.of(0, 4, 8, 11), Set.of(0, 4)), 271 8, List.of(Set.of(0, 4, 7), Set.of(0)) 272 ); 273 Assert.assertEquals(actual, expected); 274 } 275 276 static Map<Integer, List<Set<Integer>>> liveness(Op op) { 277 Liveness l = new Liveness(op); 278 System.out.println(l); 279 280 Map<Value, Integer> valueMap = valueNameMapping(op); 281 Map<Block, Integer> blockMap = blockNameMapping(op); 282 283 return op.traverse(new HashMap<>(), 284 CodeElement.blockVisitor((m, b) -> { 285 if (b.parentBody().parentOp() == op) { 286 Liveness.BlockInfo lbi = l.getLiveness(b); 287 m.put(blockMap.get(b), 288 List.of( 289 lbi.liveIn().stream().map(valueMap::get).collect(Collectors.toSet()), 290 lbi.liveOut().stream().map(valueMap::get).collect(Collectors.toSet()) 291 )); 292 } 293 return m; 294 })); 295 } 296 297 static Map<Block, Integer> blockNameMapping(Op top) { 298 AtomicInteger i = new AtomicInteger(); 299 return top.traverse(new HashMap<>(), CodeElement.blockVisitor((m, b) -> { 300 if (b.parentBody().parentOp() != top) { 301 return m; 302 } 303 304 m.computeIfAbsent(b, _ -> i.getAndIncrement()); 305 for (Block.Reference s : b.successors()) { 306 m.computeIfAbsent(s.targetBlock(), _ -> i.getAndIncrement()); 307 } 308 309 return m; 310 })); 311 } 312 313 static Map<Value, Integer> valueNameMapping(Op top) { 314 AtomicInteger i = new AtomicInteger(); 315 return top.traverse(new HashMap<>(), (m, e) -> { 316 switch (e) { 317 case Block b -> { 318 for (Block.Parameter p : b.parameters()) { 319 m.put(p, i.getAndIncrement()); 320 } 321 } 322 case Op op -> { 323 Op.Result r = op.result(); 324 if (r != null && !r.type().equals(JavaType.VOID)) { 325 m.put(r, i.getAndIncrement()); 326 } 327 } 328 default -> { 329 } 330 } 331 return m; 332 }); 333 } 334 }