1 /* 2 * Copyright (c) 2016, 2018, 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.compiler.core.common.cfg.AbstractControlFlowGraph.strictlyDominates; 28 import static org.graalvm.compiler.hotspot.nodes.aot.LoadMethodCountersNode.getLoadMethodCountersNodes; 29 import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes; 30 31 import java.util.HashSet; 32 import java.util.List; 33 34 import jdk.internal.vm.compiler.collections.EconomicMap; 35 import org.graalvm.compiler.core.common.cfg.BlockMap; 36 import org.graalvm.compiler.core.common.type.ObjectStamp; 37 import org.graalvm.compiler.core.common.type.Stamp; 38 import org.graalvm.compiler.core.common.type.StampFactory; 39 import org.graalvm.compiler.debug.GraalError; 40 import org.graalvm.compiler.graph.Node; 41 import org.graalvm.compiler.graph.NodeMap; 42 import org.graalvm.compiler.hotspot.meta.HotSpotConstantLoadAction; 43 import org.graalvm.compiler.hotspot.nodes.aot.InitializeKlassNode; 44 import org.graalvm.compiler.hotspot.nodes.aot.LoadConstantIndirectlyFixedNode; 45 import org.graalvm.compiler.hotspot.nodes.aot.LoadConstantIndirectlyNode; 46 import org.graalvm.compiler.hotspot.nodes.aot.LoadMethodCountersNode; 47 import org.graalvm.compiler.hotspot.nodes.aot.ResolveConstantNode; 48 import org.graalvm.compiler.hotspot.nodes.aot.ResolveDynamicConstantNode; 49 import org.graalvm.compiler.hotspot.nodes.aot.ResolveMethodAndLoadCountersNode; 50 import org.graalvm.compiler.nodes.AbstractBeginNode; 51 import org.graalvm.compiler.nodes.AbstractMergeNode; 52 import org.graalvm.compiler.nodes.ConstantNode; 53 import org.graalvm.compiler.nodes.FixedNode; 54 import org.graalvm.compiler.nodes.FixedWithNextNode; 55 import org.graalvm.compiler.nodes.FrameState; 56 import org.graalvm.compiler.nodes.LoopBeginNode; 57 import org.graalvm.compiler.nodes.LoopExitNode; 58 import org.graalvm.compiler.nodes.StateSplit; 59 import org.graalvm.compiler.nodes.StructuredGraph; 60 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; 61 import org.graalvm.compiler.nodes.ValueNode; 62 import org.graalvm.compiler.nodes.calc.FloatingNode; 63 import org.graalvm.compiler.nodes.cfg.Block; 64 import org.graalvm.compiler.nodes.spi.CoreProviders; 65 import org.graalvm.compiler.phases.BasePhase; 66 import org.graalvm.compiler.phases.graph.ReentrantNodeIterator; 67 import org.graalvm.compiler.phases.graph.ReentrantNodeIterator.NodeIteratorClosure; 68 import org.graalvm.compiler.phases.schedule.SchedulePhase; 69 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy; 70 71 import jdk.vm.ci.code.BytecodeFrame; 72 import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant; 73 import jdk.vm.ci.hotspot.HotSpotObjectConstant; 74 import jdk.vm.ci.hotspot.HotSpotResolvedJavaType; 75 import jdk.vm.ci.hotspot.HotSpotResolvedObjectType; 76 import jdk.vm.ci.meta.Constant; 77 import jdk.vm.ci.meta.ConstantReflectionProvider; 78 import jdk.vm.ci.meta.MetaAccessProvider; 79 import jdk.vm.ci.meta.ResolvedJavaType; 80 81 public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> { 82 83 private final boolean verifyFingerprints; 84 85 static Class<?> characterCacheClass = Character.class.getDeclaredClasses()[0]; 86 static Class<?> byteCacheClass = Byte.class.getDeclaredClasses()[0]; 87 static Class<?> shortCacheClass = Short.class.getDeclaredClasses()[0]; 88 static Class<?> integerCacheClass = Integer.class.getDeclaredClasses()[0]; 89 static Class<?> longCacheClass = Long.class.getDeclaredClasses()[0]; 90 91 static class ClassInfo { 92 93 private ResolvedJavaType stringType; 94 private final HashSet<ResolvedJavaType> builtIns = new HashSet<>(); 95 96 ClassInfo(MetaAccessProvider metaAccessProvider) { 97 builtIns.add(metaAccessProvider.lookupJavaType(Boolean.class)); 98 99 assert "java.lang.Character$CharacterCache".equals(characterCacheClass.getName()); 100 builtIns.add(metaAccessProvider.lookupJavaType(characterCacheClass)); 101 102 assert "java.lang.Byte$ByteCache".equals(byteCacheClass.getName()); 103 builtIns.add(metaAccessProvider.lookupJavaType(byteCacheClass)); 104 105 assert "java.lang.Short$ShortCache".equals(shortCacheClass.getName()); 106 builtIns.add(metaAccessProvider.lookupJavaType(shortCacheClass)); 107 108 assert "java.lang.Integer$IntegerCache".equals(integerCacheClass.getName()); 109 builtIns.add(metaAccessProvider.lookupJavaType(integerCacheClass)); 110 111 assert "java.lang.Long$LongCache".equals(longCacheClass.getName()); 112 builtIns.add(metaAccessProvider.lookupJavaType(longCacheClass)); 113 114 stringType = metaAccessProvider.lookupJavaType(String.class); 115 } 116 } 117 118 private static boolean isReplacementNode(Node n) { 119 // @formatter:off 120 return n instanceof LoadConstantIndirectlyNode || 121 n instanceof LoadConstantIndirectlyFixedNode || 122 n instanceof ResolveDynamicConstantNode || 123 n instanceof ResolveConstantNode || 124 n instanceof InitializeKlassNode; 125 // @formatter:on 126 } 127 128 private static boolean anyUsagesNeedReplacement(ConstantNode node) { 129 return node.usages().filter(n -> !isReplacementNode(n)).isNotEmpty(); 130 } 131 132 private static boolean anyUsagesNeedReplacement(LoadMethodCountersNode node) { 133 return node.usages().filter(n -> !(n instanceof ResolveMethodAndLoadCountersNode)).isNotEmpty(); 134 } 135 136 private static boolean checkForBadFingerprint(HotSpotResolvedJavaType type) { 137 if (type.isArray()) { 138 if (type.getElementalType().isPrimitive()) { 139 return false; 140 } 141 return ((HotSpotResolvedObjectType) (type.getElementalType())).getFingerprint() == 0; 142 } 143 return ((HotSpotResolvedObjectType) type).getFingerprint() == 0; 144 } 145 146 /** 147 * Insert the replacement node into the graph. We may need to insert it into a place different 148 * than the original {@link FloatingNode} since we need to make sure that replacement will have 149 * a valid state assigned. 150 * 151 * @param graph 152 * @param stateMapper 153 * @param node 154 * @param replacement 155 */ 156 private static void insertReplacement(StructuredGraph graph, FrameStateMapperClosure stateMapper, FloatingNode node, FixedWithNextNode replacement) { 157 FixedWithNextNode insertionPoint = findInsertionPoint(graph, stateMapper, node); 158 graph.addAfterFixed(insertionPoint, replacement); 159 stateMapper.addState(replacement, stateMapper.getState(insertionPoint)); 160 } 161 162 /** 163 * Find a good place to insert a stateful fixed node that is above the given node. A good 164 * insertion point should have a valid FrameState reaching it. 165 * 166 * @param graph 167 * @param stateMapper 168 * @param node start search from this node up 169 * @return an insertion point 170 */ 171 private static FixedWithNextNode findInsertionPoint(StructuredGraph graph, FrameStateMapperClosure stateMapper, FloatingNode node) { 172 FixedWithNextNode fixed = findFixedBeforeFloating(graph, node); 173 FixedWithNextNode result = findFixedWithValidState(graph, stateMapper, fixed); 174 return result; 175 } 176 177 /** 178 * Find the first {@link FixedWithNextNode} that is currently scheduled before the given 179 * floating node. 180 * 181 * @param graph 182 * @param node start search from this node up 183 * @return the first {@link FixedWithNextNode} 184 */ 185 private static FixedWithNextNode findFixedBeforeFloating(StructuredGraph graph, FloatingNode node) { 186 ScheduleResult schedule = graph.getLastSchedule(); 187 NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap(); 188 Block block = nodeToBlock.get(node); 189 BlockMap<List<Node>> blockToNodes = schedule.getBlockToNodesMap(); 190 FixedWithNextNode result = null; 191 for (Node n : blockToNodes.get(block)) { 192 if (n.equals(node)) { 193 break; 194 } 195 if (n instanceof FixedWithNextNode) { 196 result = (FixedWithNextNode) n; 197 } 198 } 199 assert result != null; 200 return result; 201 } 202 203 /** 204 * Find first dominating {@link FixedWithNextNode} that has a valid state reaching it starting 205 * from the given node. 206 * 207 * @param graph 208 * @param stateMapper 209 * @param node 210 * @return {@link FixedWithNextNode} that we can use as an insertion point 211 */ 212 private static FixedWithNextNode findFixedWithValidState(StructuredGraph graph, FrameStateMapperClosure stateMapper, FixedWithNextNode node) { 213 ScheduleResult schedule = graph.getLastSchedule(); 214 NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap(); 215 Block block = nodeToBlock.get(node); 216 217 Node n = node; 218 do { 219 if (isFixedWithValidState(stateMapper, n)) { 220 return (FixedWithNextNode) n; 221 } 222 while (n != block.getBeginNode()) { 223 n = n.predecessor(); 224 if (isFixedWithValidState(stateMapper, n)) { 225 return (FixedWithNextNode) n; 226 } 227 } 228 block = block.getDominator(); 229 if (block != null) { 230 n = block.getEndNode(); 231 } 232 } while (block != null); 233 234 return graph.start(); 235 } 236 237 private static boolean isFixedWithValidState(FrameStateMapperClosure stateMapper, Node n) { 238 if (n instanceof FixedWithNextNode) { 239 FixedWithNextNode fixed = (FixedWithNextNode) n; 240 assert stateMapper.getState(fixed) != null; 241 if (!BytecodeFrame.isPlaceholderBci(stateMapper.getState(fixed).bci)) { 242 return true; 243 } 244 } 245 return false; 246 } 247 248 /** 249 * Compute frame states for all fixed nodes in the graph. 250 */ 251 private static class FrameStateMapperClosure extends NodeIteratorClosure<FrameState> { 252 private NodeMap<FrameState> reachingStates; 253 254 @Override 255 protected FrameState processNode(FixedNode node, FrameState previousState) { 256 FrameState currentState = previousState; 257 if (node instanceof StateSplit) { 258 StateSplit stateSplit = (StateSplit) node; 259 FrameState stateAfter = stateSplit.stateAfter(); 260 if (stateAfter != null) { 261 currentState = stateAfter; 262 } 263 } 264 reachingStates.put(node, currentState); 265 return currentState; 266 } 267 268 @Override 269 protected FrameState merge(AbstractMergeNode merge, List<FrameState> states) { 270 FrameState singleFrameState = singleFrameState(states); 271 FrameState currentState = singleFrameState == null ? merge.stateAfter() : singleFrameState; 272 reachingStates.put(merge, currentState); 273 return currentState; 274 } 275 276 @Override 277 protected FrameState afterSplit(AbstractBeginNode node, FrameState oldState) { 278 return oldState; 279 } 280 281 @Override 282 protected EconomicMap<LoopExitNode, FrameState> processLoop(LoopBeginNode loop, FrameState initialState) { 283 return ReentrantNodeIterator.processLoop(this, loop, initialState).exitStates; 284 } 285 286 private static FrameState singleFrameState(List<FrameState> states) { 287 FrameState singleState = states.get(0); 288 for (int i = 1; i < states.size(); ++i) { 289 if (states.get(i) != singleState) { 290 return null; 291 } 292 } 293 return singleState; 294 } 295 296 FrameStateMapperClosure(StructuredGraph graph) { 297 reachingStates = new NodeMap<>(graph); 298 } 299 300 public FrameState getState(Node n) { 301 return reachingStates.get(n); 302 } 303 304 public void addState(Node n, FrameState s) { 305 reachingStates.setAndGrow(n, s); 306 } 307 } 308 309 /** 310 * Try to find dominating node doing the resolution that can be reused. 311 * 312 * @param graph 313 * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs 314 * resolution. 315 */ 316 private static void tryToReplaceWithExisting(StructuredGraph graph, ConstantNode node) { 317 ScheduleResult schedule = graph.getLastSchedule(); 318 NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap(); 319 BlockMap<List<Node>> blockToNodes = schedule.getBlockToNodesMap(); 320 321 EconomicMap<Block, Node> blockToExisting = EconomicMap.create(); 322 for (Node n : node.usages().filter(n -> isReplacementNode(n))) { 323 blockToExisting.put(nodeToBlock.get(n), n); 324 } 325 for (Node use : node.usages().filter(n -> !isReplacementNode(n)).snapshot()) { 326 boolean replaced = false; 327 Block b = nodeToBlock.get(use); 328 Node e = blockToExisting.get(b); 329 if (e != null) { 330 // There is an initialization or resolution in the same block as the use, look if 331 // the use is scheduled after it. 332 for (Node n : blockToNodes.get(b)) { 333 if (n.equals(use)) { 334 // Usage is before initialization, can't use it 335 break; 336 } 337 if (n.equals(e)) { 338 use.replaceFirstInput(node, e); 339 replaced = true; 340 break; 341 } 342 } 343 } 344 if (!replaced) { 345 // Look for dominating blocks that have existing nodes 346 for (Block d : blockToExisting.getKeys()) { 347 if (strictlyDominates(d, b)) { 348 use.replaceFirstInput(node, blockToExisting.get(d)); 349 break; 350 } 351 } 352 } 353 } 354 } 355 356 /** 357 * Replace the uses of a constant with either {@link LoadConstantIndirectlyNode} or 358 * {@link ResolveConstantNode}. 359 * 360 * @param graph 361 * @param stateMapper 362 * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs 363 * resolution. 364 */ 365 private static void replaceWithResolution(StructuredGraph graph, FrameStateMapperClosure stateMapper, ConstantNode node, ClassInfo classInfo) { 366 HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant(); 367 HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType(); 368 ResolvedJavaType topMethodHolder = graph.method().getDeclaringClass(); 369 ValueNode replacement; 370 371 if (type.isArray() && type.getComponentType().isPrimitive()) { 372 // Special case for primitive arrays. The AOT runtime pre-resolves them, so we may 373 // omit the resolution call. 374 replacement = graph.addOrUnique(new LoadConstantIndirectlyNode(node)); 375 } else if (type.equals(topMethodHolder) || (type.isAssignableFrom(topMethodHolder) && !type.isInterface())) { 376 // If it's a supertype of or the same class that declares the top method, we are 377 // guaranteed to have it resolved already. If it's an interface, we just test for 378 // equality. 379 replacement = graph.addOrUnique(new LoadConstantIndirectlyNode(node)); 380 } else { 381 FixedWithNextNode fixedReplacement; 382 if (classInfo.builtIns.contains(type)) { 383 // Special case of klass constants that come from {@link BoxingSnippets}. 384 fixedReplacement = graph.add(new ResolveConstantNode(node, HotSpotConstantLoadAction.INITIALIZE)); 385 } else { 386 fixedReplacement = graph.add(new ResolveConstantNode(node)); 387 } 388 insertReplacement(graph, stateMapper, node, fixedReplacement); 389 replacement = fixedReplacement; 390 } 391 node.replaceAtUsages(replacement, n -> !isReplacementNode(n)); 392 } 393 394 /** 395 * Replace {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} with indirection. 396 * 397 * @param graph 398 * @param stateMapper 399 * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs 400 * resolution. 401 */ 402 private void handleHotSpotMetaspaceConstant(StructuredGraph graph, FrameStateMapperClosure stateMapper, ConstantNode node, ClassInfo classInfo) { 403 HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant(); 404 HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType(); 405 406 if (type != null) { 407 if (verifyFingerprints && checkForBadFingerprint(type)) { 408 throw new GraalError("Type with bad fingerprint: " + type); 409 } 410 assert !metaspaceConstant.isCompressed() : "No support for replacing compressed metaspace constants"; 411 tryToReplaceWithExisting(graph, node); 412 if (anyUsagesNeedReplacement(node)) { 413 replaceWithResolution(graph, stateMapper, node, classInfo); 414 } 415 } else { 416 throw new GraalError("Unsupported metaspace constant type: " + type); 417 } 418 } 419 420 /** 421 * Replace an object constant with an indirect load {@link ResolveConstantNode}. Currently we 422 * support only strings. 423 * 424 * @param graph 425 * @param stateMapper 426 * @param node {@link ConstantNode} containing a {@link HotSpotObjectConstant} that needs 427 * resolution. 428 */ 429 private static void handleHotSpotObjectConstant(StructuredGraph graph, FrameStateMapperClosure stateMapper, ConstantNode node, ClassInfo classInfo) { 430 HotSpotObjectConstant constant = (HotSpotObjectConstant) node.asJavaConstant(); 431 HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) constant.getType(); 432 if (type.equals(classInfo.stringType)) { 433 assert !constant.isCompressed() : "No support for replacing compressed oop constants"; 434 FixedWithNextNode replacement = graph.add(new ResolveConstantNode(node)); 435 insertReplacement(graph, stateMapper, node, replacement); 436 node.replaceAtUsages(replacement, n -> !(n instanceof ResolveConstantNode)); 437 } else { 438 throw new GraalError("Unsupported object constant type: " + type); 439 } 440 } 441 442 /** 443 * Replace {@link LoadMethodCountersNode} with indirect load 444 * {@link ResolveMethodAndLoadCountersNode}, expose a klass constant of the holder. 445 * 446 * @param graph 447 * @param stateMapper 448 * @param node 449 * @param context 450 */ 451 private static void handleLoadMethodCounters(StructuredGraph graph, FrameStateMapperClosure stateMapper, LoadMethodCountersNode node, CoreProviders context) { 452 ResolvedJavaType type = node.getMethod().getDeclaringClass(); 453 Stamp hubStamp = context.getStampProvider().createHubStamp((ObjectStamp) StampFactory.objectNonNull()); 454 ConstantReflectionProvider constantReflection = context.getConstantReflection(); 455 ConstantNode klassHint = ConstantNode.forConstant(hubStamp, constantReflection.asObjectHub(type), context.getMetaAccess(), graph); 456 FixedWithNextNode replacement = graph.add(new ResolveMethodAndLoadCountersNode(node.getMethod(), klassHint)); 457 insertReplacement(graph, stateMapper, node, replacement); 458 node.replaceAtUsages(replacement, n -> !(n instanceof ResolveMethodAndLoadCountersNode)); 459 } 460 461 /** 462 * Replace {@link LoadMethodCountersNode} with {@link ResolveMethodAndLoadCountersNode}, expose 463 * klass constants. 464 * 465 * @param graph 466 * @param stateMapper 467 * @param context 468 */ 469 private static void replaceLoadMethodCounters(StructuredGraph graph, FrameStateMapperClosure stateMapper, CoreProviders context) { 470 new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, true).apply(graph, false); 471 472 for (LoadMethodCountersNode node : getLoadMethodCountersNodes(graph)) { 473 if (anyUsagesNeedReplacement(node)) { 474 handleLoadMethodCounters(graph, stateMapper, node, context); 475 } 476 } 477 } 478 479 /** 480 * Replace object and klass constants with resolution nodes or reuse preceding initializations. 481 * 482 * @param graph 483 * @param stateMapper 484 */ 485 private void replaceKlassesAndObjects(StructuredGraph graph, FrameStateMapperClosure stateMapper, ClassInfo classInfo) { 486 new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, true).apply(graph, false); 487 488 for (ConstantNode node : getConstantNodes(graph)) { 489 Constant constant = node.asConstant(); 490 if (constant instanceof HotSpotMetaspaceConstant && anyUsagesNeedReplacement(node)) { 491 handleHotSpotMetaspaceConstant(graph, stateMapper, node, classInfo); 492 } else if (constant instanceof HotSpotObjectConstant && anyUsagesNeedReplacement(node)) { 493 handleHotSpotObjectConstant(graph, stateMapper, node, classInfo); 494 } 495 } 496 } 497 498 @Override 499 protected void run(StructuredGraph graph, CoreProviders context) { 500 FrameStateMapperClosure stateMapper = new FrameStateMapperClosure(graph); 501 ReentrantNodeIterator.apply(stateMapper, graph.start(), null); 502 503 // Replace LoadMethodCountersNode with ResolveMethodAndLoadCountersNode, expose klass 504 // constants. 505 replaceLoadMethodCounters(graph, stateMapper, context); 506 507 // Replace object and klass constants (including the ones added in the previous pass) with 508 // resolution nodes. 509 replaceKlassesAndObjects(graph, stateMapper, new ClassInfo(context.getMetaAccess())); 510 } 511 512 @Override 513 public boolean checkContract() { 514 return false; 515 } 516 517 public ReplaceConstantNodesPhase() { 518 this(true); 519 } 520 521 public ReplaceConstantNodesPhase(boolean verifyFingerprints) { 522 this.verifyFingerprints = verifyFingerprints; 523 } 524 }