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  */
  23 package org.graalvm.compiler.phases.common.instrumentation;
  24 
  25 import static org.graalvm.compiler.core.common.CompilationIdentifier.INVALID_COMPILATION_ID;
  26 
  27 import java.util.Collections;
  28 import java.util.Map;
  29 
  30 import org.graalvm.compiler.core.common.type.StampPair;
  31 import org.graalvm.compiler.debug.Debug;
  32 import org.graalvm.compiler.debug.GraalError;
  33 import org.graalvm.compiler.graph.Node;
  34 import org.graalvm.compiler.graph.NodeBitMap;
  35 import org.graalvm.compiler.graph.NodeFlood;
  36 import org.graalvm.compiler.graph.Position;
  37 import org.graalvm.compiler.nodeinfo.InputType;
  38 import org.graalvm.compiler.nodes.AbstractEndNode;
  39 import org.graalvm.compiler.nodes.AbstractLocalNode;
  40 import org.graalvm.compiler.nodes.FixedNode;
  41 import org.graalvm.compiler.nodes.FrameState;
  42 import org.graalvm.compiler.nodes.LoopEndNode;
  43 import org.graalvm.compiler.nodes.ParameterNode;
  44 import org.graalvm.compiler.nodes.ReturnNode;
  45 import org.graalvm.compiler.nodes.StructuredGraph;
  46 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
  47 import org.graalvm.compiler.nodes.ValueNode;
  48 import org.graalvm.compiler.nodes.calc.FloatingNode;
  49 import org.graalvm.compiler.nodes.debug.instrumentation.InstrumentationBeginNode;
  50 import org.graalvm.compiler.nodes.debug.instrumentation.InstrumentationEndNode;
  51 import org.graalvm.compiler.nodes.debug.instrumentation.InstrumentationNode;
  52 import org.graalvm.compiler.nodes.util.GraphUtil;
  53 import org.graalvm.compiler.nodes.virtual.EscapeObjectState;
  54 import org.graalvm.compiler.phases.BasePhase;
  55 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
  56 import org.graalvm.compiler.phases.tiers.HighTierContext;
  57 
  58 /**
  59  * The {@code ExtractInstrumentationPhase} extracts the instrumentation (whose boundary are
  60  * indicated by instrumentationBegin and instrumentationEnd), and insert an
  61  * {@link InstrumentationNode} in the graph to take place of the instrumentation.
  62  */
  63 public class ExtractInstrumentationPhase extends BasePhase<HighTierContext> {
  64 
  65     @Override
  66     protected void run(StructuredGraph graph, HighTierContext context) {
  67         for (InstrumentationBeginNode begin : graph.getNodes().filter(InstrumentationBeginNode.class)) {
  68             Instrumentation instrumentation = new Instrumentation(begin);
  69             if (begin.isAnchored() || begin.getTarget() != null) {
  70                 // we create InstrumentationNode when the instrumentation is anchored (when 0 is
  71                 // passed to instrumentationBegin), or when the instrumentation is associated with
  72                 // some target.
  73                 InstrumentationNode instrumentationNode = graph.addWithoutUnique(new InstrumentationNode(begin.getTarget(), begin.isAnchored()));
  74                 graph.addBeforeFixed(begin, instrumentationNode);
  75                 FrameState currentState = begin.stateAfter();
  76                 FrameState newState = graph.addWithoutUnique(new FrameState(currentState.outerFrameState(), currentState.getCode(), currentState.bci, 0, 0,
  77                                 0, currentState.rethrowException(), currentState.duringCall(), null,
  78                                 Collections.<EscapeObjectState> emptyList()));
  79                 instrumentationNode.setStateBefore(newState);
  80 
  81                 StructuredGraph instrumentationGraph = instrumentation.genInstrumentationGraph(graph, instrumentationNode);
  82                 new DeadCodeEliminationPhase().apply(instrumentationGraph, false);
  83                 instrumentationNode.setInstrumentationGraph(instrumentationGraph);
  84                 Debug.dump(Debug.INFO_LOG_LEVEL, instrumentationGraph, "After extracted instrumentation at %s", instrumentation);
  85             }
  86             instrumentation.unlink();
  87         }
  88     }
  89 
  90     /**
  91      * This class denotes the instrumentation code being detached from the graph.
  92      */
  93     private static class Instrumentation {
  94 
  95         private InstrumentationBeginNode begin;
  96         private InstrumentationEndNode end;
  97         private NodeBitMap nodes;
  98 
  99         Instrumentation(InstrumentationBeginNode begin) {
 100             this.begin = begin;
 101 
 102             // travel along the control flow for the paired InstrumentationEndNode
 103             NodeFlood cfgFlood = begin.graph().createNodeFlood();
 104             cfgFlood.add(begin.next());
 105             for (Node current : cfgFlood) {
 106                 if (current instanceof InstrumentationEndNode) {
 107                     this.end = (InstrumentationEndNode) current;
 108                 } else if (current instanceof LoopEndNode) {
 109                     // do nothing
 110                 } else if (current instanceof AbstractEndNode) {
 111                     cfgFlood.add(((AbstractEndNode) current).merge());
 112                 } else {
 113                     cfgFlood.addAll(current.successors());
 114                 }
 115             }
 116 
 117             if (this.end == null) {
 118                 // this may be caused by DeoptimizationReason.Unresolved
 119                 throw GraalError.shouldNotReachHere("could not find invocation to instrumentationEnd()");
 120             }
 121 
 122             // all FloatingNodes (except AbstractLocalNodes), which the FixedNodes in the
 123             // instrumentation depend on, are included in the instrumentation if they are not used
 124             // by other nodes in the graph
 125             NodeBitMap cfgNodes = cfgFlood.getVisited();
 126             NodeFlood dfgFlood = begin.graph().createNodeFlood();
 127             dfgFlood.addAll(cfgNodes);
 128             dfgFlood.add(begin.stateAfter());
 129             for (Node current : dfgFlood) {
 130                 for (Position pos : current.inputPositions()) {
 131                     Node input = pos.get(current);
 132                     if (pos.getInputType() == InputType.Value) {
 133                         if (current instanceof FrameState) {
 134                             // don't include value input for the FrameState
 135                             continue;
 136                         }
 137                         if (!(input instanceof FloatingNode)) {
 138                             // we only consider FloatingNode for this input type
 139                             continue;
 140                         }
 141                         if (input instanceof AbstractLocalNode) {
 142                             // AbstractLocalNode is invalid in the instrumentation sub-graph
 143                             continue;
 144                         }
 145                         if (shouldIncludeValueInput((FloatingNode) input, cfgNodes)) {
 146                             dfgFlood.add(input);
 147                         }
 148                     } else {
 149                         dfgFlood.add(input);
 150                     }
 151                 }
 152             }
 153             this.nodes = dfgFlood.getVisited();
 154         }
 155 
 156         /**
 157          * Copy the instrumentation nodes into a separate graph. During the copying, this method
 158          * updates the input of the given InstrumentationNode. Hence, it is essential that the given
 159          * InstrumentationNode is alive.
 160          */
 161         StructuredGraph genInstrumentationGraph(StructuredGraph oldGraph, InstrumentationNode instrumentationNode) {
 162             StructuredGraph instrumentationGraph = new StructuredGraph(AllowAssumptions.YES, INVALID_COMPILATION_ID);
 163             Map<Node, Node> replacements = Node.newMap();
 164             int index = 0; // for ParameterNode index
 165             for (Node current : nodes) {
 166                 // mark any input that is not included in the instrumentation a weak dependency
 167                 for (Node input : current.inputs()) {
 168                     if (input instanceof ValueNode) {
 169                         ValueNode valueNode = (ValueNode) input;
 170                         if (!nodes.isMarked(input) && !replacements.containsKey(input)) {
 171                             // create a ParameterNode in case the input is not within the
 172                             // instrumentation
 173                             ParameterNode parameter = new ParameterNode(index++, StampPair.createSingle(valueNode.stamp()));
 174                             instrumentationGraph.addWithoutUnique(parameter);
 175                             instrumentationNode.addWeakDependency(valueNode);
 176                             replacements.put(input, parameter);
 177                         }
 178                     }
 179                 }
 180             }
 181             replacements = instrumentationGraph.addDuplicates(nodes, oldGraph, nodes.count(), replacements);
 182             instrumentationGraph.start().setNext((FixedNode) replacements.get(begin.next()));
 183             instrumentationGraph.start().setStateAfter((FrameState) replacements.get(begin.stateAfter()));
 184             replacements.get(end).replaceAtPredecessor(instrumentationGraph.addWithoutUnique(new ReturnNode(null)));
 185             return instrumentationGraph;
 186         }
 187 
 188         /**
 189          * @return true if the given FloatingNode does not contain any FixedNode input of types
 190          *         other than InputType.Value.
 191          */
 192         private static boolean shouldIncludeValueInput(FloatingNode node, NodeBitMap cfgNodes) {
 193             for (Position pos : node.inputPositions()) {
 194                 if (pos.getInputType() == InputType.Value) {
 195                     continue;
 196                 }
 197                 Node input = pos.get(node);
 198                 if (input instanceof FixedNode && !cfgNodes.isMarked(input)) {
 199                     return false;
 200                 }
 201             }
 202             return true;
 203         }
 204 
 205         void unlink() {
 206             FixedNode next = end.next();
 207             end.setNext(null);
 208             begin.replaceAtPredecessor(next);
 209             GraphUtil.killCFG(begin);
 210         }
 211 
 212     }
 213 
 214     @Override
 215     public boolean checkContract() {
 216         return false;
 217     }
 218 
 219 }