650f29b41961e74c43b84a13b0bfce77d1f8eec1
[IRC.git] / Robust / src / Analysis / OwnershipAnalysis / OwnershipAnalysis.java
1 package Analysis.OwnershipAnalysis;
2
3 import Analysis.CallGraph.*;
4 import IR.*;
5 import IR.Flat.*;
6 import IR.Tree.Modifiers;
7 import java.util.*;
8 import java.io.*;
9
10
11 public class OwnershipAnalysis {
12
13
14   ///////////////////////////////////////////
15   //
16   //  Public interface to discover possible
17   //  aliases in the program under analysis
18   //
19   ///////////////////////////////////////////
20
21   public HashSet<AllocationSite>
22   getFlaggedAllocationSitesReachableFromTask(TaskDescriptor td) {
23     return getFlaggedAllocationSitesReachableFromTaskPRIVATE(td);
24   }
25
26   public AllocationSite getAllocationSiteFromFlatNew(FlatNew fn) {
27     return getAllocationSiteFromFlatNewPRIVATE(fn);
28   }
29
30
31   public boolean createsPotentialAliases(Descriptor taskOrMethod,
32                                          int paramIndex1,
33                                          int paramIndex2) {
34
35     OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
36     assert(og != null);
37     return og.hasPotentialAlias(paramIndex1, paramIndex2);
38   }
39
40   public boolean createsPotentialAliases(Descriptor taskOrMethod,
41                                          int paramIndex,
42                                          AllocationSite alloc) {
43
44     OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
45     assert(og != null);
46     return og.hasPotentialAlias(paramIndex, alloc);
47   }
48
49   public boolean createsPotentialAliases(Descriptor taskOrMethod,
50                                          AllocationSite alloc,
51                                          int paramIndex) {
52
53     OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
54     assert(og != null);
55     return og.hasPotentialAlias(paramIndex, alloc);
56   }
57
58   public boolean createsPotentialAliases(Descriptor taskOrMethod,
59                                          AllocationSite alloc1,
60                                          AllocationSite alloc2) {
61
62     OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
63     assert(og != null);
64     return og.hasPotentialAlias(alloc1, alloc2);
65   }
66
67
68   protected OwnershipGraph getGraphOfAllContextsFromDescriptor(Descriptor d) {
69     assert d != null;
70
71     OwnershipGraph og = new OwnershipGraph( allocationDepth, typeUtil );
72
73     assert mapDescriptorToAllMethodContexts.containsKey( d );
74     HashSet<MethodContext> contexts = mapDescriptorToAllMethodContexts.get( d );
75     Iterator<MethodContext> mcItr = contexts.iterator();
76     while( mcItr.hasNext() ) {
77       MethodContext mc = mcItr.next();
78
79       OwnershipGraph ogContext = mapMethodContextToCompleteOwnershipGraph.get(mc);
80       assert ogContext != null;
81
82       og.merge( ogContext );
83     }
84
85     return og;
86   }
87
88
89   // use the methods given above to check every possible alias
90   // between task parameters and flagged allocation sites reachable
91   // from the task
92   public void writeAllAliases(String outputFile) throws java.io.IOException {
93
94     BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
95
96     bw.write("Conducting ownership analysis with allocation depth = "+allocationDepth);
97
98     // look through every task for potential aliases
99     Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
100     while( taskItr.hasNext() ) {
101       TaskDescriptor td = (TaskDescriptor) taskItr.next();
102
103       bw.write("\n---------"+td+"--------\n");
104
105       HashSet<AllocationSite> allocSites = getFlaggedAllocationSitesReachableFromTask(td);
106
107       // for each task parameter, check for aliases with
108       // other task parameters and every allocation site
109       // reachable from this task
110       boolean foundSomeAlias = false;
111
112       FlatMethod fm = state.getMethodFlat(td);
113       for( int i = 0; i < fm.numParameters(); ++i ) {
114
115         // for the ith parameter check for aliases to all
116         // higher numbered parameters
117         for( int j = i + 1; j < fm.numParameters(); ++j ) {
118           if( createsPotentialAliases(td, i, j) ) {
119             foundSomeAlias = true;
120             bw.write("Potential alias between parameters "+i+" and "+j+".\n");
121           }
122         }
123
124         // for the ith parameter, check for aliases against
125         // the set of allocation sites reachable from this
126         // task context
127         Iterator allocItr = allocSites.iterator();
128         while( allocItr.hasNext() ) {
129           AllocationSite as = (AllocationSite) allocItr.next();
130           if( createsPotentialAliases(td, i, as) ) {
131             foundSomeAlias = true;
132             bw.write("Potential alias between parameter "+i+" and "+as.getFlatNew()+".\n");
133           }
134         }
135       }
136
137       // for each allocation site check for aliases with
138       // other allocation sites in the context of execution
139       // of this task
140       HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
141       Iterator allocItr1 = allocSites.iterator();
142       while( allocItr1.hasNext() ) {
143         AllocationSite as1 = (AllocationSite) allocItr1.next();
144
145         Iterator allocItr2 = allocSites.iterator();
146         while( allocItr2.hasNext() ) {
147           AllocationSite as2 = (AllocationSite) allocItr2.next();
148
149           if( !outerChecked.contains(as2) &&
150               createsPotentialAliases(td, as1, as2) ) {
151             foundSomeAlias = true;
152             bw.write("Potential alias between "+as1.getFlatNew()+" and "+as2.getFlatNew()+".\n");
153           }
154         }
155
156         outerChecked.add(as1);
157       }
158
159       if( !foundSomeAlias ) {
160         bw.write("No aliases between flagged objects in Task "+td+".\n");
161       }
162     }
163
164     bw.close();
165   }
166
167
168   // this version of writeAllAliases is for Java programs that have no tasks
169   public void writeAllAliasesJava(String outputFile) throws java.io.IOException {
170     assert !state.TASK;    
171
172     BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
173
174     bw.write("Conducting ownership analysis with allocation depth = "+allocationDepth+"\n");
175     boolean foundSomeAlias = false;
176
177     Descriptor d = typeUtil.getMain();
178     HashSet<AllocationSite> allocSites = getFlaggedAllocationSites(d);
179
180     // for each allocation site check for aliases with
181     // other allocation sites in the context of execution
182     // of this task
183     HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
184     Iterator allocItr1 = allocSites.iterator();
185     while( allocItr1.hasNext() ) {
186       AllocationSite as1 = (AllocationSite) allocItr1.next();
187       
188       Iterator allocItr2 = allocSites.iterator();
189       while( allocItr2.hasNext() ) {
190         AllocationSite as2 = (AllocationSite) allocItr2.next();
191         
192         if( !outerChecked.contains(as2) &&
193             createsPotentialAliases(d, as1, as2) ) {
194           foundSomeAlias = true;
195           bw.write("Potential alias between "+as1.getDisjointId()+" and "+as2.getDisjointId()+".\n");
196         }
197       }
198       
199       outerChecked.add(as1);
200     }
201     
202     if( !foundSomeAlias ) {
203       bw.write("No aliases between flagged objects found.\n");
204     }
205
206     bw.close();
207   }
208   ///////////////////////////////////////////
209   //
210   // end public interface
211   //
212   ///////////////////////////////////////////
213
214
215
216
217
218
219
220
221   // data from the compiler
222   private State state;
223   private TypeUtil typeUtil;
224   private CallGraph callGraph;
225   private int allocationDepth;
226
227   // used to identify HeapRegionNode objects
228   // A unique ID equates an object in one
229   // ownership graph with an object in another
230   // graph that logically represents the same
231   // heap region
232   // start at 10 and increment to leave some
233   // reserved IDs for special purposes
234   static private int uniqueIDcount = 10;
235
236
237   // Use these data structures to track progress of
238   // processing all methods in the program, and by methods
239   // TaskDescriptor and MethodDescriptor are combined
240   // together, with a common parent class Descriptor
241   private Hashtable<MethodContext, OwnershipGraph>           mapMethodContextToInitialParamAllocGraph;
242   private Hashtable<MethodContext, OwnershipGraph>           mapMethodContextToCompleteOwnershipGraph;
243   private Hashtable<FlatNew,       AllocationSite>           mapFlatNewToAllocationSite;
244   private Hashtable<Descriptor,    HashSet<AllocationSite> > mapDescriptorToAllocationSiteSet;
245   private Hashtable<MethodContext, Integer>                  mapMethodContextToNumUpdates;
246   private Hashtable<Descriptor,    HashSet<MethodContext> >  mapDescriptorToAllMethodContexts;
247
248   // Use these data structures to track progress of one pass of
249   // processing the FlatNodes of a particular method
250   private HashSet  <FlatNode>                 flatNodesToVisit;
251   private Hashtable<FlatNode, OwnershipGraph> mapFlatNodeToOwnershipGraph;
252   private HashSet  <FlatReturnNode>           returnNodesToCombineForCompleteOwnershipGraph;
253
254   // descriptorsToAnalyze identifies the set of tasks and methods
255   // that are reachable from the program tasks, this set is initialized
256   // and then remains static
257   private HashSet<Descriptor> descriptorsToAnalyze;
258
259   // descriptorsToVisit is initialized to descriptorsToAnalyze and is
260   // reduced by visiting a descriptor during analysis.  When dependents
261   // must be scheduled, only those contained in descriptorsToAnalyze
262   // should be re-added to this set
263   private HashSet   <MethodContext> methodContextsToVisit;
264
265   // used in conjunction with the methodContextsToVisit set, fill with
266   // a topological sort of methodContextsToVisit and then empty that set
267   // algorithm should analyze something in the linked list until it is
268   // empty, and then work on the set as normal.  The sorted linked list
269   // is just another, specially sorted bucket that is part of the
270   // methodContextsToVisit set
271   private LinkedList<MethodContext> sortedMethodContextsToVisit;
272
273
274   // a special field descriptor for all array elements
275   private static FieldDescriptor fdElement = new FieldDescriptor(new Modifiers(Modifiers.PUBLIC),
276                                                                  new TypeDescriptor("Array[]"),
277                                                                  "elements",
278                                                                  null,
279                                                                  false);
280
281   // a special temp descriptor for setting more than one parameter label
282   // to the all-aliased-parameters heap region node
283   protected static TempDescriptor tdAliasedParams = new TempDescriptor("_AllAliasedParams___");
284
285
286   // for controlling DOT file output
287   private boolean writeDOTs;
288   private boolean writeAllDOTs;
289
290
291
292   // this analysis generates an ownership graph for every task
293   // in the program
294   public OwnershipAnalysis(State state,
295                            TypeUtil tu,
296                            CallGraph callGraph,
297                            int allocationDepth,
298                            boolean writeDOTs,
299                            boolean writeAllDOTs,
300                            String aliasFile) throws java.io.IOException {
301
302     double timeStartAnalysis = (double) System.nanoTime();
303
304     this.state           = state;
305     this.typeUtil        = tu;
306     this.callGraph       = callGraph;
307     this.allocationDepth = allocationDepth;
308     this.writeDOTs       = writeDOTs;
309     this.writeAllDOTs    = writeAllDOTs;
310
311     descriptorsToAnalyze = new HashSet<Descriptor>();
312
313     mapMethodContextToInitialParamAllocGraph =
314       new Hashtable<MethodContext, OwnershipGraph>();
315
316     mapMethodContextToCompleteOwnershipGraph =
317       new Hashtable<MethodContext, OwnershipGraph>();
318
319     mapFlatNewToAllocationSite =
320       new Hashtable<FlatNew, AllocationSite>();
321
322     mapDescriptorToAllocationSiteSet =
323       new Hashtable<Descriptor, HashSet<AllocationSite> >();
324
325     mapDescriptorToAllMethodContexts = 
326       new Hashtable<Descriptor, HashSet<MethodContext> >();
327
328
329     if( writeAllDOTs ) {
330       mapMethodContextToNumUpdates = new Hashtable<MethodContext, Integer>();
331     }
332
333
334     if( state.TASK ) {
335       // initialize methods to visit as the set of all tasks in the
336       // program and then any method that could be called starting
337       // from those tasks
338       Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
339       while( taskItr.hasNext() ) {
340         Descriptor d = (Descriptor) taskItr.next();
341         scheduleAllCallees(d);
342       }
343
344     } else {
345       // we are not in task mode, just normal Java, so start with
346       // the main method
347       Descriptor d = typeUtil.getMain();
348       scheduleAllCallees(d);
349     }
350
351
352     // before beginning analysis, initialize every scheduled method
353     // with an ownership graph that has populated parameter index tables
354     // by analyzing the first node which is always a FlatMethod node
355     Iterator<Descriptor> dItr = descriptorsToAnalyze.iterator();
356     while( dItr.hasNext() ) {
357       Descriptor d  = dItr.next();
358       OwnershipGraph og = new OwnershipGraph(allocationDepth, typeUtil);
359
360       FlatMethod fm;
361       if( d instanceof MethodDescriptor ) {
362         fm = state.getMethodFlat( (MethodDescriptor) d);
363       } else {
364         assert d instanceof TaskDescriptor;
365         fm = state.getMethodFlat( (TaskDescriptor) d);
366       }
367
368       MethodContext mc = new MethodContext( d );
369       assert !mapDescriptorToAllMethodContexts.containsKey( d );
370       HashSet<MethodContext> s = new HashSet<MethodContext>();
371       s.add( mc );
372       mapDescriptorToAllMethodContexts.put( d, s );
373
374       //System.out.println("Previsiting " + mc);
375
376       og = analyzeFlatNode(mc, fm, null, og);
377       setGraphForMethodContext(mc, og);
378     }
379
380     // as mentioned above, analyze methods one-by-one, possibly revisiting
381     // a method if the methods that it calls are updated
382     analyzeMethods();
383
384     double timeEndAnalysis = (double) System.nanoTime();
385     double dt = (timeEndAnalysis - timeStartAnalysis)/(Math.pow( 10.0, 9.0 ) );
386     String treport = String.format( "The analysis took %.3f sec.", dt );
387     System.out.println( treport );
388
389     if( writeDOTs && !writeAllDOTs ) {
390       writeFinalContextGraphs();      
391     }  
392
393     if( aliasFile != null ) {
394       if( state.TASK ) {
395         writeAllAliases(aliasFile);
396       } else {
397         writeAllAliasesJava(aliasFile);
398       }
399     }
400   }
401
402   // called from the constructor to help initialize the set
403   // of methods that needs to be analyzed by ownership analysis
404   private void scheduleAllCallees(Descriptor d) {
405     if( descriptorsToAnalyze.contains(d) ) {
406       return;
407     }
408     descriptorsToAnalyze.add(d);
409
410     // start with all method calls to further schedule
411     Set moreMethodsToCheck = moreMethodsToCheck = callGraph.getMethodCalls(d);
412
413     if( d instanceof MethodDescriptor ) {
414       // see if this method has virtual dispatch
415       Set virtualMethods = callGraph.getMethods( (MethodDescriptor)d);
416       moreMethodsToCheck.addAll(virtualMethods);
417     }
418
419     // keep following any further methods identified in
420     // the call chain
421     Iterator methItr = moreMethodsToCheck.iterator();
422     while( methItr.hasNext() ) {
423       Descriptor m = (Descriptor) methItr.next();
424       scheduleAllCallees(m);
425     }
426   }
427
428
429   // manage the set of tasks and methods to be analyzed
430   // and be sure to reschedule tasks/methods when the methods
431   // they call are updated
432   private void analyzeMethods() throws java.io.IOException {
433
434     methodContextsToVisit       = new HashSet   <MethodContext>();    
435     sortedMethodContextsToVisit = new LinkedList<MethodContext>();
436
437     Iterator<Descriptor> itrd2a = descriptorsToAnalyze.iterator();
438     while( itrd2a.hasNext() ) {
439       HashSet<MethodContext> mcs = mapDescriptorToAllMethodContexts.get( itrd2a.next() );
440       assert mcs != null;
441
442       Iterator<MethodContext> itrmc = mcs.iterator();
443       while( itrmc.hasNext() ) {
444         methodContextsToVisit.add( itrmc.next() );
445       }
446     }
447
448     sortedMethodContextsToVisit = topologicalSort( methodContextsToVisit );
449     methodContextsToVisit.clear();
450
451     while( !methodContextsToVisit.isEmpty()       ||
452            !sortedMethodContextsToVisit.isEmpty()    ) {
453       
454       MethodContext mc = null;
455
456       if( !sortedMethodContextsToVisit.isEmpty() ) {
457         mc = sortedMethodContextsToVisit.removeFirst();
458       } else {
459         mc = methodContextsToVisit.iterator().next();
460         methodContextsToVisit.remove(mc);
461       }
462
463
464       // because the task or method descriptor just extracted
465       // was in the "to visit" set it either hasn't been analyzed
466       // yet, or some method that it depends on has been
467       // updated.  Recompute a complete ownership graph for
468       // this task/method and compare it to any previous result.
469       // If there is a change detected, add any methods/tasks
470       // that depend on this one to the "to visit" set.
471
472       System.out.println("Analyzing " + mc);
473
474       Descriptor d = mc.getDescriptor();
475       FlatMethod fm;
476       if( d instanceof MethodDescriptor ) {
477         fm = state.getMethodFlat( (MethodDescriptor) d);
478       } else {
479         assert d instanceof TaskDescriptor;
480         fm = state.getMethodFlat( (TaskDescriptor) d);
481       }
482
483       OwnershipGraph og = analyzeFlatMethod(mc, fm);
484       OwnershipGraph ogPrev = mapMethodContextToCompleteOwnershipGraph.get(mc);
485       if( !og.equals(ogPrev) ) {
486         setGraphForMethodContext(mc, og);
487
488         // only methods have dependents, tasks cannot
489         // be invoked by any user program calls
490         if( d instanceof MethodDescriptor ) {
491           MethodDescriptor md = (MethodDescriptor) d;
492           Set dependents = callGraph.getCallerSet(md);
493           if( dependents != null ) {
494             Iterator depItr = dependents.iterator();
495             while( depItr.hasNext() ) {
496               Descriptor dependent = (Descriptor) depItr.next();
497               if( descriptorsToAnalyze.contains(dependent) ) {
498                 
499                 HashSet<MethodContext> mcs = mapDescriptorToAllMethodContexts.get( dependent );
500                 assert mcs != null;
501                 
502                 Iterator<MethodContext> itrmc = mcs.iterator();
503                 while( itrmc.hasNext() ) {
504                   methodContextsToVisit.add( itrmc.next() );
505                 }
506               }
507             }
508           }
509         }
510       }
511     }
512
513   }
514
515
516   // keep passing the Descriptor of the method along for debugging
517   // and dot file writing
518   private OwnershipGraph
519   analyzeFlatMethod(MethodContext mc,
520                     FlatMethod flatm) throws java.io.IOException {
521
522     // initialize flat nodes to visit as the flat method
523     // because it is the entry point
524
525     flatNodesToVisit = new HashSet<FlatNode>();
526     flatNodesToVisit.add(flatm);
527
528     // initilize the mapping of flat nodes in this flat method to
529     // ownership graph results to an empty mapping
530     mapFlatNodeToOwnershipGraph = new Hashtable<FlatNode, OwnershipGraph>();
531
532     // initialize the set of return nodes that will be combined as
533     // the final ownership graph result to return as an empty set
534     returnNodesToCombineForCompleteOwnershipGraph = new HashSet<FlatReturnNode>();
535
536
537     while( !flatNodesToVisit.isEmpty() ) {
538       FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
539       flatNodesToVisit.remove(fn);
540
541       //System.out.println( "  "+fn );
542
543       // perform this node's contributions to the ownership
544       // graph on a new copy, then compare it to the old graph
545       // at this node to see if anything was updated.
546       OwnershipGraph og = new OwnershipGraph(allocationDepth, typeUtil);
547
548       // start by merging all node's parents' graphs
549       for( int i = 0; i < fn.numPrev(); ++i ) {
550         FlatNode pn = fn.getPrev(i);
551         if( mapFlatNodeToOwnershipGraph.containsKey(pn) ) {
552           OwnershipGraph ogParent = mapFlatNodeToOwnershipGraph.get(pn);
553           og.merge(ogParent);
554         }
555       }
556
557       // apply the analysis of the flat node to the
558       // ownership graph made from the merge of the
559       // parent graphs
560       og = analyzeFlatNode(mc,
561                            fn,
562                            returnNodesToCombineForCompleteOwnershipGraph,
563                            og);
564
565       /*
566       if( mc.getDescriptor().getSymbol().equals( "addFlightPlan" ) ) {
567         debugSnapshot(og,fn);
568       }
569       */
570
571
572       // if the results of the new graph are different from
573       // the current graph at this node, replace the graph
574       // with the update and enqueue the children for
575       // processing
576       OwnershipGraph ogPrev = mapFlatNodeToOwnershipGraph.get(fn);
577       if( !og.equals(ogPrev) ) {
578         mapFlatNodeToOwnershipGraph.put(fn, og);
579
580         for( int i = 0; i < fn.numNext(); i++ ) {
581           FlatNode nn = fn.getNext(i);
582           flatNodesToVisit.add(nn);
583         }
584       }
585     }
586
587     // end by merging all return nodes into a complete
588     // ownership graph that represents all possible heap
589     // states after the flat method returns
590     OwnershipGraph completeGraph = new OwnershipGraph(allocationDepth, typeUtil);
591     Iterator retItr = returnNodesToCombineForCompleteOwnershipGraph.iterator();
592     while( retItr.hasNext() ) {
593       FlatReturnNode frn = (FlatReturnNode) retItr.next();
594       assert mapFlatNodeToOwnershipGraph.containsKey(frn);
595       OwnershipGraph ogr = mapFlatNodeToOwnershipGraph.get(frn);
596       completeGraph.merge(ogr);
597     }
598
599     return completeGraph;
600   }
601
602
603   private OwnershipGraph
604   analyzeFlatNode(MethodContext mc,
605                   FlatNode fn,
606                   HashSet<FlatReturnNode> setRetNodes,
607                   OwnershipGraph og) throws java.io.IOException {
608
609     TempDescriptor lhs;
610     TempDescriptor rhs;
611     FieldDescriptor fld;
612
613     // use node type to decide what alterations to make
614     // to the ownership graph
615     switch( fn.kind() ) {
616
617     case FKind.FlatMethod:
618       FlatMethod fm = (FlatMethod) fn;
619
620       // there should only be one FlatMethod node as the
621       // parent of all other FlatNode objects, so take
622       // the opportunity to construct the initial graph by
623       // adding parameters labels to new heap regions
624       // AND this should be done once globally so that the
625       // parameter IDs are consistent between analysis
626       // iterations, so if this step has been done already
627       // just merge in the cached version
628       OwnershipGraph ogInitParamAlloc = mapMethodContextToInitialParamAllocGraph.get(mc);
629       if( ogInitParamAlloc == null ) {
630
631         // if the method context has aliased parameters, make sure
632         // there is a blob region for all those param labels to
633         // reference
634         Set<Integer> aliasedParamIndices = mc.getAliasedParamIndices();
635         if( !aliasedParamIndices.isEmpty() ) {
636           og.makeAliasedParamHeapRegionNode( tdAliasedParams );
637         }
638
639         // set up each parameter
640         for( int i = 0; i < fm.numParameters(); ++i ) {
641           TempDescriptor tdParam = fm.getParameter( i );
642           Integer        paramIndex = new Integer( i );
643
644           if( aliasedParamIndices.contains( paramIndex ) ) {
645             // just point this one to the alias blob
646             og.assignTempEqualToAliasedParam( tdParam,
647                                               tdAliasedParams,
648                                               paramIndex );         
649           } else {
650             // this parameter is not aliased to others, give it
651             // a fresh parameter heap region
652             
653             og.assignTempEqualToParamAlloc(tdParam,
654                                            mc.getDescriptor() instanceof TaskDescriptor,
655                                            paramIndex );
656           }
657         }
658         
659         // cache the graph
660         OwnershipGraph ogResult = new OwnershipGraph(allocationDepth, typeUtil);
661         ogResult.merge(og);
662         mapMethodContextToInitialParamAllocGraph.put(mc, ogResult);
663
664       } else {
665         // or just leverage the cached copy
666         og.merge(ogInitParamAlloc);
667       }
668       break;
669
670     case FKind.FlatOpNode:
671       FlatOpNode fon = (FlatOpNode) fn;
672       if( fon.getOp().getOp() == Operation.ASSIGN ) {
673         lhs = fon.getDest();
674         rhs = fon.getLeft();
675         og.assignTempXEqualToTempY(lhs, rhs);
676       }
677       break;
678
679     case FKind.FlatFieldNode:
680       FlatFieldNode ffn = (FlatFieldNode) fn;
681       lhs = ffn.getDst();
682       rhs = ffn.getSrc();
683       fld = ffn.getField();
684       if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
685         og.assignTempXEqualToTempYFieldF(lhs, rhs, fld);
686       }
687       break;
688
689     case FKind.FlatSetFieldNode:
690       FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
691       lhs = fsfn.getDst();
692       fld = fsfn.getField();
693       rhs = fsfn.getSrc();
694       if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
695         og.assignTempXFieldFEqualToTempY(lhs, fld, rhs);
696       }
697       break;
698
699     case FKind.FlatElementNode:
700       FlatElementNode fen = (FlatElementNode) fn;
701       lhs = fen.getDst();
702       rhs = fen.getSrc();
703       if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
704         og.assignTempXEqualToTempYFieldF(lhs, rhs, fdElement);
705       }
706       break;
707
708     case FKind.FlatSetElementNode:
709       FlatSetElementNode fsen = (FlatSetElementNode) fn;
710       lhs = fsen.getDst();
711       rhs = fsen.getSrc();
712       if( !rhs.getType().isImmutable() || rhs.getType().isArray() ) {
713         og.assignTempXFieldFEqualToTempY(lhs, fdElement, rhs);
714       }
715       break;
716
717     case FKind.FlatNew:
718       FlatNew fnn = (FlatNew) fn;
719       lhs = fnn.getDst();
720       if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
721         AllocationSite as = getAllocationSiteFromFlatNewPRIVATE(fnn);
722         og.assignTempEqualToNewAlloc(lhs, as);
723       }
724       break;
725
726     case FKind.FlatCall:
727       FlatCall fc = (FlatCall) fn;
728       MethodDescriptor md = fc.getMethod();
729       FlatMethod flatm = state.getMethodFlat(md);
730       OwnershipGraph ogMergeOfAllPossibleCalleeResults = new OwnershipGraph(allocationDepth, typeUtil);
731
732       if( md.isStatic() ) {
733         // a static method is simply always the same, makes life easy
734         ogMergeOfAllPossibleCalleeResults = og;
735
736         Set<Integer> aliasedParamIndices = 
737           ogMergeOfAllPossibleCalleeResults.calculateAliasedParamSet(fc, md.isStatic(), flatm);
738         MethodContext mcNew = new MethodContext( md, aliasedParamIndices );      
739         OwnershipGraph onlyPossibleCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
740
741         if( onlyPossibleCallee == null ) {
742           // if this method context has never been analyzed just schedule it for analysis
743           // and skip over this call site for now
744           methodContextsToVisit.add( mcNew );
745           
746         } else {
747           ogMergeOfAllPossibleCalleeResults.resolveMethodCall(fc, md.isStatic(), flatm, onlyPossibleCallee);
748         }
749
750       } else {
751         // if the method descriptor is virtual, then there could be a
752         // set of possible methods that will actually be invoked, so
753         // find all of them and merge all of their results together
754         TypeDescriptor typeDesc = fc.getThis().getType();
755         Set possibleCallees = callGraph.getMethods(md, typeDesc);
756
757         Iterator i = possibleCallees.iterator();
758         while( i.hasNext() ) {
759           MethodDescriptor possibleMd = (MethodDescriptor) i.next();
760           FlatMethod pflatm = state.getMethodFlat(possibleMd);
761
762           // don't alter the working graph (og) until we compute a result for every
763           // possible callee, merge them all together, then set og to that
764           OwnershipGraph ogCopy = new OwnershipGraph(allocationDepth, typeUtil);
765           ogCopy.merge(og);
766
767           Set<Integer> aliasedParamIndices = 
768             ogCopy.calculateAliasedParamSet(fc, possibleMd.isStatic(), pflatm);
769           MethodContext mcNew = new MethodContext( possibleMd, aliasedParamIndices );
770           OwnershipGraph ogPotentialCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
771
772           if( ogPotentialCallee == null ) {
773             // if this method context has never been analyzed just schedule it for analysis
774             // and skip over this call site for now
775             methodContextsToVisit.add( mcNew );
776             
777           } else {
778             ogCopy.resolveMethodCall(fc, possibleMd.isStatic(), pflatm, ogPotentialCallee);
779           }
780
781           ogMergeOfAllPossibleCalleeResults.merge(ogCopy);
782         }
783       }
784
785       og = ogMergeOfAllPossibleCalleeResults;
786       break;
787
788     case FKind.FlatReturnNode:
789       FlatReturnNode frn = (FlatReturnNode) fn;
790       rhs = frn.getReturnTemp();
791       if( rhs != null && !rhs.getType().isImmutable() ) {
792         og.assignReturnEqualToTemp(rhs);
793       }
794       setRetNodes.add(frn);
795       break;
796     }
797
798     return og;
799   }
800
801
802   // insert a call to debugSnapshot() somewhere in the analysis to get
803   // successive captures of the analysis state
804   int debugCounter        = 0;
805   int numStartCountReport = 0;
806   int freqCountReport     = 1;
807   int iterStartCapture    = 0;
808   int numIterToCapture    = 500;
809   void debugSnapshot(OwnershipGraph og, FlatNode fn) {
810     if( debugCounter > numStartCountReport + numIterToCapture ) {
811       return;
812     }
813
814     ++debugCounter;
815     if( debugCounter > numStartCountReport &&
816         debugCounter % freqCountReport == 0 ) {
817       System.out.println("    @@@ debug counter = "+debugCounter);
818     }
819     if( debugCounter > iterStartCapture ) {
820       System.out.println("    @@@ capturing debug "+(debugCounter-iterStartCapture)+" @@@");
821       String graphName = String.format("snap%04d",debugCounter-iterStartCapture);
822       if( fn != null ) {
823         graphName = graphName+fn;
824       }
825       try {
826         og.writeGraph(graphName, true, true, false, false, false);
827       } catch( Exception e ) {
828         System.out.println("Error writing debug capture.");
829         System.exit(0);
830       }
831     }
832     /*
833     if( debugCounter == iterStartCapture + numIterToCapture ) {
834       System.out.println("Stopping analysis after debug captures.");
835       System.exit(0);
836     }
837     */
838   }
839
840
841
842   // this method should generate integers strictly greater than zero!
843   // special "shadow" regions are made from a heap region by negating
844   // the ID
845   static public Integer generateUniqueHeapRegionNodeID() {
846     ++uniqueIDcount;
847     return new Integer(uniqueIDcount);
848   }
849
850
851   private void setGraphForMethodContext(MethodContext mc, OwnershipGraph og) {
852
853     mapMethodContextToCompleteOwnershipGraph.put(mc, og);
854
855     if( writeDOTs && writeAllDOTs ) {
856       if( !mapMethodContextToNumUpdates.containsKey(mc) ) {
857         mapMethodContextToNumUpdates.put(mc, new Integer(0) );
858       }
859       Integer n = mapMethodContextToNumUpdates.get(mc);
860       try {
861         og.writeGraph(mc, n, true, true, true, false, false);
862       } catch( IOException e ) {}
863       mapMethodContextToNumUpdates.put(mc, n + 1);
864     }
865   }
866
867
868   private void writeFinalContextGraphs() {
869     // arguments to writeGraph are:
870     // boolean writeLabels,
871     // boolean labelSelect,
872     // boolean pruneGarbage,
873     // boolean writeReferencers
874     // boolean writeParamMappings
875
876     Set entrySet = mapMethodContextToCompleteOwnershipGraph.entrySet();
877     Iterator itr = entrySet.iterator();
878     while( itr.hasNext() ) {
879       Map.Entry      me = (Map.Entry)      itr.next();
880       MethodContext  mc = (MethodContext)  me.getKey();
881       OwnershipGraph og = (OwnershipGraph) me.getValue();
882
883       try {
884         og.writeGraph(mc, true, true, true, false, false);
885       } catch( IOException e ) {}    
886     }
887   }
888   
889
890   // return just the allocation site associated with one FlatNew node
891   private AllocationSite getAllocationSiteFromFlatNewPRIVATE(FlatNew fn) {
892
893     if( !mapFlatNewToAllocationSite.containsKey(fn) ) {
894       AllocationSite as = new AllocationSite(allocationDepth, fn, fn.getDisjointId());
895
896       // the newest nodes are single objects
897       for( int i = 0; i < allocationDepth; ++i ) {
898         Integer id = generateUniqueHeapRegionNodeID();
899         as.setIthOldest(i, id);
900       }
901
902       // the oldest node is a summary node
903       Integer idSummary = generateUniqueHeapRegionNodeID();
904       as.setSummary(idSummary);
905
906       mapFlatNewToAllocationSite.put(fn, as);
907     }
908
909     return mapFlatNewToAllocationSite.get(fn);
910   }
911
912
913   // return all allocation sites in the method (there is one allocation
914   // site per FlatNew node in a method)
915   private HashSet<AllocationSite> getAllocationSiteSet(Descriptor d) {
916     if( !mapDescriptorToAllocationSiteSet.containsKey(d) ) {
917       buildAllocationSiteSet(d);
918     }
919
920     return mapDescriptorToAllocationSiteSet.get(d);
921
922   }
923
924   private void buildAllocationSiteSet(Descriptor d) {
925     HashSet<AllocationSite> s = new HashSet<AllocationSite>();
926
927     FlatMethod fm;
928     if( d instanceof MethodDescriptor ) {
929       fm = state.getMethodFlat( (MethodDescriptor) d);
930     } else {
931       assert d instanceof TaskDescriptor;
932       fm = state.getMethodFlat( (TaskDescriptor) d);
933     }
934
935     // visit every node in this FlatMethod's IR graph
936     // and make a set of the allocation sites from the
937     // FlatNew node's visited
938     HashSet<FlatNode> visited = new HashSet<FlatNode>();
939     HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
940     toVisit.add(fm);
941
942     while( !toVisit.isEmpty() ) {
943       FlatNode n = toVisit.iterator().next();
944
945       if( n instanceof FlatNew ) {
946         s.add(getAllocationSiteFromFlatNewPRIVATE( (FlatNew) n) );
947       }
948
949       toVisit.remove(n);
950       visited.add(n);
951
952       for( int i = 0; i < n.numNext(); ++i ) {
953         FlatNode child = n.getNext(i);
954         if( !visited.contains(child) ) {
955           toVisit.add(child);
956         }
957       }
958     }
959
960     mapDescriptorToAllocationSiteSet.put(d, s);
961   }
962
963
964   private HashSet<AllocationSite> getFlaggedAllocationSites(Descriptor dIn) {
965     
966     HashSet<AllocationSite> out     = new HashSet<AllocationSite>();
967     HashSet<Descriptor>     toVisit = new HashSet<Descriptor>();
968     HashSet<Descriptor>     visited = new HashSet<Descriptor>();
969
970     toVisit.add(dIn);
971
972     while( !toVisit.isEmpty() ) {
973       Descriptor d = toVisit.iterator().next();
974       toVisit.remove(d);
975       visited.add(d);
976
977       HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
978       Iterator asItr = asSet.iterator();
979       while( asItr.hasNext() ) {
980         AllocationSite as = (AllocationSite) asItr.next();
981         if( as.getDisjointId() != null ) {
982           out.add(as);
983         }
984       }
985
986       // enqueue callees of this method to be searched for
987       // allocation sites also
988       Set callees = callGraph.getCalleeSet(d);
989       if( callees != null ) {
990         Iterator methItr = callees.iterator();
991         while( methItr.hasNext() ) {
992           MethodDescriptor md = (MethodDescriptor) methItr.next();
993
994           if( !visited.contains(md) ) {
995             toVisit.add(md);
996           }
997         }
998       }
999     }
1000     
1001     return out;
1002   }
1003
1004
1005   private HashSet<AllocationSite>
1006   getFlaggedAllocationSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
1007
1008     HashSet<AllocationSite> asSetTotal = new HashSet<AllocationSite>();
1009     HashSet<Descriptor>     toVisit    = new HashSet<Descriptor>();
1010     HashSet<Descriptor>     visited    = new HashSet<Descriptor>();
1011
1012     toVisit.add(td);
1013
1014     // traverse this task and all methods reachable from this task
1015     while( !toVisit.isEmpty() ) {
1016       Descriptor d = toVisit.iterator().next();
1017       toVisit.remove(d);
1018       visited.add(d);
1019
1020       HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
1021       Iterator asItr = asSet.iterator();
1022       while( asItr.hasNext() ) {
1023         AllocationSite as = (AllocationSite) asItr.next();
1024         TypeDescriptor typed = as.getType();
1025         if( typed != null ) {
1026           ClassDescriptor cd = typed.getClassDesc();
1027           if( cd != null && cd.hasFlags() ) {
1028             asSetTotal.add(as);
1029           }
1030         }
1031       }
1032
1033       // enqueue callees of this method to be searched for
1034       // allocation sites also
1035       Set callees = callGraph.getCalleeSet(d);
1036       if( callees != null ) {
1037         Iterator methItr = callees.iterator();
1038         while( methItr.hasNext() ) {
1039           MethodDescriptor md = (MethodDescriptor) methItr.next();
1040
1041           if( !visited.contains(md) ) {
1042             toVisit.add(md);
1043           }
1044         }
1045       }
1046     }
1047
1048
1049     return asSetTotal;
1050   }
1051
1052
1053   private LinkedList<MethodContext> topologicalSort( HashSet<MethodContext> set ) {
1054     HashSet   <MethodContext> discovered = new HashSet   <MethodContext>();
1055     LinkedList<MethodContext> sorted     = new LinkedList<MethodContext>();
1056   
1057     Iterator<MethodContext> itr = set.iterator();
1058     while( itr.hasNext() ) {
1059       MethodContext mc = itr.next();
1060           
1061       if( !discovered.contains( mc ) ) {
1062         dfsVisit( set, mc, sorted, discovered );
1063       }
1064     }
1065     
1066     return sorted;
1067   }
1068   
1069   private void dfsVisit( HashSet<MethodContext> set,
1070                          MethodContext mc,
1071                          LinkedList<MethodContext> sorted,
1072                          HashSet   <MethodContext> discovered ) {
1073     discovered.add( mc );
1074     
1075     Descriptor d = mc.getDescriptor();
1076     if( d instanceof MethodDescriptor ) {
1077       MethodDescriptor md = (MethodDescriptor) d;      
1078       Iterator itr = callGraph.getCallerSet( md ).iterator();
1079       while( itr.hasNext() ) {
1080         Descriptor dCaller = (Descriptor) itr.next();
1081         
1082         // only consider the callers in the original set to analyze
1083         Set<MethodContext> callerContexts = mapDescriptorToAllMethodContexts.get( dCaller );
1084         if( callerContexts == null )
1085           continue;     
1086         
1087         // since the analysis hasn't started, there should be exactly one
1088         // context if there are any at all
1089         assert callerContexts.size() == 1;      
1090         MethodContext mcCaller = callerContexts.iterator().next();
1091         assert set.contains( mcCaller );
1092
1093         if( !discovered.contains( mcCaller ) ) {
1094           dfsVisit( set, mcCaller, sorted, discovered );
1095         }
1096       }
1097     }
1098
1099     sorted.addFirst( mc );
1100   }
1101 }