1 /*
   2  * Copyright (c) 2012, 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.phases.common;
  26 
  27 import java.util.Arrays;
  28 import java.util.Collection;
  29 import java.util.HashSet;
  30 
  31 import org.graalvm.compiler.core.common.cfg.Loop;
  32 import org.graalvm.compiler.graph.Node;
  33 import org.graalvm.compiler.nodes.AbstractBeginNode;
  34 import org.graalvm.compiler.nodes.AbstractEndNode;
  35 import org.graalvm.compiler.nodes.AbstractMergeNode;
  36 import org.graalvm.compiler.nodes.CallTargetNode;
  37 import org.graalvm.compiler.nodes.ConstantNode;
  38 import org.graalvm.compiler.nodes.DeoptimizeNode;
  39 import org.graalvm.compiler.nodes.FixedNode;
  40 import org.graalvm.compiler.nodes.FixedWithNextNode;
  41 import org.graalvm.compiler.nodes.IfNode;
  42 import org.graalvm.compiler.nodes.Invoke;
  43 import org.graalvm.compiler.nodes.LogicNode;
  44 import org.graalvm.compiler.nodes.ParameterNode;
  45 import org.graalvm.compiler.nodes.ReturnNode;
  46 import org.graalvm.compiler.nodes.SafepointNode;
  47 import org.graalvm.compiler.nodes.StructuredGraph;
  48 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
  49 import org.graalvm.compiler.nodes.UnwindNode;
  50 import org.graalvm.compiler.nodes.VirtualState;
  51 import org.graalvm.compiler.nodes.calc.BinaryNode;
  52 import org.graalvm.compiler.nodes.calc.ConvertNode;
  53 import org.graalvm.compiler.nodes.calc.FloatDivNode;
  54 import org.graalvm.compiler.nodes.calc.IntegerDivRemNode;
  55 import org.graalvm.compiler.nodes.calc.MulNode;
  56 import org.graalvm.compiler.nodes.calc.NotNode;
  57 import org.graalvm.compiler.nodes.calc.ReinterpretNode;
  58 import org.graalvm.compiler.nodes.calc.RemNode;
  59 import org.graalvm.compiler.nodes.cfg.Block;
  60 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  61 import org.graalvm.compiler.nodes.debug.DynamicCounterNode;
  62 import org.graalvm.compiler.nodes.extended.SwitchNode;
  63 import org.graalvm.compiler.nodes.java.AbstractNewObjectNode;
  64 import org.graalvm.compiler.nodes.java.AccessMonitorNode;
  65 import org.graalvm.compiler.nodes.java.MonitorIdNode;
  66 import org.graalvm.compiler.nodes.memory.Access;
  67 import org.graalvm.compiler.nodes.spi.ValueProxy;
  68 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
  69 import org.graalvm.compiler.phases.Phase;
  70 import org.graalvm.compiler.phases.schedule.SchedulePhase;
  71 
  72 import jdk.vm.ci.services.Services;
  73 
  74 /**
  75  * This phase add counters for the dynamically executed number of nodes. Incrementing the counter
  76  * for each node would be too costly, so this phase takes the compromise that it trusts split
  77  * probabilities, but not loop frequencies. This means that it will insert counters at the start of
  78  * a method and at each loop header.
  79  *
  80  * A schedule is created so that floating nodes can also be taken into account. The weight of a node
  81  * is determined heuristically in the {@link ProfileCompiledMethodsPhase#getNodeWeight(Node)}
  82  * method.
  83  *
  84  * Additionally, there's a second counter that's only increased for code sections without invokes.
  85  */
  86 public class ProfileCompiledMethodsPhase extends Phase {
  87 
  88     private static final String GROUP_NAME = "~profiled weight";
  89     private static final String GROUP_NAME_WITHOUT = "~profiled weight (invoke-free sections)";
  90     private static final String GROUP_NAME_INVOKES = "~profiled invokes";
  91 
  92     private static String getProperty(String name, String def) {
  93         String value = Services.getSavedProperties().get(name);
  94         if (value == null) {
  95             return def;
  96         }
  97         return value;
  98     }
  99 
 100     private static final boolean WITH_SECTION_HEADER = Boolean.parseBoolean(getProperty("ProfileCompiledMethodsPhase.WITH_SECTION_HEADER", "false"));
 101     private static final boolean WITH_INVOKE_FREE_SECTIONS = Boolean.parseBoolean(getProperty("ProfileCompiledMethodsPhase.WITH_FREE_SECTIONS", "false"));
 102     private static final boolean WITH_INVOKES = Boolean.parseBoolean(getProperty("ProfileCompiledMethodsPhase.WITH_INVOKES", "true"));
 103 
 104     @Override
 105     protected void run(StructuredGraph graph) {
 106         SchedulePhase schedule = new SchedulePhase(graph.getOptions());
 107         schedule.apply(graph, false);
 108 
 109         ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, true, true);
 110         for (Loop<Block> loop : cfg.getLoops()) {
 111             double loopProbability = cfg.blockFor(loop.getHeader().getBeginNode()).getRelativeFrequency();
 112             if (loopProbability > (1D / Integer.MAX_VALUE)) {
 113                 addSectionCounters(loop.getHeader().getBeginNode(), loop.getBlocks(), loop.getChildren(), graph.getLastSchedule(), cfg);
 114             }
 115         }
 116         // don't put the counter increase directly after the start (problems with OSR)
 117         FixedWithNextNode current = graph.start();
 118         while (current.next() instanceof FixedWithNextNode) {
 119             current = (FixedWithNextNode) current.next();
 120         }
 121         addSectionCounters(current, Arrays.asList(cfg.getBlocks()), cfg.getLoops(), graph.getLastSchedule(), cfg);
 122 
 123         if (WITH_INVOKES) {
 124             for (Node node : graph.getNodes()) {
 125                 if (node instanceof Invoke) {
 126                     Invoke invoke = (Invoke) node;
 127                     DynamicCounterNode.addCounterBefore(GROUP_NAME_INVOKES, invoke.callTarget().targetName(), 1, true, invoke.asNode());
 128 
 129                 }
 130             }
 131         }
 132     }
 133 
 134     private static void addSectionCounters(FixedWithNextNode start, Collection<Block> sectionBlocks, Collection<Loop<Block>> childLoops, ScheduleResult schedule, ControlFlowGraph cfg) {
 135         HashSet<Block> blocks = new HashSet<>(sectionBlocks);
 136         for (Loop<Block> loop : childLoops) {
 137             blocks.removeAll(loop.getBlocks());
 138         }
 139         long increment = DynamicCounterNode.clampIncrement((long) (getSectionWeight(schedule, blocks) / cfg.blockFor(start).getRelativeFrequency()));
 140         DynamicCounterNode.addCounterBefore(GROUP_NAME, sectionHead(start), increment, true, start.next());
 141         if (WITH_INVOKE_FREE_SECTIONS && !hasInvoke(blocks)) {
 142             DynamicCounterNode.addCounterBefore(GROUP_NAME_WITHOUT, sectionHead(start), increment, true, start.next());
 143         }
 144     }
 145 
 146     private static String sectionHead(Node node) {
 147         if (WITH_SECTION_HEADER) {
 148             return node.toString();
 149         } else {
 150             return "";
 151         }
 152     }
 153 
 154     private static double getSectionWeight(ScheduleResult schedule, Collection<Block> blocks) {
 155         double count = 0;
 156         for (Block block : blocks) {
 157             double blockProbability = block.getRelativeFrequency();
 158             for (Node node : schedule.getBlockToNodesMap().get(block)) {
 159                 count += blockProbability * getNodeWeight(node);
 160             }
 161         }
 162         return count;
 163     }
 164 
 165     private static double getNodeWeight(Node node) {
 166         if (node instanceof AbstractMergeNode) {
 167             return ((AbstractMergeNode) node).phiPredecessorCount();
 168         } else if (node instanceof AbstractBeginNode || node instanceof AbstractEndNode || node instanceof MonitorIdNode || node instanceof ConstantNode || node instanceof ParameterNode ||
 169                         node instanceof CallTargetNode || node instanceof ValueProxy || node instanceof VirtualObjectNode || node instanceof ReinterpretNode) {
 170             return 0;
 171         } else if (node instanceof AccessMonitorNode) {
 172             return 10;
 173         } else if (node instanceof Access) {
 174             return 2;
 175         } else if (node instanceof LogicNode || node instanceof ConvertNode || node instanceof NotNode) {
 176             return 1;
 177         } else if (node instanceof IntegerDivRemNode || node instanceof FloatDivNode || node instanceof RemNode) {
 178             return 10;
 179         } else if (node instanceof MulNode) {
 180             return 3;
 181         } else if (node instanceof Invoke) {
 182             return 5;
 183         } else if (node instanceof IfNode || node instanceof SafepointNode || node instanceof BinaryNode) {
 184             return 1;
 185         } else if (node instanceof SwitchNode) {
 186             return node.successors().count();
 187         } else if (node instanceof ReturnNode || node instanceof UnwindNode || node instanceof DeoptimizeNode) {
 188             return node.successors().count();
 189         } else if (node instanceof AbstractNewObjectNode) {
 190             return 10;
 191         } else if (node instanceof VirtualState) {
 192             return 0;
 193         }
 194         return 2;
 195     }
 196 
 197     private static boolean hasInvoke(Collection<Block> blocks) {
 198         boolean hasInvoke = false;
 199         for (Block block : blocks) {
 200             for (FixedNode fixed : block.getNodes()) {
 201                 if (fixed instanceof Invoke) {
 202                     hasInvoke = true;
 203                 }
 204             }
 205         }
 206         return hasInvoke;
 207     }
 208 
 209     @Override
 210     public boolean checkContract() {
 211         return false;
 212     }
 213 
 214 }