package Analysis.Locality; import java.util.*; import Analysis.CallGraph.CallGraph; import IR.SymbolTable; import IR.State; import IR.TypeUtil; import IR.MethodDescriptor; import IR.Flat.*; import IR.ClassDescriptor; public class LocalityAnalysis { State state; Stack lbtovisit; Hashtable discovered; Hashtable> dependence; Hashtable> calldep; Hashtable>> temptab; Hashtable> atomictab; Hashtable>> tempstosave; Hashtable> classtolb; Hashtable> methodtolb; private LocalityBinding lbmain; private LocalityBinding lbrun; CallGraph callgraph; TypeUtil typeutil; public static final Integer LOCAL=new Integer(0); public static final Integer GLOBAL=new Integer(1); public static final Integer EITHER=new Integer(2); public static final Integer CONFLICT=new Integer(3); public LocalityAnalysis(State state, CallGraph callgraph, TypeUtil typeutil) { this.typeutil=typeutil; this.state=state; this.discovered=new Hashtable(); this.dependence=new Hashtable>(); this.calldep=new Hashtable>(); this.temptab=new Hashtable>>(); this.atomictab=new Hashtable>(); this.lbtovisit=new Stack(); this.callgraph=callgraph; this.tempstosave=new Hashtable>>(); this.classtolb=new Hashtable>(); this.methodtolb=new Hashtable>(); doAnalysis(); } public LocalityBinding getMain() { return lbmain; } /** This method returns the set of LocalityBindings that a given * flatcall could invoke */ public LocalityBinding getBinding(LocalityBinding currlb, FlatCall fc) { boolean isatomic=getAtomic(currlb).get(fc).intValue()>0; Hashtable currtable=getNodePreTempInfo(currlb,fc); MethodDescriptor md=fc.getMethod(); boolean isnative=md.getModifiers().isNative(); LocalityBinding lb=new LocalityBinding(md, isatomic); for(int i=0; i getClassBindings(ClassDescriptor cd) { return classtolb.get(cd); } /** This method returns a set of LocalityBindings for the parameter method. */ public Set getMethodBindings(MethodDescriptor md) { return methodtolb.get(md); } public Set getMethods() { return methodtolb.keySet(); } /** This method returns a set of LocalityBindings. A * LocalityBinding specifies a context a method can be invoked in. * It specifies whether the method is in a transaction and whether * its parameter objects are locals or globals. */ public Set getLocalityBindings() { return discovered.keySet(); } /** This method returns a hashtable for a given LocalityBinding * that tells the current local/global status of temps at the each * node in the flat representation. */ public Hashtable> getNodeTempInfo(LocalityBinding lb) { return temptab.get(lb); } /** This method returns a hashtable for a given LocalityBinding * that tells the current local/global status of temps at the * beginning of each node in the flat representation. */ public Hashtable getNodePreTempInfo(LocalityBinding lb, FlatNode fn) { Hashtable currtable=new Hashtable(); Hashtable> temptable=getNodeTempInfo(lb); for(int i=0; i prevtable=temptable.get(prevnode); for(Iterator tempit=prevtable.keySet().iterator(); tempit.hasNext();) { TempDescriptor temp=tempit.next(); Integer tmpint=prevtable.get(temp); Integer oldint=currtable.containsKey(temp) ? currtable.get(temp) : EITHER; Integer newint=merge(tmpint, oldint); currtable.put(temp, newint); } } return currtable; } /** This method returns an hashtable for a given LocalitBinding * that tells whether a node in the flat represenation is in a * transaction or not. Integer values greater than 0 indicate * that the node is in a transaction and give the nesting depth. * The outermost AtomicEnterNode will have a value of 1 and the * outermost AtomicExitNode will have a value of 0. */ public Hashtable getAtomic(LocalityBinding lb) { return atomictab.get(lb); } /** This methods returns a hashtable for a given LocalityBinding * that tells which temps needs to be saved for each * AtomicEnterNode. */ public Hashtable> getTemps(LocalityBinding lb) { return tempstosave.get(lb); } public Set getTempSet(LocalityBinding lb) { HashSet set=new HashSet(); Hashtable> table=getTemps(lb); if (table!=null) for(Iterator faenit=table.keySet().iterator(); faenit.hasNext();) { FlatAtomicEnterNode faen=faenit.next(); set.addAll(table.get(faen)); } return set; } private void doAnalysis() { computeLocalityBindings(); computeTempstoSave(); cleanSets(); } private void cleanSets() { HashSet lbset=new HashSet(); Stack lbstack=new Stack(); lbstack.add(lbmain); lbstack.add(lbrun); lbset.add(lbmain); lbset.add(lbrun); while(!lbstack.isEmpty()) { LocalityBinding lb=lbstack.pop(); if (calldep.containsKey(lb)) { Set set=new HashSet(); set.addAll(calldep.get(lb)); set.removeAll(lbset); lbstack.addAll(set); lbset.addAll(set); } } for(Iterator lbit=discovered.keySet().iterator(); lbit.hasNext();) { LocalityBinding lb=lbit.next(); if (!lbset.contains(lb)) { lbit.remove(); classtolb.get(lb.getMethod().getClassDesc()).remove(lb); methodtolb.get(lb.getMethod()).remove(lb); } } } private void computeLocalityBindings() { lbmain=new LocalityBinding(typeutil.getMain(), false); lbmain.setGlobalReturn(EITHER); lbmain.setGlobal(0, LOCAL); lbtovisit.add(lbmain); discovered.put(lbmain, lbmain); if (!classtolb.containsKey(lbmain.getMethod().getClassDesc())) classtolb.put(lbmain.getMethod().getClassDesc(), new HashSet()); classtolb.get(lbmain.getMethod().getClassDesc()).add(lbmain); if (!methodtolb.containsKey(lbmain.getMethod())) methodtolb.put(lbmain.getMethod(), new HashSet()); methodtolb.get(lbmain.getMethod()).add(lbmain); //Do this to force a virtual table number for the run method lbrun=new LocalityBinding(typeutil.getRun(), false); lbrun.setGlobalReturn(EITHER); lbrun.setGlobalThis(GLOBAL); lbtovisit.add(lbrun); discovered.put(lbrun, lbrun); if (!classtolb.containsKey(lbrun.getMethod().getClassDesc())) classtolb.put(lbrun.getMethod().getClassDesc(), new HashSet()); classtolb.get(lbrun.getMethod().getClassDesc()).add(lbrun); if (!methodtolb.containsKey(lbrun.getMethod())) methodtolb.put(lbrun.getMethod(), new HashSet()); methodtolb.get(lbrun.getMethod()).add(lbrun); while(!lbtovisit.empty()) { LocalityBinding lb=(LocalityBinding) lbtovisit.pop(); Integer returnglobal=lb.getGlobalReturn(); MethodDescriptor md=lb.getMethod(); Hashtable> temptable=new Hashtable>(); Hashtable atomictable=new Hashtable(); calldep.remove(lb); try { computeCallsFlags(md, lb, temptable, atomictable); } catch (Error e) { System.out.println("Error in "+md+" context "+lb); e.printStackTrace(); System.exit(-1); } atomictab.put(lb, atomictable); temptab.put(lb, temptable); if (md.getReturnType()!=null&&!returnglobal.equals(lb.getGlobalReturn())) { //return type is more precise now //rerun everything that call us lbtovisit.addAll(dependence.get(lb)); } } } public void computeCallsFlags(MethodDescriptor md, LocalityBinding lb, Hashtable> temptable, Hashtable atomictable) { FlatMethod fm=state.getMethodFlat(md); HashSet tovisit=new HashSet(); tovisit.add(fm.getNext(0)); { // Build table for initial node Hashtable table=new Hashtable(); temptable.put(fm, table); atomictable.put(fm, lb.isAtomic() ? 1 : 0); int offset=md.isStatic() ? 0 : 1; if (!md.isStatic()) { table.put(fm.getParameter(0), lb.getGlobalThis()); } for(int i=offset; i currtable=new Hashtable(); int atomicstate=0; for(int i=0; i prevtable=temptable.get(prevnode); for(Iterator tempit=prevtable.keySet().iterator(); tempit.hasNext();) { TempDescriptor temp=tempit.next(); Integer tmpint=prevtable.get(temp); Integer oldint=currtable.containsKey(temp) ? currtable.get(temp) : EITHER; Integer newint=merge(tmpint, oldint); currtable.put(temp, newint); } } atomictable.put(fn, atomicstate); // Process this node switch(fn.kind()) { case FKind.FlatAtomicEnterNode: processAtomicEnterNode((FlatAtomicEnterNode)fn, atomictable); if (!lb.isAtomic()) lb.setHasAtomic(); break; case FKind.FlatAtomicExitNode: processAtomicExitNode((FlatAtomicExitNode)fn, atomictable); break; case FKind.FlatCall: processCallNode(lb, (FlatCall)fn, currtable, isAtomic(atomictable, fn)); break; case FKind.FlatFieldNode: processFieldNode(lb, (FlatFieldNode)fn, isAtomic(atomictable, fn), currtable); break; case FKind.FlatSetFieldNode: processSetFieldNode(lb, (FlatSetFieldNode)fn, isAtomic(atomictable,fn), currtable); break; case FKind.FlatNew: processNew(lb, (FlatNew)fn, isAtomic(atomictable, fn), currtable); break; case FKind.FlatOpNode: processOpNode((FlatOpNode)fn, currtable); break; case FKind.FlatCastNode: processCastNode((FlatCastNode)fn, currtable); break; case FKind.FlatLiteralNode: processLiteralNode((FlatLiteralNode)fn, currtable); break; case FKind.FlatReturnNode: processReturnNode(lb, (FlatReturnNode)fn, currtable); break; case FKind.FlatSetElementNode: processSetElementNode(lb, (FlatSetElementNode)fn, currtable, isAtomic(atomictable, fn)); break; case FKind.FlatElementNode: processElementNode(lb, (FlatElementNode)fn, currtable, isAtomic(atomictable, fn)); break; case FKind.FlatCondBranch: case FKind.FlatBackEdge: case FKind.FlatNop: case FKind.FlatPrefetchNode: //No action needed for these break; case FKind.FlatFlagActionNode: case FKind.FlatCheckNode: case FKind.FlatTagDeclaration: throw new Error("Incompatible with tasks!"); case FKind.FlatMethod: default: throw new Error(); } Hashtable oldtable=temptable.get(fn); if (oldtable==null||!oldtable.equals(currtable)) { // Update table for this node temptable.put(fn, currtable); for(int i=0; i atomictable, FlatNode fn) { return atomictable.get(fn).intValue()>0; } private static Integer merge(Integer a, Integer b) { if (a==null||a.equals(EITHER)) return b; if (b==null||b.equals(EITHER)) return a; if (a.equals(b)) return a; return CONFLICT; } void processCallNode(LocalityBinding currlb, FlatCall fc, Hashtable currtable, boolean isatomic) { MethodDescriptor nodemd=fc.getMethod(); Set methodset=null; Set runmethodset=null; if (nodemd.isStatic()||nodemd.getReturnType()==null) { methodset=new HashSet(); methodset.add(nodemd); } else { methodset=callgraph.getMethods(nodemd, fc.getThis().getType()); // Build start -> run link if (nodemd.getClassDesc().getSymbol().equals(TypeUtil.ThreadClass)&& nodemd.getSymbol().equals("start")&&!nodemd.getModifiers().isStatic()&& nodemd.numParameters()==1&&nodemd.getParamType(0).isInt()) { assert(nodemd.getModifiers().isNative()); MethodDescriptor runmd=null; for(Iterator methodit=nodemd.getClassDesc().getMethodTable().getSet("run").iterator(); methodit.hasNext();) { MethodDescriptor md=(MethodDescriptor) methodit.next(); if (md.numParameters()!=0||md.getModifiers().isStatic()) continue; runmd=md; break; } if (runmd!=null) { runmethodset=callgraph.getMethods(runmd,fc.getThis().getType()); methodset.addAll(runmethodset); } else throw new Error("Can't find run method"); } } Integer currreturnval=EITHER; //Start off with the either value for(Iterator methodit=methodset.iterator(); methodit.hasNext();) { MethodDescriptor md=(MethodDescriptor) methodit.next(); boolean isnative=md.getModifiers().isNative(); boolean isjoin = md.getClassDesc().getSymbol().equals(TypeUtil.ThreadClass)&&!nodemd.getModifiers().isStatic()&&nodemd.numParameters()==0&&md.getSymbol().equals("join"); LocalityBinding lb=new LocalityBinding(md, isatomic); if (isnative&&isatomic) { System.out.println("Don't call native methods in atomic blocks!"+currlb.getMethod()); } if (runmethodset==null||!runmethodset.contains(md)) { //Skip this part if it is a run method for(int i=0; i()); classtolb.get(lb.getMethod().getClassDesc()).add(lb); if (!methodtolb.containsKey(lb.getMethod())) methodtolb.put(lb.getMethod(), new HashSet()); methodtolb.get(lb.getMethod()).add(lb); } else lb=discovered.get(lb); Integer returnval=lb.getGlobalReturn(); currreturnval=merge(returnval, currreturnval); if (!dependence.containsKey(lb)) dependence.put(lb, new HashSet()); dependence.get(lb).add(currlb); if (!calldep.containsKey(currlb)) calldep.put(currlb, new HashSet()); calldep.get(currlb).add(lb); } if (fc.getReturnTemp()!=null) { currtable.put(fc.getReturnTemp(), currreturnval); } } void processFieldNode(LocalityBinding lb, FlatFieldNode ffn, boolean transaction, Hashtable currtable) { Integer type=currtable.get(ffn.getSrc()); TempDescriptor dst=ffn.getDst(); if (type.equals(LOCAL)) { if (ffn.getField().isGlobal()) currtable.put(dst,GLOBAL); else currtable.put(dst,LOCAL); } else if (type.equals(GLOBAL)) { if (!transaction) throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation()); if (ffn.getField().getType().isPrimitive()&&!ffn.getField().getType().isArray()) currtable.put(dst, LOCAL); // primitives are local else currtable.put(dst, GLOBAL); } else if (type.equals(EITHER)) { if (ffn.getField().getType().isPrimitive()&&!ffn.getField().getType().isArray()) currtable.put(dst, LOCAL); // primitives are local else if (ffn.getField().isGlobal()) currtable.put(dst, GLOBAL); else currtable.put(dst, EITHER); } else if (type.equals(CONFLICT)) { throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation()); } } //need to handle primitives void processSetFieldNode(LocalityBinding lb, FlatSetFieldNode fsfn, boolean transaction, Hashtable currtable) { Integer srctype=currtable.get(fsfn.getSrc()); Integer dsttype=currtable.get(fsfn.getDst()); if (dsttype.equals(LOCAL)) { if (fsfn.getField().isGlobal()) { if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER))) throw new Error("Writing possible local reference to global field in context: \n"+lb.getExplanation()); } else { if (!(srctype.equals(LOCAL)||srctype.equals(EITHER))) throw new Error("Writing possible global reference to local object in context: \n"+lb.getExplanation()); } } else if (dsttype.equals(GLOBAL)) { if (!transaction) throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation()); //okay to store primitives in global object if (srctype.equals(LOCAL) && fsfn.getField().getType().isPrimitive() && !fsfn.getField().getType().isArray()) return; if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER))) throw new Error("Writing possible local reference to global object in context:\n"+lb.getExplanation()+" for FlatFieldNode "+fsfn); } else if (dsttype.equals(EITHER)) { if (srctype.equals(CONFLICT)) throw new Error("Using reference that could be local or global in context:\n"+lb.getExplanation()); } else if (dsttype.equals(CONFLICT)) { throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation()); } } void processNew(LocalityBinding lb, FlatNew fn, boolean transaction, Hashtable currtable) { if (fn.isGlobal()&&!transaction) { throw new Error("Allocating global object outside of transaction in context:"+lb.getExplanation()); } if (fn.isGlobal()) currtable.put(fn.getDst(), GLOBAL); else currtable.put(fn.getDst(), LOCAL); } void processOpNode(FlatOpNode fon, Hashtable currtable) { /* Just propagate value */ Integer srcvalue=currtable.get(fon.getLeft()); if (srcvalue==null) { if (!fon.getLeft().getType().isPtr()) { srcvalue=LOCAL; } else throw new Error(fon.getLeft()+" is undefined!"); } currtable.put(fon.getDest(), srcvalue); } void processCastNode(FlatCastNode fcn, Hashtable currtable) { currtable.put(fcn.getDst(), currtable.get(fcn.getSrc())); } void processLiteralNode(FlatLiteralNode fln, Hashtable currtable) { //null is either if (fln.getValue()==null) currtable.put(fln.getDst(), EITHER); else currtable.put(fln.getDst(), LOCAL); } void processReturnNode(LocalityBinding lb, FlatReturnNode frn, Hashtable currtable) { if(frn.getReturnTemp()!=null) { Integer returntype=currtable.get(frn.getReturnTemp()); lb.setGlobalReturn(merge(returntype, lb.getGlobalReturn())); } } void processSetElementNode(LocalityBinding lb, FlatSetElementNode fsen, Hashtable currtable, boolean isatomic) { Integer srctype=currtable.get(fsen.getSrc()); Integer dsttype=currtable.get(fsen.getDst()); if (dsttype.equals(LOCAL)) { if (!(srctype.equals(LOCAL)||srctype.equals(EITHER))) throw new Error("Writing possible global reference to local object in context:\n"+lb.getExplanation()+fsen); } else if (dsttype.equals(GLOBAL)) { if (srctype.equals(LOCAL) && fsen.getDst().getType().dereference().isPrimitive() && !fsen.getDst().getType().dereference().isArray()) return; if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER))) throw new Error("Writing possible local reference to global object in context:\n"+lb.getExplanation()); if (!isatomic) throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation()); } else if (dsttype.equals(EITHER)) { if (srctype.equals(CONFLICT)) throw new Error("Using reference that could be local or global in context:\n"+lb.getExplanation()); } else if (dsttype.equals(CONFLICT)) { throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation()); } } void processElementNode(LocalityBinding lb, FlatElementNode fen, Hashtable currtable, boolean isatomic) { Integer type=currtable.get(fen.getSrc()); TempDescriptor dst=fen.getDst(); if (type.equals(LOCAL)) { currtable.put(dst,LOCAL); } else if (type.equals(GLOBAL)) { if (!isatomic) throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation()); if(fen.getSrc().getType().dereference().isPrimitive()&& !fen.getSrc().getType().dereference().isArray()) currtable.put(dst, LOCAL); else currtable.put(dst, GLOBAL); } else if (type.equals(EITHER)) { if(fen.getSrc().getType().dereference().isPrimitive()&& !fen.getSrc().getType().dereference().isArray()) currtable.put(dst, LOCAL); else currtable.put(dst, EITHER); } else if (type.equals(CONFLICT)) { throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation()); } } void processAtomicEnterNode(FlatAtomicEnterNode fen, Hashtable atomictable) { int atomic=atomictable.get(fen).intValue(); atomictable.put(fen, new Integer(atomic+1)); } void processAtomicExitNode(FlatAtomicExitNode fen, Hashtable atomictable) { int atomic=atomictable.get(fen).intValue(); atomictable.put(fen, new Integer(atomic-1)); } private Hashtable> computeLiveTemps(FlatMethod fm) { Hashtable> nodetotemps=new Hashtable>(); Set toprocess=fm.getNodeSet(); while(!toprocess.isEmpty()) { FlatNode fn=toprocess.iterator().next(); toprocess.remove(fn); List reads=Arrays.asList(fn.readsTemps()); List writes=Arrays.asList(fn.writesTemps()); HashSet tempset=new HashSet(); for(int i=0; i lbit=getLocalityBindings().iterator(); lbit.hasNext();) { LocalityBinding lb=lbit.next(); computeTempstoSave(lb); } } /* Need to checkpoint all temps that could be read from along any * path that are either: 1) Written to by any assignment inside the transaction 2) Read from a global temp. Generate tempstosave map from localitybinding->flatatomicenternode->Set */ private void computeTempstoSave(LocalityBinding lb) { if (lb.isAtomic()) return; Hashtable atomictab=getAtomic(lb); Hashtable> temptab=getNodeTempInfo(lb); MethodDescriptor md=lb.getMethod(); FlatMethod fm=state.getMethodFlat(md); Hashtable> nodetotemps=computeLiveTemps(fm); Hashtable> nodetosavetemps=new Hashtable>(); tempstosave.put(lb, nodetosavetemps); Hashtable nodemap=new Hashtable(); HashSet toprocess=new HashSet(); HashSet discovered=new HashSet(); toprocess.add(fm); discovered.add(fm); while(!toprocess.isEmpty()) { FlatNode fn=toprocess.iterator().next(); toprocess.remove(fn); boolean isatomic=atomictab.get(fn).intValue()>0; if (isatomic&& atomictab.get(fn.getPrev(0)).intValue()==0) { assert(fn.getPrev(0).kind()==FKind.FlatAtomicEnterNode); nodemap.put(fn, (FlatAtomicEnterNode)fn); nodetosavetemps.put((FlatAtomicEnterNode)fn, new HashSet()); } else if (isatomic) { FlatAtomicEnterNode atomicnode=nodemap.get(fn); Set livetemps=nodetotemps.get(fn); List reads=Arrays.asList(fn.readsTemps()); List writes=Arrays.asList(fn.readsTemps()); for(Iterator tempit=livetemps.iterator(); tempit.hasNext();) { TempDescriptor tmp=tempit.next(); if (writes.contains(tmp)) { nodetosavetemps.get(atomicnode).add(tmp); } else if (reads.contains(tmp)&&temptab.get(fn).get(tmp)==GLOBAL) { nodetosavetemps.get(atomicnode).add(tmp); } } } for(int i=0; i