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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.reflect.code; 27 28 import java.lang.reflect.code.type.FunctionType; 29 import java.util.*; 30 31 /** 32 * A body containing a sequence of (basic) blocks. 33 * <p> 34 * The sequence of blocks form a graph. The last operation in a block, a terminating operation, 35 * may refer to other blocks in the sequence as successors, thus forming the graph. Otherwise, the last 36 * operation defines how the body passes control flow back to the parent operation, and in doing so may optionally 37 * yield a value. 38 * <p> 39 * The first block in the sequence is the entry block, and no other blocks refer to it as a successor. 40 * <p> 41 * A body has a function type whose return type is the body's yield type and whose parameter types are the entry 42 * block's parameters types, in order. 43 * The function type describes the sequence of input parameters types for arguments that are passed to the 44 * body when control flow is passed to it, and describes the return type of values that are returned when body passes 45 * control back to its parent operation. 46 */ 47 public final class Body implements CodeElement<Body, Block> { 48 // Parent operation 49 // Non-null when body is built, and therefore bound to operation 50 Op parentOp; 51 52 // The ancestor body, when null the body is isolated and cannot refer to values defined outside 53 // When non-null and body is built, ancestorBody == parentOp.result.block.parentBody 54 final Body ancestorBody; 55 56 final TypeElement yieldType; 57 58 // Sorted in reverse postorder 59 final List<Block> blocks; 60 61 // Map of a block to its immediate dominator 62 // Computed lazily, null if not computed 63 Map<Block, Block> idoms; 64 65 /** 66 * Constructs a body, whose ancestor is the given ancestor body. 67 */ 68 Body(Body ancestorBody, TypeElement yieldType) { 69 this.ancestorBody = ancestorBody; 70 this.yieldType = yieldType; 71 this.blocks = new ArrayList<>(); 72 } 73 74 /** 75 * Returns this body's parent operation. 76 * 77 * @return the body's parent operation. 78 */ 79 @Override 80 public Op parent() { 81 return parentOp; 82 } 83 84 /** 85 * Returns this body's parent operation. 86 * 87 * @return the body's parent operation. 88 */ 89 public Op parentOp() { 90 return parentOp; 91 } 92 93 @Override 94 public List<Block> children() { 95 return blocks(); 96 } 97 98 /** 99 * Returns body's blocks in reverse-postorder as an unmodifiable list. 100 * 101 * @return the body's blocks in reverse-postorder. 102 */ 103 public List<Block> blocks() { 104 return Collections.unmodifiableList(blocks); 105 } 106 107 /** 108 * {@return the yield type of this body} 109 */ 110 public TypeElement yieldType() { 111 return yieldType; 112 } 113 114 /** 115 * Returns the body's function type. 116 * <p>The function type is composed of the body's entry block parameter types and 117 * the body's yield type. 118 * 119 * @return the body type. 120 */ 121 public FunctionType bodyType() { 122 Block entryBlock = entryBlock(); 123 return FunctionType.functionType(yieldType, entryBlock.parameterTypes()); 124 } 125 126 /** 127 * Finds the block in this body that is the ancestor of the given block. 128 * 129 * @param b the given block. 130 * @return the block in this body that is the ancestor of the given block, 131 * otherwise {@code null} 132 */ 133 public Block findAncestorBlockInBody(Block b) { 134 Objects.requireNonNull(b); 135 136 while (b != null && b.parentBody() != this) { 137 b = b.parentBody().parentOp().parentBlock(); 138 } 139 140 return b; 141 } 142 143 /** 144 * Returns this body's entry block. 145 * <p> 146 * The entry block is the first block in the sequence. No other blocks refer to it as a successor. 147 * 148 * @return the body's entry block 149 */ 150 public Block entryBlock() { 151 return blocks.getFirst(); 152 } 153 154 /** 155 * Returns a map of block to its immediate dominator. 156 * 157 * @return a map of block to its immediate dominator 158 */ 159 public Map<Block, Block> immediateDominators() { 160 /* 161 * Compute dominators of blocks in a body. 162 * <p> 163 * https://www.cs.rice.edu/~keith/EMBED/dom.pdf 164 * A Simple, Fast Dominance Algorithm 165 * Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy 166 */ 167 168 if (idoms != null) { 169 return idoms; 170 } 171 172 Map<Block, Block> doms = idoms = new HashMap<>(); 173 doms.put(entryBlock(), entryBlock()); 174 175 // Blocks are sorted in reverse postorder 176 boolean changed; 177 do { 178 changed = false; 179 // Iterate through blocks in reverse postorder, except for entry block 180 for (int i = 1; i < blocks.size(); i++) { 181 Block b = blocks.get(i); 182 183 // Find first processed predecessor of b 184 Block newIdom = null; 185 for (Block p : b.predecessors()) { 186 if (doms.containsKey(p)) { 187 newIdom = p; 188 break; 189 } 190 } 191 assert b.predecessors().isEmpty() || newIdom != null : b; 192 193 // For all other predecessors, p, of b 194 for (Block p : b.predecessors()) { 195 if (p == newIdom) { 196 continue; 197 } 198 199 if (doms.containsKey(p)) { 200 // If already calculated 201 newIdom = intersect(doms, p, newIdom); 202 } 203 } 204 205 if (doms.get(b) != newIdom) { 206 doms.put(b, newIdom); 207 changed = true; 208 } 209 } 210 } while (changed); 211 212 return doms; 213 } 214 215 static Block intersect(Map<Block, Block> doms, Block b1, Block b2) { 216 while (b1 != b2) { 217 while (b1.index > b2.index) { 218 b1 = doms.get(b1); 219 } 220 221 while (b2.index > b1.index) { 222 b2 = doms.get(b2); 223 } 224 } 225 226 return b1; 227 } 228 229 /** 230 * Returns the dominance frontier of each block in the body. 231 * <p> 232 * The dominance frontier of block, {@code B} say, is the set of all blocks, {@code C} say, 233 * such that {@code B} dominates a predecessor of {@code C} but does not strictly dominate 234 * {@code C}. 235 * 236 * @return the dominance frontier of each block in the body 237 */ 238 public Map<Block, Set<Block>> dominanceFrontier() { 239 // @@@ cache result? 240 Map<Block, Block> idoms = immediateDominators(); 241 Map<Block, Set<Block>> df = new HashMap<>(); 242 243 for (Block b : blocks) { 244 Set<Block> preds = b.predecessors(); 245 246 if (preds.size() > 1) { 247 for (Block p : preds) { 248 Block runner = p; 249 while (runner != idoms.get(b)) { 250 df.computeIfAbsent(runner, k -> new LinkedHashSet<>()).add(b); 251 runner = idoms.get(runner); 252 } 253 } 254 } 255 } 256 257 return df; 258 } 259 260 /** 261 * Returns {@code true} if this body is dominated by the given body {@code dom}. 262 * <p> 263 * A body, {@code b} say, is dominated by {@code dom} if {@code b} is the same as {@code dom} or a descendant of 264 * {@code dom}. Specifically, if {@code b} and {@code dom} are not equal then {@code b} becomes the nearest ancestor 265 * body, the result of {@code b.parentOp().parentBlock().parentBody()}, and so on until either: 266 * {@code b == dom}, therefore {@code b} is dominated by {@code dom} and this method returns {@code true}; 267 * or {@code b.parentOp().parentBlock() == null}, therefore {@code b} is <b>not</b> dominated 268 * by {@code dom} and this method returns {@code false}. 269 * 270 * @param dom the dominating body 271 * @return {@code true} if this body is dominated by the given body {@code dom}. 272 */ 273 public boolean isDominatedBy(Body dom) { 274 return isDominatedBy(this, dom); 275 } 276 277 static boolean isDominatedBy(Body r, Body dom) { 278 while (r != dom) { 279 Block eb = r.parentOp().parentBlock(); 280 if (eb == null) { 281 return false; 282 } 283 284 r = eb.parentBody(); 285 } 286 287 return true; 288 } 289 290 /** 291 * Computes values captured by this body. A captured value is a value that dominates 292 * this body and is used by a descendant operation of this body. 293 * <p> 294 * The order of the captured values is first use encountered in depth 295 * first search of this body's descendant operations. 296 * 297 * @return the list of captured values, modifiable 298 */ 299 public List<Value> capturedValues() { 300 Set<Value> cvs = new LinkedHashSet<>(); 301 302 capturedValues(cvs, new ArrayDeque<>(), this); 303 return new ArrayList<>(cvs); 304 } 305 306 static void capturedValues(Set<Value> capturedValues, Deque<Body> bodyStack, Body body) { 307 bodyStack.push(body); 308 309 for (Block b : body.blocks()) { 310 for (Op op : b.ops()) { 311 for (Body childBody : op.bodies()) { 312 capturedValues(capturedValues, bodyStack, childBody); 313 } 314 315 for (Value a : op.operands()) { 316 if (!bodyStack.contains(a.declaringBlock().parentBody())) { 317 capturedValues.add(a); 318 } 319 } 320 321 for (Block.Reference s : op.successors()) { 322 for (Value a : s.arguments()) { 323 if (!bodyStack.contains(a.declaringBlock().parentBody())) { 324 capturedValues.add(a); 325 } 326 } 327 } 328 } 329 } 330 331 bodyStack.pop(); 332 } 333 334 /** 335 * A builder of a body. 336 * <p> 337 * When the body builder is built any associated block builders are also considered built. 338 */ 339 public final class Builder { 340 /** 341 * Creates a body build with a new context, and a copying transformer. 342 * 343 * @param ancestorBody the nearest ancestor body builder 344 * @param bodyType the body's function type 345 * @return the body builder 346 * @throws IllegalStateException if the ancestor body builder is built 347 * @see #of(Builder, FunctionType, CopyContext, OpTransformer) 348 */ 349 public static Builder of(Builder ancestorBody, FunctionType bodyType) { 350 // @@@ Creation of CopyContext 351 return of(ancestorBody, bodyType, CopyContext.create(), OpTransformer.COPYING_TRANSFORMER); 352 } 353 354 /** 355 * Creates a body build with a copying transformer. 356 * 357 * @param ancestorBody the nearest ancestor body builder 358 * @param bodyType the body's function type 359 * @param cc the context 360 * @return the body builder 361 * @throws IllegalStateException if the ancestor body builder is built 362 * @see #of(Builder, FunctionType, CopyContext, OpTransformer) 363 */ 364 public static Builder of(Builder ancestorBody, FunctionType bodyType, CopyContext cc) { 365 return of(ancestorBody, bodyType, cc, OpTransformer.COPYING_TRANSFORMER); 366 } 367 368 /** 369 * Creates a body builder. 370 * <p> 371 * Structurally, the created body builder must be built before its ancestor body builder (if non-null) is built, 372 * otherwise an {@code IllegalStateException} will occur. 373 * <p> 374 * A function type, referred to as the body type, defines the body's yield type and the initial sequence of 375 * entry block parameters. 376 * The body's yield type is the function type's return type. 377 * An entry block builder is created with appended block parameters corresponding, in order, to 378 * the function type's parameter types. 379 * <p> 380 * If the ancestor body is null then the created body builder is isolated and descendant operations may only 381 * refer to values declared within the created body builder. Otherwise, operations 382 * may refer to values declared in the ancestor body builders (outside the created body builder). 383 * 384 * @param ancestorBody the nearest ancestor body builder, may be null if isolated 385 * @param bodyType the body's function type 386 * @param cc the context 387 * @param ot the transformer 388 * @return the body builder 389 * @throws IllegalStateException if the ancestor body builder is built 390 * @see #of(Builder, FunctionType, CopyContext, OpTransformer) 391 */ 392 public static Builder of(Builder ancestorBody, FunctionType bodyType, 393 CopyContext cc, OpTransformer ot) { 394 Body body = new Body(ancestorBody != null ? ancestorBody.target() : null, bodyType.returnType()); 395 return body.new Builder(ancestorBody, bodyType, cc, ot); 396 } 397 398 // The ancestor body, may be null 399 final Builder ancestorBody; 400 401 // The entry block of this body, whose parameters are given by the body's function type 402 final Block.Builder entryBlock; 403 404 // When non-null contains one or more great-grandchildren 405 List<Builder> greatgrandchildren; 406 407 // True when built 408 boolean closed; 409 410 Builder(Builder ancestorBody, FunctionType bodyType, 411 CopyContext cc, OpTransformer ot) { 412 // Structural check 413 // The ancestor body should not be built before this body is created 414 if (ancestorBody != null) { 415 ancestorBody.check(); 416 ancestorBody.addGreatgrandchild(this); 417 } 418 419 this.ancestorBody = ancestorBody; 420 // Create entry block from the body's function type 421 Block eb = Body.this.createBlock(bodyType.parameterTypes()); 422 this.entryBlock = eb.new Builder(this, cc, ot); 423 } 424 425 void addGreatgrandchild(Builder greatgrandchild) { 426 var l = greatgrandchildren == null 427 ? (greatgrandchildren = new ArrayList<>()) : greatgrandchildren; 428 l.add(greatgrandchild); 429 } 430 431 /** 432 * Builds the body and its blocks, associating the body with a parent operation. 433 * <p> 434 * Structurally, any descendant body builders must be built before this body builder is built, 435 * otherwise an {@code IllegalStateException} will occur. 436 * <p> 437 * Blocks are sorted in reserve postorder. 438 * <p> 439 * Any unreferenced empty blocks are removed. An unreferenced block is a non-entry block with no predecessors. 440 * 441 * @param op the parent operation 442 * @return the build body 443 * @throws IllegalStateException if this body builder is built 444 * @throws IllegalStateException if any descendant body builders are not built 445 * @throws IllegalStateException if a block has no terminal operation, unless unreferenced and empty 446 */ 447 // @@@ Validation 448 // e.g., every operand dominates the operation result (potentially expensive) 449 public Body build(Op op) { 450 // Structural check 451 // This body should not be closed 452 check(); 453 closed = true; 454 455 // Structural check 456 // All great-grandchildren bodies should be built 457 if (greatgrandchildren != null) { 458 for (Builder greatgrandchild : greatgrandchildren) { 459 if (!greatgrandchild.closed) { 460 throw new IllegalStateException("Descendant body builder is not built"); 461 } 462 } 463 } 464 465 Iterator<Block> i = blocks.iterator(); 466 while (i.hasNext()) { 467 Block block = i.next(); 468 469 // Structural check 470 // All referenced blocks should have a terminating operation as the last operation 471 if (block.ops.isEmpty()) { 472 if (block.isEntryBlock() || !block.predecessors.isEmpty()) { 473 throw noTerminatingOperation(); 474 } 475 476 // Remove unreferenced empty block 477 assert !block.isEntryBlock() && block.predecessors.isEmpty(); 478 i.remove(); 479 } else if (!(block.ops.getLast() instanceof Op.Terminating)) { 480 throw noTerminatingOperation(); 481 } 482 } 483 484 sortReversePostorder(); 485 486 Body.this.parentOp = op; 487 return Body.this; 488 } 489 490 static IllegalStateException noTerminatingOperation() { 491 return new IllegalStateException("Block has no terminating operation as the last operation"); 492 } 493 494 /** 495 * Returns the body builder's function type. 496 * <p> 497 * The function type is composed of the body builder's yield type, as the function type's return type, and the 498 * currently built entry block's parameter types, in order, as the function type's parameter types. 499 * 500 * @return the body builder's function type 501 */ 502 public FunctionType bodyType() { 503 TypeElement returnType = Body.this.yieldType(); 504 Block eb = Body.this.entryBlock(); 505 return FunctionType.functionType(returnType, eb.parameterTypes()); 506 } 507 508 /** 509 * {@return the body builder's nearest ancestor body builder} 510 */ 511 public Builder ancestorBody() { 512 return ancestorBody; 513 } 514 515 /** 516 * {@return the body's entry block builder} 517 */ 518 public Block.Builder entryBlock() { 519 return entryBlock; 520 } 521 522 @Override 523 public boolean equals(Object o) { 524 if (this == o) return true; 525 return o instanceof Builder that && Body.this == that.target(); 526 } 527 528 @Override 529 public int hashCode() { 530 return Body.this.hashCode(); 531 } 532 533 void check() { 534 if (closed) { 535 throw new IllegalStateException("Builder is closed"); 536 } 537 } 538 539 Body target() { 540 return Body.this; 541 } 542 543 // Build new block in body 544 Block.Builder block(List<TypeElement> params, CopyContext cc, OpTransformer ot) { 545 check(); 546 Block block = Body.this.createBlock(params); 547 548 return block.new Builder(this, cc, ot); 549 } 550 } 551 552 /** 553 * Copies the contents of this body. 554 * 555 * @param cc the copy context 556 * @return the builder of a body containing the copied body 557 * @see #transform(CopyContext, OpTransformer) 558 */ 559 public Builder copy(CopyContext cc) { 560 return transform(cc, OpTransformer.COPYING_TRANSFORMER); 561 } 562 563 /** 564 * Transforms this body. 565 * <p> 566 * A new body builder is created with the same function type as this body. 567 * Then, this body is {@link Block.Builder#transformBody(Body, java.util.List, CopyContext, OpTransformer) transformed} 568 * into the body builder's entry block builder with the given copy context, operation transformer, and values 569 * that are the entry block's parameters. 570 * 571 * @param cc the copy context 572 * @param ot the operation transformer 573 * @return a body builder containing the transformed body 574 */ 575 public Builder transform(CopyContext cc, OpTransformer ot) { 576 Block.Builder ancestorBlockBuilder = ancestorBody != null 577 ? cc.getBlock(ancestorBody.entryBlock()) : null; 578 Builder ancestorBodyBuilder = ancestorBlockBuilder != null 579 ? ancestorBlockBuilder.parentBody() : null; 580 Builder body = Builder.of(ancestorBodyBuilder, 581 // Create function type with just the return type and add parameters afterward 582 FunctionType.functionType(yieldType), 583 cc, ot); 584 585 for (Block.Parameter p : entryBlock().parameters()) { 586 body.entryBlock.parameter(p.type()); 587 } 588 589 body.entryBlock.transformBody(this, body.entryBlock.parameters(), cc, ot); 590 return body; 591 } 592 593 // Sort blocks in reverse post order 594 // After sorting the following holds for a block 595 // block.parentBody().blocks().indexOf(block) == block.index() 596 private void sortReversePostorder() { 597 if (blocks.size() < 2) { 598 for (int i = 0; i < blocks.size(); i++) { 599 blocks.get(i).index = i; 600 } 601 return; 602 } 603 604 // Reset block indexes 605 // Also ensuring blocks with no predecessors occur last 606 for (Block b : blocks) { 607 b.index = Integer.MAX_VALUE; 608 } 609 610 Deque<Block> stack = new ArrayDeque<>(); 611 stack.push(blocks.get(0)); 612 613 // Postorder iteration 614 int index = blocks.size(); 615 while (!stack.isEmpty()) { 616 Block n = stack.peek(); 617 if (n.index == Integer.MIN_VALUE) { 618 // If n's successor has been processed then add n 619 stack.pop(); 620 n.index = --index; 621 } else if (n.index < Integer.MAX_VALUE) { 622 // If n has already been processed then ignore 623 stack.pop(); 624 } else { 625 // Mark before processing successors, a successor may refer back to n 626 n.index = Integer.MIN_VALUE; 627 for (Block.Reference s : n.successors()) { 628 if (s.target.index < Integer.MAX_VALUE) { 629 continue; 630 } 631 632 stack.push(s.target); 633 } 634 } 635 } 636 637 blocks.sort(Comparator.comparingInt(b -> b.index)); 638 if (blocks.get(0).index > 0) { 639 // There are blocks with no predecessors 640 // Reassign indexes to their natural indexes, sort order is preserved 641 for (int i = 0; i < blocks.size(); i++) { 642 blocks.get(i).index = i; 643 } 644 } 645 } 646 647 // Modifying methods 648 649 // Create block 650 private Block createBlock(List<TypeElement> params) { 651 Block b = new Block(this, params); 652 blocks.add(b); 653 return b; 654 } 655 }