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