1 /* 2 * Copyright (c) 2016, 2019, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.hotspot.phases.aot; 26 27 import static org.graalvm.util.CollectionsUtil.anyMatch; 28 29 import java.util.ArrayList; 30 import java.util.Iterator; 31 import java.util.List; 32 33 import jdk.internal.vm.compiler.collections.EconomicSet; 34 import org.graalvm.compiler.graph.Node; 35 import org.graalvm.compiler.hotspot.nodes.aot.InitializeKlassNode; 36 import org.graalvm.compiler.hotspot.nodes.aot.ResolveConstantNode; 37 import org.graalvm.compiler.nodes.AbstractMergeNode; 38 import org.graalvm.compiler.nodes.FixedNode; 39 import org.graalvm.compiler.nodes.FixedWithNextNode; 40 import org.graalvm.compiler.nodes.Invoke; 41 import org.graalvm.compiler.nodes.StructuredGraph; 42 import org.graalvm.compiler.nodes.spi.CoreProviders; 43 import org.graalvm.compiler.phases.BasePhase; 44 import org.graalvm.compiler.phases.graph.MergeableState; 45 import org.graalvm.compiler.phases.graph.PostOrderNodeIterator; 46 47 import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant; 48 import jdk.vm.ci.meta.Constant; 49 import jdk.vm.ci.meta.ResolvedJavaType; 50 51 public class EliminateRedundantInitializationPhase extends BasePhase<CoreProviders> { 52 /** 53 * Find each {@link Invoke} that has a corresponding {@link InitializeKlassNode}. These 54 * {@link InitializeKlassNode} are redundant and are removed. 55 */ 56 private static void removeInitsAtStaticCalls(StructuredGraph graph) { 57 for (Invoke invoke : graph.getInvokes()) { 58 Node classInit = invoke.classInit(); 59 if (classInit != null) { 60 classInit.replaceAtUsages(null); 61 graph.removeFixed((FixedWithNextNode) classInit); 62 } 63 } 64 } 65 66 /** 67 * Remove redundant {@link InitializeKlassNode} or {@link ResolveConstantNode} instances from 68 * the graph. 69 * 70 * @param graph the program graph 71 */ 72 private static void removeRedundantInits(StructuredGraph graph) { 73 // Find and remove redundant nodes from the graph. 74 List<FixedWithNextNode> redundantNodes = findRedundantInits(graph); 75 for (FixedWithNextNode n : redundantNodes) { 76 graph.removeFixed(n); 77 } 78 } 79 80 /** 81 * Find {@link InitializeKlassNode} and {@link ResolveConstantNode} instances that can be 82 * removed because there is an existing dominating node. 83 * 84 * @param graph the program graph 85 */ 86 private static List<FixedWithNextNode> findRedundantInits(StructuredGraph graph) { 87 EliminateRedundantInitializationIterator i = new EliminateRedundantInitializationIterator(graph.start(), new InitializedTypes()); 88 i.apply(); 89 return i.getRedundantNodes(); 90 } 91 92 /** 93 * State for {@link EliminateRedundantInitializationIterator}. 94 */ 95 private static class InitializedTypes extends MergeableState<InitializedTypes> implements Cloneable { 96 private EconomicSet<ResolvedJavaType> types; 97 98 InitializedTypes() { 99 types = EconomicSet.create(); 100 } 101 102 private InitializedTypes(EconomicSet<ResolvedJavaType> types) { 103 this.types = types; 104 } 105 106 @Override 107 public InitializedTypes clone() { 108 return new InitializedTypes(EconomicSet.create(types)); 109 } 110 111 public boolean contains(ResolvedJavaType type) { 112 if (type.isInterface() || type.isArray()) { 113 // Check for exact match for interfaces 114 return types.contains(type); 115 } 116 // For other types see if there is the same type or a subtype 117 return anyMatch(types, t -> type.isAssignableFrom(t)); 118 } 119 120 public void add(ResolvedJavaType type) { 121 types.add(type); 122 } 123 124 /** 125 * Merge two given types. Interfaces and arrays have to be the same to merge successfully. 126 * For other types the answer is the LCA. 127 * 128 * @param a initialized type 129 * @param b initialized type 130 * @return lowest common type that is initialized if either a or b are initialized, null if 131 * no such type exists. 132 */ 133 private static ResolvedJavaType merge(ResolvedJavaType a, ResolvedJavaType b) { 134 // We want exact match for interfaces or arrays 135 if (a.isInterface() || b.isInterface() || a.isArray() || b.isArray()) { 136 if (a.equals(b)) { 137 return a; 138 } else { 139 return null; 140 } 141 } else { 142 // And LCA for other types 143 ResolvedJavaType c = a.findLeastCommonAncestor(b); 144 if (c.isJavaLangObject()) { 145 // Not a very useful type, always initialized, don't pollute the sets. 146 return null; 147 } 148 return c; 149 } 150 } 151 152 /** 153 * Merge two sets of types. Essentially a computation of the LCA for each element of the 154 * cartesian product of the input sets. Interfaces have to match exactly. 155 * 156 * @param a set of initialized types 157 * @param b set of initialized types 158 * @return set of common types that would be initialized if types in either a or b are 159 * initialized 160 */ 161 private static EconomicSet<ResolvedJavaType> merge(EconomicSet<ResolvedJavaType> a, EconomicSet<ResolvedJavaType> b) { 162 EconomicSet<ResolvedJavaType> c = EconomicSet.create(); 163 for (ResolvedJavaType ta : a) { 164 for (ResolvedJavaType tb : b) { 165 ResolvedJavaType tc = merge(ta, tb); 166 if (tc != null) { 167 c.add(tc); 168 if (tc.isInterface() || tc.isArray()) { 169 // Interfaces and arrays are not going merge with anything else, so bail 170 // out early. 171 break; 172 } 173 } 174 } 175 } 176 return c; 177 } 178 179 @Override 180 public boolean merge(AbstractMergeNode merge, List<InitializedTypes> withStates) { 181 for (InitializedTypes ts : withStates) { 182 types = merge(types, ts.types); 183 } 184 return true; 185 } 186 187 protected static String toString(EconomicSet<ResolvedJavaType> types) { 188 StringBuilder b = new StringBuilder(); 189 b.append("["); 190 Iterator<ResolvedJavaType> i = types.iterator(); 191 while (i.hasNext()) { 192 ResolvedJavaType t = i.next(); 193 b.append(t.toString()); 194 if (i.hasNext()) { 195 b.append(","); 196 } 197 } 198 b.append("]"); 199 return b.toString(); 200 } 201 202 @Override 203 public String toString() { 204 return toString(types); 205 } 206 } 207 208 /** 209 * Do data flow analysis of class initializations and array resolutions. Collect redundant 210 * nodes. 211 */ 212 private static class EliminateRedundantInitializationIterator extends PostOrderNodeIterator<InitializedTypes> { 213 private List<FixedWithNextNode> redundantNodes = new ArrayList<>(); 214 215 public List<FixedWithNextNode> getRedundantNodes() { 216 return redundantNodes; 217 } 218 219 EliminateRedundantInitializationIterator(FixedNode start, InitializedTypes initialState) { 220 super(start, initialState); 221 } 222 223 private void processType(FixedWithNextNode node, Constant c) { 224 HotSpotMetaspaceConstant klass = (HotSpotMetaspaceConstant) c; 225 ResolvedJavaType t = klass.asResolvedJavaType(); 226 if (t != null) { 227 if (state.contains(t)) { 228 redundantNodes.add(node); 229 } else { 230 state.add(t); 231 } 232 } 233 } 234 235 @Override 236 protected void node(FixedNode node) { 237 if (node instanceof InitializeKlassNode) { 238 InitializeKlassNode i = (InitializeKlassNode) node; 239 if (i.value().isConstant()) { 240 processType(i, i.value().asConstant()); 241 } 242 } else if (node instanceof ResolveConstantNode) { 243 ResolveConstantNode r = (ResolveConstantNode) node; 244 if (r.hasNoUsages()) { 245 if (r.value().isConstant()) { 246 processType(r, r.value().asConstant()); 247 } 248 } 249 } 250 } 251 252 } 253 254 @Override 255 protected void run(StructuredGraph graph, CoreProviders context) { 256 removeInitsAtStaticCalls(graph); 257 removeRedundantInits(graph); 258 } 259 }