/* * 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.cache; import com.jopdesign.common.MethodInfo; import com.jopdesign.common.code.CallString; 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.CfgVisitor; import com.jopdesign.common.code.ExecutionContext; import com.jopdesign.common.processormodel.JOPConfig; import com.jopdesign.wcet.WCETTool; import com.jopdesign.wcet.analysis.AnalysisContext; import com.jopdesign.wcet.analysis.AnalysisContextCallString; import com.jopdesign.wcet.analysis.InvalidFlowFactException; import com.jopdesign.wcet.analysis.RecursiveAnalysis; import com.jopdesign.wcet.analysis.RecursiveAnalysis.RecursiveStrategy; import com.jopdesign.wcet.analysis.RecursiveWcetAnalysis; import com.jopdesign.wcet.ipet.CostProvider; import com.jopdesign.wcet.ipet.IPETBuilder; import com.jopdesign.wcet.ipet.CostProvider.MapCostProvider; import com.jopdesign.wcet.ipet.IPETConfig; import com.jopdesign.wcet.jop.ObjectCache; import org.apache.bcel.generic.InstructionHandle; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import lpsolve.LpSolveException; /** A demonstration of the persistence analysis for the object cache * <p> * As we have not yet implemented unsharing (this is not as trivial as it sounds), * we use, once again, a recursive analysis * </p><p> * We compute a WCET problem, with the following cost model: * A object handle access has cost 1, everything else is cost 0. * Solve the problem once with and once without persistence analysis, and compare costs. * </p> * */ public class ObjectCacheAnalysisDemo { public static final int DEFAULT_SET_SIZE = 64; public class RecursiveOCacheAnalysis extends RecursiveAnalysis<AnalysisContext, ObjectCache.ObjectCacheCost> { private RecursiveStrategy<AnalysisContext, ObjectCache.ObjectCacheCost> recursiveStrategy; public RecursiveOCacheAnalysis(WCETTool p, IPETConfig ipetConfig, RecursiveStrategy<AnalysisContext, ObjectCache.ObjectCacheCost> recursiveStrategy) { super(p, ipetConfig); this.recursiveStrategy = recursiveStrategy; } @Override protected ObjectCache.ObjectCacheCost computeCostOfNode(CFGNode n, AnalysisContext ctx) { return new OCacheVisitor(this.getWCETTool(), this, recursiveStrategy, ctx).computeCost(n); } @Override protected CostProvider<CFGNode> getCostProvider( Map<CFGNode, ObjectCache.ObjectCacheCost> nodeCosts) { HashMap<CFGNode, Long> costMap = new HashMap<CFGNode, Long>(); for(Entry<CFGNode, ObjectCache.ObjectCacheCost> entry : nodeCosts.entrySet()) { costMap.put(entry.getKey(),entry.getValue().getCost()); } return new MapCostProvider<CFGNode>(costMap, 1000); } @Override protected ObjectCache.ObjectCacheCost extractSolution(ControlFlowGraph cfg, Map<CFGNode, ObjectCache.ObjectCacheCost> nodeCosts, long maxCost, Map<IPETBuilder.ExecutionEdge, Long> executionEdgeFlow) { Map <ControlFlowGraph.CFGEdge, Long> edgeFlow = RecursiveWcetAnalysis.executionToProgramFlow(cfg.getGraph(), executionEdgeFlow); Map <CFGNode, Long> nodeFlow = RecursiveWcetAnalysis.edgeToNodeFlow(cfg.getGraph(),edgeFlow); ObjectCache.ObjectCacheCost ocCost = new ObjectCache.ObjectCacheCost(); for(Entry<CFGNode, Long> entry : nodeFlow.entrySet()) { ocCost.addCost(nodeCosts.get(entry.getKey()).times(entry.getValue())); } if(maxCost != ocCost.getCost()) { throw new AssertionError( String.format("Object Cache Cost: Cost of lp solver (%d) and reconstructed cost (%d) do not coincide",maxCost,ocCost.getCost())); } return ocCost; } } /** Visitor for computing the WCET of CFG nodes */ private class OCacheVisitor implements CfgVisitor { private ObjectCache.ObjectCacheCost cost; private RecursiveAnalysis<AnalysisContext, ObjectCache.ObjectCacheCost> recursiveAnalysis; private RecursiveStrategy<AnalysisContext, ObjectCache.ObjectCacheCost> recursiveStrategy; private AnalysisContext context; private WCETTool project; public OCacheVisitor( WCETTool p, RecursiveAnalysis<AnalysisContext, ObjectCache.ObjectCacheCost> recursiveAnalysis, RecursiveStrategy<AnalysisContext, ObjectCache.ObjectCacheCost> recursiveStrategy, AnalysisContext ctx ) { this.project = p; this.recursiveAnalysis = recursiveAnalysis; this.recursiveStrategy = recursiveStrategy; this.context = ctx; } // Cost ~ number of cache misses // TODO: A basic block is a scope too! public void visitBasicBlockNode(BasicBlockNode n) { long worstCaseMissCost = objectCache.getLoadBlockCycles(); for(InstructionHandle ih : n.getBasicBlock().getInstructions()) { if(ObjectCacheAnalysis.getHandleType(project, n.getControlFlowGraph(), ih) == null) continue; if(! ObjectCacheAnalysis.isFieldCached(project, n.getControlFlowGraph(), ih, objectCache.getMaxCachedFieldIndex())) { cost.addBypassCost(worstCaseMissCost,1); } else { cost.addMissCost(worstCaseMissCost,1); cost.addAccessToCachedField(1); } } } public void visitInvokeNode(ControlFlowGraph.InvokeNode n) { visitBasicBlockNode(n); if(n.isVirtual()) { throw new AssertionError("Invoke node "+n.getReferenced()+" without implementation in WCET analysis - did you preprocess virtual methods ?"); } cost.addCost(recursiveStrategy.recursiveCost(recursiveAnalysis, n, context)); } public void visitVirtualNode(ControlFlowGraph.VirtualNode n) { } public void visitReturnNode(ControlFlowGraph.ReturnNode n) { } public void visitSummaryNode(ControlFlowGraph.SummaryNode n) { ControlFlowGraph subCfg = n.getControlFlowGraph(); cost.addCost(recursiveAnalysis.computeCostUncached(n.toString(), subCfg, this.context)); } public ObjectCache.ObjectCacheCost computeCost(CFGNode n) { this.cost = new ObjectCache.ObjectCacheCost(); n.accept(this); return cost; } } // Ok, a few notes what is probably incorrect at the moment: // a) Cannot handle java implemented methods (I think) // b) invokevirtual also accesses the object, this is not considered private class RecursiveWCETOCache implements RecursiveStrategy<AnalysisContext,ObjectCache.ObjectCacheCost> { public ObjectCache.ObjectCacheCost recursiveCost( RecursiveAnalysis<AnalysisContext,ObjectCache.ObjectCacheCost> stagedAnalysis, ControlFlowGraph.InvokeNode invocation, AnalysisContext ctx) { MethodInfo invoked = invocation.getImplementingMethod(); ObjectCache.ObjectCacheCost cost; try { if(allPersistent(invoked, ctx.getCallString())) { cost = getAllFitCost(invoked, ctx.getCallString()); } //System.out.println("Cost for: "+invocation.getImplementedMethod()+" [all fit]: "+cost); else { // FIXME: callstring missing // AnalysisContext recCtx = ctx.withCallString(ctx.getCallString().push(invocation,project.getProjectConfig().callstringLength())); cost = stagedAnalysis.computeCost(invoked, ctx); //System.out.println("Cost for: "+invocation.getImplementedMethod()+" [recursive]: "+cost); } } catch (InvalidFlowFactException e) { throw new RuntimeException(e); } catch (LpSolveException e) { throw new RuntimeException(e); } return cost; } } private WCETTool project; private ObjectCacheAnalysis objRefAnalysis; private boolean assumeAllMiss; private ObjectCache objectCache; public ObjectCacheAnalysisDemo(WCETTool p, ObjectCache objectCache) { this.project = p; this.objectCache = objectCache; this.objRefAnalysis = new ObjectCacheAnalysis(project, objectCache); } public void setAssumeAlwaysMiss() { this.assumeAllMiss = true; } public ObjectCache.ObjectCacheCost computeCost() { /* Cache Analysis */ RecursiveAnalysis<AnalysisContext, ObjectCache.ObjectCacheCost> recAna = new RecursiveOCacheAnalysis(project, new IPETConfig(project.getConfig()), new RecursiveWCETOCache()); return recAna.computeCost(project.getTargetMethod(), new AnalysisContextCallString(CallString.EMPTY)); } public long getMaxAccessedTags(MethodInfo invoked, CallString context) throws InvalidFlowFactException, LpSolveException { if(! context.isEmpty()) { throw new AssertionError("Callstrings are not yet supported for object cache analysis"); } return objRefAnalysis.countDistinctCachedTagsAccessed(new ExecutionContext(invoked, context)); } private ObjectCache.ObjectCacheCost getAllFitCost(MethodInfo invoked, CallString context) throws InvalidFlowFactException, LpSolveException { if(! context.isEmpty()) { throw new AssertionError("Callstrings are not yet supported for object cache analysis"); } return objRefAnalysis.getMaxCacheCost(new ExecutionContext(invoked, context), objectCache.getCostModel()); } private boolean allPersistent(MethodInfo invoked, CallString context) throws InvalidFlowFactException, LpSolveException { if(assumeAllMiss) return false; return getMaxAccessedTags(invoked, context) <= objectCache.getAssociativity(); } }