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 }