/* * This file is part of JOP, the Java Optimized Processor * see <http://www.jopdesign.com/> * * Copyright (C) 2010, 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.MethodInfo; import com.jopdesign.common.code.ControlFlowGraph; import com.jopdesign.common.code.ControlFlowGraph.CFGNode; import com.jopdesign.wcet.WCETTool; import com.jopdesign.wcet.ipet.CostProvider; import com.jopdesign.wcet.ipet.IPETBuilder; import com.jopdesign.wcet.ipet.IPETConfig; import com.jopdesign.wcet.ipet.IPETSolver; import com.jopdesign.wcet.ipet.IPETUtils; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * Class for recursive maximization problems. * Generalizes some concepts also useful outside the actual WCET analysis. * * @author Benedikt Huber <benedikt.huber@gmail.com> * @param <Context> Different Contexts may lead to different results. * Recomputation with the same context is cached. * @param <Rval> Type of the thing being computed (e.g., WcetCost, long) etc. */ public abstract class RecursiveAnalysis<Context extends AnalysisContext, Rval> { /** * Used for configuring recursive WCET caluclation */ public interface RecursiveStrategy<Context extends AnalysisContext, Rval> { Rval recursiveCost(RecursiveAnalysis<Context, Rval> stagedAnalysis, ControlFlowGraph.InvokeNode invocation, Context ctx); } /** * Key for caching recursive calculations */ protected class CacheKey { MethodInfo m; Context ctx; public CacheKey(MethodInfo m, Context mode) { this.m = m; this.ctx = mode; } public int hashCode() { final int prime = 31; int result = prime * m.getFQMethodName().hashCode(); result = prime * result + ctx.hashCode(); return result; } @SuppressWarnings("unchecked") @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; RecursiveAnalysis.CacheKey other = (RecursiveAnalysis.CacheKey) obj; if (!ctx.equals(other.ctx)) return false; if (!m.equals(other.m)) return false; return true; } @Override public String toString() { return this.m.getFQMethodName() + "[" + this.ctx.hashCode() + "]"; } } private WCETTool project; private HashMap<CacheKey, Rval> costMap; private IPETConfig ipetConfig; public WCETTool getWCETTool() { return project; } public RecursiveAnalysis(WCETTool p, IPETConfig ipetConfig) { this.project = p; this.ipetConfig = ipetConfig; this.costMap = new HashMap<CacheKey, Rval>(); } public Rval computeCost(MethodInfo m, Context ctx) { /* use memoization to speed up analysis */ CacheKey key = new CacheKey(m, ctx); if (isCached(key)) return getCached(key); /* compute solution */ Rval rval = computeCostUncached(key.toString(), project.getFlowGraph(m), ctx); recordCost(key, rval); return rval; } public Rval computeCostUncached(String ilpName, ControlFlowGraph cfg, Context ctx) { Map<CFGNode, Rval> nodeCosts = buildNodeCostMap(cfg, ctx); CostProvider<CFGNode> costProvider = getCostProvider(nodeCosts); Map<IPETBuilder.ExecutionEdge, Long> edgeFlowOut = new HashMap<IPETBuilder.ExecutionEdge, Long>(); long maxCost = runLocalComputation(ilpName, cfg, ctx, costProvider, edgeFlowOut); return extractSolution(cfg, nodeCosts, maxCost, edgeFlowOut); } protected abstract Rval extractSolution( ControlFlowGraph cfg, Map<CFGNode, Rval> nodeCosts, long maxCost, Map<IPETBuilder.ExecutionEdge, Long> edgeFlow); protected abstract CostProvider<CFGNode> getCostProvider(Map<CFGNode, Rval> nodeCosts); protected abstract Rval computeCostOfNode(CFGNode n, Context ctx); /** * Compute the cost of the given control flow graph, using a local ILP * * @param name name for the ILP problem * @param cfg the control flow graph * @param ctx the context to use * @return the cost for the given CFG */ public long runLocalComputation( String name, ControlFlowGraph cfg, Context ctx, CostProvider<CFGNode> costProvider, Map<IPETBuilder.ExecutionEdge, Long> edgeFlowOut) { IPETSolver problem = IPETUtils.buildLocalILPModel(project, name, ctx.getCallString(), cfg, costProvider, ipetConfig); /* solve ILP */ /* extract node flow, local cost, cache cost, cummulative cost */ long maxCost = 0; try { maxCost = Math.round(problem.solve(edgeFlowOut)); } catch (Exception e) { throw new Error("Failed to solve LP problem: " + e, e); } return maxCost; } /** * map flowgraph nodes to costs * If the node is a invoke, we need to compute the cost for the invoked method * otherwise, just take the basic block cost * * @param fg the target flowgraph * @param ctx the cost computation context * @return */ public Map<CFGNode, Rval> buildNodeCostMap(ControlFlowGraph fg, Context ctx) { HashMap<CFGNode, Rval> nodeCost = new HashMap<CFGNode, Rval>(); for (CFGNode n : fg.vertexSet()) { nodeCost.put(n, computeCostOfNode(n, ctx)); } return nodeCost; } public void recordCost(MethodInfo invoked, Context ctx, Rval cost) { recordCost(new CacheKey(invoked, ctx), cost); } protected void recordCost(CacheKey key, Rval cost) { costMap.put(key, cost); } protected boolean isCached(CacheKey key) { return costMap.containsKey(key); } public boolean isCached(MethodInfo invoked, Context ctx) { return isCached(new CacheKey(invoked, ctx)); } protected Rval getCached(CacheKey cacheKey) { return costMap.get(cacheKey); } public Rval getCached(MethodInfo invoked, Context ctx) { return getCached(new CacheKey(invoked, ctx)); } public void clearCache(Set<MethodInfo> methods) { Iterator<Entry<CacheKey,Rval>> it = costMap.entrySet().iterator(); while (it.hasNext()) { CacheKey key = it.next().getKey(); if (methods.contains(key.m)) { it.remove(); } } } }