/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2008, Benedikt Huber (benedikt.huber@gmail.com)
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.wcet.analysis;
import com.jopdesign.common.ClassInfo;
import com.jopdesign.common.MethodInfo;
import com.jopdesign.common.code.BasicBlock;
import com.jopdesign.common.code.ControlFlowGraph;
import com.jopdesign.common.code.ControlFlowGraph.BasicBlockNode;
import com.jopdesign.common.code.ControlFlowGraph.CFGNode;
import com.jopdesign.common.code.ControlFlowGraph.InvokeNode;
import com.jopdesign.wcet.WCETProcessorModel;
import com.jopdesign.wcet.WCETTool;
import com.jopdesign.wcet.analysis.cache.MethodCacheAnalysis;
import com.jopdesign.wcet.analysis.cache.CachePersistenceAnalysis.PersistenceCheck;
import com.jopdesign.wcet.ipet.CostProvider;
import com.jopdesign.wcet.ipet.IPETBuilder;
import com.jopdesign.wcet.ipet.IPETConfig;
import com.jopdesign.wcet.report.ClassReport;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.log4j.Logger;
import org.jgrapht.DirectedGraph;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
/**
* Simple and fast local analysis, with the possibility to use more expensive analysis
* methods (global IPET for miss-once fit-all, UPPAAL) for parts of the program.
*
* @author Benedikt Huber (benedikt.huber@gmail.com)
*
*/
public class RecursiveWcetAnalysis<Context extends AnalysisContext>
extends RecursiveAnalysis<Context, WcetCost> {
/** Visitor for computing the WCET of CFG nodes */
private class LocalWcetVisitor extends WcetVisitor {
Context ctx;
public LocalWcetVisitor(WCETTool project, Context ctx) {
super(project);
this.ctx = ctx;
}
@Override
public void visitSummaryNode(ControlFlowGraph.SummaryNode n) {
cost.addCost(runWCETComputation("summary",n.getSubGraph(),ctx).getCost());
}
@Override
public void visitInvokeNode(ControlFlowGraph.InvokeNode n) {
//[Bug #3] Callgraph Pruning
if(n.getVirtualNode() != null) {
if(project.isInfeasibleReceiver(n.getImplementingMethod(), ctx.getCallString().push(n.getInvokeSite(),project.getCallstringLength()))) {
WCETTool.logger.info("RecursiveWcetAnalayis: Pruned InvokeNode: "+n.getImplementingMethod());
return;
}
}
cost.addLocalCost(processor.getExecutionTime(ctx.getExecutionContext(n),n.getInstructionHandle()));
if(n.isVirtual()) {
throw new AssertionError("Invoke node "+n.getReferenced()+" without implementation in WCET analysis - did you preprocess virtual methods ?");
}
cost.addCost(RecursiveWcetAnalysis.this.recursiveWCET.recursiveCost(RecursiveWcetAnalysis.this, n, ctx));
}
@Override
public void visitReturnNode(ControlFlowGraph.ReturnNode n) {
}
@Override
public void visitBasicBlockNode(BasicBlockNode n) {
cost.addLocalCost(project.getWCETProcessorModel().basicBlockWCET(ctx.getExecutionContext(n),n.getBasicBlock()));
}
}
/** Solution to local WCET problem */
public class LocalWCETSolution {
private DirectedGraph<CFGNode, ControlFlowGraph.CFGEdge> graph;
private long lpCost;
private WcetCost cost;
private Map<CFGNode,Long> nodeFlow;
private Map<ControlFlowGraph.CFGEdge,Long> edgeFlow;
private Map<CFGNode, WcetCost> nodeCosts;
public LocalWCETSolution(DirectedGraph<CFGNode,ControlFlowGraph.CFGEdge> g, Map<CFGNode,WcetCost> nodeCosts) {
this.graph = g;
this.nodeCosts= nodeCosts;
}
public Map<CFGNode, WcetCost> getNodeCostMap() {
return nodeCosts;
}
public void setSolution(long lpCost, Map<IPETBuilder.ExecutionEdge, Long> executionEdgeFlow) {
this.lpCost = lpCost;
this.edgeFlow = RecursiveWcetAnalysis.executionToProgramFlow(graph, executionEdgeFlow);
this.nodeFlow = edgeToNodeFlow(graph, edgeFlow);
computeCost();
}
public long getLpCost() {
return lpCost;
}
public WcetCost getCost() {
return cost;
}
public WcetCost getTotalCost() {
WcetCost tCost = cost.clone();
tCost.moveLocalToGlobalCost();
return tCost;
}
public long getNodeFlow(CFGNode n) {
return getNodeFlow().get(n);
}
public Map<CFGNode, Long> getNodeFlow() {
return nodeFlow;
}
/**
* This merges the results for invokes and adds results for the virtual nodes. This
* is required if results for the virtual nodes are needed too, e.g. if
* {@link ControlFlowGraph#getHandleNode(InstructionHandle)} is used.
*
* @return the flow graph, including results for virtual blocks.
*/
public Map<CFGNode, Long> getNodeFlowVirtual() {
Map<CFGNode,Long> flow = new HashMap<CFGNode, Long>(nodeFlow);
for (CFGNode node : nodeFlow.keySet()) {
if (!(node instanceof InvokeNode)) continue;
InvokeNode inv = (InvokeNode) node;
if (!inv.isVirtualInstance()) continue;
InvokeNode virt = inv.getVirtualNode();
if (virt != null) {
Long val = flow.get(virt);
if (val == null) {
flow.put(virt, flow.get(inv));
} else {
flow.put(virt, flow.get(inv) + val);
}
}
}
return flow;
}
public Map<ControlFlowGraph.CFGEdge, Long> getEdgeFlow() {
return edgeFlow;
}
/** Safety check: compare flow*cost to actual solution */
public void checkConsistency() {
if(cost.getCost() != lpCost) {
throw new AssertionError("The solution implies that the flow graph cost is "
+ cost.getCost() + ", but the ILP solver reported "+lpCost);
}
}
/* Compute cost, separating local and non-local cost */
private void computeCost() {
cost = new WcetCost();
for(CFGNode n : graph.vertexSet()) {
long flow = nodeFlow.get(n);
cost.addLocalCost(flow * nodeCosts.get(n).getLocalCost());
cost.addCacheCost(flow * nodeCosts.get(n).getCacheCost());
cost.addNonLocalCost(flow * nodeCosts.get(n).getNonLocalCost());
cost.addPotentialCacheFlushes((int)flow * nodeCosts.get(n).getPotentialCacheFlushes());
}
}
}
/** Provide execution cost using a node->cost table
*/
public static class WcetCostProvider<T> implements CostProvider<T> {
private Map<T, WcetCost> costMap;
private WcetCost defCost;
public WcetCostProvider(Map<T,WcetCost> costMap) {
this.costMap = costMap;
this.defCost = null;
}
public WcetCostProvider(Map<T,WcetCost> costMap, WcetCost defCost) {
this.costMap = costMap;
this.defCost = defCost;
}
public long getCost(T obj) {
WcetCost cost = costMap.get(obj);
if(cost == null) {
if(defCost == null) {
throw new AssertionError("Missing entry for "+obj+" in cost map");
} else {
return defCost.getCost();
}
} else {
return cost.getCost();
}
}
}
static final Logger logger = Logger.getLogger(WCETTool.LOG_WCET_ANALYSIS+".RecursiveWcetAnalysis");
private WCETProcessorModel processor;
private RecursiveAnalysis.RecursiveStrategy<Context, WcetCost> recursiveWCET;
private Set<MethodInfo> costsPerLineReported = new HashSet<MethodInfo>();
public RecursiveWcetAnalysis(WCETTool project,
RecursiveAnalysis.RecursiveStrategy<Context, WcetCost> recursiveStrategy) {
this(project, new IPETConfig(project.getConfig()), recursiveStrategy);
}
public RecursiveWcetAnalysis(WCETTool project,
IPETConfig ipetConfig,
RecursiveAnalysis.RecursiveStrategy<Context,WcetCost> recursiveStrategy) {
super(project, ipetConfig);
this.processor = project.getWCETProcessorModel();
this.recursiveWCET = recursiveStrategy;
}
/**
* WCET analysis of the given method, using some strategy for recursive WCET calculation and cache
* approximation.cache approximation scheme.
* @param m the method to be analyzed
* @return
*
* <p>FIXME: Logging/Report is somewhat broken and messy </p>
*/
public WcetCost computeCost(MethodInfo m, Context ctx) {
/* use memorization to speed up analysis */
CacheKey key = new CacheKey(m,ctx);
if(isCached(key)) return getCached(key);
/* compute solution */
LocalWCETSolution sol = runWCETComputation(key.toString(), getWCETTool().getFlowGraph(m), ctx);
sol.checkConsistency();
recordCost(key, sol.getCost());
/* Logging and Report */
logger.debug("WCET for " + key + ": "+sol.getCost());
if(getWCETTool().reportGenerationActive()) {
updateReport(key, sol);
}
return sol.getTotalCost();
}
public LocalWCETSolution computeSolution(MethodInfo m, Context ctx) {
/* just compute the solution using cached results, and also place the result into the cache,
but always return the complete solution. Also, no reports are generated here..
*/
// TODO return type could be made more generic and this method could be moved to RecursiveAnalysis
CacheKey key = new CacheKey(m,ctx);
/* compute solution */
ControlFlowGraph cfg = getWCETTool().getFlowGraph(m);
LocalWCETSolution sol = runWCETComputation(key.toString(), cfg, ctx);
sol.checkConsistency();
recordCost(key, sol.getCost());
/* Logging */
logger.debug("WCET for " + key + ": "+sol.getCost());
return sol;
}
public LocalWCETSolution runWCETComputation(
String key,
ControlFlowGraph cfg,
Context ctx) {
Map<CFGNode,WcetCost> nodeCosts = buildNodeCostMap(cfg,ctx);
CostProvider<CFGNode> costProvider = getCostProvider(nodeCosts);
Map<IPETBuilder.ExecutionEdge, Long> edgeFlowOut = new HashMap<IPETBuilder.ExecutionEdge, Long>();
long maxCost = runLocalComputation(key, cfg, ctx, costProvider, edgeFlowOut );
LocalWCETSolution sol = new LocalWCETSolution(cfg.getGraph(), nodeCosts);
sol.setSolution(maxCost, edgeFlowOut);
return sol;
}
// FIXME: [recursive-wcet-analysis] Report generation is a big mess
// FIXME: [recursive-wcet-analysis] For now, we only add line costs once per method
private void updateReport(CacheKey key, LocalWCETSolution sol) {
MethodInfo m = key.m;
HashMap<CFGNode, String> nodeFlowCostDescrs = new HashMap<CFGNode, String>();
updateClassReport(key, sol);
Map<String,Object> stats = new HashMap<String, Object>();
stats.put("WCET",sol.getCost());
stats.put("mode",key.ctx);
MethodCacheAnalysis mca = new MethodCacheAnalysis(getWCETTool());
stats.put("all-methods-fit-in-cache", mca.isPersistenceRegion(getWCETTool(), m,
key.ctx.getCallString(), EnumSet.allOf(PersistenceCheck.class)));
getWCETTool().getReport().addDetailedReport(m,"WCET_"+key.ctx.toString(),stats,nodeFlowCostDescrs,sol.getEdgeFlow());
}
/**
* Update class report (cost per line number)
*
* @param key
* @param sol FIXME: Currently only reported once per method
*/
private void updateClassReport(CacheKey key, LocalWCETSolution sol) {
MethodInfo m = key.m;
if (costsPerLineReported.contains(m)) return;
costsPerLineReported.add(m);
Map<CFGNode, WcetCost> nodeCosts = sol.getNodeCostMap();
HashMap<CFGNode, String> nodeFlowCostDescrs = new HashMap<CFGNode, String>();
long anonymousCost = 0; // for autogenerated code
for (Entry<CFGNode, WcetCost> entry : nodeCosts.entrySet()) {
CFGNode n = entry.getKey();
WcetCost cost = entry.getValue();
if (sol.getNodeFlow(n) > 0) {
nodeFlowCostDescrs.put(n, cost.toString());
BasicBlock basicBlock = n.getBasicBlock();
/* prototyping */
if (basicBlock != null) {
Map<ClassInfo,TreeSet<Integer>> lineMap = basicBlock.getSourceLines();
if (lineMap.isEmpty()) {
logger.error("No source code lines associated with basic block " + basicBlock + " in " + m + " ! ");
continue;
}
// we attach to the first class in the map only
ClassInfo cli = lineMap.keySet().iterator().next();
TreeSet<Integer> lineRange = lineMap.get(cli);
ClassReport cr = getWCETTool().getReport().getClassReport(cli);
long newCost = sol.getNodeFlow(n) * nodeCosts.get(n).getCost();
// Autogenerated code, attach to next entry
if(lineRange.isEmpty()) {
anonymousCost += newCost;
continue;
} else {
newCost += anonymousCost;
anonymousCost = 0;
}
Long oldCost = (Long) cr.getLineProperty(lineRange.first(), "cost");
if (oldCost == null) oldCost = 0L;
if (logger.isTraceEnabled()) {
logger.trace("Attaching cost " + oldCost + " + " + newCost +
" to line " + lineRange.first() + " in " + basicBlock.getMethodInfo());
}
cr.addLineProperty(lineRange.first(), "cost", oldCost + newCost);
for(int i : lineRange) {
cr.addLineProperty(i, "color", "red");
}
}
} else {
nodeFlowCostDescrs.put(n, ""+nodeCosts.get(n).getCost());
}
}
}
@Override
public WcetCost computeCostOfNode(CFGNode n ,Context ctx) {
WcetVisitor wcetVisitor = new LocalWcetVisitor(getWCETTool(), ctx);
return wcetVisitor.computeCost(n);
}
@Override
protected CostProvider<CFGNode> getCostProvider(
Map<CFGNode, WcetCost> nodeCosts) {
return new WcetCostProvider<CFGNode>(nodeCosts);
}
// currently unused
@Override
protected WcetCost extractSolution(ControlFlowGraph cfg,
Map<CFGNode, WcetCost> nodeCosts,
long maxCost,
Map<IPETBuilder.ExecutionEdge, Long> edgeFlowOut) {
LocalWCETSolution sol = new LocalWCETSolution(cfg.getGraph(), nodeCosts);
sol.setSolution(maxCost, edgeFlowOut);
return sol.getCost();
}
/**
* Extract CFGEdge execution frequencies from IPET ExecutionEdge flow.
* Eliminates context dependencies and low level models.
* @param graph
* @param executionEdgeFlow
* @return
*/
public static Map<ControlFlowGraph.CFGEdge, Long> executionToProgramFlow(
DirectedGraph<CFGNode, ControlFlowGraph.CFGEdge> graph,
Map<IPETBuilder.ExecutionEdge, Long> executionEdgeFlow) {
Map<ControlFlowGraph.CFGEdge, Long> cfgEdgeFlow = new HashMap<ControlFlowGraph.CFGEdge, Long>();
for(Entry<IPETBuilder.ExecutionEdge, Long> entry : executionEdgeFlow.entrySet()) {
ControlFlowGraph.CFGEdge cfgEdge = entry.getKey().getModelledEdge();
if(cfgEdge == null) continue;
Long oldFlow = cfgEdgeFlow.get(cfgEdge);
if(oldFlow == null) oldFlow = 0L;
cfgEdgeFlow.put(cfgEdge, oldFlow + entry.getValue());
}
return cfgEdgeFlow;
}
public static Map<CFGNode, Long> edgeToNodeFlow(DirectedGraph<CFGNode,ControlFlowGraph.CFGEdge> graph, Map<ControlFlowGraph.CFGEdge, Long> cfgEdgeFlow) {
HashMap<CFGNode, Long> nodeFlow = new HashMap<CFGNode, Long>();
for(CFGNode n : graph.vertexSet()) {
if(graph.inDegreeOf(n) == 0) nodeFlow.put(n, 0L); // ENTRY and DEAD CODE (no flow)
else {
long flow = 0;
for(ControlFlowGraph.CFGEdge inEdge : graph.incomingEdgesOf(n)) {
flow+=cfgEdgeFlow.get(inEdge);
}
nodeFlow.put(n, flow);
}
}
return nodeFlow;
}
}