1 package Analysis.Locality;
4 import Analysis.CallGraph.CallGraph;
8 import IR.MethodDescriptor;
10 import IR.ClassDescriptor;
12 public class LocalityAnalysis {
15 Hashtable<LocalityBinding,LocalityBinding> discovered;
16 Hashtable<LocalityBinding, Set<LocalityBinding>> dependence;
17 Hashtable<LocalityBinding, Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>>> temptab;
18 Hashtable<LocalityBinding, Hashtable<FlatNode, Integer>> atomictab;
19 Hashtable<LocalityBinding, Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>>> tempstosave;
20 Hashtable<ClassDescriptor, Set<LocalityBinding>> classtolb;
24 public static final Integer LOCAL=new Integer(0);
25 public static final Integer GLOBAL=new Integer(1);
26 public static final Integer EITHER=new Integer(2);
27 public static final Integer CONFLICT=new Integer(3);
29 public LocalityAnalysis(State state, CallGraph callgraph, TypeUtil typeutil) {
30 this.typeutil=typeutil;
32 this.discovered=new Hashtable<LocalityBinding,LocalityBinding>();
33 this.dependence=new Hashtable<LocalityBinding, Set<LocalityBinding>>();
34 this.temptab=new Hashtable<LocalityBinding, Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>>>();
35 this.atomictab=new Hashtable<LocalityBinding, Hashtable<FlatNode, Integer>>();
36 this.lbtovisit=new Stack();
37 this.callgraph=callgraph;
38 this.tempstosave=new Hashtable<LocalityBinding, Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>>>();
39 this.classtolb=new Hashtable<ClassDescriptor, Set<LocalityBinding>>();
43 /** This method returns a set of LocalityBindings for the parameter class. */
45 public Set<LocalityBinding> getClassBindings(ClassDescriptor cd) {
46 return classtolb.get(cd);
49 /** This method returns a set of LocalityBindings. A
50 * LocalityBinding specifies a context a method can be invoked in.
51 * It specifies whether the method is in a transaction and whether
52 * its parameter objects are locals or globals. */
54 public Set<LocalityBinding> getLocalityBindings() {
55 return discovered.keySet();
58 /** This method returns a hashtable for a given LocalityBinding
59 * that tells the current local/global status of temps at the each
60 * node in the flat representation. */
62 public Hashtable<FlatNode, Hashtable<TempDescriptor, Integer>> getNodeTempInfo(LocalityBinding lb) {
63 return temptab.get(lb);
66 /** This method returns an hashtable for a given LocalitBinding
67 * that tells whether a node in the flat represenation is in a
68 * transaction or not. Integer values greater than 0 indicate
69 * that the node is in a transaction and give the nesting depth.
70 * The outermost AtomicEnterNode will have a value of 1 and the
71 * outermost AtomicExitNode will have a value of 0. */
73 public Hashtable<FlatNode, Integer> getAtomic(LocalityBinding lb) {
74 return atomictab.get(lb);
77 /** This methods returns a hashtable for a given LocalityBinding
78 * that tells which temps needs to be saved for each
81 public Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>> getTemps(LocalityBinding lb) {
82 return tempstosave.get(lb);
85 public Set<TempDescriptor> getTempSet(LocalityBinding lb) {
86 HashSet<TempDescriptor> set=new HashSet<TempDescriptor>();
87 Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>> table=getTemps(lb);
88 for(Iterator<FlatAtomicEnterNode> faenit=table.keySet().iterator();faenit.hasNext();) {
89 FlatAtomicEnterNode faen=faenit.next();
90 set.addAll(table.get(faen));
95 private void doAnalysis() {
96 computeLocalityBindings();
100 private void computeLocalityBindings() {
101 LocalityBinding lbmain=new LocalityBinding(typeutil.getMain(), false);
102 lbmain.setGlobal(0, LOCAL);
103 lbtovisit.add(lbmain);
104 discovered.put(lbmain, lbmain);
105 if (!classtolb.containsKey(lbmain.getMethod().getClassDesc()))
106 classtolb.put(lbmain.getMethod().getClassDesc(), new HashSet<LocalityBinding>());
107 classtolb.get(lbmain.getMethod().getClassDesc()).add(lbmain);
109 while(!lbtovisit.empty()) {
110 LocalityBinding lb=(LocalityBinding) lbtovisit.pop();
111 Integer returnglobal=lb.getGlobalReturn();
112 MethodDescriptor md=lb.getMethod();
113 Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>> temptable=new Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>>();
114 Hashtable<FlatNode, Integer> atomictable=new Hashtable<FlatNode, Integer>();
115 computeCallsFlags(md, lb, temptable, atomictable);
116 atomictab.put(lb, atomictable);
117 temptab.put(lb, temptable);
119 if (!md.isStatic()&&!returnglobal.equals(lb.getGlobalReturn())) {
120 //return type is more precise now
121 //rerun everything that call us
122 lbtovisit.addAll(dependence.get(lb));
128 public void computeCallsFlags(MethodDescriptor md, LocalityBinding lb, Hashtable<FlatNode, Hashtable<TempDescriptor, Integer>> temptable, Hashtable<FlatNode, Integer> atomictable) {
129 FlatMethod fm=state.getMethodFlat(md);
130 HashSet<FlatNode> tovisit=new HashSet<FlatNode>();
131 tovisit.add(fm.getNext(0));
133 // Build table for initial node
134 Hashtable<TempDescriptor,Integer> table=new Hashtable<TempDescriptor,Integer>();
135 temptable.put(fm, table);
136 atomictable.put(fm, lb.isAtomic()?1:0);
137 int offset=md.isStatic()?0:1;
138 if (!md.isStatic()) {
139 table.put(fm.getParameter(0), lb.getGlobalThis());
141 for(int i=offset;i<fm.numParameters();i++) {
142 TempDescriptor temp=fm.getParameter(i);
143 Integer b=lb.isGlobal(i-offset);
148 while(!tovisit.isEmpty()) {
149 FlatNode fn=tovisit.iterator().next();
151 Hashtable<TempDescriptor, Integer> currtable=new Hashtable<TempDescriptor, Integer>();
153 for(int i=0;i<fn.numPrev();i++) {
154 FlatNode prevnode=fn.getPrev(i);
155 if (atomictable.containsKey(prevnode)) {
156 atomicstate=atomictable.get(prevnode).intValue();
158 if (!temptable.containsKey(prevnode))
160 Hashtable<TempDescriptor, Integer> prevtable=temptable.get(prevnode);
161 for(Iterator<TempDescriptor> tempit=prevtable.keySet().iterator();tempit.hasNext();) {
162 TempDescriptor temp=tempit.next();
163 Integer tmpint=prevtable.get(temp);
164 Integer oldint=currtable.containsKey(temp)?currtable.get(temp):EITHER;
165 Integer newint=merge(tmpint, oldint);
166 currtable.put(temp, newint);
169 atomictable.put(fn, atomicstate);
172 case FKind.FlatAtomicEnterNode:
173 processAtomicEnterNode((FlatAtomicEnterNode)fn, atomictable);
177 case FKind.FlatAtomicExitNode:
178 processAtomicExitNode((FlatAtomicExitNode)fn, atomictable);
181 processCallNode(lb, (FlatCall)fn, currtable, isAtomic(atomictable, fn));
183 case FKind.FlatFieldNode:
184 processFieldNode(lb, (FlatFieldNode)fn, isAtomic(atomictable, fn), currtable);
186 case FKind.FlatSetFieldNode:
187 processSetFieldNode(lb, (FlatSetFieldNode)fn, isAtomic(atomictable,fn), currtable);
190 processNew(lb, (FlatNew)fn, isAtomic(atomictable, fn), currtable);
192 case FKind.FlatOpNode:
193 processOpNode((FlatOpNode)fn, currtable);
195 case FKind.FlatCastNode:
196 processCastNode((FlatCastNode)fn, currtable);
198 case FKind.FlatLiteralNode:
199 processLiteralNode((FlatLiteralNode)fn, currtable);
201 case FKind.FlatReturnNode:
202 processReturnNode(lb, (FlatReturnNode)fn, currtable);
204 case FKind.FlatSetElementNode:
205 processSetElementNode(lb, (FlatSetElementNode)fn, currtable, isAtomic(atomictable, fn));
207 case FKind.FlatElementNode:
208 processElementNode(lb, (FlatElementNode)fn, currtable, isAtomic(atomictable, fn));
210 case FKind.FlatCondBranch:
211 case FKind.FlatBackEdge:
213 //No action needed for these
215 case FKind.FlatFlagActionNode:
216 case FKind.FlatCheckNode:
217 case FKind.FlatTagDeclaration:
218 throw new Error("Incompatible with tasks!");
219 case FKind.FlatMethod:
223 Hashtable<TempDescriptor,Integer> oldtable=temptable.get(fn);
224 if (oldtable==null||!oldtable.equals(currtable)) {
225 // Update table for this node
226 temptable.put(fn, currtable);
227 for(int i=0;i<fn.numNext();i++) {
228 tovisit.add(fn.getNext(i));
234 private static boolean isAtomic(Hashtable<FlatNode, Integer> atomictable, FlatNode fn) {
235 return atomictable.get(fn).intValue()>0;
238 private static Integer merge(Integer a, Integer b) {
239 if (a==null||a.equals(EITHER))
241 if (b==null||b.equals(EITHER))
248 void processCallNode(LocalityBinding currlb, FlatCall fc, Hashtable<TempDescriptor, Integer> currtable, boolean isatomic) {
249 MethodDescriptor nodemd=fc.getMethod();
250 Set methodset=fc.getThis()==null?callgraph.getMethods(nodemd):
251 callgraph.getMethods(nodemd, fc.getThis().getType());
252 Integer currreturnval=EITHER; //Start off with the either value
253 for(Iterator methodit=methodset.iterator();methodit.hasNext();) {
254 MethodDescriptor md=(MethodDescriptor) methodit.next();
255 boolean isnative=md.getModifiers().isNative();
257 LocalityBinding lb=new LocalityBinding(md, isatomic);
258 if (isnative&&isatomic) {
259 System.out.println("Don't call native methods in atomic blocks!");
261 for(int i=0;i<fc.numArgs();i++) {
262 TempDescriptor arg=fc.getArg(i);
263 if(isnative&&(currtable.get(arg).equals(GLOBAL)||
264 currtable.get(arg).equals(CONFLICT)))
265 throw new Error("Potential call to native method "+md+" with global parameter:\n"+currlb.getExplanation());
266 lb.setGlobal(i,currtable.get(arg));
268 if (fc.getThis()!=null) {
269 Integer thistype=currtable.get(fc.getThis());
272 if(thistype.equals(CONFLICT))
273 throw new Error("Using type that can be either local or global in context:\n"+currlb.getExplanation());
274 if(thistype.equals(GLOBAL)&&!isatomic)
275 throw new Error("Using global object outside of transaction in context:\n"+currlb.getExplanation());
276 if (isnative&&thistype.equals(GLOBAL))
277 throw new Error("Potential call to native method "+md+" on global objects:\n"+currlb.getExplanation());
278 lb.setGlobalThis(thistype);
280 lb.setGlobalThis(EITHER);//default value
283 if (!discovered.containsKey(lb)) {
285 lb.setGlobalReturn(LOCAL);
287 lb.setGlobalReturn(EITHER);
288 lb.setParent(currlb);
290 discovered.put(lb, lb);
291 if (!classtolb.containsKey(lb.getMethod().getClassDesc()))
292 classtolb.put(lb.getMethod().getClassDesc(), new HashSet<LocalityBinding>());
293 classtolb.get(lb.getMethod().getClassDesc()).add(lb);
295 lb=discovered.get(lb);
296 Integer returnval=lb.getGlobalReturn();
297 currreturnval=merge(returnval, currreturnval);
298 if (!dependence.containsKey(lb))
299 dependence.put(lb, new HashSet<LocalityBinding>());
300 dependence.get(lb).add(currlb);
302 if (fc.getReturnTemp()!=null) {
303 currtable.put(fc.getReturnTemp(), currreturnval);
307 void processFieldNode(LocalityBinding lb, FlatFieldNode ffn, boolean transaction, Hashtable<TempDescriptor, Integer> currtable) {
308 Integer type=currtable.get(ffn.getSrc());
309 TempDescriptor dst=ffn.getDst();
310 if (type.equals(LOCAL)) {
311 if (ffn.getField().isGlobal())
312 currtable.put(dst,GLOBAL);
314 currtable.put(dst,LOCAL);
315 } else if (type.equals(GLOBAL)) {
317 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
318 if (ffn.getField().getType().isPrimitive())
319 currtable.put(dst, LOCAL); // primitives are local
321 currtable.put(dst, GLOBAL);
322 } else if (type.equals(EITHER)) {
323 if (ffn.getField().getType().isPrimitive())
324 currtable.put(dst, LOCAL); // primitives are local
325 else if (ffn.getField().isGlobal())
326 currtable.put(dst, GLOBAL);
328 currtable.put(dst, EITHER);
329 } else if (type.equals(CONFLICT)) {
330 throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
334 //need to handle primitives
335 void processSetFieldNode(LocalityBinding lb, FlatSetFieldNode fsfn, boolean transaction, Hashtable<TempDescriptor, Integer> currtable) {
336 Integer srctype=currtable.get(fsfn.getSrc());
337 Integer dsttype=currtable.get(fsfn.getDst());
339 if (dsttype.equals(LOCAL)) {
340 if (fsfn.getField().isGlobal()) {
341 if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER)))
342 throw new Error("Writing possible local reference to global field in context: \n"+lb.getExplanation());
344 if (!(srctype.equals(LOCAL)||srctype.equals(EITHER)))
345 throw new Error("Writing possible global reference to local object in context: \n"+lb.getExplanation());
347 } else if (dsttype.equals(GLOBAL)) {
349 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
350 //okay to store primitives in global object
351 if (srctype.equals(LOCAL) && fsfn.getField().getType().isPrimitive())
353 if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER)))
354 throw new Error("Writing possible local reference to global object in context:\n"+lb.getExplanation());
355 } else if (dsttype.equals(EITHER)) {
356 if (srctype.equals(CONFLICT))
357 throw new Error("Using reference that could be local or global in context:\n"+lb.getExplanation());
358 } else if (dsttype.equals(CONFLICT)) {
359 throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
363 void processNew(LocalityBinding lb, FlatNew fn, boolean transaction, Hashtable<TempDescriptor, Integer> currtable) {
364 if (fn.isGlobal()&&!transaction) {
365 throw new Error("Allocating global object outside of transaction in context:"+lb.getExplanation());
368 currtable.put(fn.getDst(), GLOBAL);
370 currtable.put(fn.getDst(), LOCAL);
373 void processOpNode(FlatOpNode fon, Hashtable<TempDescriptor, Integer> currtable) {
374 /* Just propagate value */
375 currtable.put(fon.getDest(), currtable.get(fon.getLeft()));
378 void processCastNode(FlatCastNode fcn, Hashtable<TempDescriptor, Integer> currtable) {
379 currtable.put(fcn.getDst(), currtable.get(fcn.getSrc()));
382 void processLiteralNode(FlatLiteralNode fln, Hashtable<TempDescriptor, Integer> currtable) {
384 if (fln.getValue()==null)
385 currtable.put(fln.getDst(), EITHER);
387 currtable.put(fln.getDst(), LOCAL);
390 void processReturnNode(LocalityBinding lb, FlatReturnNode frn, Hashtable<TempDescriptor, Integer> currtable) {
391 if(frn.getReturnTemp()!=null) {
392 Integer returntype=currtable.get(frn.getReturnTemp());
393 lb.setGlobalReturn(merge(returntype, lb.getGlobalReturn()));
397 void processSetElementNode(LocalityBinding lb, FlatSetElementNode fsen, Hashtable<TempDescriptor, Integer> currtable, boolean isatomic) {
398 Integer srctype=currtable.get(fsen.getSrc());
399 Integer dsttype=currtable.get(fsen.getDst());
401 if (dsttype.equals(LOCAL)) {
402 if (!(srctype.equals(LOCAL)||srctype.equals(EITHER)))
403 throw new Error("Writing possible global reference to local object in context:\n"+lb.getExplanation());
404 } else if (dsttype.equals(GLOBAL)) {
405 if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER)))
406 throw new Error("Writing possible local reference to global object in context:\n"+lb.getExplanation());
408 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
409 } else if (dsttype.equals(EITHER)) {
410 if (srctype.equals(CONFLICT))
411 throw new Error("Using reference that could be local or global in context:\n"+lb.getExplanation());
412 } else if (dsttype.equals(CONFLICT)) {
413 throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
417 void processElementNode(LocalityBinding lb, FlatElementNode fen, Hashtable<TempDescriptor, Integer> currtable, boolean isatomic) {
418 Integer type=currtable.get(fen.getSrc());
419 TempDescriptor dst=fen.getDst();
420 if (type.equals(LOCAL)) {
421 currtable.put(dst,LOCAL);
422 } else if (type.equals(GLOBAL)) {
424 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
425 currtable.put(dst, GLOBAL);
426 } else if (type.equals(EITHER)) {
427 currtable.put(dst, EITHER);
428 } else if (type.equals(CONFLICT)) {
429 throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
433 void processAtomicEnterNode(FlatAtomicEnterNode fen, Hashtable<FlatNode, Integer> atomictable) {
434 int atomic=atomictable.get(fen).intValue();
435 atomictable.put(fen, new Integer(atomic+1));
438 void processAtomicExitNode(FlatAtomicExitNode fen, Hashtable<FlatNode, Integer> atomictable) {
439 int atomic=atomictable.get(fen).intValue();
440 atomictable.put(fen, new Integer(atomic-1));
443 private Hashtable<FlatNode, Set<TempDescriptor>> computeLiveTemps(FlatMethod fm) {
444 Hashtable<FlatNode, Set<TempDescriptor>> nodetotemps=new Hashtable<FlatNode, Set<TempDescriptor>>();
446 Set<FlatNode> toprocess=fm.getNodeSet();
448 while(!toprocess.isEmpty()) {
449 FlatNode fn=toprocess.iterator().next();
450 toprocess.remove(fn);
452 List<TempDescriptor> reads=Arrays.asList(fn.readsTemps());
453 List<TempDescriptor> writes=Arrays.asList(fn.readsTemps());
455 HashSet<TempDescriptor> tempset=new HashSet<TempDescriptor>();
456 for(int i=0;i<fn.numNext();i++) {
457 FlatNode fnnext=fn.getNext(i);
458 if (nodetotemps.containsKey(fnnext))
459 tempset.addAll(nodetotemps.get(fnnext));
461 tempset.removeAll(writes);
462 tempset.addAll(reads);
463 if (!nodetotemps.containsKey(fn)||
464 nodetotemps.get(fn).equals(tempset)) {
465 nodetotemps.put(fn, tempset);
466 for(int i=0;i<fn.numPrev();i++)
467 toprocess.add(fn.getPrev(i));
473 private void computeTempstoSave() {
474 for(Iterator<LocalityBinding> lbit=getLocalityBindings().iterator();lbit.hasNext();) {
475 LocalityBinding lb=lbit.next();
476 computeTempstoSave(lb);
480 /* Need to checkpoint all temps that could be read from along any
481 * path that are either:
482 1) Written to by any assignment inside the transaction
483 2) Read from a global temp.
485 Generate tempstosave map from
486 localitybinding->flatatomicenternode->Set<TempDescriptors>
489 private void computeTempstoSave(LocalityBinding lb) {
492 Hashtable<FlatNode, Integer> atomictab=getAtomic(lb);
493 Hashtable<FlatNode, Hashtable<TempDescriptor, Integer>> temptab=getNodeTempInfo(lb);
494 MethodDescriptor md=lb.getMethod();
495 FlatMethod fm=state.getMethodFlat(md);
497 Hashtable<FlatNode, Set<TempDescriptor>> nodetotemps=computeLiveTemps(fm);
498 Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>> nodetosavetemps=new Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>>();
499 tempstosave.put(lb, nodetosavetemps);
501 Hashtable<FlatNode, FlatAtomicEnterNode> nodemap=new Hashtable<FlatNode, FlatAtomicEnterNode>();
503 HashSet<FlatNode> toprocess=new HashSet<FlatNode>();
504 HashSet<FlatNode> discovered=new HashSet<FlatNode>();
507 while(!toprocess.isEmpty()) {
508 FlatNode fn=toprocess.iterator().next();
509 toprocess.remove(fn);
510 boolean isatomic=atomictab.get(fn).intValue()>0;
512 atomictab.get(fn.getPrev(0)).intValue()==0) {
513 assert(fn.getPrev(0).kind()==FKind.FlatAtomicEnterNode);
514 nodemap.put(fn, (FlatAtomicEnterNode)fn);
515 nodetosavetemps.put((FlatAtomicEnterNode)fn, new HashSet<TempDescriptor>());
516 } else if (isatomic) {
517 FlatAtomicEnterNode atomicnode=nodemap.get(fn);
518 Set<TempDescriptor> livetemps=nodetotemps.get(fn);
519 List<TempDescriptor> reads=Arrays.asList(fn.readsTemps());
520 List<TempDescriptor> writes=Arrays.asList(fn.readsTemps());
522 for(Iterator<TempDescriptor> tempit=livetemps.iterator();tempit.hasNext();) {
523 TempDescriptor tmp=tempit.next();
524 if (writes.contains(tmp)) {
525 nodetosavetemps.get(fn).add(tmp);
526 } else if (reads.contains(tmp)&&temptab.get(fn).get(tmp)==GLOBAL) {
527 nodetosavetemps.get(fn).add(tmp);
531 for(int i=0;i<fn.numNext();i++) {
532 FlatNode fnnext=fn.getNext(i);
533 if (!discovered.contains(fnnext)) {
534 discovered.add(fnnext);
535 toprocess.add(fnnext);
537 nodemap.put(fnnext, nodemap.get(fn));