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