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 jdk.incubator.code; 27 28 import jdk.incubator.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, as an unmodifiable map 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 // @@@ Compute the idoms as a block index mapping using int[] 173 // and wrap and a specific map implementation 174 175 Map<Block, Block> doms = new HashMap<>(); 176 doms.put(entryBlock(), entryBlock()); 177 178 // Blocks are sorted in reverse postorder 179 boolean changed; 180 do { 181 changed = false; 182 // Iterate through blocks in reverse postorder, except for entry block 183 for (int i = 1; i < blocks.size(); i++) { 184 Block b = blocks.get(i); 185 186 // Find first processed predecessor of b 187 Block newIdom = null; 188 for (Block p : b.predecessors()) { 189 if (doms.containsKey(p)) { 190 newIdom = p; 191 break; 192 } 193 } 194 assert b.predecessors().isEmpty() || newIdom != null : b; 195 196 // For all other predecessors, p, of b 197 for (Block p : b.predecessors()) { 198 if (p == newIdom) { 199 continue; 200 } 201 202 if (doms.containsKey(p)) { 203 // If already calculated 204 newIdom = intersect(doms, p, newIdom); 205 } 206 } 207 208 if (doms.get(b) != newIdom) { 209 doms.put(b, newIdom); 210 changed = true; 211 } 212 } 213 } while (changed); 214 215 return idoms = Collections.unmodifiableMap(doms); 216 } 217 218 static Block intersect(Map<Block, Block> doms, Block b1, Block b2) { 219 while (b1 != b2) { 220 while (b1.index > b2.index) { 221 b1 = doms.get(b1); 222 } 223 224 while (b2.index > b1.index) { 225 b2 = doms.get(b2); 226 } 227 } 228 229 return b1; 230 } 231 232 /** 233 * Returns the dominance frontier of each block in the body. 234 * <p> 235 * The dominance frontier of block, {@code B} say, is the set of all blocks, {@code C} say, 236 * such that {@code B} dominates a predecessor of {@code C} but does not strictly dominate 237 * {@code C}. 238 * 239 * @return the dominance frontier of each block in the body, as a modifiable map 240 */ 241 public Map<Block, Set<Block>> dominanceFrontier() { 242 // @@@ cache result? 243 Map<Block, Block> idoms = immediateDominators(); 244 Map<Block, Set<Block>> df = new HashMap<>(); 245 246 for (Block b : blocks) { 247 Set<Block> preds = b.predecessors(); 248 249 if (preds.size() > 1) { 250 for (Block p : preds) { 251 Block runner = p; 252 while (runner != idoms.get(b)) { 253 df.computeIfAbsent(runner, _ -> new LinkedHashSet<>()).add(b); 254 runner = idoms.get(runner); 255 } 256 } 257 } 258 } 259 260 return df; 261 } 262 263 /** 264 * Returns {@code true} if this body is dominated by the given body {@code dom}. 265 * <p> 266 * A body, {@code b} say, is dominated by {@code dom} if {@code b} is the same as {@code dom} or a descendant of 267 * {@code dom}. Specifically, if {@code b} and {@code dom} are not equal then {@code b} becomes the nearest ancestor 268 * body, the result of {@code b.parentOp().parentBlock().parentBody()}, and so on until either: 269 * {@code b == dom}, therefore {@code b} is dominated by {@code dom} and this method returns {@code true}; 270 * or {@code b.parentOp().parentBlock() == null}, therefore {@code b} is <b>not</b> dominated 271 * by {@code dom} and this method returns {@code false}. 272 * 273 * @param dom the dominating body 274 * @return {@code true} if this body is dominated by the given body {@code dom}. 275 */ 276 public boolean isDominatedBy(Body dom) { 277 return isDominatedBy(this, dom); 278 } 279 280 static boolean isDominatedBy(Body r, Body dom) { 281 while (r != dom) { 282 Block eb = r.parentOp().parentBlock(); 283 if (eb == null) { 284 return false; 285 } 286 287 r = eb.parentBody(); 288 } 289 290 return true; 291 } 292 293 /** 294 * Computes values captured by this body. A captured value is a value that dominates 295 * this body and is used by a descendant operation of this body. 296 * <p> 297 * The order of the captured values is first use encountered in depth 298 * first search of this body's descendant operations. 299 * 300 * @return the list of captured values, modifiable 301 */ 302 public List<Value> capturedValues() { 303 Set<Value> cvs = new LinkedHashSet<>(); 304 305 capturedValues(cvs, new ArrayDeque<>(), this); 306 return new ArrayList<>(cvs); 307 } 308 309 static void capturedValues(Set<Value> capturedValues, Deque<Body> bodyStack, Body body) { 310 bodyStack.push(body); 311 312 for (Block b : body.blocks()) { 313 for (Op op : b.ops()) { 314 for (Body childBody : op.bodies()) { 315 capturedValues(capturedValues, bodyStack, childBody); 316 } 317 318 for (Value a : op.operands()) { 319 if (!bodyStack.contains(a.declaringBlock().parentBody())) { 320 capturedValues.add(a); 321 } 322 } 323 324 for (Block.Reference s : op.successors()) { 325 for (Value a : s.arguments()) { 326 if (!bodyStack.contains(a.declaringBlock().parentBody())) { 327 capturedValues.add(a); 328 } 329 } 330 } 331 } 332 } 333 334 bodyStack.pop(); 335 } 336 337 /** 338 * A builder of a body. 339 * <p> 340 * When the body builder is built any associated block builders are also considered built. 341 */ 342 public final class Builder { 343 /** 344 * Creates a body build with a new context, and a copying transformer. 345 * 346 * @param ancestorBody the nearest ancestor body builder 347 * @param bodyType the body's function type 348 * @return the body builder 349 * @throws IllegalStateException if the ancestor body builder is built 350 * @see #of(Builder, FunctionType, CopyContext, OpTransformer) 351 */ 352 public static Builder of(Builder ancestorBody, FunctionType bodyType) { 353 // @@@ Creation of CopyContext 354 return of(ancestorBody, bodyType, CopyContext.create(), OpTransformer.COPYING_TRANSFORMER); 355 } 356 357 /** 358 * Creates a body build with a copying transformer. 359 * 360 * @param ancestorBody the nearest ancestor body builder 361 * @param bodyType the body's function type 362 * @param cc the context 363 * @return the body builder 364 * @throws IllegalStateException if the ancestor body builder is built 365 * @see #of(Builder, FunctionType, CopyContext, OpTransformer) 366 */ 367 public static Builder of(Builder ancestorBody, FunctionType bodyType, CopyContext cc) { 368 return of(ancestorBody, bodyType, cc, OpTransformer.COPYING_TRANSFORMER); 369 } 370 371 /** 372 * Creates a body builder. 373 * <p> 374 * Structurally, the created body builder must be built before its ancestor body builder (if non-null) is built, 375 * otherwise an {@code IllegalStateException} will occur. 376 * <p> 377 * A function type, referred to as the body type, defines the body's yield type and the initial sequence of 378 * entry block parameters. 379 * The body's yield type is the function type's return type. 380 * An entry block builder is created with appended block parameters corresponding, in order, to 381 * the function type's parameter types. 382 * <p> 383 * If the ancestor body is null then the created body builder is isolated and descendant operations may only 384 * refer to values declared within the created body builder. Otherwise, operations 385 * may refer to values declared in the ancestor body builders (outside the created body builder). 386 * 387 * @param ancestorBody the nearest ancestor body builder, may be null if isolated 388 * @param bodyType the body's function type 389 * @param cc the context 390 * @param ot the transformer 391 * @return the body builder 392 * @throws IllegalStateException if the ancestor body builder is built 393 * @see #of(Builder, FunctionType, CopyContext, OpTransformer) 394 */ 395 public static Builder of(Builder ancestorBody, FunctionType bodyType, 396 CopyContext cc, OpTransformer ot) { 397 Body body = new Body(ancestorBody != null ? ancestorBody.target() : null, bodyType.returnType()); 398 return body.new Builder(ancestorBody, bodyType, cc, ot); 399 } 400 401 // The ancestor body, may be null 402 final Builder ancestorBody; 403 404 // The entry block of this body, whose parameters are given by the body's function type 405 final Block.Builder entryBlock; 406 407 // When non-null contains one or more great-grandchildren 408 List<Builder> greatgrandchildren; 409 410 // True when built 411 boolean closed; 412 413 Builder(Builder ancestorBody, FunctionType bodyType, 414 CopyContext cc, OpTransformer ot) { 415 // Structural check 416 // The ancestor body should not be built before this body is created 417 if (ancestorBody != null) { 418 ancestorBody.check(); 419 ancestorBody.addGreatgrandchild(this); 420 } 421 422 this.ancestorBody = ancestorBody; 423 // Create entry block from the body's function type 424 Block eb = Body.this.createBlock(bodyType.parameterTypes()); 425 this.entryBlock = eb.new Builder(this, cc, ot); 426 } 427 428 void addGreatgrandchild(Builder greatgrandchild) { 429 var l = greatgrandchildren == null 430 ? (greatgrandchildren = new ArrayList<>()) : greatgrandchildren; 431 l.add(greatgrandchild); 432 } 433 434 /** 435 * Builds the body and its blocks, associating the body with a parent operation. 436 * <p> 437 * Structurally, any descendant body builders must be built before this body builder is built, 438 * otherwise an {@code IllegalStateException} will occur. 439 * <p> 440 * Blocks are sorted in reserve postorder. 441 * <p> 442 * Any unreferenced empty blocks are removed. An unreferenced block is a non-entry block with no predecessors. 443 * 444 * @param op the parent operation 445 * @return the build body 446 * @throws IllegalStateException if this body builder is built 447 * @throws IllegalStateException if any descendant body builders are not built 448 * @throws IllegalStateException if a block has no terminal operation, unless unreferenced and empty 449 */ 450 // @@@ Validation 451 // e.g., every operand dominates the operation result (potentially expensive) 452 public Body build(Op op) { 453 // Structural check 454 // This body should not be closed 455 check(); 456 closed = true; 457 458 // Structural check 459 // All great-grandchildren bodies should be built 460 if (greatgrandchildren != null) { 461 for (Builder greatgrandchild : greatgrandchildren) { 462 if (!greatgrandchild.closed) { 463 throw new IllegalStateException("Descendant body builder is not built"); 464 } 465 } 466 } 467 468 Iterator<Block> i = blocks.iterator(); 469 while (i.hasNext()) { 470 Block block = i.next(); 471 472 // Structural check 473 // All referenced blocks should have a terminating operation as the last operation 474 if (block.ops.isEmpty()) { 475 if (block.isEntryBlock() || !block.predecessors.isEmpty()) { 476 throw noTerminatingOperation(); 477 } 478 479 // Remove unreferenced empty block 480 assert !block.isEntryBlock() && block.predecessors.isEmpty(); 481 i.remove(); 482 } else if (!(block.ops.getLast() instanceof Op.Terminating)) { 483 throw noTerminatingOperation(); 484 } 485 } 486 487 sortReversePostorder(); 488 489 Body.this.parentOp = op; 490 return Body.this; 491 } 492 493 static IllegalStateException noTerminatingOperation() { 494 return new IllegalStateException("Block has no terminating operation as the last operation"); 495 } 496 497 /** 498 * Returns the body builder's function type. 499 * <p> 500 * The function type is composed of the body builder's yield type, as the function type's return type, and the 501 * currently built entry block's parameter types, in order, as the function type's parameter types. 502 * 503 * @return the body builder's function type 504 */ 505 public FunctionType bodyType() { 506 TypeElement returnType = Body.this.yieldType(); 507 Block eb = Body.this.entryBlock(); 508 return FunctionType.functionType(returnType, eb.parameterTypes()); 509 } 510 511 /** 512 * {@return the body builder's nearest ancestor body builder} 513 */ 514 public Builder ancestorBody() { 515 return ancestorBody; 516 } 517 518 /** 519 * {@return the body's entry block builder} 520 */ 521 public Block.Builder entryBlock() { 522 return entryBlock; 523 } 524 525 @Override 526 public boolean equals(Object o) { 527 if (this == o) return true; 528 return o instanceof Builder that && Body.this == that.target(); 529 } 530 531 @Override 532 public int hashCode() { 533 return Body.this.hashCode(); 534 } 535 536 void check() { 537 if (closed) { 538 throw new IllegalStateException("Builder is closed"); 539 } 540 } 541 542 Body target() { 543 return Body.this; 544 } 545 546 // Build new block in body 547 Block.Builder block(List<TypeElement> params, CopyContext cc, OpTransformer ot) { 548 check(); 549 Block block = Body.this.createBlock(params); 550 551 return block.new Builder(this, cc, ot); 552 } 553 } 554 555 /** 556 * Copies the contents of this body. 557 * 558 * @param cc the copy context 559 * @return the builder of a body containing the copied body 560 * @see #transform(CopyContext, OpTransformer) 561 */ 562 public Builder copy(CopyContext cc) { 563 return transform(cc, OpTransformer.COPYING_TRANSFORMER); 564 } 565 566 /** 567 * Transforms this body. 568 * <p> 569 * A new body builder is created with the same function type as this body. 570 * Then, this body is {@link Block.Builder#transformBody(Body, java.util.List, CopyContext, OpTransformer) transformed} 571 * into the body builder's entry block builder with the given copy context, operation transformer, and values 572 * that are the entry block's parameters. 573 * 574 * @param cc the copy context 575 * @param ot the operation transformer 576 * @return a body builder containing the transformed body 577 */ 578 public Builder transform(CopyContext cc, OpTransformer ot) { 579 Block.Builder ancestorBlockBuilder = ancestorBody != null 580 ? cc.getBlock(ancestorBody.entryBlock()) : null; 581 Builder ancestorBodyBuilder = ancestorBlockBuilder != null 582 ? ancestorBlockBuilder.parentBody() : null; 583 Builder body = Builder.of(ancestorBodyBuilder, 584 // Create function type with just the return type and add parameters afterward 585 FunctionType.functionType(yieldType), 586 cc, ot); 587 588 for (Block.Parameter p : entryBlock().parameters()) { 589 body.entryBlock.parameter(p.type()); 590 } 591 592 body.entryBlock.transformBody(this, body.entryBlock.parameters(), cc, ot); 593 return body; 594 } 595 596 // Sort blocks in reverse post order 597 // After sorting the following holds for a block 598 // block.parentBody().blocks().indexOf(block) == block.index() 599 private void sortReversePostorder() { 600 if (blocks.size() < 2) { 601 for (int i = 0; i < blocks.size(); i++) { 602 blocks.get(i).index = i; 603 } 604 return; 605 } 606 607 // Reset block indexes 608 // Also ensuring blocks with no predecessors occur last 609 for (Block b : blocks) { 610 b.index = Integer.MAX_VALUE; 611 } 612 613 Deque<Block> stack = new ArrayDeque<>(); 614 stack.push(blocks.get(0)); 615 616 // Postorder iteration 617 int index = blocks.size(); 618 while (!stack.isEmpty()) { 619 Block n = stack.peek(); 620 if (n.index == Integer.MIN_VALUE) { 621 // If n's successor has been processed then add n 622 stack.pop(); 623 n.index = --index; 624 } else if (n.index < Integer.MAX_VALUE) { 625 // If n has already been processed then ignore 626 stack.pop(); 627 } else { 628 // Mark before processing successors, a successor may refer back to n 629 n.index = Integer.MIN_VALUE; 630 for (Block.Reference s : n.successors()) { 631 if (s.target.index < Integer.MAX_VALUE) { 632 continue; 633 } 634 635 stack.push(s.target); 636 } 637 } 638 } 639 640 blocks.sort(Comparator.comparingInt(b -> b.index)); 641 if (blocks.get(0).index > 0) { 642 // There are blocks with no predecessors 643 // Reassign indexes to their natural indexes, sort order is preserved 644 for (int i = 0; i < blocks.size(); i++) { 645 blocks.get(i).index = i; 646 } 647 } 648 } 649 650 // Modifying methods 651 652 // Create block 653 private Block createBlock(List<TypeElement> params) { 654 Block b = new Block(this, params); 655 blocks.add(b); 656 return b; 657 } 658 }