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             tryToReplaceWithExisting(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 node doing the resolution 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 tryToReplaceWithExisting(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> blockToExisting = EconomicMap.create();
 187         for (Node n : node.usages().filter(n -> isReplacementNode(n))) {
 188             blockToExisting.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             Node e = blockToExisting.get(b);
 194             if (e != null) {
 195                 // There is an initialization or resolution in the same block as the use, look if
 196                 // the use is 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(e)) {
 203                         use.replaceFirstInput(node, e);
 204                         replaced = true;
 205                         break;
 206                     }
 207                 }
 208             }
 209             if (!replaced) {
 210                 // Look for dominating blocks that have existing nodes
 211                 for (Block d : blockToExisting.getKeys()) {
 212                     if (strictlyDominates(d, b)) {
 213                         use.replaceFirstInput(node, blockToExisting.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 }