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 extends 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 changed(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 }