1 /* 2 * Copyright (c) 2016, 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 package org.graalvm.compiler.hotspot.phases.aot; 24 25 import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.strictlyDominates; 26 import static org.graalvm.compiler.hotspot.nodes.aot.LoadMethodCountersNode.getLoadMethodCountersNodes; 27 import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes; 28 29 import java.util.HashSet; 30 import java.util.List; 31 32 import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant; 33 import jdk.vm.ci.hotspot.HotSpotObjectConstant; 34 import jdk.vm.ci.hotspot.HotSpotResolvedJavaType; 35 import jdk.vm.ci.hotspot.HotSpotResolvedObjectType; 36 import jdk.vm.ci.meta.Constant; 37 import jdk.vm.ci.meta.ConstantReflectionProvider; 38 import jdk.vm.ci.meta.ResolvedJavaType; 39 40 import org.graalvm.compiler.core.common.cfg.BlockMap; 41 import org.graalvm.compiler.core.common.type.ObjectStamp; 42 import org.graalvm.compiler.core.common.type.Stamp; 43 import org.graalvm.compiler.core.common.type.StampFactory; 44 import org.graalvm.compiler.debug.GraalError; 45 import org.graalvm.compiler.graph.Node; 46 import org.graalvm.compiler.graph.NodeMap; 47 import org.graalvm.compiler.hotspot.FingerprintUtil; 48 import org.graalvm.compiler.hotspot.meta.HotSpotConstantLoadAction; 49 import org.graalvm.compiler.hotspot.nodes.aot.InitializeKlassNode; 50 import org.graalvm.compiler.hotspot.nodes.aot.LoadConstantIndirectlyFixedNode; 51 import org.graalvm.compiler.hotspot.nodes.aot.LoadConstantIndirectlyNode; 52 import org.graalvm.compiler.hotspot.nodes.aot.LoadMethodCountersNode; 53 import org.graalvm.compiler.hotspot.nodes.aot.ResolveConstantNode; 54 import org.graalvm.compiler.hotspot.nodes.aot.ResolveMethodAndLoadCountersNode; 55 import org.graalvm.compiler.nodes.ConstantNode; 56 import org.graalvm.compiler.nodes.FixedWithNextNode; 57 import org.graalvm.compiler.nodes.StructuredGraph; 58 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; 59 import org.graalvm.compiler.nodes.ValueNode; 60 import org.graalvm.compiler.nodes.cfg.Block; 61 import org.graalvm.compiler.phases.BasePhase; 62 import org.graalvm.compiler.phases.schedule.SchedulePhase; 63 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy; 64 import org.graalvm.compiler.phases.tiers.PhaseContext; 65 import org.graalvm.util.EconomicMap; 66 67 public class ReplaceConstantNodesPhase extends BasePhase<PhaseContext> { 68 69 private static final HashSet<Class<?>> builtIns = new HashSet<>(); 70 private final boolean verifyFingerprints; 71 72 static { 73 builtIns.add(Boolean.class); 74 75 Class<?> characterCacheClass = Character.class.getDeclaredClasses()[0]; 76 assert "java.lang.Character$CharacterCache".equals(characterCacheClass.getName()); 77 builtIns.add(characterCacheClass); 78 79 Class<?> byteCacheClass = Byte.class.getDeclaredClasses()[0]; 80 assert "java.lang.Byte$ByteCache".equals(byteCacheClass.getName()); 81 builtIns.add(byteCacheClass); 82 83 Class<?> shortCacheClass = Short.class.getDeclaredClasses()[0]; 84 assert "java.lang.Short$ShortCache".equals(shortCacheClass.getName()); 85 builtIns.add(shortCacheClass); 86 87 Class<?> integerCacheClass = Integer.class.getDeclaredClasses()[0]; 88 assert "java.lang.Integer$IntegerCache".equals(integerCacheClass.getName()); 89 builtIns.add(integerCacheClass); 90 91 Class<?> longCacheClass = Long.class.getDeclaredClasses()[0]; 92 assert "java.lang.Long$LongCache".equals(longCacheClass.getName()); 93 builtIns.add(longCacheClass); 94 } 95 96 private static boolean isReplacementNode(Node n) { 97 // @formatter:off 98 return n instanceof LoadConstantIndirectlyNode || 99 n instanceof LoadConstantIndirectlyFixedNode || 100 n instanceof ResolveConstantNode || 101 n instanceof InitializeKlassNode; 102 // @formatter:on 103 } 104 105 private static boolean anyUsagesNeedReplacement(ConstantNode node) { 106 return node.usages().filter(n -> !isReplacementNode(n)).isNotEmpty(); 107 } 108 109 private static boolean anyUsagesNeedReplacement(LoadMethodCountersNode node) { 110 return node.usages().filter(n -> !(n instanceof ResolveMethodAndLoadCountersNode)).isNotEmpty(); 111 } 112 113 private static boolean checkForBadFingerprint(HotSpotResolvedJavaType type) { 114 if (type.isArray()) { 115 if (type.getElementalType().isPrimitive()) { 116 return false; 117 } 118 return FingerprintUtil.getFingerprint((HotSpotResolvedObjectType) (type.getElementalType())) == 0; 119 } 120 return FingerprintUtil.getFingerprint((HotSpotResolvedObjectType) type) == 0; 121 } 122 123 /** 124 * Replace {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} with indirection. 125 * 126 * @param graph 127 * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs 128 * resolution. 129 */ 130 private void handleHotSpotMetaspaceConstant(StructuredGraph graph, ConstantNode node) { 131 HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant(); 132 HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType(); 133 134 if (type != null) { 135 if (verifyFingerprints && checkForBadFingerprint(type)) { 136 throw new GraalError("Type with bad fingerprint: " + type); 137 } 138 assert !metaspaceConstant.isCompressed() : "No support for replacing compressed metaspace constants"; 139 replaceWithInitialization(graph, node); 140 if (anyUsagesNeedReplacement(node)) { 141 replaceWithResolution(graph, node); 142 } 143 } else { 144 throw new GraalError("Unsupported metaspace constant type: " + type); 145 } 146 } 147 148 /** 149 * Find the lowest dominating {@link FixedWithNextNode} before given node. 150 * 151 * @param graph 152 * @param node 153 * @return the last {@link FixedWithNextNode} that is scheduled before node. 154 */ 155 private static FixedWithNextNode findFixedWithNextBefore(StructuredGraph graph, Node node) { 156 ScheduleResult schedule = graph.getLastSchedule(); 157 NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap(); 158 BlockMap<List<Node>> blockToNodes = schedule.getBlockToNodesMap(); 159 160 Block block = nodeToBlock.get(node); 161 FixedWithNextNode result = null; 162 for (Node n : blockToNodes.get(block)) { 163 if (n instanceof FixedWithNextNode) { 164 result = (FixedWithNextNode) n; 165 } 166 if (n.equals(node)) { 167 break; 168 } 169 } 170 assert result != null; 171 return result; 172 } 173 174 /** 175 * Try to find dominating {@link InitializeKlassNode} that can be reused. 176 * 177 * @param graph 178 * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs 179 * resolution. 180 */ 181 private static void replaceWithInitialization(StructuredGraph graph, ConstantNode node) { 182 ScheduleResult schedule = graph.getLastSchedule(); 183 NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap(); 184 BlockMap<List<Node>> blockToNodes = schedule.getBlockToNodesMap(); 185 186 EconomicMap<Block, Node> blockToInit = EconomicMap.create(); 187 for (Node n : node.usages().filter(InitializeKlassNode.class)) { 188 blockToInit.put(nodeToBlock.get(n), n); 189 } 190 for (Node use : node.usages().filter(n -> !isReplacementNode(n)).snapshot()) { 191 boolean replaced = false; 192 Block b = nodeToBlock.get(use); 193 InitializeKlassNode i = (InitializeKlassNode) blockToInit.get(b); 194 if (i != null) { 195 // There is an initialization in the same block as the use, look if the use is 196 // scheduled after it. 197 for (Node n : blockToNodes.get(b)) { 198 if (n.equals(use)) { 199 // Usage is before initialization, can't use it 200 break; 201 } 202 if (n.equals(i)) { 203 use.replaceFirstInput(node, i); 204 replaced = true; 205 break; 206 } 207 } 208 } 209 if (!replaced) { 210 // Look for dominating blocks that have initializations 211 for (Block d : blockToInit.getKeys()) { 212 if (strictlyDominates(d, b)) { 213 use.replaceFirstInput(node, blockToInit.get(d)); 214 break; 215 } 216 } 217 } 218 } 219 } 220 221 /** 222 * Replace the uses of a constant with either {@link LoadConstantIndirectlyNode} or 223 * {@link ResolveConstantNode}. 224 * 225 * @param graph 226 * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs 227 * resolution. 228 */ 229 private static void replaceWithResolution(StructuredGraph graph, ConstantNode node) { 230 HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant(); 231 HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType(); 232 ResolvedJavaType topMethodHolder = graph.method().getDeclaringClass(); 233 ValueNode replacement; 234 235 if (type.isArray() && type.getComponentType().isPrimitive()) { 236 // Special case for primitive arrays. The AOT runtime pre-resolves them, so we may 237 // omit the resolution call. 238 replacement = graph.addOrUnique(new LoadConstantIndirectlyNode(node)); 239 } else if (type.equals(topMethodHolder) || (type.isAssignableFrom(topMethodHolder) && !type.isInterface())) { 240 // If it's a supertype of or the same class that declares the top method, we are 241 // guaranteed to have it resolved already. If it's an interface, we just test for 242 // equality. 243 replacement = graph.addOrUnique(new LoadConstantIndirectlyNode(node)); 244 } else { 245 FixedWithNextNode fixedReplacement; 246 if (builtIns.contains(type.mirror())) { 247 // Special case of klass constants that come from {@link BoxingSnippets}. 248 fixedReplacement = graph.add(new ResolveConstantNode(node, HotSpotConstantLoadAction.INITIALIZE)); 249 } else { 250 fixedReplacement = graph.add(new ResolveConstantNode(node)); 251 } 252 graph.addAfterFixed(findFixedWithNextBefore(graph, node), fixedReplacement); 253 replacement = fixedReplacement; 254 } 255 node.replaceAtUsages(replacement, n -> !isReplacementNode(n)); 256 } 257 258 /** 259 * Replace an object constant with an indirect load {@link ResolveConstantNode}. Currently we 260 * support only strings. 261 * 262 * @param graph 263 * @param node {@link ConstantNode} containing a {@link HotSpotObjectConstant} that needs 264 * resolution. 265 */ 266 private static void handleHotSpotObjectConstant(StructuredGraph graph, ConstantNode node) { 267 HotSpotObjectConstant constant = (HotSpotObjectConstant) node.asJavaConstant(); 268 HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) constant.getType(); 269 if (type.mirror().equals(String.class)) { 270 assert !constant.isCompressed() : "No support for replacing compressed oop constants"; 271 FixedWithNextNode replacement = graph.add(new ResolveConstantNode(node)); 272 graph.addAfterFixed(findFixedWithNextBefore(graph, node), replacement); 273 node.replaceAtUsages(replacement, n -> !(n instanceof ResolveConstantNode)); 274 } else { 275 throw new GraalError("Unsupported object constant type: " + type); 276 } 277 } 278 279 /** 280 * Replace {@link LoadMethodCountersNode} with indirect load 281 * {@link ResolveMethodAndLoadCountersNode}, expose a klass constant of the holder. 282 * 283 * @param graph 284 * @param node 285 * @param context 286 */ 287 private static void handleLoadMethodCounters(StructuredGraph graph, LoadMethodCountersNode node, PhaseContext context) { 288 ResolvedJavaType type = node.getMethod().getDeclaringClass(); 289 Stamp hubStamp = context.getStampProvider().createHubStamp((ObjectStamp) StampFactory.objectNonNull()); 290 ConstantReflectionProvider constantReflection = context.getConstantReflection(); 291 ConstantNode klassHint = ConstantNode.forConstant(hubStamp, constantReflection.asObjectHub(type), context.getMetaAccess(), graph); 292 FixedWithNextNode replacement = graph.add(new ResolveMethodAndLoadCountersNode(node.getMethod(), klassHint)); 293 graph.addAfterFixed(findFixedWithNextBefore(graph, node), replacement); 294 node.replaceAtUsages(replacement, n -> !(n instanceof ResolveMethodAndLoadCountersNode)); 295 } 296 297 /** 298 * Replace {@link LoadMethodCountersNode} with {@link ResolveMethodAndLoadCountersNode}, expose 299 * klass constants. 300 * 301 * @param graph 302 * @param context 303 */ 304 private static void replaceLoadMethodCounters(StructuredGraph graph, PhaseContext context) { 305 new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, true).apply(graph, false); 306 for (LoadMethodCountersNode node : getLoadMethodCountersNodes(graph)) { 307 if (anyUsagesNeedReplacement(node)) { 308 handleLoadMethodCounters(graph, node, context); 309 } 310 } 311 } 312 313 /** 314 * Replace object and klass constants with resolution nodes or reuse preceding initializations. 315 * 316 * @param graph 317 */ 318 private void replaceKlassesAndObjects(StructuredGraph graph) { 319 new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, true).apply(graph, false); 320 321 for (ConstantNode node : getConstantNodes(graph)) { 322 Constant constant = node.asConstant(); 323 if (constant instanceof HotSpotMetaspaceConstant && anyUsagesNeedReplacement(node)) { 324 handleHotSpotMetaspaceConstant(graph, node); 325 } else if (constant instanceof HotSpotObjectConstant && anyUsagesNeedReplacement(node)) { 326 handleHotSpotObjectConstant(graph, node); 327 } 328 } 329 } 330 331 @Override 332 protected void run(StructuredGraph graph, PhaseContext context) { 333 // Replace LoadMethodCountersNode with ResolveMethodAndLoadCountersNode, expose klass 334 // constants. 335 replaceLoadMethodCounters(graph, context); 336 337 // Replace object and klass constants (including the ones added in the previous pass) with 338 // resolution nodes. 339 replaceKlassesAndObjects(graph); 340 } 341 342 @Override 343 public boolean checkContract() { 344 return false; 345 } 346 347 public ReplaceConstantNodesPhase() { 348 this(true); 349 } 350 351 public ReplaceConstantNodesPhase(boolean verifyFingerprints) { 352 this.verifyFingerprints = verifyFingerprints; 353 } 354 }