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