/*
* 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.uppaal.translator;
import com.jopdesign.common.MethodInfo;
import com.jopdesign.common.code.ControlFlowGraph;
import com.jopdesign.common.code.LoopBound;
import com.jopdesign.common.code.ControlFlowGraph.CFGNode;
import com.jopdesign.common.code.SuperGraph.ContextCFG;
import com.jopdesign.common.code.SuperGraph;
import com.jopdesign.common.graphutils.Pair;
import com.jopdesign.common.misc.BadGraphError;
import com.jopdesign.common.misc.BadGraphException;
import com.jopdesign.common.misc.MiscUtils;
import com.jopdesign.wcet.WCETTool;
import com.jopdesign.wcet.analysis.AnalysisContextLocal;
import com.jopdesign.wcet.analysis.LocalAnalysis;
import com.jopdesign.wcet.analysis.RecursiveWcetAnalysis;
import com.jopdesign.wcet.analysis.WcetCost;
import com.jopdesign.wcet.ipet.IPETConfig.CacheCostCalculationMethod;
import com.jopdesign.wcet.uppaal.UppAalConfig;
import com.jopdesign.wcet.uppaal.model.DuplicateKeyException;
import com.jopdesign.wcet.uppaal.model.Location;
import com.jopdesign.wcet.uppaal.model.Transition;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Vector;
public class JavaOneProcessPerSupergraphTranslator extends JavaTranslator {
public class InvokeViaCallStackBuilder extends InvokeBuilder {
public InvokeViaCallStackBuilder(JavaTranslator mt, TemplateBuilder tBuilder) {
super(mt,tBuilder, mt.cacheSim);
}
/* when syncing via callstack, we connect the invoke node to the entry, and the exit to
* to the return node. The former pushes the invoked method on the callstack, while the
* latter pops it if we have a match (pushdown automata).
* (non-Javadoc)
* @see com.jopdesign.wcet.uppaal.translator.InvokeBuilder#translateInvoke(com.jopdesign.wcet.uppaal.translator.MethodBuilder, com.jopdesign.wcet.frontend.ControlFlowGraph.InvokeNode, long)
*/
@Override
public SubAutomaton translateInvoke(MethodBuilder mBuilder, ControlFlowGraph.InvokeNode n, long staticWCET) {
int invokedID = callSiteIDs.get(n);
String suffix = "_"+invokedID+"_"+n.getId();
/* location for executing the code */
SubAutomaton basicBlock = mBuilder.createBasicBlock(n.getId(),staticWCET);
Location startInvokeNode = basicBlock.getEntry();
Location finishInvokeNode;
Location basicBlockNode = basicBlock.getExit();
/* location for waiting */
if(javaTranslator.getCacheSim().isDynamic()) {
Location callNode = tBuilder.createCommitedLocation("CALL"+suffix);
Location returnNode = tBuilder.createLocation("RETURN"+suffix);
finishInvokeNode = tBuilder.createCommitedLocation("FINISH"+suffix);
/* miss nodes */
Location invokeMissNode = tBuilder.createLocation("INVOKE_MISS"+suffix);
Location returnMissNode = tBuilder.createLocation("RETURN_MISS"+suffix);
/* invoke access on incoming bb, insert miss node */
Transition toInvokeHit = tBuilder.createTransition(basicBlockNode, callNode);
Transition toInvokeMiss = tBuilder.createTransition(basicBlockNode, invokeMissNode);
tBuilder.createTransition(invokeMissNode, callNode);
simulateCacheAccess(
n.receiverFlowGraph(),true,
basicBlockNode, /* access cache on ingoing transitions */
toInvokeHit, /* if hit transition */
toInvokeMiss, /* if miss transition */
invokeMissNode); /* miss node */
simulateMethodInvocation(callNode,returnNode,invokedID, n);
Transition toReturnHit = tBuilder.createTransition(returnNode, finishInvokeNode);
Transition toReturnMiss = tBuilder.createTransition(returnNode, returnMissNode);
tBuilder.createTransition(returnMissNode, finishInvokeNode);
simulateCacheAccess(
n.invokerFlowGraph(),false,
returnNode, /* access cache on ingoing transitions */
toReturnHit, /* if hit transition */
toReturnMiss, /* if miss transition */
returnMissNode); /* miss node */
} else {
finishInvokeNode = tBuilder.createLocation("RETURN_"+invokedID+"_"+n.getId());
simulateMethodInvocation(basicBlockNode,finishInvokeNode,invokedID, n);
}
return new SubAutomaton(startInvokeNode, finishInvokeNode);
}
private void simulateMethodInvocation(
Location startInvokeNode,
Location endInvokeNode,
int invokedID,
ControlFlowGraph.InvokeNode n) {
MethodInfo invoked = n.getImplementingMethod();
if(n.receiverFlowGraph().isLeafMethod() && config.collapseLeaves) {
RecursiveWcetAnalysis<AnalysisContextLocal> ilpAn =
new RecursiveWcetAnalysis<AnalysisContextLocal>(project, new LocalAnalysis());
WcetCost wcet = ilpAn.computeCost(n.getImplementingMethod(),
new AnalysisContextLocal(CacheCostCalculationMethod.ALWAYS_HIT));
tBuilder.waitAtLocation(endInvokeNode, wcet.getCost());
tBuilder.createTransition(startInvokeNode, endInvokeNode);
} else {
endInvokeNode.setCommited();
Transition i = tBuilder.createTransition(startInvokeNode, this.javaTranslator.getMethodAutomaton(invoked).getEntry());
Transition r = tBuilder.createTransition(javaTranslator.getMethodAutomaton(invoked).getExit(),endInvokeNode);
i.getAttrs().appendUpdate("pushCallStack("+invokedID+")");
r.getAttrs().appendGuard("matchCallStack("+invokedID+")");
r.getAttrs().appendUpdate("popCallStack()");
}
}
}
private HashMap<MethodInfo, Integer> methodMNDs;
private SuperGraph superGraph;
private HashMap<ControlFlowGraph.InvokeNode, Integer> callSiteIDs;
public JavaOneProcessPerSupergraphTranslator(UppAalConfig c, WCETTool p,MethodInfo root) {
super(c, p, root);
this.superGraph = new SuperGraph(project, project.getFlowGraph(root), p.getCallstringLength());
}
@Override
protected void translate() {
computeCallSiteIDs();
systemBuilder.addCallStack(root,callSiteIDs.size());
/* Create one template for root method */
TemplateBuilder tBuilder = new TemplateBuilder(config,"Process",0,"t_local");
tBuilder.addClock("t_local");
SubAutomaton mRoot = tBuilder.getTemplateAutomaton();
addMethodAutomaton(root,mRoot);
recordLoops(tBuilder);
/* Create start and end nodes for other methods */
for(int i = 1; i < this.methodInfos.size(); i++) {
MethodInfo mi = methodInfos.get(i);
if(project.getCallGraph().isLeafMethod(mi) && config.collapseLeaves) continue;
SubAutomaton mAuto = tBuilder.createSubAutomaton(MiscUtils.qEncode(mi.getFQMethodName()));
addMethodAutomaton(mi,mAuto);
}
int i = 0;
for(MethodInfo mi : methodInfos) {
if(project.getCallGraph().isLeafMethod(mi) && config.collapseLeaves) continue;
translateMethod(tBuilder,
getMethodAutomaton(mi),
i++,
mi,
new InvokeViaCallStackBuilder(this,tBuilder));
}
tBuilder.getInitial().setCommited();
addProgessMeasure(tBuilder);
tBuilder.addPostEnd();
try {
systemBuilder.addTemplate(0,0, tBuilder.getFinalTemplate());
} catch (DuplicateKeyException e) {
throw new AssertionError("Unexpected exception: "+e);
}
}
private void addProgessMeasure(TemplateBuilder tBuilder) {
if(tBuilder.getLoopVarBounds().isEmpty()) return;
Vector<String> progressSummands = new Vector<String>();
Vector<Long> lvbs = tBuilder.getLoopVarBounds();
long multiplicator = 1;
for(int i = lvbs.size() - 1; i >= 0; i--) {
progressSummands.add(""+multiplicator+" * M0."+TemplateBuilder.loopVarName(i));
multiplicator *= lvbs.get(i);
}
// progress measure are not as safe as I hoped them to be :(
// systemBuilder.addProgressMeasure(MiscUtils.joinStrings(progressSummands, " + "));
}
// Global maximal nesting depth is given by the equation
// node.gmnd = node.method.gmnd + (node.loop ? node.loop.nestingDepth : 0)
// method.gmnd = max { cs.method.gmnd + cs.gmnd | cs <- method.callsites }
// Example:
// main() { for() for() X: f(); }
// f() { for() for(HOL) }
// nesting depth of HOL is 2
// gmnd of f is gmnd of X = 2 + gmnd of main = 2
// gmnd of HOL is 4
private void recordLoops(TemplateBuilder tBuilder) {
try {
computeMethodNestingDepths();
} catch (BadGraphException e) {
throw new BadGraphError(e);
}
for(MethodInfo m : methodInfos) {
ControlFlowGraph cfg = project.getFlowGraph(m);
for( Entry<CFGNode, LoopBound> entry : cfg.buildLoopBoundMap().entrySet()) {
CFGNode hol = entry.getKey();
LoopBound lb = entry.getValue();
int nesting = cfg.getLoopColoring().getLoopColor(hol).size();
int gmnd = nesting + methodMNDs.get(m);
tBuilder.addLoop(hol, gmnd, lb);
}
}
if(config.debug) tBuilder.dumpLoops();
}
private void computeMethodNestingDepths() throws BadGraphException {
this.methodMNDs = new HashMap<MethodInfo,Integer>();
/* for super graph nodes in topological order */
for(ContextCFG n : superGraph.topologicalOrderIterator().getTopologicalTraversal()) {
MethodInfo methodInvoked = n.getCfg().getMethodInfo();
int maxCaller = 0;
for(Pair<SuperGraph.SuperInvokeEdge,SuperGraph.SuperReturnEdge> callSite : superGraph.getCallSitesInvoking(n)) {
ControlFlowGraph.InvokeNode callSiteNode = callSite.first().getInvokeNode();
ControlFlowGraph cfgInvoker = callSiteNode.invokerFlowGraph();
int callerRootDepth = methodMNDs.get(cfgInvoker.getMethodInfo());
int nestingDepth = cfgInvoker.getLoopColoring().getLoopColor(callSiteNode).size();
maxCaller = Math.max(maxCaller, callerRootDepth + nestingDepth);
}
Integer oldValue = methodMNDs.get(methodInvoked);
if(oldValue == null) oldValue = 0;
methodMNDs.put(methodInvoked, Math.max(oldValue, maxCaller));
}
if(config.debug) MiscUtils.printMap(System.out, methodMNDs,30,0);
}
private void computeCallSiteIDs() {
this.callSiteIDs = new HashMap<ControlFlowGraph.InvokeNode,Integer>();
int i = 0;
for(SuperGraph.SuperInvokeEdge e : superGraph.getSuperEdgePairs().keySet()) {
callSiteIDs.put(e.getInvokeNode(), i++);
}
}
}