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