/*
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.uppaal.translator;
import com.jopdesign.common.code.ControlFlowGraph.CFGNode;
import com.jopdesign.common.code.ExecutionContext;
import com.jopdesign.common.code.LoopBound;
import com.jopdesign.wcet.uppaal.UppAalConfig;
import com.jopdesign.wcet.uppaal.model.LayoutCFG;
import com.jopdesign.wcet.uppaal.model.Location;
import com.jopdesign.wcet.uppaal.model.Location.LocationAttribute;
import com.jopdesign.wcet.uppaal.model.Template;
import com.jopdesign.wcet.uppaal.model.Transition;
import com.jopdesign.wcet.uppaal.model.TransitionAttributes;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
/**
* Builder for program templates.
* Built-in support for
* <ul>
* <li/> Initial / end location
* <li/> Wait nodes (wait for the specified amount of time on the given clock)
* <li/> Loop counters (set to _|_, T, increment, guard)
* </ul>
*/
public class TemplateBuilder {
public static String loopVarName(int loopId) {
return("loop_cnt_"+loopId);
}
public static String loopBoundConst (int loopId) {
return("LOOP_BOUND_"+loopId);
}
public static String loopLowerBoundConst(int loopId) {
return("LOOP_LOWERBOUND_"+loopId);
}
//private Vector<Integer> loopVariables;
private UppAalConfig config;
private Template template;
private Template getTemplate() { return this.template; }
private Map<Location, TransitionAttributes> outgoingAttrs;
private Map<Location, TransitionAttributes> incomingAttrs;
private Location initLoc;
private Location endLoc;
/* which variable to use for the given HOL */
private HashMap<CFGNode, Integer> loopVars;
/* upper bound for loop variables */
private Vector<Long> loopVarBounds = new Vector<Long>();
/* which bound constants to use for the given HOL */
private HashMap<CFGNode,Integer> loopBounds;
private String clockBB;
private Location postEnd;
private int pid;
public Vector<Long> getLoopVarBounds() {
return this.loopVarBounds;
}
/**
* Build a fresh template representing a CFG or a supergraph, with two
* dedicated locations {@code initial} and {@code end}.
* A number of loop variables, depending on the maximum nesting depth is created.
* @param name the template's name
* @param bbClock the basic block clock
*/
public TemplateBuilder(UppAalConfig config,
String name, int processId,
String bbClock) {
this.config = config;
this.pid = processId;
this.template = new Template(name,new Vector<String>());
//this.loopVariables = loopVariables;
this.incomingAttrs = new HashMap<Location, TransitionAttributes>();
this.outgoingAttrs = new HashMap<Location, TransitionAttributes>();
this.clockBB = bbClock;
this.initLoc = new Location("I");
template.setInitialLocation(initLoc);
this.endLoc = new Location("E");
endLoc.setCommited();
template.addLocation(endLoc);
loopVars = new HashMap<CFGNode, Integer>();
loopBounds = new HashMap<CFGNode, Integer>();
}
public void addClock(String clock) {
getTemplate().appendDeclaration(
String.format("clock %s; ", clock));
}
public int addLoop(CFGNode hol, int nestingDepth, LoopBound lb) {
ExecutionContext eCtx = new ExecutionContext(hol.getControlFlowGraph().getMethodInfo());
nestingDepth = nestingDepth - 1;
int varKey = loopBounds.size();
getTemplate().appendDeclaration(
String.format("const int %s = %d;",
loopBoundConst(varKey), lb.getUpperBound(eCtx)));
getTemplate().appendDeclaration(
String.format("const int %s = %d;",
loopLowerBoundConst(varKey), lb.getLowerBound(eCtx)));
while(loopVarBounds.size() < nestingDepth) {
loopVarBounds.add(0L);
}
if(loopVarBounds.size() <= nestingDepth) {
loopVarBounds.add(lb.getUpperBound(eCtx).longValue());
} else {
if(lb.getUpperBound(eCtx).longValue() > loopVarBounds.get(nestingDepth)) {
loopVarBounds.set(nestingDepth,lb.getUpperBound(eCtx).longValue());
}
}
this.loopBounds.put(hol, varKey);
this.loopVars.put(hol,nestingDepth);
return varKey;
}
public String getLoopVar(CFGNode hol) {
String s = loopVarName(this.loopVars.get(hol));
if(s == null) throw new AssertionError("Loop not registered: "+hol);
return s;
}
public String getLoopBoundConst(CFGNode hol) {
String s = loopBoundConst(this.loopBounds.get(hol));
if(s == null) throw new AssertionError("Loop not registered: "+hol);
return s;
}
public String getLoopLowerBoundConst(CFGNode hol) {
String s = loopLowerBoundConst(this.loopBounds.get(hol));
if(s == null) throw new AssertionError("Loop not registered: "+hol);
return s;
}
public Location getInitial() {
return this.template.getInitial();
}
public Location getEnd() {
return this.endLoc;
}
private Location createLocation(String name, LocationAttribute lAttr) {
Location l = new Location(name,lAttr);
this.template.addLocation(l);
return l;
}
public Location createLocation(String name) {
return createLocation(name,LocationAttribute.none);
}
public Location createCommitedLocation(String name) {
return createLocation(name,LocationAttribute.committed);
}
public Transition createTransition(Location src, Location target) {
Transition t = new Transition(src,target);
this.template.addTransition(t);
return t;
}
public TransitionAttributes getOutgoingAttrs(Location l) {
TransitionAttributes attrs = outgoingAttrs.get(l);
if(attrs == null) {
attrs = new TransitionAttributes();
outgoingAttrs.put(l,attrs);
}
return attrs;
}
public TransitionAttributes getIncomingAttrs(Location l) {
TransitionAttributes attrs = incomingAttrs.get(l);
if(attrs == null) {
attrs = new TransitionAttributes();
incomingAttrs.put(l,attrs);
}
return attrs;
}
public void waitAtLocation(Location location, long waitTime) {
location.setInvariant(String.format("%s <= %d", this.clockBB, waitTime));
getIncomingAttrs(location).appendUpdate(clockBB + " := 0");
/* Not necessary for WCET; according to measurements not beneficial either */
// getOutgoingAttrs(location).appendGuard(String.format("%s == %d",clockBB,waitTime));
}
public String contLoopGuard(CFGNode loop) {
return String.format("%s < %s",getLoopVar(loop),getLoopBoundConst(loop));
}
public String exitLoopGuard(CFGNode loop) {
if(config.assumeTightBounds) {
return String.format("%s == %s",getLoopVar(loop),getLoopBoundConst(loop));
} else {
return String.format("%s >= %s",getLoopVar(loop),getLoopLowerBoundConst(loop));
}
}
public String resetLoopCounter(CFGNode loop) {
return String.format("%s := 0",getLoopVar(loop));
}
public String incrLoopCounter(CFGNode loop) {
return String.format("%1$s := %1$s + 1",getLoopVar(loop));
}
public void addDescription(String string) {
this.template.addComment(string);
}
public SubAutomaton getTemplateAutomaton() {
return new SubAutomaton(getInitial(), getEnd());
}
/** add a final node */
public void addPostEnd() {
if(postEnd != null) throw new AssertionError("Called addPostEnd twice");
postEnd = new Location("EE");
template.addLocation(postEnd);
template.addTransition(new Transition(getEnd(), postEnd));
}
/** add a synchronization loop from end to start */
public void addSyncLoop() {
Transition cont = new Transition(getEnd(), getInitial());
String channel = SystemBuilder.methodChannel(pid);
cont.getAttrs().setSync(channel+"!");
if(loopBounds.size() > 0) {
cont.getAttrs().appendUpdate("rst()");
}
template.addTransition(cont);
getOutgoingAttrs(getInitial()).setSync(channel+"?");
}
/** create a subautomaton in the template */
public SubAutomaton createSubAutomaton(String name) {
Location subI = new Location("I_"+name);
subI.setCommited();
Location subE = new Location("E_"+name);
subE.setCommited();
template.addLocation(subI); template.addLocation(subE);
return new SubAutomaton(subI,subE);
}
public Template getFinalTemplate() {
if(outgoingAttrs == null) return this.template;
for(int i = 0; i < loopVarBounds.size(); i++) {
template.appendDeclaration(
String.format("int[0,%s] %s;",loopVarBounds.get(i), loopVarName(i)));
}
if(loopVarBounds.size() > 0) {
StringBuilder rst = new StringBuilder();
rst.append("void rst() {\n");
for(int i = 0; i < loopVarBounds.size(); i++) {
rst.append(String.format(" %s = 0;\n",loopVarName(i)));
}
rst.append("} \n");
template.appendDeclaration(rst.toString());
}
for(Location l : template.getLocations()) {
TransitionAttributes in = incomingAttrs.get(l);
if(in != null) {
for(Transition t : l.getPredecessors()) {
t.getAttrs().addAttributes(in);
}
}
TransitionAttributes out = outgoingAttrs.get(l);
if(out != null) {
for(Transition t : l.getSuccessors()) {
t.getAttrs().addAttributes(out);
}
}
}
outgoingAttrs = incomingAttrs = null;
/* debug: create dot file */
if(config.debug) {
File dbgFile = config.getOutFile("template_"+template.getId()+".dot");
try {
template.exportDOT(dbgFile);
} catch (IOException e) {
e.printStackTrace();
}
}
new LayoutCFG(100,120).layoutCfgModel(template);
return template;
}
/** Debug: dump loops */
public void dumpLoops() {
System.out.println("Loop Bounds");
for(int i = 0; i < this.loopVarBounds.size(); i++) {
System.out.println(String.format(" %s <= %d ",loopVarName(i),loopVarBounds.get(i)));
}
for(CFGNode hol : this.loopVars.keySet()) {
System.out.println(String.format(" %s maps to variable %s with bound constant %s",
hol.toString(),
this.loopVars.get(hol).toString(),
this.loopBounds.get(hol).toString()));
}
}
}