This commit was manufactured by cvs2svn to create tag 'buildscript'.
[IRC.git] /
1 package Analysis.Locality;
2
3 import java.util.*;
4 import Analysis.CallGraph.CallGraph;
5 import IR.SymbolTable;
6 import IR.State;
7 import IR.TypeUtil;
8 import IR.MethodDescriptor;
9 import IR.Flat.*;
10
11 public class LocalityAnalysis {
12     State state;
13     Stack lbtovisit;
14     Hashtable<LocalityBinding,LocalityBinding> discovered;
15     Hashtable<LocalityBinding, Set<LocalityBinding>> dependence;
16     Hashtable<LocalityBinding, Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>>> temptab;
17     Hashtable<LocalityBinding, Hashtable<FlatNode, Integer>> atomictab;
18     Hashtable<LocalityBinding, Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>>> tempstosave;
19
20     CallGraph callgraph;
21     TypeUtil typeutil;
22     public static final Integer LOCAL=new Integer(0);
23     public static final Integer GLOBAL=new Integer(1);
24     public static final Integer EITHER=new Integer(2);
25     public static final Integer CONFLICT=new Integer(3);
26
27     public LocalityAnalysis(State state, CallGraph callgraph, TypeUtil typeutil) {
28         this.typeutil=typeutil;
29         this.state=state;
30         this.discovered=new Hashtable<LocalityBinding,LocalityBinding>();
31         this.dependence=new Hashtable<LocalityBinding, Set<LocalityBinding>>();
32         this.temptab=new Hashtable<LocalityBinding, Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>>>();
33         this.atomictab=new Hashtable<LocalityBinding, Hashtable<FlatNode, Integer>>();
34         this.lbtovisit=new Stack();
35         this.callgraph=callgraph;
36         this.tempstosave=new Hashtable<LocalityBinding, Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>>>();
37
38         doAnalysis();
39     }
40
41     /** This method returns a set of LocalityBindings.  A
42      * LocalityBinding specifies a context a method can be invoked in.
43      * It specifies whether the method is in a transaction and whether
44      * its parameter objects are locals or globals.  */
45
46     public Set<LocalityBinding> getLocalityBindings() {
47         return discovered.keySet();
48     }
49
50     /** This method returns a hashtable for a given LocalityBinding
51      * that tells the current local/global status of temps at the each
52      * node in the flat representation. */
53
54     public Hashtable<FlatNode, Hashtable<TempDescriptor, Integer>> getNodeTempInfo(LocalityBinding lb) {
55         return temptab.get(lb);
56     }
57
58     /** This method returns an hashtable for a given LocalitBinding
59      * that tells whether a node in the flat represenation is in a
60      * transaction or not.  Integer values greater than 0 indicate
61      * that the node is in a transaction and give the nesting depth.
62      * The outermost AtomicEnterNode will have a value of 1 and the
63      * outermost AtomicExitNode will have a value of 0. */
64     
65     public Hashtable<FlatNode, Integer> getAtomic(LocalityBinding lb) {
66         return atomictab.get(lb);
67     }
68
69     /** This methods returns a hashtable for a given LocalityBinding
70      * that tells which temps needs to be saved for each
71      * AtomicEnterNode.  */
72
73     public Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>> getTemps(LocalityBinding lb) {
74         return tempstosave.get(lb);
75     }
76
77     private void doAnalysis() {
78         computeLocalityBindings();
79         computeTempstoSave();
80     }
81     
82     private void computeLocalityBindings() {
83         LocalityBinding lbmain=new LocalityBinding(typeutil.getMain(), false);
84         lbmain.setGlobal(0, LOCAL);
85         lbtovisit.add(lbmain);
86         discovered.put(lbmain, lbmain);
87         
88         while(!lbtovisit.empty()) {
89             LocalityBinding lb=(LocalityBinding) lbtovisit.pop();
90             Integer returnglobal=lb.getGlobalReturn();
91             MethodDescriptor md=lb.getMethod();
92             Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>> temptable=new Hashtable<FlatNode,Hashtable<TempDescriptor, Integer>>();
93             Hashtable<FlatNode, Integer> atomictable=new Hashtable<FlatNode, Integer>();
94             computeCallsFlags(md, lb, temptable, atomictable);
95             atomictab.put(lb, atomictable);
96             temptab.put(lb, temptable);
97
98             if (!md.isStatic()&&!returnglobal.equals(lb.getGlobalReturn())) {
99                 //return type is more precise now
100                 //rerun everything that call us
101                 lbtovisit.addAll(dependence.get(lb));
102             }
103         }
104     }
105
106
107     public void computeCallsFlags(MethodDescriptor md, LocalityBinding lb, Hashtable<FlatNode, Hashtable<TempDescriptor, Integer>> temptable, Hashtable<FlatNode, Integer> atomictable) {
108         FlatMethod fm=state.getMethodFlat(md);
109         HashSet<FlatNode> tovisit=new HashSet<FlatNode>();
110         tovisit.add(fm.getNext(0));
111         {
112             // Build table for initial node
113             Hashtable<TempDescriptor,Integer> table=new Hashtable<TempDescriptor,Integer>();
114             temptable.put(fm, table);
115             atomictable.put(fm, lb.isAtomic()?1:0);
116             int offset=md.isStatic()?0:1;
117             if (!md.isStatic()) {
118                 table.put(fm.getParameter(0), lb.getGlobalThis());
119             }
120             for(int i=offset;i<fm.numParameters();i++) {
121                 TempDescriptor temp=fm.getParameter(i);
122                 Integer b=lb.isGlobal(i-offset);
123                 table.put(temp,b);
124             }
125         }
126
127         while(!tovisit.isEmpty()) {
128             FlatNode fn=tovisit.iterator().next();
129             tovisit.remove(fn);
130             Hashtable<TempDescriptor, Integer> currtable=new Hashtable<TempDescriptor, Integer>();
131             int atomicstate=0;
132             for(int i=0;i<fn.numPrev();i++) {
133                 FlatNode prevnode=fn.getPrev(i);
134                 if (atomictable.containsKey(prevnode)) {
135                     atomicstate=atomictable.get(prevnode).intValue();
136                 }
137                 if (!temptable.containsKey(prevnode))
138                     continue;
139                 Hashtable<TempDescriptor, Integer> prevtable=temptable.get(prevnode);
140                 for(Iterator<TempDescriptor> tempit=prevtable.keySet().iterator();tempit.hasNext();) {
141                     TempDescriptor temp=tempit.next();
142                     Integer tmpint=prevtable.get(temp);
143                     Integer oldint=currtable.containsKey(temp)?currtable.get(temp):EITHER;
144                     Integer newint=merge(tmpint, oldint);
145                     currtable.put(temp, newint);
146                 }
147             }
148             atomictable.put(fn, atomicstate);
149             // Process this node
150             switch(fn.kind()) {
151             case FKind.FlatAtomicEnterNode:
152                 processAtomicEnterNode((FlatAtomicEnterNode)fn, atomictable);
153                 break;
154             case FKind.FlatAtomicExitNode:
155                 processAtomicExitNode((FlatAtomicExitNode)fn, atomictable);
156                 break;
157             case FKind.FlatCall:
158                 processCallNode(lb, (FlatCall)fn, currtable, isAtomic(atomictable, fn));
159                 break;
160             case FKind.FlatFieldNode:
161                 processFieldNode(lb, (FlatFieldNode)fn, isAtomic(atomictable, fn), currtable);
162                 break;
163             case FKind.FlatSetFieldNode:
164                 processSetFieldNode(lb, (FlatSetFieldNode)fn, isAtomic(atomictable,fn), currtable);
165                 break;
166             case FKind.FlatNew:
167                 processNew(lb, (FlatNew)fn, isAtomic(atomictable, fn), currtable);
168                 break;
169             case FKind.FlatOpNode:
170                 processOpNode((FlatOpNode)fn, currtable);
171                 break;
172             case FKind.FlatCastNode:
173                 processCastNode((FlatCastNode)fn, currtable);
174                 break;
175             case FKind.FlatLiteralNode:
176                 processLiteralNode((FlatLiteralNode)fn, currtable);
177                 break;
178             case FKind.FlatReturnNode:
179                 processReturnNode(lb, (FlatReturnNode)fn, currtable);
180                 break;
181             case FKind.FlatSetElementNode:
182                 processSetElementNode(lb, (FlatSetElementNode)fn, currtable, isAtomic(atomictable, fn));
183                 break;
184             case FKind.FlatElementNode:
185                 processElementNode(lb, (FlatElementNode)fn, currtable, isAtomic(atomictable, fn));
186                 break;
187             case FKind.FlatCondBranch:
188             case FKind.FlatBackEdge:
189             case FKind.FlatNop:
190                 //No action needed for these
191                 break;
192             case FKind.FlatFlagActionNode:
193             case FKind.FlatCheckNode:
194             case FKind.FlatTagDeclaration:
195                 throw new Error("Incompatible with tasks!");
196             case FKind.FlatMethod:
197             default:
198                 throw new Error();
199             }
200             Hashtable<TempDescriptor,Integer> oldtable=temptable.get(fn);
201             if (oldtable==null||!oldtable.equals(currtable)) {
202                 // Update table for this node
203                 temptable.put(fn, currtable);
204                 for(int i=0;i<fn.numNext();i++) {
205                     tovisit.add(fn.getNext(i));
206                 }
207             }
208         }
209     }
210
211     private static boolean isAtomic(Hashtable<FlatNode, Integer> atomictable, FlatNode fn) {
212         return atomictable.get(fn).intValue()>0;
213     }
214
215     private static Integer merge(Integer a, Integer b) {
216         if (a==null||a.equals(EITHER))
217             return b;
218         if (b==null||b.equals(EITHER))
219             return a;
220         if (a.equals(b))
221             return a;
222         return CONFLICT;
223     }
224
225     void processCallNode(LocalityBinding currlb, FlatCall fc, Hashtable<TempDescriptor, Integer> currtable, boolean isatomic) {
226         MethodDescriptor nodemd=fc.getMethod();
227         Set methodset=fc.getThis()==null?callgraph.getMethods(nodemd):
228             callgraph.getMethods(nodemd, fc.getThis().getType());
229         Integer currreturnval=EITHER; //Start off with the either value
230         for(Iterator methodit=methodset.iterator();methodit.hasNext();) {
231             MethodDescriptor md=(MethodDescriptor) methodit.next();
232             LocalityBinding lb=new LocalityBinding(md, isatomic);
233             for(int i=0;i<fc.numArgs();i++) {
234                 TempDescriptor arg=fc.getArg(i);
235                 lb.setGlobal(i,currtable.get(arg));
236             }
237             if (fc.getThis()!=null) {
238                 Integer thistype=currtable.get(fc.getThis());
239                 if (thistype==null)
240                     thistype=EITHER;
241                 if(thistype.equals(CONFLICT))
242                     throw new Error("Using type that can be either local or global in context:\n"+currlb.getExplanation());
243                 if(thistype.equals(GLOBAL)&&!isatomic)
244                     throw new Error("Using global object outside of transaction in context:\n"+currlb.getExplanation());
245                 lb.setGlobalThis(thistype);
246             } else
247                 lb.setGlobalThis(EITHER);//default value
248             //lb is built
249             if (!discovered.containsKey(lb)) {
250                 lb.setGlobalReturn(EITHER);
251                 lb.setParent(currlb);
252                 lbtovisit.add(lb);
253                 discovered.put(lb, lb);
254             } else
255                 lb=discovered.get(lb);
256             Integer returnval=lb.getGlobalReturn();
257             currreturnval=merge(returnval, currreturnval);
258             if (!dependence.containsKey(lb))
259                 dependence.put(lb, new HashSet<LocalityBinding>());
260             dependence.get(lb).add(currlb);
261         }
262         if (fc.getReturnTemp()!=null) {
263             currtable.put(fc.getReturnTemp(), currreturnval);
264         }
265     }
266
267     void processFieldNode(LocalityBinding lb, FlatFieldNode ffn, boolean transaction, Hashtable<TempDescriptor, Integer> currtable) {
268         Integer type=currtable.get(ffn.getSrc());
269         TempDescriptor dst=ffn.getDst();
270         if (type.equals(LOCAL)) {
271             if (ffn.getField().isGlobal())
272                 currtable.put(dst,GLOBAL);
273             else
274                 currtable.put(dst,LOCAL);
275         } else if (type.equals(GLOBAL)) {
276             if (!transaction)
277                 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
278             if (ffn.getField().getType().isPrimitive())
279                 currtable.put(dst, LOCAL); // primitives are local
280             else
281                 currtable.put(dst, GLOBAL);
282         } else if (type.equals(EITHER)) {
283             if (ffn.getField().getType().isPrimitive())
284                 currtable.put(dst, LOCAL); // primitives are local
285             else
286                 currtable.put(dst, EITHER);
287         } else if (type.equals(CONFLICT)) {
288             throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
289         }
290     }
291
292     //need to handle primitives
293     void processSetFieldNode(LocalityBinding lb, FlatSetFieldNode fsfn, boolean transaction, Hashtable<TempDescriptor, Integer> currtable) {
294         Integer srctype=currtable.get(fsfn.getSrc());
295         Integer dsttype=currtable.get(fsfn.getDst());
296         
297         if (dsttype.equals(LOCAL)) {
298             if (fsfn.getField().isGlobal()) {
299                 if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER)))
300                     throw new Error("Writing possible local reference to global field in context: \n"+lb.getExplanation());
301             } else {
302                 if (!(srctype.equals(LOCAL)||srctype.equals(EITHER)))
303                     throw new Error("Writing possible global reference to local object in context: \n"+lb.getExplanation());
304             }
305         } else if (dsttype.equals(GLOBAL)) {
306             if (!transaction)
307                 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
308             //okay to store primitives in global object
309             if (srctype.equals(LOCAL) && fsfn.getField().getType().isPrimitive())
310                 return;
311             if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER)))
312                 throw new Error("Writing possible local reference to global object in context:\n"+lb.getExplanation());
313         } else if (dsttype.equals(EITHER)) {
314             if (srctype.equals(CONFLICT))
315                 throw new Error("Using reference that could be local or global in context:\n"+lb.getExplanation());
316         } else if (dsttype.equals(CONFLICT)) {
317             throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
318         }
319     }
320
321     void processNew(LocalityBinding lb, FlatNew fn, boolean transaction, Hashtable<TempDescriptor, Integer> currtable) {
322         if (fn.isGlobal()&&!transaction) {
323             throw new Error("Allocating global object outside of transaction in context:"+lb.getExplanation());
324         }
325         if (fn.isGlobal())
326             currtable.put(fn.getDst(), GLOBAL);
327         else
328             currtable.put(fn.getDst(), LOCAL);
329     }
330
331     void processOpNode(FlatOpNode fon, Hashtable<TempDescriptor, Integer> currtable) {
332         /* Just propagate value */
333         currtable.put(fon.getDest(), currtable.get(fon.getLeft()));
334     }
335
336     void processCastNode(FlatCastNode fcn, Hashtable<TempDescriptor, Integer> currtable) {
337         currtable.put(fcn.getDst(), currtable.get(fcn.getSrc()));
338     }
339
340     void processLiteralNode(FlatLiteralNode fln, Hashtable<TempDescriptor, Integer> currtable) {
341         //null is either
342         if (fln.getValue()==null)
343             currtable.put(fln.getDst(), EITHER);
344         else
345             currtable.put(fln.getDst(), LOCAL);
346     }
347
348     void processReturnNode(LocalityBinding lb, FlatReturnNode frn, Hashtable<TempDescriptor, Integer> currtable) {
349         if(frn.getReturnTemp()!=null) {
350             Integer returntype=currtable.get(frn.getReturnTemp());
351             lb.setGlobalReturn(merge(returntype, lb.getGlobalReturn()));
352         }
353     }
354
355     void processSetElementNode(LocalityBinding lb, FlatSetElementNode fsen, Hashtable<TempDescriptor, Integer> currtable, boolean isatomic) {
356         Integer srctype=currtable.get(fsen.getSrc());
357         Integer dsttype=currtable.get(fsen.getDst());
358
359         if (dsttype.equals(LOCAL)) {
360             if (!(srctype.equals(LOCAL)||srctype.equals(EITHER)))
361                 throw new Error("Writing possible global reference to local object in context:\n"+lb.getExplanation());
362         } else if (dsttype.equals(GLOBAL)) {
363             if (!(srctype.equals(GLOBAL)||srctype.equals(EITHER)))
364                 throw new Error("Writing possible local reference to global object in context:\n"+lb.getExplanation());
365             if (!isatomic)
366                 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
367         } else if (dsttype.equals(EITHER)) {
368             if (srctype.equals(CONFLICT))
369                 throw new Error("Using reference that could be local or global in context:\n"+lb.getExplanation());
370         } else if (dsttype.equals(CONFLICT)) {
371             throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
372         }
373     }
374
375     void processElementNode(LocalityBinding lb, FlatElementNode fen, Hashtable<TempDescriptor, Integer> currtable, boolean isatomic) {
376         Integer type=currtable.get(fen.getSrc());
377         TempDescriptor dst=fen.getDst();
378         if (type.equals(LOCAL)) {
379             currtable.put(dst,LOCAL);
380         } else if (type.equals(GLOBAL)) {
381             if (!isatomic)
382                 throw new Error("Global access outside of a transaction in context:\n"+lb.getExplanation());
383             currtable.put(dst, GLOBAL);
384         } else if (type.equals(EITHER)) {
385             currtable.put(dst, EITHER);
386         } else if (type.equals(CONFLICT)) {
387             throw new Error("Access to object that could be either global or local in context:\n"+lb.getExplanation());
388         }
389     }
390
391     void processAtomicEnterNode(FlatAtomicEnterNode fen, Hashtable<FlatNode, Integer> atomictable) {
392         int atomic=atomictable.get(fen).intValue();
393         atomictable.put(fen, new Integer(atomic+1));
394     }
395
396     void processAtomicExitNode(FlatAtomicExitNode fen, Hashtable<FlatNode, Integer> atomictable) {
397         int atomic=atomictable.get(fen).intValue();
398         atomictable.put(fen, new Integer(atomic-1));
399     }
400
401     private Hashtable<FlatNode, Set<TempDescriptor>> computeLiveTemps(FlatMethod fm) {
402         Hashtable<FlatNode, Set<TempDescriptor>> nodetotemps=new Hashtable<FlatNode, Set<TempDescriptor>>();
403
404         Set<FlatNode> toprocess=fm.getNodeSet();
405
406         while(!toprocess.isEmpty()) {
407             FlatNode fn=toprocess.iterator().next();
408             toprocess.remove(fn);
409
410             List<TempDescriptor> reads=Arrays.asList(fn.readsTemps());
411             List<TempDescriptor> writes=Arrays.asList(fn.readsTemps());
412
413             HashSet<TempDescriptor> tempset=new HashSet<TempDescriptor>();
414             for(int i=0;i<fn.numNext();i++) {
415                 FlatNode fnnext=fn.getNext(i);
416                 if (nodetotemps.containsKey(fnnext))
417                     tempset.addAll(nodetotemps.get(fnnext));
418             }
419             tempset.removeAll(writes);
420             tempset.addAll(reads);
421             if (!nodetotemps.containsKey(fn)||
422                 nodetotemps.get(fn).equals(tempset)) {
423                 nodetotemps.put(fn, tempset);
424                 for(int i=0;i<fn.numPrev();i++)
425                     toprocess.add(fn.getPrev(i));
426             }
427         }
428         return nodetotemps;
429     }
430
431     private void computeTempstoSave() {
432         for(Iterator<LocalityBinding> lbit=getLocalityBindings().iterator();lbit.hasNext();) {
433             LocalityBinding lb=lbit.next();
434             computeTempstoSave(lb);
435         }
436     }
437
438     /* Need to checkpoint all temps that could be read from along any
439      * path that are either:
440        1) Written to by any assignment inside the transaction
441        2) Read from a global temp.
442
443        Generate tempstosave map from
444        localitybinding->flatatomicenternode->Set<TempDescriptors>
445     */
446
447     private void computeTempstoSave(LocalityBinding lb) {
448         if (lb.isAtomic())
449             return;
450         Hashtable<FlatNode, Integer> atomictab=getAtomic(lb);
451         Hashtable<FlatNode, Hashtable<TempDescriptor, Integer>> temptab=getNodeTempInfo(lb);
452         MethodDescriptor md=lb.getMethod();
453         FlatMethod fm=state.getMethodFlat(md);
454
455         Hashtable<FlatNode, Set<TempDescriptor>> nodetotemps=computeLiveTemps(fm);
456         Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>> nodetosavetemps=new Hashtable<FlatAtomicEnterNode, Set<TempDescriptor>>();
457         tempstosave.put(lb, nodetosavetemps);
458
459         Hashtable<FlatNode, FlatAtomicEnterNode> nodemap=new Hashtable<FlatNode, FlatAtomicEnterNode>();
460         
461         HashSet<FlatNode> toprocess=new HashSet<FlatNode>();
462         HashSet<FlatNode> discovered=new HashSet<FlatNode>();
463         toprocess.add(fm);
464         discovered.add(fm);
465         while(!toprocess.isEmpty()) {
466             FlatNode fn=toprocess.iterator().next();
467             toprocess.remove(fn);
468             boolean isatomic=atomictab.get(fn).intValue()>0;
469             if (isatomic&&
470                 atomictab.get(fn.getPrev(0)).intValue()==0) {
471                 assert(fn.getPrev(0).kind()==FKind.FlatAtomicEnterNode);
472                 nodemap.put(fn, (FlatAtomicEnterNode)fn);
473                 nodetosavetemps.put((FlatAtomicEnterNode)fn, new HashSet<TempDescriptor>());
474             } else if (isatomic) {
475                 FlatAtomicEnterNode atomicnode=nodemap.get(fn);
476                 Set<TempDescriptor> livetemps=nodetotemps.get(fn);
477                 List<TempDescriptor> reads=Arrays.asList(fn.readsTemps());
478                 List<TempDescriptor> writes=Arrays.asList(fn.readsTemps());
479
480                 for(Iterator<TempDescriptor> tempit=livetemps.iterator();tempit.hasNext();) {
481                     TempDescriptor tmp=tempit.next();
482                     if (writes.contains(tmp)) {
483                         nodetosavetemps.get(fn).add(tmp);
484                     } else if (reads.contains(tmp)&&temptab.get(fn).get(tmp)==GLOBAL) {
485                         nodetosavetemps.get(fn).add(tmp);
486                     }
487                 }
488             }
489             for(int i=0;i<fn.numNext();i++) {
490                 FlatNode fnnext=fn.getNext(i);
491                 if (!discovered.contains(fnnext)) {
492                     discovered.add(fnnext);
493                     toprocess.add(fnnext);
494                     if(isatomic) {
495                         nodemap.put(fnnext, nodemap.get(fn));
496                     }
497                 }
498             }
499         }
500     }
501 }