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