blob: e0fe3c71713634270836df303cbd7dc1958969e1 [file] [log] [blame]
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.hotspot.phases.profiling;
import static org.graalvm.compiler.hotspot.nodes.profiling.ProfileInvokeNode.getProfileInvokeNodes;
import static org.graalvm.compiler.hotspot.nodes.profiling.ProfileNode.getProfileNodes;
import java.util.HashMap;
import java.util.Map;
import org.graalvm.compiler.core.common.cfg.Loop;
import org.graalvm.compiler.hotspot.nodes.profiling.ProfileInvokeNode;
import org.graalvm.compiler.hotspot.nodes.profiling.ProfileNode;
import org.graalvm.compiler.hotspot.nodes.profiling.RandomSeedNode;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.InvokeNode;
import org.graalvm.compiler.nodes.LoopBeginNode;
import org.graalvm.compiler.nodes.PhiNode;
import org.graalvm.compiler.nodes.ValuePhiNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.calc.AddNode;
import org.graalvm.compiler.nodes.calc.MulNode;
import org.graalvm.compiler.nodes.cfg.Block;
import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
import org.graalvm.compiler.nodes.util.GraphUtil;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.options.OptionType;
import org.graalvm.compiler.options.OptionValue;
import org.graalvm.compiler.phases.BasePhase;
import org.graalvm.compiler.phases.tiers.PhaseContext;
import jdk.vm.ci.meta.ResolvedJavaMethod;
public class FinalizeProfileNodesPhase extends BasePhase<PhaseContext> {
private int inlineeInvokeNotificationFreqLog;
public static class Options {
@Option(help = "Profile simple methods", type = OptionType.Expert)//
public static final OptionValue<Boolean> ProfileSimpleMethods = new OptionValue<>(true);
@Option(help = "Maximum number of nodes in a graph for a simple method", type = OptionType.Expert)//
public static final OptionValue<Integer> SimpleMethodGraphSize = new OptionValue<>(256);
@Option(help = "Maximum number of calls in a simple method", type = OptionType.Expert)//
public static final OptionValue<Integer> SimpleMethodCalls = new OptionValue<>(1);
@Option(help = "Maximum number of indirect calls in a simple moethod", type = OptionType.Expert)//
public static final OptionValue<Integer> SimpleMethodIndirectCalls = new OptionValue<>(0);
}
public FinalizeProfileNodesPhase(int inlineeInvokeNotificationFreqLog) {
this.inlineeInvokeNotificationFreqLog = inlineeInvokeNotificationFreqLog;
}
private static void removeAllProfilingNodes(StructuredGraph graph) {
getProfileNodes(graph).forEach((n) -> GraphUtil.removeFixedWithUnusedInputs(n));
}
private void assignInlineeInvokeFrequencies(StructuredGraph graph) {
for (ProfileInvokeNode node : getProfileInvokeNodes(graph)) {
ResolvedJavaMethod profiledMethod = node.getProfiledMethod();
if (!profiledMethod.equals(graph.method())) {
// Some inlinee, reassign the inlinee frequency
node.setNotificationFreqLog(inlineeInvokeNotificationFreqLog);
}
}
}
// Hacky heuristic to determine whether we want any profiling in this method.
// The heuristic is applied after the graph is fully formed and before the first lowering.
private static boolean simpleMethodHeuristic(StructuredGraph graph) {
if (Options.ProfileSimpleMethods.getValue()) {
return false;
}
// Check if the graph is smallish..
if (graph.getNodeCount() > Options.SimpleMethodGraphSize.getValue()) {
return false;
}
// Check if method has loops
if (graph.hasLoops()) {
return false;
}
// Check if method has calls
if (graph.getNodes().filter(InvokeNode.class).count() > Options.SimpleMethodCalls.getValue()) {
return false;
}
// Check if method has calls that need profiling
if (graph.getNodes().filter(InvokeNode.class).filter((n) -> ((InvokeNode) n).getInvokeKind().isIndirect()).count() > Options.SimpleMethodIndirectCalls.getDefaultValue()) {
return false;
}
return true;
}
private static void assignRandomSources(StructuredGraph graph) {
ValueNode seed = graph.unique(new RandomSeedNode());
ControlFlowGraph cfg = ControlFlowGraph.compute(graph, false, true, false, false);
Map<LoopBeginNode, ValueNode> loopRandomValueCache = new HashMap<>();
for (ProfileNode node : getProfileNodes(graph)) {
ValueNode random;
Block block = cfg.blockFor(node);
Loop<Block> loop = block.getLoop();
// Inject <a href=https://en.wikipedia.org/wiki/Linear_congruential_generator>LCG</a>
// pseudo-random number generator into the loop
if (loop != null) {
LoopBeginNode loopBegin = (LoopBeginNode) loop.getHeader().getBeginNode();
random = loopRandomValueCache.get(loopBegin);
if (random == null) {
PhiNode phi = graph.addWithoutUnique(new ValuePhiNode(seed.stamp(), loopBegin));
phi.addInput(seed);
// X_{n+1} = a*X_n + c, using glibc-like constants
ValueNode a = ConstantNode.forInt(1103515245, graph);
ValueNode c = ConstantNode.forInt(12345, graph);
ValueNode next = graph.addOrUniqueWithInputs(new AddNode(c, new MulNode(phi, a)));
for (int i = 0; i < loopBegin.getLoopEndCount(); i++) {
phi.addInput(next);
}
random = phi;
loopRandomValueCache.put(loopBegin, random);
}
} else {
// Graal doesn't compile methods with irreducible loops. So all profile nodes that
// are not in a loop are guaranteed to be executed at most once. We feed the seed
// value to such nodes directly.
random = seed;
}
node.setRandom(random);
}
}
@Override
protected void run(StructuredGraph graph, PhaseContext context) {
if (simpleMethodHeuristic(graph)) {
removeAllProfilingNodes(graph);
return;
}
assignInlineeInvokeFrequencies(graph);
if (ProfileNode.Options.ProbabilisticProfiling.getValue()) {
assignRandomSources(graph);
}
}
@Override
public boolean checkContract() {
return false;
}
}