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.DebugContext;
26import org.graalvm.compiler.graph.Graph.NodeEvent;
27import org.graalvm.compiler.graph.Graph.NodeEventScope;
28import org.graalvm.compiler.graph.Node;
29import org.graalvm.compiler.nodes.LogicConstantNode;
30import org.graalvm.compiler.nodes.StructuredGraph;
31import org.graalvm.compiler.phases.BasePhase;
32import org.graalvm.compiler.phases.PhaseSuite;
33import org.graalvm.compiler.phases.common.util.HashSetNodeEventListener;
34import org.graalvm.compiler.phases.tiers.PhaseContext;
35import org.graalvm.util.EconomicSet;
36import org.graalvm.util.Equivalence;
37
38/**
39 * A utility phase for detecting when a phase would change the graph and reporting extra information
40 * about the effects. The phase is first run on a copy of the graph and if a change in that graph is
41 * detected then it's rerun on the original graph inside a new debug scope under
42 * GraphChangeMonitoringPhase. The message argument can be used to distinguish between the same
43 * phase run at different points.
44 *
45 * @param <C>
46 */
47public class GraphChangeMonitoringPhase<C extends PhaseContext> extends PhaseSuite<C> {
48
49    private final String message;
50
51    public GraphChangeMonitoringPhase(String message, BasePhase<C> phase) {
52        super();
53        this.message = message;
54        appendPhase(phase);
55    }
56
57    public GraphChangeMonitoringPhase(String message) {
58        super();
59        this.message = message;
60    }
61
62    @Override
63    @SuppressWarnings("try")
64    protected void run(StructuredGraph graph, C context) {
65        /*
66         * Phase may add nodes but not end up using them so ignore additions. Nodes going dead and
67         * having their inputs change are the main interesting differences.
68         */
69        HashSetNodeEventListener listener = new HashSetNodeEventListener().exclude(NodeEvent.NODE_ADDED);
70        StructuredGraph graphCopy = (StructuredGraph) graph.copy(graph.getDebug());
71        DebugContext debug = graph.getDebug();
72        try (NodeEventScope s = graphCopy.trackNodeEvents(listener)) {
73            try (DebugContext.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 (DebugContext.Scope s2 = debug.scope("WithGraphChangeMonitoring")) {
94                    if (debug.isDumpEnabled(DebugContext.DETAILED_LEVEL)) {
95                        debug.dump(DebugContext.DETAILED_LEVEL, graph, "*** Before phase %s", getName());
96                    }
97                    super.run(graph, context);
98                    if (debug.isDumpEnabled(DebugContext.DETAILED_LEVEL)) {
99                        debug.dump(DebugContext.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