001/*
002 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.phases.common;
024
025import java.util.*;
026
027import com.oracle.graal.compiler.common.cfg.*;
028import com.oracle.graal.graph.*;
029import com.oracle.graal.nodes.*;
030import com.oracle.graal.nodes.calc.*;
031import com.oracle.graal.nodes.cfg.*;
032import com.oracle.graal.nodes.debug.*;
033import com.oracle.graal.nodes.extended.*;
034import com.oracle.graal.nodes.java.*;
035import com.oracle.graal.nodes.memory.*;
036import com.oracle.graal.nodes.spi.*;
037import com.oracle.graal.nodes.virtual.*;
038import com.oracle.graal.phases.*;
039import com.oracle.graal.phases.schedule.*;
040
041/**
042 * This phase add counters for the dynamically executed number of nodes. Incrementing the counter
043 * for each node would be too costly, so this phase takes the compromise that it trusts split
044 * probabilities, but not loop frequencies. This means that it will insert counters at the start of
045 * a method and at each loop header.
046 *
047 * A schedule is created so that floating nodes can also be taken into account. The weight of a node
048 * is determined heuristically in the {@link ProfileCompiledMethodsPhase#getNodeWeight(Node)}
049 * method.
050 *
051 * Additionally, there's a second counter that's only increased for code sections without invokes.
052 */
053public class ProfileCompiledMethodsPhase extends Phase {
054
055    private static final String GROUP_NAME = "~profiled weight";
056    private static final String GROUP_NAME_WITHOUT = "~profiled weight (invoke-free sections)";
057    private static final String GROUP_NAME_INVOKES = "~profiled invokes";
058
059    private static final boolean WITH_SECTION_HEADER = Boolean.parseBoolean(System.getProperty("ProfileCompiledMethodsPhase.WITH_SECTION_HEADER", "false"));
060    private static final boolean WITH_INVOKE_FREE_SECTIONS = Boolean.parseBoolean(System.getProperty("ProfileCompiledMethodsPhase.WITH_FREE_SECTIONS", "false"));
061    private static final boolean WITH_INVOKES = Boolean.parseBoolean(System.getProperty("ProfileCompiledMethodsPhase.WITH_INVOKES", "true"));
062
063    @Override
064    protected void run(StructuredGraph graph) {
065        SchedulePhase schedule = new SchedulePhase();
066        schedule.apply(graph, false);
067
068        ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, true, true);
069        for (Loop<Block> loop : cfg.getLoops()) {
070            double loopProbability = cfg.blockFor(loop.getHeader().getBeginNode()).probability();
071            if (loopProbability > (1D / Integer.MAX_VALUE)) {
072                addSectionCounters(loop.getHeader().getBeginNode(), loop.getBlocks(), loop.getChildren(), schedule, cfg);
073            }
074        }
075        // don't put the counter increase directly after the start (problems with OSR)
076        FixedWithNextNode current = graph.start();
077        while (current.next() instanceof FixedWithNextNode) {
078            current = (FixedWithNextNode) current.next();
079        }
080        addSectionCounters(current, cfg.getBlocks(), cfg.getLoops(), schedule, cfg);
081
082        if (WITH_INVOKES) {
083            for (Node node : graph.getNodes()) {
084                if (node instanceof Invoke) {
085                    Invoke invoke = (Invoke) node;
086                    DynamicCounterNode.addCounterBefore(GROUP_NAME_INVOKES, invoke.callTarget().targetName(), 1, true, invoke.asNode());
087
088                }
089            }
090        }
091    }
092
093    private static void addSectionCounters(FixedWithNextNode start, Collection<Block> sectionBlocks, Collection<Loop<Block>> childLoops, SchedulePhase schedule, ControlFlowGraph cfg) {
094        HashSet<Block> blocks = new HashSet<>(sectionBlocks);
095        for (Loop<?> loop : childLoops) {
096            blocks.removeAll(loop.getBlocks());
097        }
098        double weight = getSectionWeight(schedule, blocks) / cfg.blockFor(start).probability();
099        DynamicCounterNode.addCounterBefore(GROUP_NAME, sectionHead(start), (long) weight, true, start.next());
100        if (WITH_INVOKE_FREE_SECTIONS && !hasInvoke(blocks)) {
101            DynamicCounterNode.addCounterBefore(GROUP_NAME_WITHOUT, sectionHead(start), (long) weight, true, start.next());
102        }
103    }
104
105    private static String sectionHead(Node node) {
106        if (WITH_SECTION_HEADER) {
107            return node.toString();
108        } else {
109            return "";
110        }
111    }
112
113    private static double getSectionWeight(SchedulePhase schedule, Collection<Block> blocks) {
114        double count = 0;
115        for (Block block : blocks) {
116            double blockProbability = block.probability();
117            for (Node node : schedule.getBlockToNodesMap().get(block)) {
118                count += blockProbability * getNodeWeight(node);
119            }
120        }
121        return count;
122    }
123
124    private static double getNodeWeight(Node node) {
125        if (node instanceof AbstractMergeNode) {
126            return ((AbstractMergeNode) node).phiPredecessorCount();
127        } else if (node instanceof AbstractBeginNode || node instanceof AbstractEndNode || node instanceof MonitorIdNode || node instanceof ConstantNode || node instanceof ParameterNode ||
128                        node instanceof CallTargetNode || node instanceof ValueProxy || node instanceof VirtualObjectNode || node instanceof ReinterpretNode) {
129            return 0;
130        } else if (node instanceof AccessMonitorNode) {
131            return 10;
132        } else if (node instanceof Access) {
133            return 2;
134        } else if (node instanceof LogicNode || node instanceof ConvertNode || node instanceof BinaryNode || node instanceof NotNode) {
135            return 1;
136        } else if (node instanceof IntegerDivNode || node instanceof DivNode || node instanceof IntegerRemNode || node instanceof RemNode) {
137            return 10;
138        } else if (node instanceof MulNode) {
139            return 3;
140        } else if (node instanceof Invoke) {
141            return 5;
142        } else if (node instanceof IfNode || node instanceof SafepointNode) {
143            return 1;
144        } else if (node instanceof SwitchNode) {
145            return node.successors().count();
146        } else if (node instanceof ReturnNode || node instanceof UnwindNode || node instanceof DeoptimizeNode) {
147            return node.successors().count();
148        } else if (node instanceof AbstractNewObjectNode) {
149            return 10;
150        } else if (node instanceof VirtualState) {
151            return 0;
152        }
153        return 2;
154    }
155
156    private static boolean hasInvoke(Collection<Block> blocks) {
157        boolean hasInvoke = false;
158        for (Block block : blocks) {
159            for (FixedNode fixed : block.getNodes()) {
160                if (fixed instanceof Invoke) {
161                    hasInvoke = true;
162                }
163            }
164        }
165        return hasInvoke;
166    }
167}