GraphChangeMonitoringPhase.java revision 12995:5e441a7ec5e3
1/*
2 * Copyright (c) 2015, 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 */
23package org.graalvm.compiler.core.phases;
24
25import org.graalvm.compiler.debug.Debug;
26import org.graalvm.compiler.debug.Debug.Scope;
27import org.graalvm.compiler.graph.Graph.NodeEvent;
28import org.graalvm.compiler.graph.Graph.NodeEventScope;
29import org.graalvm.compiler.graph.Node;
30import org.graalvm.compiler.nodes.LogicConstantNode;
31import org.graalvm.compiler.nodes.StructuredGraph;
32import org.graalvm.compiler.phases.BasePhase;
33import org.graalvm.compiler.phases.PhaseSuite;
34import org.graalvm.compiler.phases.common.util.HashSetNodeEventListener;
35import org.graalvm.compiler.phases.tiers.PhaseContext;
36import org.graalvm.util.Equivalence;
37import org.graalvm.util.EconomicSet;
38
39/**
40 * A utility phase for detecting when a phase would change the graph and reporting extra information
41 * about the effects. The phase is first run on a copy of the graph and if a change in that graph is
42 * detected then it's rerun on the original graph inside a new debug scope under
43 * GraphChangeMonitoringPhase. The message argument can be used to distinguish between the same
44 * phase run at different points.
45 *
46 * @param <C>
47 */
48public class GraphChangeMonitoringPhase<C extends PhaseContext> extends PhaseSuite<C> {
49
50    private final String message;
51
52    public GraphChangeMonitoringPhase(String message, BasePhase<C> phase) {
53        super();
54        this.message = message;
55        appendPhase(phase);
56    }
57
58    public GraphChangeMonitoringPhase(String message) {
59        super();
60        this.message = message;
61    }
62
63    @Override
64    @SuppressWarnings("try")
65    protected void run(StructuredGraph graph, C context) {
66        /*
67         * Phase may add nodes but not end up using them so ignore additions. Nodes going dead and
68         * having their inputs change are the main interesting differences.
69         */
70        HashSetNodeEventListener listener = new HashSetNodeEventListener().exclude(NodeEvent.NODE_ADDED);
71        StructuredGraph graphCopy = (StructuredGraph) graph.copy();
72        try (NodeEventScope s = graphCopy.trackNodeEvents(listener)) {
73            try (Scope s2 = Debug.sandbox("WithoutMonitoring", null)) {
74                super.run(graphCopy, context);
75            } catch (Throwable t) {
76                Debug.handle(t);
77            }
78        }
79
80        EconomicSet<Node> filteredNodes = EconomicSet.create(Equivalence.IDENTITY);
81        for (Node n : listener.getNodes()) {
82            if (n instanceof LogicConstantNode) {
83                // Ignore LogicConstantNode since those are sometimes created and deleted as part of
84                // running a phase.
85            } else {
86                filteredNodes.add(n);
87            }
88        }
89        if (!filteredNodes.isEmpty()) {
90            /* rerun it on the real graph in a new Debug scope so Dump and Log can find it. */
91            listener = new HashSetNodeEventListener();
92            try (NodeEventScope s = graph.trackNodeEvents(listener)) {
93                try (Scope s2 = Debug.scope("WithGraphChangeMonitoring")) {
94                    if (Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
95                        Debug.dump(Debug.DETAILED_LEVEL, graph, "*** Before phase %s", getName());
96                    }
97                    super.run(graph, context);
98                    if (Debug.isDumpEnabled(Debug.DETAILED_LEVEL)) {
99                        Debug.dump(Debug.DETAILED_LEVEL, graph, "*** After phase %s %s", getName(), filteredNodes);
100                    }
101                    Debug.log("*** %s %s %s\n", message, graph, filteredNodes);
102                }
103            }
104        } else {
105            // Go ahead and run it normally even though it should have no effect
106            super.run(graph, context);
107        }
108    }
109}
110