package xsched.analysis.wala; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import xsched.analysis.core.TaskSchedule; import xsched.analysis.core.TaskScheduleManager; import xsched.analysis.wala.schedule_extraction.NormalNodeFlowData; import xsched.analysis.wala.schedule_extraction.TaskVariable; import xsched.analysis.wala.util.SimpleGraph; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.ipa.callgraph.impl.Everywhere; import com.ibm.wala.ssa.DefUse; import com.ibm.wala.ssa.IR; import com.ibm.wala.ssa.SSACache; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAInvokeInstruction; import com.ibm.wala.ssa.SSANewInstruction; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.graph.traverse.FloydWarshall; public class WalaTaskScheduleManager implements TaskScheduleManager<Integer> { public static WalaTaskScheduleManager make(SSACache cache, IR ir, NormalNodeFlowData flowData) { assert WalaConstants.isTaskMethod(ir.getMethod().getReference()); WalaTaskScheduleManager result = new WalaTaskScheduleManager(ir, flowData.partialSchedule()); DefUse defUse = cache.findOrCreateDU(ir, Everywhere.EVERYWHERE); //this is mildly annoying because this code has to agree with the init code in TaskScheduleSolver //but it's not 100% obvious how to do it much better; //we could iterate through all the nodes in the partial schedule to see what ssa variables are parameters etc but then we lose the ordering //of the param array which is important; also there can be several occurrences of the same ssa variable in the partial schedule with different loop contexts //we could also compute the "occurrences" map here already which gives us all the task ssa variables but then we still would have to find the formal //parameter ssa variables in there and find their place... so i just do it the 'less robust' way for(int i = 0; i < ir.getNumberOfParameters(); i++) { TypeReference paramType = ir.getParameterType(i); if(WalaConstants.isTaskType(paramType)) { int ssaVariable = ir.getParameter(i); result.formalParameters.add(ssaVariable); } } Iterator<SSAInstruction> instructions = ir.iterateNormalInstructions(); while(instructions.hasNext()) { SSAInstruction instruction = instructions.next(); if(instruction instanceof SSAInvokeInstruction) { SSAInvokeInstruction invoke = (SSAInvokeInstruction)instruction; if(WalaConstants.isTaskMethod(invoke.getDeclaredTarget())) { //the task as an ssa variable int ssaTaskVariable = invoke.isStatic() ? invoke.getUse(0) : invoke.getUse(1); //the corresponding new site; we just trust the compiler that there is one SSANewInstruction newTaskInstruction = (SSANewInstruction)defUse.getDef(ssaTaskVariable); result.newSitesBySSAVariable.put(ssaTaskVariable, newTaskInstruction); result.callSitesBySSAVariable.put(ssaTaskVariable, invoke); } } } return result; } private final HashMap<Integer, SSANewInstruction> newSitesBySSAVariable = new HashMap<Integer, SSANewInstruction>(); private final HashMap<Integer, SSAInvokeInstruction> callSitesBySSAVariable = new HashMap<Integer, SSAInvokeInstruction>(); //the list of formal parameters that are actually task variables; other parameters are left out //e.g., task method is foo(Task t, int x, Task a) then the formal parameters array is [t, a] private final List<Integer> formalParameters = new ArrayList<Integer>(); //those fields are only used until initializeFullSchedule() is called; they will be cleared then private SimpleGraph<TaskVariable> partialSchedule; private IR ir; private WalaTaskScheduleManager(IR ir, SimpleGraph<TaskVariable> partialSchedule) { this.ir = ir; this.partialSchedule = partialSchedule; } public SSANewInstruction newSiteForNode(Integer ssaVariable) { return newSitesBySSAVariable.get(ssaVariable); } public SSAInvokeInstruction scheduleSiteForNode(Integer ssaVariable) { return callSitesBySSAVariable.get(ssaVariable); } @Override public ArrayList<Integer> actualParametersForNode(Integer node) { SSAInvokeInstruction invoke = callSitesBySSAVariable.get(node); ArrayList<Integer> actuals = new ArrayList<Integer>(); MethodReference target = invoke.getDeclaredTarget(); for(int i = 1; i < invoke.getNumberOfParameters(); i++) { //MethodReference.getParameterType() does not include "this" but that's OK because "this" can never be a task anyways if(WalaConstants.isTaskType(target.getParameterType(i-1))) { actuals.add(invoke.getUse(i)); } } return actuals; } @Override public List<Integer> formalTaskParameterNodes() { return formalParameters; } @Override public Set<Integer> scheduleSiteNodes() { assert callSitesBySSAVariable.keySet().equals(newSitesBySSAVariable.keySet()); return callSitesBySSAVariable.keySet(); } @Override public void initializeFullSchedule(TaskSchedule<Integer, ?> schedule) { //for transitive information int[][] paths = FloydWarshall.shortestPathLengths(partialSchedule); //for each ssaVariable, find the nodes in the partial schedule that are occurrences of it HashMap<Integer, Set<Integer>> occurrences = this.computeOccurrences(); assert schedule.numberOfAllTaskVariables() == occurrences.keySet().size(); //force the occurrences (ssa variables) into a nice array so that we can iterate in a diagonal matrix style //this breaks encapsulation because we assume that the schedule stores task variables in an array and they are actually indexes int numTasks = schedule.numberOfAllTaskVariables(); for(int lhsTaskVariable = 0; lhsTaskVariable < numTasks; lhsTaskVariable++) { for(int rhsTaskVariable = lhsTaskVariable; rhsTaskVariable < numTasks; rhsTaskVariable++) { Integer lhsSSA = schedule.nodeForTaskVariable(lhsTaskVariable); Integer rhsSSA = schedule.nodeForTaskVariable(rhsTaskVariable); TaskSchedule.Relation relation = computeRelation(occurrences, paths, lhsSSA, rhsSSA); //the addRelation automatically adds the inverse, too schedule.addRelationForTaskVariables(lhsTaskVariable, relation, rhsTaskVariable); } } this.partialSchedule = null; this.ir = null; } private boolean isOutsideLoop(int occurrence) { TaskVariable variable = partialSchedule.getNode(occurrence); return variable.loopContext().isEmpty(); } //a map from ssa variable to node in the schedule graph private HashMap<Integer, Set<Integer>> computeOccurrences() { HashMap<Integer, Set<Integer>> occurrences = new HashMap<Integer, Set<Integer>>(); Iterator<TaskVariable> taskVariables = partialSchedule.iterator(); while(taskVariables.hasNext()) { TaskVariable task = taskVariables.next(); Set<Integer> occs = occurrences.get(task.ssaVariable()); if(occs == null) { occs = new HashSet<Integer>(); occurrences.put(task.ssaVariable(), occs); } occs.add(partialSchedule.getNumber(task)); } return occurrences; } private boolean isNow(int lhsSSA) { IMethod method = ir.getMethod(); if(method.isStatic()) { return ir.getParameter(0) == lhsSSA; } else { return ir.getParameter(1) == lhsSSA; } } private TaskSchedule.Relation computeRelation(HashMap<Integer, Set<Integer>> occurrences, int[][] paths, int lhsSSA, int rhsSSA) { TaskSchedule.Relation result = null; if(this.isNow(lhsSSA)) { if(lhsSSA == rhsSSA) { return TaskSchedule.Relation.singleton; } else { return TaskSchedule.Relation.happensBefore; } } else if(this.isNow(rhsSSA)) { return TaskSchedule.Relation.happensAfter; } if(lhsSSA == rhsSSA) { Set<Integer> occs = occurrences.get(lhsSSA); if(occs.size() == 1) { assert isOutsideLoop(occs.iterator().next()) : "there can't be a node inside a loop without having at least one duplicate"; //a single node that is not in a loop return TaskSchedule.Relation.singleton; } else { //more than one occurrence of the same task //the task can be at most ordered result = TaskSchedule.Relation.ordered; for(int lhsNode : occurrences.get(lhsSSA)) { for(int rhsNode : occurrences.get(rhsSSA)) { if(lhsNode == rhsNode && isOutsideLoop(lhsNode)) { //that's OK; it's the first iteration of the loop } else if(lhsNode == rhsNode && ! isOutsideLoop(lhsNode)) { //make sure that the node is ordered with itself if(paths[lhsNode][lhsNode] == Integer.MAX_VALUE) result = TaskSchedule.Relation.unordered; } } } return result; } } for(int lhsNode : occurrences.get(lhsSSA)) { for(int rhsNode : occurrences.get(rhsSSA)) { assert lhsNode != rhsNode; //lhs != rhs and therefore their occurrences must be different, too if(paths[lhsNode][rhsNode] == Integer.MAX_VALUE) { //no lhs->rhs path if(paths[rhsNode][lhsNode] == Integer.MAX_VALUE) { //no lhs->rhs and no rhs->lhs result = TaskSchedule.Relation.unordered; } else { //no lhs-> but lhs<-rhs if(result == null) { result = TaskSchedule.Relation.happensAfter; } else { switch(result) { case happensBefore: result = TaskSchedule.Relation.ordered; break; //we don't change ordered or unordered } } } } else { //lhs -> rhs if(paths[rhsNode][lhsNode] == Integer.MAX_VALUE) { //lhs->rhs but not rhs->lhs if(result == null) { result = TaskSchedule.Relation.happensBefore; } else { switch(result) { case happensAfter: result = TaskSchedule.Relation.ordered; break; //we don't change ordered or unordered } } } else { //lhs<->rhs if(result == null) { result = TaskSchedule.Relation.ordered; } else { switch(result) { case happensBefore: result = TaskSchedule.Relation.ordered; break; case happensAfter: result = TaskSchedule.Relation.ordered; break; //we don't change ordered or unordered } } } } } } return result; } }