1 /* 2 * Copyright (c) 2011, 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.phases; 26 27 import java.util.regex.Pattern; 28 29 import org.graalvm.compiler.debug.CounterKey; 30 import org.graalvm.compiler.debug.DebugCloseable; 31 import org.graalvm.compiler.debug.DebugContext; 32 import org.graalvm.compiler.debug.DebugOptions; 33 import org.graalvm.compiler.debug.MemUseTrackerKey; 34 import org.graalvm.compiler.debug.TimerKey; 35 import org.graalvm.compiler.graph.Graph; 36 import org.graalvm.compiler.graph.Graph.Mark; 37 import org.graalvm.compiler.graph.Graph.NodeEvent; 38 import org.graalvm.compiler.graph.Graph.NodeEventListener; 39 import org.graalvm.compiler.graph.Graph.NodeEventScope; 40 import org.graalvm.compiler.graph.Node; 41 import org.graalvm.compiler.nodes.StructuredGraph; 42 import org.graalvm.compiler.options.Option; 43 import org.graalvm.compiler.options.OptionKey; 44 import org.graalvm.compiler.options.OptionType; 45 import org.graalvm.compiler.options.OptionValues; 46 import org.graalvm.compiler.phases.contract.NodeCostUtil; 47 import org.graalvm.compiler.phases.contract.PhaseSizeContract; 48 49 /** 50 * Base class for all compiler phases. Subclasses should be stateless. There will be one global 51 * instance for each compiler phase that is shared for all compilations. VM-, target- and 52 * compilation-specific data can be passed with a context object. 53 */ 54 public abstract class BasePhase<C> implements PhaseSizeContract { 55 56 public static class PhaseOptions { 57 // @formatter:off 58 @Option(help = "Verify before - after relation of the relative, computed, code size of a graph", type = OptionType.Debug) 59 public static final OptionKey<Boolean> VerifyGraalPhasesSize = new OptionKey<>(false); 60 // @formatter:on 61 } 62 63 /** 64 * Records time spent in {@link #apply(StructuredGraph, Object, boolean)}. 65 */ 66 private final TimerKey timer; 67 68 /** 69 * Counts calls to {@link #apply(StructuredGraph, Object, boolean)}. 70 */ 71 private final CounterKey executionCount; 72 73 /** 74 * Accumulates the {@linkplain Graph#getNodeCount() live node count} of all graphs sent to 75 * {@link #apply(StructuredGraph, Object, boolean)}. 76 */ 77 private final CounterKey inputNodesCount; 78 79 /** 80 * Records memory usage within {@link #apply(StructuredGraph, Object, boolean)}. 81 */ 82 private final MemUseTrackerKey memUseTracker; 83 84 /** Lazy initialization to create pattern only when assertions are enabled. */ 85 static class NamePatternHolder { 86 static final Pattern NAME_PATTERN = Pattern.compile("[A-Z][A-Za-z0-9]+"); 87 } 88 89 public static class BasePhaseStatistics { 90 /** 91 * Records time spent in {@link BasePhase#apply(StructuredGraph, Object, boolean)}. 92 */ 93 private final TimerKey timer; 94 95 /** 96 * Counts calls to {@link BasePhase#apply(StructuredGraph, Object, boolean)}. 97 */ 98 private final CounterKey executionCount; 99 100 /** 101 * Accumulates the {@linkplain Graph#getNodeCount() live node count} of all graphs sent to 102 * {@link BasePhase#apply(StructuredGraph, Object, boolean)}. 103 */ 104 private final CounterKey inputNodesCount; 105 106 /** 107 * Records memory usage within {@link BasePhase#apply(StructuredGraph, Object, boolean)}. 108 */ 109 private final MemUseTrackerKey memUseTracker; 110 BasePhaseStatistics(Class<?> clazz)111 public BasePhaseStatistics(Class<?> clazz) { 112 timer = DebugContext.timer("PhaseTime_%s", clazz).doc("Time spent in phase."); 113 executionCount = DebugContext.counter("PhaseCount_%s", clazz).doc("Number of phase executions."); 114 memUseTracker = DebugContext.memUseTracker("PhaseMemUse_%s", clazz).doc("Memory allocated in phase."); 115 inputNodesCount = DebugContext.counter("PhaseNodes_%s", clazz).doc("Number of nodes input to phase."); 116 } 117 } 118 119 private static final ClassValue<BasePhaseStatistics> statisticsClassValue = new ClassValue<BasePhaseStatistics>() { 120 @Override 121 protected BasePhaseStatistics computeValue(Class<?> c) { 122 return new BasePhaseStatistics(c); 123 } 124 }; 125 getBasePhaseStatistics(Class<?> c)126 private static BasePhaseStatistics getBasePhaseStatistics(Class<?> c) { 127 return statisticsClassValue.get(c); 128 } 129 BasePhase()130 protected BasePhase() { 131 BasePhaseStatistics statistics = getBasePhaseStatistics(getClass()); 132 timer = statistics.timer; 133 executionCount = statistics.executionCount; 134 memUseTracker = statistics.memUseTracker; 135 inputNodesCount = statistics.inputNodesCount; 136 } 137 apply(final StructuredGraph graph, final C context)138 public final void apply(final StructuredGraph graph, final C context) { 139 apply(graph, context, true); 140 } 141 getEnclosingPhase(DebugContext debug)142 private BasePhase<?> getEnclosingPhase(DebugContext debug) { 143 for (Object c : debug.context()) { 144 if (c != this && c instanceof BasePhase) { 145 if (!(c instanceof PhaseSuite)) { 146 return (BasePhase<?>) c; 147 } 148 } 149 } 150 return null; 151 } 152 dumpBefore(final StructuredGraph graph, final C context, boolean isTopLevel)153 private boolean dumpBefore(final StructuredGraph graph, final C context, boolean isTopLevel) { 154 DebugContext debug = graph.getDebug(); 155 if (isTopLevel && (debug.isDumpEnabled(DebugContext.VERBOSE_LEVEL) || shouldDumpBeforeAtBasicLevel() && debug.isDumpEnabled(DebugContext.BASIC_LEVEL))) { 156 if (shouldDumpBeforeAtBasicLevel()) { 157 debug.dump(DebugContext.BASIC_LEVEL, graph, "Before phase %s", getName()); 158 } else { 159 debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Before phase %s", getName()); 160 } 161 } else if (!isTopLevel && debug.isDumpEnabled(DebugContext.VERBOSE_LEVEL + 1)) { 162 debug.dump(DebugContext.VERBOSE_LEVEL + 1, graph, "Before subphase %s", getName()); 163 } else if (debug.isDumpEnabled(DebugContext.ENABLED_LEVEL) && shouldDump(graph, context)) { 164 debug.dump(DebugContext.ENABLED_LEVEL, graph, "Before %s %s", isTopLevel ? "phase" : "subphase", getName()); 165 return true; 166 } 167 return false; 168 } 169 shouldDumpBeforeAtBasicLevel()170 protected boolean shouldDumpBeforeAtBasicLevel() { 171 return false; 172 } 173 shouldDumpAfterAtBasicLevel()174 protected boolean shouldDumpAfterAtBasicLevel() { 175 return false; 176 } 177 178 @SuppressWarnings("try") apply(final StructuredGraph graph, final C context, final boolean dumpGraph)179 protected final void apply(final StructuredGraph graph, final C context, final boolean dumpGraph) { 180 graph.checkCancellation(); 181 DebugContext debug = graph.getDebug(); 182 try (DebugCloseable a = timer.start(debug); DebugContext.Scope s = debug.scope(getClass(), this); DebugCloseable c = memUseTracker.start(debug)) { 183 int sizeBefore = 0; 184 Mark before = null; 185 OptionValues options = graph.getOptions(); 186 boolean verifySizeContract = PhaseOptions.VerifyGraalPhasesSize.getValue(options) && checkContract(); 187 if (verifySizeContract) { 188 sizeBefore = NodeCostUtil.computeGraphSize(graph); 189 before = graph.getMark(); 190 } 191 boolean isTopLevel = getEnclosingPhase(graph.getDebug()) == null; 192 boolean dumpedBefore = false; 193 if (dumpGraph && debug.areScopesEnabled()) { 194 dumpedBefore = dumpBefore(graph, context, isTopLevel); 195 } 196 inputNodesCount.add(debug, graph.getNodeCount()); 197 this.run(graph, context); 198 executionCount.increment(debug); 199 if (verifySizeContract) { 200 if (!before.isCurrent()) { 201 int sizeAfter = NodeCostUtil.computeGraphSize(graph); 202 NodeCostUtil.phaseFulfillsSizeContract(graph, sizeBefore, sizeAfter, this); 203 } 204 } 205 206 if (dumpGraph && debug.areScopesEnabled()) { 207 dumpAfter(graph, isTopLevel, dumpedBefore); 208 } 209 if (debug.isVerifyEnabled()) { 210 debug.verify(graph, "%s", getName()); 211 } 212 assert graph.verify(); 213 } catch (Throwable t) { 214 throw debug.handle(t); 215 } 216 } 217 dumpAfter(final StructuredGraph graph, boolean isTopLevel, boolean dumpedBefore)218 private void dumpAfter(final StructuredGraph graph, boolean isTopLevel, boolean dumpedBefore) { 219 boolean dumped = false; 220 DebugContext debug = graph.getDebug(); 221 if (isTopLevel) { 222 if (shouldDumpAfterAtBasicLevel()) { 223 if (debug.isDumpEnabled(DebugContext.BASIC_LEVEL)) { 224 debug.dump(DebugContext.BASIC_LEVEL, graph, "After phase %s", getName()); 225 dumped = true; 226 } 227 } else { 228 if (debug.isDumpEnabled(DebugContext.INFO_LEVEL)) { 229 debug.dump(DebugContext.INFO_LEVEL, graph, "After phase %s", getName()); 230 dumped = true; 231 } 232 } 233 } else { 234 if (debug.isDumpEnabled(DebugContext.INFO_LEVEL + 1)) { 235 debug.dump(DebugContext.INFO_LEVEL + 1, graph, "After subphase %s", getName()); 236 dumped = true; 237 } 238 } 239 if (!dumped && debug.isDumpEnabled(DebugContext.ENABLED_LEVEL) && dumpedBefore) { 240 debug.dump(DebugContext.ENABLED_LEVEL, graph, "After %s %s", isTopLevel ? "phase" : "subphase", getName()); 241 } 242 } 243 244 @SuppressWarnings("try") shouldDump(StructuredGraph graph, C context)245 private boolean shouldDump(StructuredGraph graph, C context) { 246 DebugContext debug = graph.getDebug(); 247 String phaseChange = DebugOptions.DumpOnPhaseChange.getValue(graph.getOptions()); 248 if (phaseChange != null && Pattern.matches(phaseChange, getClass().getSimpleName())) { 249 StructuredGraph graphCopy = (StructuredGraph) graph.copy(graph.getDebug()); 250 GraphChangeListener listener = new GraphChangeListener(graphCopy); 251 try (NodeEventScope s = graphCopy.trackNodeEvents(listener)) { 252 try (DebugContext.Scope s2 = debug.sandbox("GraphChangeListener", null)) { 253 run(graphCopy, context); 254 } catch (Throwable t) { 255 debug.handle(t); 256 } 257 } 258 return listener.changed; 259 } 260 return false; 261 } 262 263 private static final class GraphChangeListener extends NodeEventListener { 264 boolean changed; 265 private StructuredGraph graph; 266 private Mark mark; 267 GraphChangeListener(StructuredGraph graphCopy)268 GraphChangeListener(StructuredGraph graphCopy) { 269 this.graph = graphCopy; 270 this.mark = graph.getMark(); 271 } 272 273 @Override changed(NodeEvent e, Node node)274 public void changed(NodeEvent e, Node node) { 275 if (!graph.isNew(mark, node) && node.isAlive()) { 276 if (e == NodeEvent.INPUT_CHANGED || e == NodeEvent.ZERO_USAGES) { 277 changed = true; 278 } 279 } 280 } 281 } 282 getName()283 protected CharSequence getName() { 284 return new ClassTypeSequence(BasePhase.this.getClass()); 285 } 286 run(StructuredGraph graph, C context)287 protected abstract void run(StructuredGraph graph, C context); 288 289 @Override contractorName()290 public String contractorName() { 291 return getName().toString(); 292 } 293 294 @Override codeSizeIncrease()295 public float codeSizeIncrease() { 296 return 1.25f; 297 } 298 } 299