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.analysis; 27 28 import java.lang.reflect.code.*; 29 import java.lang.reflect.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 Op.BlockTerminating) { 82 for (Block.Reference successor : op.successors()) { 83 removeUnusedBlockParameters(b, successor); 84 } 85 } 86 b.op(op); 87 return b; 88 } 89 90 // Remove any unused block parameters and successor arguments 91 private void removeUnusedBlockParameters(Block.Builder b, Block.Reference successor) { 92 Block target = successor.targetBlock(); 93 BitSet unusedParameterIndexes = adjustedBlocks.computeIfAbsent(target, 94 k -> adjustBlock(b, k)); 95 if (!unusedParameterIndexes.isEmpty()) { 96 adjustSuccessor(unusedParameterIndexes, b, successor); 97 } 98 99 } 100 101 // Remove any unused block parameters 102 BitSet adjustBlock(Block.Builder b, Block target) { 103 // Determine the indexes of unused block parameters 104 List<Block.Parameter> parameters = target.parameters(); 105 BitSet unusedParameterIndexes = parameters.stream() 106 .filter(p -> p.uses().isEmpty()) 107 .mapToInt(Block.Parameter::index) 108 .collect(BitSet::new, BitSet::set, BitSet::or); 109 110 if (!unusedParameterIndexes.isEmpty()) { 111 // Create a new output block and remap it to the target block, 112 // overriding any previous mapping 113 Block.Builder adjustedBlock = b.block(); 114 b.context().mapBlock(target, adjustedBlock); 115 116 // Update and remap the output block parameters 117 for (int i = 0; i < parameters.size(); i++) { 118 if (!unusedParameterIndexes.get(i)) { 119 Block.Parameter parameter = parameters.get(i); 120 b.context().mapValue( 121 parameter, 122 adjustedBlock.parameter(parameter.type())); 123 } 124 } 125 } 126 127 return unusedParameterIndexes; 128 } 129 130 // Remove any unused successor arguments 131 void adjustSuccessor(BitSet unusedParameterIndexes, Block.Builder b, Block.Reference successor) { 132 // Create a new output successor and remap it 133 List<Value> arguments = new ArrayList<>(); 134 for (int i = 0; i < successor.arguments().size(); i++) { 135 if (!unusedParameterIndexes.get(i)) { 136 arguments.add(b.context().getValue(successor.arguments().get(i))); 137 } 138 } 139 Block.Reference adjustedSuccessor = b.context().getBlock(successor.targetBlock()) 140 .successor(arguments); 141 b.context().mapSuccessor(successor, adjustedSuccessor); 142 } 143 144 void mergeBlock(Block.Builder b, CoreOp.BranchOp bop) { 145 Block.Reference reference = bop.branch(); 146 Block successor = reference.targetBlock(); 147 // Replace use of the successor's parameters with the reference's arguments 148 b.context().mapValues(successor.parameters(), 149 b.context().getValues(reference.arguments())); 150 mergeBlock(b, successor); 151 } 152 153 void mergeBlock(Block.Builder b, Block successor) { 154 mergedBlocks.add(successor); 155 156 // Merge non-terminal operations 157 for (int i = 0; i < successor.ops().size() - 1; i++) { 158 b.op(successor.ops().get(i)); 159 } 160 161 // Check if subsequent successor block can be normalized 162 apply(b, successor.terminatingOp()); 163 } 164 }