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()) { | 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 #apply(StructuredGraph, Object, boolean)}. 90 */ 91 private final TimerKey timer; 92 93 /** 94 * Counts calls to {@link #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 #apply(StructuredGraph, Object, boolean)}. 101 */ 102 private final CounterKey inputNodesCount; 103 104 /** 105 * Records memory usage within {@link #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()) { |