fix edge propagation to union effects
[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 Set<HeapRegionNode> 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 Set<HeapRegionNode> 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 Set<HeapRegionNode> 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 Set<HeapRegionNode> 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   public String prettyPrintNodeSet( Set<HeapRegionNode> s ) {
90     String out = "{\n";
91
92     Iterator<HeapRegionNode> i = s.iterator();
93     while( i.hasNext() ) {
94       HeapRegionNode n = i.next();
95
96       AllocationSite as = n.getAllocationSite();
97       if( as == null ) {
98         out += "  "+n.toString()+",\n";
99       } else {
100         out += "  "+n.toString()+": "+as.toStringVerbose()+",\n";
101       }
102     }
103
104     out += "}\n";
105     return out;
106   }
107
108
109   // use the methods given above to check every possible alias
110   // between task parameters and flagged allocation sites reachable
111   // from the task
112   public void writeAllAliases(String outputFile, String timeReport) throws java.io.IOException {
113
114     BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
115
116     bw.write("Conducting ownership analysis with allocation depth = "+allocationDepth+"\n");
117     bw.write(timeReport+"\n");
118
119     // look through every task for potential aliases
120     Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
121     while( taskItr.hasNext() ) {
122       TaskDescriptor td = (TaskDescriptor) taskItr.next();
123
124       bw.write("\n---------"+td+"--------\n");
125
126       HashSet<AllocationSite> allocSites = getFlaggedAllocationSitesReachableFromTask(td);
127
128       Set<HeapRegionNode> common;
129
130       // for each task parameter, check for aliases with
131       // other task parameters and every allocation site
132       // reachable from this task
133       boolean foundSomeAlias = false;
134
135       FlatMethod fm = state.getMethodFlat(td);
136       for( int i = 0; i < fm.numParameters(); ++i ) {
137
138         // for the ith parameter check for aliases to all
139         // higher numbered parameters
140         for( int j = i + 1; j < fm.numParameters(); ++j ) {
141           common = createsPotentialAliases(td, i, j);
142           if( !common.isEmpty() ) {
143             foundSomeAlias = true;
144             bw.write("Potential alias between parameters "+i+" and "+j+".\n");
145             bw.write(prettyPrintNodeSet( common )+"\n" );
146           }
147         }
148
149         // for the ith parameter, check for aliases against
150         // the set of allocation sites reachable from this
151         // task context
152         Iterator allocItr = allocSites.iterator();
153         while( allocItr.hasNext() ) {
154           AllocationSite as = (AllocationSite) allocItr.next();
155           common = createsPotentialAliases(td, i, as);
156           if( !common.isEmpty() ) {
157             foundSomeAlias = true;
158             bw.write("Potential alias between parameter "+i+" and "+as.getFlatNew()+".\n");
159             bw.write(prettyPrintNodeSet( common )+"\n" );
160           }
161         }
162       }
163
164       // for each allocation site check for aliases with
165       // other allocation sites in the context of execution
166       // of this task
167       HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
168       Iterator allocItr1 = allocSites.iterator();
169       while( allocItr1.hasNext() ) {
170         AllocationSite as1 = (AllocationSite) allocItr1.next();
171
172         Iterator allocItr2 = allocSites.iterator();
173         while( allocItr2.hasNext() ) {
174           AllocationSite as2 = (AllocationSite) allocItr2.next();
175           
176           if( !outerChecked.contains(as2) ) {
177             common = createsPotentialAliases(td, as1, as2);
178
179             if( !common.isEmpty() ) {
180               foundSomeAlias = true;
181               bw.write("Potential alias between "+as1.getFlatNew()+" and "+as2.getFlatNew()+".\n");
182               bw.write(prettyPrintNodeSet( common )+"\n" );
183             }
184           }
185         }
186
187         outerChecked.add(as1);
188       }
189
190       if( !foundSomeAlias ) {
191         bw.write("No aliases between flagged objects in Task "+td+".\n");
192       }
193     }
194
195     bw.write( "\n"+computeAliasContextHistogram() );
196     bw.close();
197   }
198
199
200   // this version of writeAllAliases is for Java programs that have no tasks
201   public void writeAllAliasesJava(String outputFile, String timeReport) throws java.io.IOException {
202     assert !state.TASK;    
203
204     BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
205
206     bw.write("Conducting ownership analysis with allocation depth = "+allocationDepth+"\n");
207     bw.write(timeReport+"\n\n");
208
209     boolean foundSomeAlias = false;
210
211     Descriptor d = typeUtil.getMain();
212     HashSet<AllocationSite> allocSites = getFlaggedAllocationSites(d);
213
214     // for each allocation site check for aliases with
215     // other allocation sites in the context of execution
216     // of this task
217     HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
218     Iterator allocItr1 = allocSites.iterator();
219     while( allocItr1.hasNext() ) {
220       AllocationSite as1 = (AllocationSite) allocItr1.next();
221       
222       Iterator allocItr2 = allocSites.iterator();
223       while( allocItr2.hasNext() ) {
224         AllocationSite as2 = (AllocationSite) allocItr2.next();
225         
226         if( !outerChecked.contains(as2) ) {       
227           Set<HeapRegionNode> common = createsPotentialAliases(d, as1, as2);
228
229           if( !common.isEmpty() ) {
230             foundSomeAlias = true;
231             bw.write("Potential alias between "+as1.getDisjointId()+" and "+as2.getDisjointId()+".\n");
232             bw.write( prettyPrintNodeSet( common )+"\n" );
233           }
234         }
235       }
236       
237       outerChecked.add(as1);
238     }
239     
240     if( !foundSomeAlias ) {
241       bw.write("No aliases between flagged objects found.\n");
242     }
243
244     bw.write( "\n"+computeAliasContextHistogram() );
245     bw.close();
246   }
247   ///////////////////////////////////////////
248   //
249   // end public interface
250   //
251   ///////////////////////////////////////////
252
253
254
255
256
257
258
259
260   // data from the compiler
261   private State state;
262   private TypeUtil typeUtil;
263   private CallGraph callGraph;
264   private int allocationDepth;
265
266   // used to identify HeapRegionNode objects
267   // A unique ID equates an object in one
268   // ownership graph with an object in another
269   // graph that logically represents the same
270   // heap region
271   // start at 10 and increment to leave some
272   // reserved IDs for special purposes
273   static private int uniqueIDcount = 10;
274
275
276   // Use these data structures to track progress of
277   // processing all methods in the program, and by methods
278   // TaskDescriptor and MethodDescriptor are combined
279   // together, with a common parent class Descriptor
280   private Hashtable<MethodContext, OwnershipGraph>           mapMethodContextToInitialParamAllocGraph;
281   private Hashtable<MethodContext, OwnershipGraph>           mapMethodContextToCompleteOwnershipGraph;
282   private Hashtable<FlatNew,       AllocationSite>           mapFlatNewToAllocationSite;
283   private Hashtable<Descriptor,    HashSet<AllocationSite> > mapDescriptorToAllocationSiteSet;
284   private Hashtable<MethodContext, Integer>                  mapMethodContextToNumUpdates;
285   private Hashtable<Descriptor,    HashSet<MethodContext> >  mapDescriptorToAllMethodContexts;
286   private Hashtable<MethodContext, HashSet<MethodContext> >  mapMethodContextToDependentContexts;
287
288   // Use these data structures to track progress of one pass of
289   // processing the FlatNodes of a particular method
290   private HashSet  <FlatNode>                 flatNodesToVisit;
291   private Hashtable<FlatNode, OwnershipGraph> mapFlatNodeToOwnershipGraph;
292   private HashSet  <FlatReturnNode>           returnNodesToCombineForCompleteOwnershipGraph;
293
294   // descriptorsToAnalyze identifies the set of tasks and methods
295   // that are reachable from the program tasks, this set is initialized
296   // and then remains static
297   private HashSet<Descriptor> descriptorsToAnalyze;
298
299   // descriptorsToVisit is initialized to descriptorsToAnalyze and is
300   // reduced by visiting a descriptor during analysis.  When dependents
301   // must be scheduled, only those contained in descriptorsToAnalyze
302   // should be re-added to this queue
303   private PriorityQueue<MethodContextQWrapper> methodContextsToVisitQ;
304   private Set          <MethodContext>         methodContextsToVisitSet;
305   private Hashtable<Descriptor, Integer> mapDescriptorToPriority;
306
307
308   // special field descriptors for array elements
309   public static final String arrayElementFieldName = "___element_";
310   private static Hashtable<TypeDescriptor, FieldDescriptor> mapTypeToArrayField =
311     new Hashtable<TypeDescriptor, FieldDescriptor>();
312
313
314   // for controlling DOT file output
315   private boolean writeDOTs;
316   private boolean writeAllDOTs;
317
318
319
320   // this analysis generates an ownership graph for every task
321   // in the program
322   public OwnershipAnalysis(State state,
323                            TypeUtil tu,
324                            CallGraph callGraph,
325                            int allocationDepth,
326                            boolean writeDOTs,
327                            boolean writeAllDOTs,
328                            String aliasFile) throws java.io.IOException {
329
330     double timeStartAnalysis = (double) System.nanoTime();
331
332     this.state           = state;
333     this.typeUtil        = tu;
334     this.callGraph       = callGraph;
335     this.allocationDepth = allocationDepth;
336     this.writeDOTs       = writeDOTs;
337     this.writeAllDOTs    = writeAllDOTs;
338
339     descriptorsToAnalyze = new HashSet<Descriptor>();
340
341     mapMethodContextToInitialParamAllocGraph =
342       new Hashtable<MethodContext, OwnershipGraph>();
343
344     mapMethodContextToCompleteOwnershipGraph =
345       new Hashtable<MethodContext, OwnershipGraph>();
346
347     mapFlatNewToAllocationSite =
348       new Hashtable<FlatNew, AllocationSite>();
349
350     mapDescriptorToAllocationSiteSet =
351       new Hashtable<Descriptor, HashSet<AllocationSite> >();
352
353     mapDescriptorToAllMethodContexts = 
354       new Hashtable<Descriptor, HashSet<MethodContext> >();
355
356     mapMethodContextToDependentContexts =
357       new Hashtable<MethodContext, HashSet<MethodContext> >();
358
359     mapDescriptorToPriority = 
360       new Hashtable<Descriptor, Integer>();
361
362
363     if( writeAllDOTs ) {
364       mapMethodContextToNumUpdates = new Hashtable<MethodContext, Integer>();
365     }
366
367
368     if( state.TASK ) {
369       // initialize methods to visit as the set of all tasks in the
370       // program and then any method that could be called starting
371       // from those tasks
372       Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
373       while( taskItr.hasNext() ) {
374         Descriptor d = (Descriptor) taskItr.next();
375         scheduleAllCallees(d);
376       }
377
378     } else {
379       // we are not in task mode, just normal Java, so start with
380       // the main method
381       Descriptor d = typeUtil.getMain();
382       scheduleAllCallees(d);
383     }
384
385
386     // before beginning analysis, initialize every scheduled method
387     // with an ownership graph that has populated parameter index tables
388     // by analyzing the first node which is always a FlatMethod node
389     Iterator<Descriptor> dItr = descriptorsToAnalyze.iterator();
390     while( dItr.hasNext() ) {
391       Descriptor d  = dItr.next();
392       OwnershipGraph og = new OwnershipGraph(allocationDepth, typeUtil);
393
394       FlatMethod fm;
395       if( d instanceof MethodDescriptor ) {
396         fm = state.getMethodFlat( (MethodDescriptor) d);
397       } else {
398         assert d instanceof TaskDescriptor;
399         fm = state.getMethodFlat( (TaskDescriptor) d);
400       }
401
402       MethodContext mc = new MethodContext( d );
403       assert !mapDescriptorToAllMethodContexts.containsKey( d );
404       HashSet<MethodContext> s = new HashSet<MethodContext>();
405       s.add( mc );
406       mapDescriptorToAllMethodContexts.put( d, s );
407
408       //System.out.println("Previsiting " + mc);
409
410       og = analyzeFlatNode(mc, fm, null, og);
411
412       /*
413       try {
414         og.writeGraph( "previsit"+mc, true, true, true, false, false );
415       } catch( Exception e ) {}
416       */
417
418       setGraphForMethodContext(mc, og);
419     }
420
421     // as mentioned above, analyze methods one-by-one, possibly revisiting
422     // a method if the methods that it calls are updated
423     analyzeMethods();
424
425     double timeEndAnalysis = (double) System.nanoTime();
426     double dt = (timeEndAnalysis - timeStartAnalysis)/(Math.pow( 10.0, 9.0 ) );
427     String treport = String.format( "The analysis took %.3f sec.", dt );
428     System.out.println( treport );
429
430     if( writeDOTs && !writeAllDOTs ) {
431       writeFinalContextGraphs();      
432     }  
433
434     if( aliasFile != null ) {
435       if( state.TASK ) {
436         writeAllAliases(aliasFile, treport);
437       } else {
438         writeAllAliasesJava(aliasFile, treport);
439       }
440     }
441   }
442
443   // called from the constructor to help initialize the set
444   // of methods that needs to be analyzed by ownership analysis
445   private void scheduleAllCallees(Descriptor d) {
446     if( descriptorsToAnalyze.contains(d) ) {
447       return;
448     }
449     descriptorsToAnalyze.add(d);
450
451     // start with all method calls to further schedule
452     Set moreMethodsToCheck = moreMethodsToCheck = callGraph.getMethodCalls(d);
453
454     if( d instanceof MethodDescriptor ) {
455       // see if this method has virtual dispatch
456       Set virtualMethods = callGraph.getMethods( (MethodDescriptor)d);
457       moreMethodsToCheck.addAll(virtualMethods);
458     }
459
460     // keep following any further methods identified in
461     // the call chain
462     Iterator methItr = moreMethodsToCheck.iterator();
463     while( methItr.hasNext() ) {
464       Descriptor m = (Descriptor) methItr.next();
465       scheduleAllCallees(m);
466     }
467   }
468
469
470   // manage the set of tasks and methods to be analyzed
471   // and be sure to reschedule tasks/methods when the methods
472   // they call are updated
473   private void analyzeMethods() throws java.io.IOException {  
474
475     // first gather all of the method contexts to analyze
476     HashSet<MethodContext> allContexts = new HashSet<MethodContext>();
477     Iterator<Descriptor> itrd2a = descriptorsToAnalyze.iterator();
478     while( itrd2a.hasNext() ) {
479       HashSet<MethodContext> mcs = mapDescriptorToAllMethodContexts.get( itrd2a.next() );
480       assert mcs != null;
481
482       Iterator<MethodContext> itrmc = mcs.iterator();
483       while( itrmc.hasNext() ) {
484         allContexts.add( itrmc.next() );
485       }
486     }
487
488     // topologically sort them according to the caller graph so leaf calls are
489     // ordered first; use that ordering to give method contexts priorities
490     LinkedList<MethodContext> sortedMethodContexts = topologicalSort( allContexts );   
491
492     methodContextsToVisitQ   = new PriorityQueue<MethodContextQWrapper>();
493     methodContextsToVisitSet = new HashSet<MethodContext>();
494
495     int p = 0;
496     Iterator<MethodContext> mcItr = sortedMethodContexts.iterator();
497     while( mcItr.hasNext() ) {
498       MethodContext mc = mcItr.next();
499       mapDescriptorToPriority.put( mc.getDescriptor(), new Integer( p ) );
500       methodContextsToVisitQ.add( new MethodContextQWrapper( p, mc ) );
501       methodContextsToVisitSet.add( mc );
502       ++p;
503     }
504
505     // analyze methods from the priority queue until it is empty
506     while( !methodContextsToVisitQ.isEmpty() ) {
507       MethodContext mc = methodContextsToVisitQ.poll().getMethodContext();
508       assert methodContextsToVisitSet.contains( mc );
509       methodContextsToVisitSet.remove( mc );
510
511       // because the task or method descriptor just extracted
512       // was in the "to visit" set it either hasn't been analyzed
513       // yet, or some method that it depends on has been
514       // updated.  Recompute a complete ownership graph for
515       // this task/method and compare it to any previous result.
516       // If there is a change detected, add any methods/tasks
517       // that depend on this one to the "to visit" set.
518
519       System.out.println("Analyzing " + mc);
520
521       Descriptor d = mc.getDescriptor();
522       FlatMethod fm;
523       if( d instanceof MethodDescriptor ) {
524         fm = state.getMethodFlat( (MethodDescriptor) d);
525       } else {
526         assert d instanceof TaskDescriptor;
527         fm = state.getMethodFlat( (TaskDescriptor) d);
528       }
529
530       OwnershipGraph og = analyzeFlatMethod(mc, fm);
531       OwnershipGraph ogPrev = mapMethodContextToCompleteOwnershipGraph.get(mc);
532       if( !og.equals(ogPrev) ) {
533         setGraphForMethodContext(mc, og);
534
535         Iterator<MethodContext> depsItr = iteratorDependents( mc );
536         while( depsItr.hasNext() ) {
537           MethodContext mcNext = depsItr.next();
538
539           if( !methodContextsToVisitSet.contains( mcNext ) ) {
540             methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( mcNext.getDescriptor() ), 
541                                                                    mcNext ) );
542             methodContextsToVisitSet.add( mcNext );
543           }
544         }
545       }
546     }
547
548   }
549
550
551   // keep passing the Descriptor of the method along for debugging
552   // and dot file writing
553   private OwnershipGraph
554   analyzeFlatMethod(MethodContext mc,
555                     FlatMethod flatm) throws java.io.IOException {
556
557     // initialize flat nodes to visit as the flat method
558     // because it is the entry point
559
560     flatNodesToVisit = new HashSet<FlatNode>();
561     flatNodesToVisit.add(flatm);
562
563     // initilize the mapping of flat nodes in this flat method to
564     // ownership graph results to an empty mapping
565     mapFlatNodeToOwnershipGraph = new Hashtable<FlatNode, OwnershipGraph>();
566
567     // initialize the set of return nodes that will be combined as
568     // the final ownership graph result to return as an empty set
569     returnNodesToCombineForCompleteOwnershipGraph = new HashSet<FlatReturnNode>();
570
571
572     while( !flatNodesToVisit.isEmpty() ) {
573       FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
574       flatNodesToVisit.remove(fn);
575
576       //System.out.println( "  "+fn );
577
578       // perform this node's contributions to the ownership
579       // graph on a new copy, then compare it to the old graph
580       // at this node to see if anything was updated.
581       OwnershipGraph og = new OwnershipGraph(allocationDepth, typeUtil);
582
583       // start by merging all node's parents' graphs
584       for( int i = 0; i < fn.numPrev(); ++i ) {
585         FlatNode pn = fn.getPrev(i);
586         if( mapFlatNodeToOwnershipGraph.containsKey(pn) ) {
587           OwnershipGraph ogParent = mapFlatNodeToOwnershipGraph.get(pn);
588           og.merge(ogParent);
589         }
590       }
591
592       // apply the analysis of the flat node to the
593       // ownership graph made from the merge of the
594       // parent graphs
595       og = analyzeFlatNode(mc,
596                            fn,
597                            returnNodesToCombineForCompleteOwnershipGraph,
598                            og);
599
600       
601
602      
603       if( takeDebugSnapshots && 
604           mc.getDescriptor().getSymbol().equals( mcDescSymbolDebug ) ) {
605         debugSnapshot(og,fn);
606       }
607      
608
609
610       // if the results of the new graph are different from
611       // the current graph at this node, replace the graph
612       // with the update and enqueue the children for
613       // processing
614       OwnershipGraph ogPrev = mapFlatNodeToOwnershipGraph.get(fn);
615       if( !og.equals(ogPrev) ) {
616         mapFlatNodeToOwnershipGraph.put(fn, og);
617
618         for( int i = 0; i < fn.numNext(); i++ ) {
619           FlatNode nn = fn.getNext(i);
620           flatNodesToVisit.add(nn);
621         }
622       }
623     }
624
625     // end by merging all return nodes into a complete
626     // ownership graph that represents all possible heap
627     // states after the flat method returns
628     OwnershipGraph completeGraph = new OwnershipGraph(allocationDepth, typeUtil);
629     Iterator retItr = returnNodesToCombineForCompleteOwnershipGraph.iterator();
630     while( retItr.hasNext() ) {
631       FlatReturnNode frn = (FlatReturnNode) retItr.next();
632       assert mapFlatNodeToOwnershipGraph.containsKey(frn);
633       OwnershipGraph ogr = mapFlatNodeToOwnershipGraph.get(frn);
634       completeGraph.merge(ogr);
635     }
636
637     return completeGraph;
638   }
639
640
641   private OwnershipGraph
642   analyzeFlatNode(MethodContext mc,
643                   FlatNode fn,
644                   HashSet<FlatReturnNode> setRetNodes,
645                   OwnershipGraph og) throws java.io.IOException {
646
647     TempDescriptor lhs;
648     TempDescriptor rhs;
649     FieldDescriptor fld;
650
651     // use node type to decide what alterations to make
652     // to the ownership graph
653     switch( fn.kind() ) {
654
655     case FKind.FlatMethod:
656       FlatMethod fm = (FlatMethod) fn;
657
658       // there should only be one FlatMethod node as the
659       // parent of all other FlatNode objects, so take
660       // the opportunity to construct the initial graph by
661       // adding parameters labels to new heap regions
662       // AND this should be done once globally so that the
663       // parameter IDs are consistent between analysis
664       // iterations, so if this step has been done already
665       // just merge in the cached version
666       OwnershipGraph ogInitParamAlloc = mapMethodContextToInitialParamAllocGraph.get(mc);
667       if( ogInitParamAlloc == null ) {
668
669         // if the method context has aliased parameters, make sure
670         // there is a blob region for all those param to reference
671         Set<Integer> aliasedParamIndices = mc.getAliasedParamIndices();
672
673         if( !aliasedParamIndices.isEmpty() ) {
674           og.makeAliasedParamHeapRegionNode();
675         }
676
677         // set up each parameter
678         for( int i = 0; i < fm.numParameters(); ++i ) {
679           TempDescriptor tdParam    = fm.getParameter( i );
680           TypeDescriptor typeParam  = tdParam.getType();
681           Integer        paramIndex = new Integer( i );
682
683           if( typeParam.isImmutable() && !typeParam.isArray() ) {
684             // don't bother with this primitive parameter, it
685             // cannot affect reachability
686             continue;
687           }
688
689           if( aliasedParamIndices.contains( paramIndex ) ) {
690             // use the alias blob but give parameters their
691             // own primary obj region
692             og.assignTempEqualToAliasedParam( tdParam,
693                                               paramIndex );         
694           } else {
695             // this parameter is not aliased to others, give it
696             // a fresh primary obj and secondary object
697             og.assignTempEqualToParamAlloc( tdParam,
698                                             mc.getDescriptor() instanceof TaskDescriptor,
699                                             paramIndex );
700           }
701         }
702         
703         // add additional edges for aliased regions if necessary
704         if( !aliasedParamIndices.isEmpty() ) {
705           og.addParam2ParamAliasEdges( fm, aliasedParamIndices );
706         }
707         
708         // clean up reachability on initial parameter shapes
709         og.globalSweep();
710
711         // this maps tokens to parameter indices and vice versa
712         // for when this method is a callee
713         og.prepareParamTokenMaps( fm );
714
715         // cache the graph
716         OwnershipGraph ogResult = new OwnershipGraph(allocationDepth, typeUtil);
717         ogResult.merge(og);
718         mapMethodContextToInitialParamAllocGraph.put(mc, ogResult);
719
720       } else {
721         // or just leverage the cached copy
722         og.merge(ogInitParamAlloc);
723       }
724       break;
725
726     case FKind.FlatOpNode:
727       FlatOpNode fon = (FlatOpNode) fn;
728       if( fon.getOp().getOp() == Operation.ASSIGN ) {
729         lhs = fon.getDest();
730         rhs = fon.getLeft();
731         og.assignTempXEqualToTempY(lhs, rhs);
732       }
733       break;
734
735     case FKind.FlatCastNode:
736       FlatCastNode fcn = (FlatCastNode) fn;
737       lhs = fcn.getDst();
738       rhs = fcn.getSrc();
739
740       TypeDescriptor td = fcn.getType();
741       assert td != null;
742       
743       og.assignTypedTempXEqualToTempY(lhs, rhs, td);
744       break;
745
746     case FKind.FlatFieldNode:
747       FlatFieldNode ffn = (FlatFieldNode) fn;
748       lhs = ffn.getDst();
749       rhs = ffn.getSrc();
750       fld = ffn.getField();
751       if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
752         og.assignTempXEqualToTempYFieldF(lhs, rhs, fld);
753       }
754       break;
755
756     case FKind.FlatSetFieldNode:
757       FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
758       lhs = fsfn.getDst();
759       fld = fsfn.getField();
760       rhs = fsfn.getSrc();
761       if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
762         og.assignTempXFieldFEqualToTempY(lhs, fld, rhs);
763       }
764       break;
765
766     case FKind.FlatElementNode:
767       FlatElementNode fen = (FlatElementNode) fn;
768       lhs = fen.getDst();
769       rhs = fen.getSrc();
770       if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
771
772         assert rhs.getType() != null;
773         assert rhs.getType().isArray();
774         
775         TypeDescriptor  tdElement = rhs.getType().dereference();
776         FieldDescriptor fdElement = getArrayField( tdElement );
777   
778         og.assignTempXEqualToTempYFieldF(lhs, rhs, fdElement);
779       }
780       break;
781
782     case FKind.FlatSetElementNode:
783       FlatSetElementNode fsen = (FlatSetElementNode) fn;
784       lhs = fsen.getDst();
785       rhs = fsen.getSrc();
786       if( !rhs.getType().isImmutable() || rhs.getType().isArray() ) {
787
788         assert lhs.getType() != null;
789         assert lhs.getType().isArray();
790         
791         TypeDescriptor  tdElement = lhs.getType().dereference();
792         FieldDescriptor fdElement = getArrayField( tdElement );
793
794         og.assignTempXFieldFEqualToTempY(lhs, fdElement, rhs);
795       }
796       break;
797
798     case FKind.FlatNew:
799       FlatNew fnn = (FlatNew) fn;
800       lhs = fnn.getDst();
801       if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
802         AllocationSite as = getAllocationSiteFromFlatNewPRIVATE(fnn);
803         og.assignTempEqualToNewAlloc(lhs, as);
804       }
805       break;
806
807     case FKind.FlatCall:
808       FlatCall fc = (FlatCall) fn;
809       MethodDescriptor md = fc.getMethod();
810       FlatMethod flatm = state.getMethodFlat(md);
811       OwnershipGraph ogMergeOfAllPossibleCalleeResults = new OwnershipGraph(allocationDepth, typeUtil);
812
813       if( md.isStatic() ) {
814         // a static method is simply always the same, makes life easy
815         ogMergeOfAllPossibleCalleeResults = og;
816
817         Set<Integer> aliasedParamIndices = 
818           ogMergeOfAllPossibleCalleeResults.calculateAliasedParamSet(fc, md.isStatic(), flatm);
819
820         MethodContext mcNew = new MethodContext( md, aliasedParamIndices );
821         Set contexts = mapDescriptorToAllMethodContexts.get( md );
822         assert contexts != null;
823         contexts.add( mcNew );
824
825         addDependent( mc, mcNew );
826
827         OwnershipGraph onlyPossibleCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
828
829         if( onlyPossibleCallee == null ) {
830           // if this method context has never been analyzed just schedule it for analysis
831           // and skip over this call site for now
832           if( !methodContextsToVisitSet.contains( mcNew ) ) {
833             methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( md ), 
834                                                                    mcNew ) );
835             methodContextsToVisitSet.add( mcNew );
836           }
837           
838         } else {
839           ogMergeOfAllPossibleCalleeResults.resolveMethodCall(fc, md.isStatic(), flatm, onlyPossibleCallee, mc);
840         }
841
842       } else {
843         // if the method descriptor is virtual, then there could be a
844         // set of possible methods that will actually be invoked, so
845         // find all of them and merge all of their results together
846         TypeDescriptor typeDesc = fc.getThis().getType();
847         Set possibleCallees = callGraph.getMethods(md, typeDesc);
848
849         Iterator i = possibleCallees.iterator();
850         while( i.hasNext() ) {
851           MethodDescriptor possibleMd = (MethodDescriptor) i.next();
852           FlatMethod pflatm = state.getMethodFlat(possibleMd);
853
854           // don't alter the working graph (og) until we compute a result for every
855           // possible callee, merge them all together, then set og to that
856           OwnershipGraph ogCopy = new OwnershipGraph(allocationDepth, typeUtil);
857           ogCopy.merge(og);
858
859           Set<Integer> aliasedParamIndices = 
860             ogCopy.calculateAliasedParamSet(fc, possibleMd.isStatic(), pflatm);
861
862           MethodContext mcNew = new MethodContext( possibleMd, aliasedParamIndices );
863           Set contexts = mapDescriptorToAllMethodContexts.get( md );
864           assert contexts != null;
865           contexts.add( mcNew );
866
867           addDependent( mc, mcNew );
868
869           OwnershipGraph ogPotentialCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
870
871           if( ogPotentialCallee == null ) {
872             // if this method context has never been analyzed just schedule it for analysis
873             // and skip over this call site for now
874             if( !methodContextsToVisitSet.contains( mcNew ) ) {
875               methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( md ), 
876                                                                      mcNew ) );
877               methodContextsToVisitSet.add( mcNew );
878             }
879             
880           } else {
881             ogCopy.resolveMethodCall(fc, possibleMd.isStatic(), pflatm, ogPotentialCallee, mc);
882           }
883
884           ogMergeOfAllPossibleCalleeResults.merge(ogCopy);
885         }
886       }
887
888       og = ogMergeOfAllPossibleCalleeResults;
889       break;
890
891     case FKind.FlatReturnNode:
892       FlatReturnNode frn = (FlatReturnNode) fn;
893       rhs = frn.getReturnTemp();
894       if( rhs != null && !rhs.getType().isImmutable() ) {
895         og.assignReturnEqualToTemp(rhs);
896       }
897       setRetNodes.add(frn);
898       break;
899     }
900
901     return og;
902   }
903
904
905   // this method should generate integers strictly greater than zero!
906   // special "shadow" regions are made from a heap region by negating
907   // the ID
908   static public Integer generateUniqueHeapRegionNodeID() {
909     ++uniqueIDcount;
910     return new Integer(uniqueIDcount);
911   }
912
913
914   static public FieldDescriptor getArrayField( TypeDescriptor tdElement ) {
915     FieldDescriptor fdElement = mapTypeToArrayField.get( tdElement );
916     if( fdElement == null ) {
917       fdElement = new FieldDescriptor(new Modifiers(Modifiers.PUBLIC),
918                                       tdElement,
919                                       arrayElementFieldName,
920                                       null,
921                                       false);
922       mapTypeToArrayField.put( tdElement, fdElement );
923     }
924     return fdElement;
925   }
926
927
928   private void setGraphForMethodContext(MethodContext mc, OwnershipGraph og) {
929
930     mapMethodContextToCompleteOwnershipGraph.put(mc, og);
931
932     if( writeDOTs && writeAllDOTs ) {
933       if( !mapMethodContextToNumUpdates.containsKey(mc) ) {
934         mapMethodContextToNumUpdates.put(mc, new Integer(0) );
935       }
936       Integer n = mapMethodContextToNumUpdates.get(mc);
937       try {
938         og.writeGraph(mc, n, true, true, true, false, false);
939       } catch( IOException e ) {}
940       mapMethodContextToNumUpdates.put(mc, n + 1);
941     }
942   }
943
944
945   private void addDependent( MethodContext caller, MethodContext callee ) {
946     HashSet<MethodContext> deps = mapMethodContextToDependentContexts.get( callee );
947     if( deps == null ) {
948       deps = new HashSet<MethodContext>();
949     }
950     deps.add( caller );
951     mapMethodContextToDependentContexts.put( callee, deps );
952   }
953
954   private Iterator<MethodContext> iteratorDependents( MethodContext callee ) {
955     HashSet<MethodContext> deps = mapMethodContextToDependentContexts.get( callee );
956     if( deps == null ) {
957       deps = new HashSet<MethodContext>();
958       mapMethodContextToDependentContexts.put( callee, deps );
959     }
960     return deps.iterator();
961   }
962
963
964   private void writeFinalContextGraphs() {
965     // arguments to writeGraph are:
966     // boolean writeLabels,
967     // boolean labelSelect,
968     // boolean pruneGarbage,
969     // boolean writeReferencers
970     // boolean writeParamMappings
971
972     Set entrySet = mapMethodContextToCompleteOwnershipGraph.entrySet();
973     Iterator itr = entrySet.iterator();
974     while( itr.hasNext() ) {
975       Map.Entry      me = (Map.Entry)      itr.next();
976       MethodContext  mc = (MethodContext)  me.getKey();
977       OwnershipGraph og = (OwnershipGraph) me.getValue();
978
979       try {
980         og.writeGraph(mc, true, true, true, false, false);
981       } catch( IOException e ) {}    
982     }
983   }
984   
985
986   // return just the allocation site associated with one FlatNew node
987   private AllocationSite getAllocationSiteFromFlatNewPRIVATE(FlatNew fn) {
988
989     if( !mapFlatNewToAllocationSite.containsKey(fn) ) {
990       AllocationSite as = new AllocationSite(allocationDepth, fn, fn.getDisjointId());
991
992       // the newest nodes are single objects
993       for( int i = 0; i < allocationDepth; ++i ) {
994         Integer id = generateUniqueHeapRegionNodeID();
995         as.setIthOldest(i, id);
996       }
997
998       // the oldest node is a summary node
999       Integer idSummary = generateUniqueHeapRegionNodeID();
1000       as.setSummary(idSummary);
1001
1002       mapFlatNewToAllocationSite.put(fn, as);
1003     }
1004
1005     return mapFlatNewToAllocationSite.get(fn);
1006   }
1007
1008
1009   // return all allocation sites in the method (there is one allocation
1010   // site per FlatNew node in a method)
1011   private HashSet<AllocationSite> getAllocationSiteSet(Descriptor d) {
1012     if( !mapDescriptorToAllocationSiteSet.containsKey(d) ) {
1013       buildAllocationSiteSet(d);
1014     }
1015
1016     return mapDescriptorToAllocationSiteSet.get(d);
1017
1018   }
1019
1020   private void buildAllocationSiteSet(Descriptor d) {
1021     HashSet<AllocationSite> s = new HashSet<AllocationSite>();
1022
1023     FlatMethod fm;
1024     if( d instanceof MethodDescriptor ) {
1025       fm = state.getMethodFlat( (MethodDescriptor) d);
1026     } else {
1027       assert d instanceof TaskDescriptor;
1028       fm = state.getMethodFlat( (TaskDescriptor) d);
1029     }
1030
1031     // visit every node in this FlatMethod's IR graph
1032     // and make a set of the allocation sites from the
1033     // FlatNew node's visited
1034     HashSet<FlatNode> visited = new HashSet<FlatNode>();
1035     HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
1036     toVisit.add(fm);
1037
1038     while( !toVisit.isEmpty() ) {
1039       FlatNode n = toVisit.iterator().next();
1040
1041       if( n instanceof FlatNew ) {
1042         s.add(getAllocationSiteFromFlatNewPRIVATE( (FlatNew) n) );
1043       }
1044
1045       toVisit.remove(n);
1046       visited.add(n);
1047
1048       for( int i = 0; i < n.numNext(); ++i ) {
1049         FlatNode child = n.getNext(i);
1050         if( !visited.contains(child) ) {
1051           toVisit.add(child);
1052         }
1053       }
1054     }
1055
1056     mapDescriptorToAllocationSiteSet.put(d, s);
1057   }
1058
1059
1060   private HashSet<AllocationSite> getFlaggedAllocationSites(Descriptor dIn) {
1061     
1062     HashSet<AllocationSite> out     = new HashSet<AllocationSite>();
1063     HashSet<Descriptor>     toVisit = new HashSet<Descriptor>();
1064     HashSet<Descriptor>     visited = new HashSet<Descriptor>();
1065
1066     toVisit.add(dIn);
1067
1068     while( !toVisit.isEmpty() ) {
1069       Descriptor d = toVisit.iterator().next();
1070       toVisit.remove(d);
1071       visited.add(d);
1072
1073       HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
1074       Iterator asItr = asSet.iterator();
1075       while( asItr.hasNext() ) {
1076         AllocationSite as = (AllocationSite) asItr.next();
1077         if( as.getDisjointId() != null ) {
1078           out.add(as);
1079         }
1080       }
1081
1082       // enqueue callees of this method to be searched for
1083       // allocation sites also
1084       Set callees = callGraph.getCalleeSet(d);
1085       if( callees != null ) {
1086         Iterator methItr = callees.iterator();
1087         while( methItr.hasNext() ) {
1088           MethodDescriptor md = (MethodDescriptor) methItr.next();
1089
1090           if( !visited.contains(md) ) {
1091             toVisit.add(md);
1092           }
1093         }
1094       }
1095     }
1096     
1097     return out;
1098   }
1099
1100
1101   private HashSet<AllocationSite>
1102   getFlaggedAllocationSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
1103
1104     HashSet<AllocationSite> asSetTotal = new HashSet<AllocationSite>();
1105     HashSet<Descriptor>     toVisit    = new HashSet<Descriptor>();
1106     HashSet<Descriptor>     visited    = new HashSet<Descriptor>();
1107
1108     toVisit.add(td);
1109
1110     // traverse this task and all methods reachable from this task
1111     while( !toVisit.isEmpty() ) {
1112       Descriptor d = toVisit.iterator().next();
1113       toVisit.remove(d);
1114       visited.add(d);
1115
1116       HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
1117       Iterator asItr = asSet.iterator();
1118       while( asItr.hasNext() ) {
1119         AllocationSite as = (AllocationSite) asItr.next();
1120         TypeDescriptor typed = as.getType();
1121         if( typed != null ) {
1122           ClassDescriptor cd = typed.getClassDesc();
1123           if( cd != null && cd.hasFlags() ) {
1124             asSetTotal.add(as);
1125           }
1126         }
1127       }
1128
1129       // enqueue callees of this method to be searched for
1130       // allocation sites also
1131       Set callees = callGraph.getCalleeSet(d);
1132       if( callees != null ) {
1133         Iterator methItr = callees.iterator();
1134         while( methItr.hasNext() ) {
1135           MethodDescriptor md = (MethodDescriptor) methItr.next();
1136
1137           if( !visited.contains(md) ) {
1138             toVisit.add(md);
1139           }
1140         }
1141       }
1142     }
1143
1144
1145     return asSetTotal;
1146   }
1147
1148
1149   private LinkedList<MethodContext> topologicalSort( HashSet<MethodContext> set ) {
1150     HashSet   <MethodContext> discovered = new HashSet   <MethodContext>();
1151     LinkedList<MethodContext> sorted     = new LinkedList<MethodContext>();
1152   
1153     Iterator<MethodContext> itr = set.iterator();
1154     while( itr.hasNext() ) {
1155       MethodContext mc = itr.next();
1156           
1157       if( !discovered.contains( mc ) ) {
1158         dfsVisit( set, mc, sorted, discovered );
1159       }
1160     }
1161     
1162     return sorted;
1163   }
1164   
1165   private void dfsVisit( HashSet<MethodContext> set,
1166                          MethodContext mc,
1167                          LinkedList<MethodContext> sorted,
1168                          HashSet   <MethodContext> discovered ) {
1169     discovered.add( mc );
1170     
1171     Descriptor d = mc.getDescriptor();
1172     if( d instanceof MethodDescriptor ) {
1173       MethodDescriptor md = (MethodDescriptor) d;      
1174       Iterator itr = callGraph.getCallerSet( md ).iterator();
1175       while( itr.hasNext() ) {
1176         Descriptor dCaller = (Descriptor) itr.next();
1177         
1178         // only consider the callers in the original set to analyze
1179         Set<MethodContext> callerContexts = mapDescriptorToAllMethodContexts.get( dCaller );
1180         if( callerContexts == null )
1181           continue;     
1182         
1183         // since the analysis hasn't started, there should be exactly one
1184         // context if there are any at all
1185         assert callerContexts.size() == 1;      
1186         MethodContext mcCaller = callerContexts.iterator().next();
1187         assert set.contains( mcCaller );
1188
1189         if( !discovered.contains( mcCaller ) ) {
1190           dfsVisit( set, mcCaller, sorted, discovered );
1191         }
1192       }
1193     }
1194
1195     sorted.addFirst( mc );
1196   }
1197
1198
1199
1200   private String computeAliasContextHistogram() {
1201     
1202     Hashtable<Integer, Integer> mapNumContexts2NumDesc = 
1203       new Hashtable<Integer, Integer>();
1204   
1205     Iterator itr = mapDescriptorToAllMethodContexts.entrySet().iterator();
1206     while( itr.hasNext() ) {
1207       Map.Entry me = (Map.Entry) itr.next();
1208       HashSet<MethodContext> s = (HashSet<MethodContext>) me.getValue();
1209       
1210       Integer i = mapNumContexts2NumDesc.get( s.size() );
1211       if( i == null ) {
1212         i = new Integer( 0 );
1213       }
1214       mapNumContexts2NumDesc.put( s.size(), i + 1 );
1215     }   
1216
1217     String s = "";
1218     int total = 0;
1219
1220     itr = mapNumContexts2NumDesc.entrySet().iterator();
1221     while( itr.hasNext() ) {
1222       Map.Entry me = (Map.Entry) itr.next();
1223       Integer c0 = (Integer) me.getKey();
1224       Integer d0 = (Integer) me.getValue();
1225       total += d0;
1226       s += String.format( "%4d methods had %4d unique alias contexts.\n", d0, c0 );
1227     }
1228
1229     s += String.format( "\n%4d total methods analayzed.\n", total );
1230
1231     return s;
1232   }
1233
1234
1235
1236   // insert a call to debugSnapshot() somewhere in the analysis 
1237   // to get successive captures of the analysis state
1238   boolean takeDebugSnapshots = false;
1239   String mcDescSymbolDebug = "setFlightPlan";
1240   boolean stopAfterCapture = true;
1241
1242   // increments every visit to debugSnapshot, don't fiddle with it
1243   int debugCounter = 0;
1244
1245   // the value of debugCounter to start reporting the debugCounter
1246   // to the screen to let user know what debug iteration we're at
1247   int numStartCountReport = 0;
1248
1249   // the frequency of debugCounter values to print out, 0 no report
1250   int freqCountReport = 0;
1251
1252   // the debugCounter value at which to start taking snapshots
1253   int iterStartCapture = 0;
1254
1255   // the number of snapshots to take
1256   int numIterToCapture = 10;
1257
1258   void debugSnapshot(OwnershipGraph og, FlatNode fn) {
1259     if( debugCounter > iterStartCapture + numIterToCapture ) {
1260       return;
1261     }
1262
1263     ++debugCounter;
1264     if( debugCounter > numStartCountReport &&
1265         freqCountReport > 0 &&
1266         debugCounter % freqCountReport == 0 ) {
1267       System.out.println("    @@@ debug counter = "+debugCounter);
1268     }
1269     if( debugCounter > iterStartCapture ) {
1270       System.out.println("    @@@ capturing debug "+(debugCounter-iterStartCapture)+" @@@");
1271       String graphName = String.format("snap%04d",debugCounter-iterStartCapture);
1272       if( fn != null ) {
1273         graphName = graphName+fn;
1274       }
1275       try {
1276         og.writeGraph(graphName, true, true, true, false, false);
1277       } catch( Exception e ) {
1278         System.out.println("Error writing debug capture.");
1279         System.exit(0);
1280       }
1281     }
1282
1283     if( debugCounter == iterStartCapture + numIterToCapture && stopAfterCapture ) {
1284       System.out.println("Stopping analysis after debug captures.");
1285       System.exit(0);
1286     }
1287   }
1288 }