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.analysis; 27 28 import jdk.incubator.code.*; 29 import jdk.incubator.code.op.CoreOp; 30 import java.util.*; 31 32 /** 33 * A model transformer that normalizes blocks. 34 * <p> 35 * Merges redundant blocks with their predecessors, those which are unconditionally 36 * branched to and have only one predecessor. 37 * <p> 38 * Removes unused block parameters. 39 */ 40 public final class NormalizeBlocksTransformer implements OpTransformer { 41 final Set<Block> mergedBlocks = new HashSet<>(); 42 final Map<Block, BitSet> adjustedBlocks = new HashMap<>(); 43 44 private NormalizeBlocksTransformer() { 45 } 46 47 /** 48 * Transforms an operation, merging redundant blocks. 49 * 50 * @param op the operation to transform 51 * @param <O> the type of operation 52 * @return the transformed operation 53 */ 54 @SuppressWarnings("unchecked") 55 public static <O extends Op> O transform(O op) { 56 return (O) op.transform(CopyContext.create(), new NormalizeBlocksTransformer()); 57 } 58 59 ; 60 61 @Override 62 public void apply(Block.Builder block, Block b) { 63 // Ignore merged block 64 if (!mergedBlocks.contains(b)) { 65 OpTransformer.super.apply(block, b); 66 } 67 } 68 69 @Override 70 public Block.Builder apply(Block.Builder b, Op op) { 71 if (op instanceof CoreOp.BranchOp bop && 72 bop.branch().targetBlock().predecessors().size() == 1) { 73 // Merge the successor's target block with this block, and so on 74 // The terminal branch operation is replaced with the operations in the 75 // successor's target block 76 mergeBlock(b, bop); 77 return b; 78 } else if (op instanceof CoreOp.ExceptionRegionEnter ere) { 79 // Cannot remove block parameters from exception handlers 80 removeUnusedBlockParameters(b, ere.start()); 81 } else if (op instanceof CoreOp.ExceptionRegionExit ere) { 82 // Cannot remove block parameters from exception handlers 83 removeUnusedBlockParameters(b, ere.end()); 84 } else if (op instanceof Op.BlockTerminating) { 85 for (Block.Reference successor : op.successors()) { 86 removeUnusedBlockParameters(b, successor); 87 } 88 } 89 b.op(op); 90 return b; 91 } 92 93 // Remove any unused block parameters and successor arguments 94 private void removeUnusedBlockParameters(Block.Builder b, Block.Reference successor) { 95 Block target = successor.targetBlock(); 96 BitSet unusedParameterIndexes = adjustedBlocks.computeIfAbsent(target, 97 k -> adjustBlock(b, k)); 98 if (!unusedParameterIndexes.isEmpty()) { 99 adjustSuccessor(unusedParameterIndexes, b, successor); 100 } 101 102 } 103 104 // Remove any unused block parameters 105 BitSet adjustBlock(Block.Builder b, Block target) { 106 // Determine the indexes of unused block parameters 107 List<Block.Parameter> parameters = target.parameters(); 108 BitSet unusedParameterIndexes = parameters.stream() 109 .filter(p -> p.uses().isEmpty()) 110 .mapToInt(Block.Parameter::index) 111 .collect(BitSet::new, BitSet::set, BitSet::or); 112 113 if (!unusedParameterIndexes.isEmpty()) { 114 // Create a new output block and remap it to the target block, 115 // overriding any previous mapping 116 Block.Builder adjustedBlock = b.block(); 117 b.context().mapBlock(target, adjustedBlock); 118 119 // Update and remap the output block parameters 120 for (int i = 0; i < parameters.size(); i++) { 121 if (!unusedParameterIndexes.get(i)) { 122 Block.Parameter parameter = parameters.get(i); 123 b.context().mapValue( 124 parameter, 125 adjustedBlock.parameter(parameter.type())); 126 } 127 } 128 } 129 130 return unusedParameterIndexes; 131 } 132 133 // Remove any unused successor arguments 134 void adjustSuccessor(BitSet unusedParameterIndexes, Block.Builder b, Block.Reference successor) { 135 // Create a new output successor and remap it 136 List<Value> arguments = new ArrayList<>(); 137 for (int i = 0; i < successor.arguments().size(); i++) { 138 if (!unusedParameterIndexes.get(i)) { 139 arguments.add(b.context().getValue(successor.arguments().get(i))); 140 } 141 } 142 Block.Reference adjustedSuccessor = b.context().getBlock(successor.targetBlock()) 143 .successor(arguments); 144 b.context().mapSuccessor(successor, adjustedSuccessor); 145 } 146 147 void mergeBlock(Block.Builder b, CoreOp.BranchOp bop) { 148 Block.Reference reference = bop.branch(); 149 Block successor = reference.targetBlock(); 150 // Replace use of the successor's parameters with the reference's arguments 151 b.context().mapValues(successor.parameters(), 152 b.context().getValues(reference.arguments())); 153 mergeBlock(b, successor); 154 } 155 156 void mergeBlock(Block.Builder b, Block successor) { 157 mergedBlocks.add(successor); 158 159 // Merge non-terminal operations 160 for (int i = 0; i < successor.ops().size() - 1; i++) { 161 b.op(successor.ops().get(i)); 162 } 163 164 // Check if subsequent successor block can be normalized 165 apply(b, successor.terminatingOp()); 166 } 167 }