/*
* This file is part of JOP, the Java Optimized Processor
* see <http://www.jopdesign.com/>
*
* Copyright (C) 2011, Stefan Hepp (stefan@stefant.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.jcopter.analysis;
import com.jopdesign.common.AppInfo;
import com.jopdesign.common.MethodInfo;
import com.jopdesign.common.code.CallGraph;
import com.jopdesign.common.code.CallGraph.ContextEdge;
import com.jopdesign.common.code.ControlFlowGraph;
import com.jopdesign.common.code.ControlFlowGraph.BasicBlockNode;
import com.jopdesign.common.code.ControlFlowGraph.CFGEdge;
import com.jopdesign.common.code.ControlFlowGraph.CFGNode;
import com.jopdesign.common.code.ExecutionContext;
import com.jopdesign.common.code.InvokeSite;
import com.jopdesign.common.code.LoopBound;
import com.jopdesign.common.graphutils.BackEdgeFinder;
import com.jopdesign.common.graphutils.EdgeProvider;
import com.jopdesign.common.graphutils.GraphUtils;
import com.jopdesign.common.graphutils.LoopColoring;
import com.jopdesign.jcopter.JCopter;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.log4j.Logger;
import org.jgrapht.DirectedGraph;
import org.jgrapht.traverse.TopologicalOrderIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Stefan Hepp (stefan@stefant.org)
*/
public class ExecFrequencyAnalysis extends ExecFrequencyProvider {
private class InlineEdgeProvider implements EdgeProvider<ExecutionContext,ContextEdge> {
private final Set<InvokeSite> newInvokeSites;
private InlineEdgeProvider(Set<InvokeSite> newInvokeSites) {
this.newInvokeSites = newInvokeSites;
}
@Override
public Collection<ContextEdge> outgoingEdgesOf(ExecutionContext node) {
Collection<ContextEdge> edges = callGraph.getOutgoingEdges(node);
List<ContextEdge> result = new ArrayList<ContextEdge>(edges.size());
for (ContextEdge edge : edges) {
for (InvokeSite is : edge.getTarget().getCallString()) {
if (newInvokeSites.contains(is)) {
result.add(edge);
}
}
}
return result;
}
@Override
public ExecutionContext getEdgeTarget(ContextEdge edge) {
return edge.getTarget();
}
}
private static final Logger logger = Logger.getLogger(JCopter.LOG_ANALYSIS+".ExecFrequencyAnalysis");
private static final int DEFAULT_ACET_LOOP_BOUND = 10;
private final AnalysisManager analyses;
private final Map<ExecutionContext, Long> roots;
private final CallGraph callGraph;
private final Map<ExecutionContext,Long> nodeCount;
private final Set<MethodInfo> changeSet;
////////////////////////////////////////////////////////////////////////////////////
// Construction, initialization
////////////////////////////////////////////////////////////////////////////////////
public ExecFrequencyAnalysis(AnalysisManager analyses, Map<ExecutionContext, Long> roots) {
this.analyses = analyses;
this.roots = new LinkedHashMap<ExecutionContext, Long>(roots);
callGraph = AppInfo.getSingleton().getCallGraph();
nodeCount = new LinkedHashMap<ExecutionContext, Long>();
changeSet = new LinkedHashSet<MethodInfo>(1);
}
public ExecFrequencyAnalysis(AnalysisManager analyses, CallGraph callGraph) {
this.analyses = analyses;
this.callGraph = callGraph;
nodeCount = new LinkedHashMap<ExecutionContext, Long>();
changeSet = new LinkedHashSet<MethodInfo>(1);
roots = new LinkedHashMap<ExecutionContext, Long>(callGraph.getRootNodes().size());
for (ExecutionContext node : callGraph.getRootNodes()) {
roots.put(node, 1L);
}
}
public void initialize() {
nodeCount.clear();
// initialize roots
nodeCount.putAll(roots);
BackEdgeFinder<ExecutionContext,ContextEdge> finder = callGraph.getBackEdgeFinder();
DirectedGraph<ExecutionContext,ContextEdge> dag = finder.createDAG();
updateExecCounts(dag);
}
////////////////////////////////////////////////////////////////////////////////////
// Query the analysis results
////////////////////////////////////////////////////////////////////////////////////
@Override
public long getExecCount(MethodInfo methodInfo) {
long count = 0;
for (ExecutionContext ec : callGraph.getNodes(methodInfo)) {
Long c = nodeCount.get(ec);
count += c;
}
return count;
}
@Override
public long getExecCount(ExecutionContext context) {
Long c = nodeCount.get(context);
if (c != null) {
return c;
}
return super.getExecCount(context);
}
@Override
public long getExecFrequency(MethodInfo method, InstructionHandle ih) {
return getExecFrequency(new ExecutionContext(method), ih);
}
@Override
public long getExecFrequency(InvokeSite invokeSite, MethodInfo invokee) {
// TODO we could check the nodes of the invokee, use only nodes with the invokeSite at the top
// of the callstring, but we would not get a better result than this if we do not improve the
// callgraph results first
return getExecFrequency(invokeSite);
}
public long getExecCount(ExecutionContext context, InstructionHandle ih) {
return getExecCount(context) * getExecFrequency(context, ih);
}
public long getExecFrequency(ExecutionContext context, InstructionHandle ih) {
MethodInfo method = context.getMethodInfo();
// By loading the CFG, loopbounds are attached to the blocks if the WCA tool is loaded
ControlFlowGraph cfg = method.getCode().getControlFlowGraph(false);
LoopColoring<CFGNode,CFGEdge> lc = cfg.getLoopColoring();
BasicBlockNode node = cfg.getHandleNode(ih, true);
if (node == null) {
// Since the CFG does not represent the complete code, there might be some instructions without block
// (exception handlers, ..)
// THIS IS UNSAFE! but what can you do ...
return 1;
}
long ef = 1;
for (CFGNode hol : lc.getLoopColor(node)) {
LoopBound lb = hol.getLoopBound();
if (lb != null) {
if (lb.isDefaultBound() && !analyses.isWCAMethod(method)) {
ef *= DEFAULT_ACET_LOOP_BOUND;
} else {
ef *= lb.getUpperBound(context);
}
} else {
ef *= DEFAULT_ACET_LOOP_BOUND;
}
}
return ef;
}
////////////////////////////////////////////////////////////////////////////////////
// Notify of updates of the underlying callgraph, recalculate
////////////////////////////////////////////////////////////////////////////////////
public void clearChangeSet() {
changeSet.clear();
}
@Override
public Set<MethodInfo> getChangeSet() {
return changeSet;
}
/**
* Update the execution frequencies after inlining.
* This must be called after the underlying callgraph has been updated!
*
* @param invokeSite the inlined invokesite.
* @param invokee the inlined method.
* @param newInvokeSites the set of new invokesites in the invoker
*/
public void inline(InvokeSite invokeSite, MethodInfo invokee, Set<InvokeSite> newInvokeSites) {
List<ExecutionContext> queue = new ArrayList<ExecutionContext>();
for (ExecutionContext context : callGraph.getNodes(invokeSite.getInvoker())) {
for (ExecutionContext child : callGraph.getChildren(context)) {
if (child.getCallString().isEmpty() && child.getMethodInfo().equals(invokee)) {
// there can be at most one such node in the graph.. remove the total exec count of the
// inlined invokesite
nodeCount.put(child, nodeCount.get(child) - getExecCount(invokeSite, invokee));
}
else if (!child.getCallString().isEmpty() && newInvokeSites.contains(child.getCallString().top())) {
// This is a new node, sum up the execution counts of all invokesite instances
addExecCount(child, getExecCount(context, child.getCallString().top().getInstructionHandle()));
queue.add(child);
}
}
}
// update exec frequencies for all new nodes. A node is new if it contains one of the new invoke sites
// We do not need to remove exec frequencies, since all nodes containing the old invokesite are now no
// longer in the callgraph. We could however remove those nodes from our data structures in a
// separate step before the callgraph is updated.
// To do this, we create a temporary subgraph containing all new nodes and no back edges, and then traverse
// it in topological order
// TODO if the callgraph is compressed, we need to look down up to callstringLength for new nodes!
DirectedGraph<ExecutionContext,ContextEdge> dag =
GraphUtils.copyGraph(new InlineEdgeProvider(newInvokeSites), callGraph.getEdgeFactory(), queue, false);
updateExecCounts(dag);
// Despite all that is going on, the only *method* for which something changes in total is the inlined invokee
changeSet.add(invokee);
}
////////////////////////////////////////////////////////////////////////////////////
// Private stuff
////////////////////////////////////////////////////////////////////////////////////
private void addExecCount(ExecutionContext node, long count) {
Long c = nodeCount.get(node);
long val = (c == null) ? 0 : c;
val += count;
nodeCount.put(node, val);
}
private void updateExecCounts(DirectedGraph<ExecutionContext, ContextEdge> dag) {
// For now, we just require the graph to be a DAG.
// TODO for all back-edges, we should find some max recursion count and update reachable nodes accordingly..
// For now, we just assume we have no recursion (backedges are only due to insufficient callgraph thinning)
// or simply ignore recursion (unsafe, of course..)
// for the rest of the graph, we can now use a topological order
TopologicalOrderIterator<ExecutionContext,ContextEdge> topOrder =
new TopologicalOrderIterator<ExecutionContext, ContextEdge>(dag);
while (topOrder.hasNext()) {
ExecutionContext next = topOrder.next();
if (logger.isTraceEnabled()) {
logger.trace("Updating: " + next);
}
updateChilds(next);
}
}
private void updateChilds(ExecutionContext context) {
long ecCount = nodeCount.get(context);
for (Map.Entry<InvokeSite,Set<ExecutionContext>> entry :
callGraph.getChildsPerInvokeSite(context).entrySet())
{
InvokeSite invokeSite = entry.getKey();
long count = ecCount * getExecFrequency(context, invokeSite.getInstructionHandle());
for (ExecutionContext child : entry.getValue()) {
addExecCount(child, count);
}
}
}
}