working support for disjointness on Java, still needs a little more functionality
[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   //
169   // end public interface
170   //
171   ///////////////////////////////////////////
172
173
174
175
176
177
178
179
180   // data from the compiler
181   private State state;
182   private TypeUtil typeUtil;
183   private CallGraph callGraph;
184   private int allocationDepth;
185
186   // used to identify HeapRegionNode objects
187   // A unique ID equates an object in one
188   // ownership graph with an object in another
189   // graph that logically represents the same
190   // heap region
191   // start at 10 and incerement to leave some
192   // reserved IDs for special purposes
193   static private int uniqueIDcount = 10;
194
195
196   // Use these data structures to track progress of
197   // processing all methods in the program, and by methods
198   // TaskDescriptor and MethodDescriptor are combined
199   // together, with a common parent class Descriptor
200   private Hashtable<MethodContext, OwnershipGraph>           mapMethodContextToInitialParamAllocGraph;
201   private Hashtable<MethodContext, OwnershipGraph>           mapMethodContextToCompleteOwnershipGraph;
202   private Hashtable<FlatNew,       AllocationSite>           mapFlatNewToAllocationSite;
203   private Hashtable<Descriptor,    HashSet<AllocationSite> > mapDescriptorToAllocationSiteSet;
204   private Hashtable<MethodContext, Integer>                  mapMethodContextToNumUpdates;
205   private Hashtable<Descriptor,    HashSet<MethodContext> >  mapDescriptorToAllMethodContexts;
206
207   // Use these data structures to track progress of one pass of
208   // processing the FlatNodes of a particular method
209   private HashSet  <FlatNode>                 flatNodesToVisit;
210   private Hashtable<FlatNode, OwnershipGraph> mapFlatNodeToOwnershipGraph;
211   private HashSet  <FlatReturnNode>           returnNodesToCombineForCompleteOwnershipGraph;
212
213   // descriptorsToAnalyze identifies the set of tasks and methods
214   // that are reachable from the program tasks, this set is initialized
215   // and then remains static
216   private HashSet<Descriptor> descriptorsToAnalyze;
217
218   // descriptorsToVisit is initialized to descriptorsToAnalyze and is
219   // reduced by visiting a descriptor during analysis.  When dependents
220   // must be scheduled, only those contained in descriptorsToAnalyze
221   // should be re-added to this set
222   private HashSet<MethodContext> methodContextsToVisit;
223
224   // a special field descriptor for all array elements
225   private static FieldDescriptor fdElement = new FieldDescriptor(new Modifiers(Modifiers.PUBLIC),
226                                                                  new TypeDescriptor("Array[]"),
227                                                                  "elements",
228                                                                  null,
229                                                                  false);
230
231   // a special temp descriptor for setting more than one parameter label
232   // to the all-aliased-parameters heap region node
233   protected static TempDescriptor tdAliasedParams = new TempDescriptor("_AllAliasedParams___");
234
235
236   // for controlling DOT file output
237   private boolean writeDOTs;
238   private boolean writeAllDOTs;
239
240
241
242   // this analysis generates an ownership graph for every task
243   // in the program
244   public OwnershipAnalysis(State state,
245                            TypeUtil tu,
246                            CallGraph callGraph,
247                            int allocationDepth,
248                            boolean writeDOTs,
249                            boolean writeAllDOTs,
250                            String aliasFile) throws java.io.IOException {
251
252     double timeStartAnalysis = (double) System.nanoTime();
253
254     this.state           = state;
255     this.typeUtil        = tu;
256     this.callGraph       = callGraph;
257     this.allocationDepth = allocationDepth;
258     this.writeDOTs       = writeDOTs;
259     this.writeAllDOTs    = writeAllDOTs;
260
261     descriptorsToAnalyze = new HashSet<Descriptor>();
262
263     mapMethodContextToInitialParamAllocGraph =
264       new Hashtable<MethodContext, OwnershipGraph>();
265
266     mapMethodContextToCompleteOwnershipGraph =
267       new Hashtable<MethodContext, OwnershipGraph>();
268
269     mapFlatNewToAllocationSite =
270       new Hashtable<FlatNew, AllocationSite>();
271
272     mapDescriptorToAllocationSiteSet =
273       new Hashtable<Descriptor, HashSet<AllocationSite> >();
274
275     mapDescriptorToAllMethodContexts = 
276       new Hashtable<Descriptor, HashSet<MethodContext> >();
277
278
279     if( writeAllDOTs ) {
280       mapMethodContextToNumUpdates = new Hashtable<MethodContext, Integer>();
281     }
282
283
284     if( state.TASK ) {
285       // initialize methods to visit as the set of all tasks in the
286       // program and then any method that could be called starting
287       // from those tasks
288       Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
289       while( taskItr.hasNext() ) {
290         Descriptor d = (Descriptor) taskItr.next();
291         scheduleAllCallees(d);
292       }
293
294     } else {
295       // we are not in task mode, just normal Java, so start with
296       // the main method
297       
298       // get all classes, get all methods, look for static main, then stop?
299       
300       // YOU CAN DEFINE MORE THAN ONE MAIN!!!!
301
302       Iterator classItr = state.getClassSymbolTable().getDescriptorsIterator();
303       while( classItr.hasNext() ) {
304         ClassDescriptor cd = (ClassDescriptor) classItr.next();
305
306         Iterator methItr = cd.getMethods();
307         while( methItr.hasNext() ) {
308           Descriptor d = (Descriptor) methItr.next();
309
310           if( d.getSymbol().equals( "main" ) ) {
311             scheduleAllCallees(d);
312           }
313         }
314       }
315       
316     }
317
318
319     // before beginning analysis, initialize every scheduled method
320     // with an ownership graph that has populated parameter index tables
321     // by analyzing the first node which is always a FlatMethod node
322     Iterator<Descriptor> dItr = descriptorsToAnalyze.iterator();
323     while( dItr.hasNext() ) {
324       Descriptor d  = dItr.next();
325       OwnershipGraph og = new OwnershipGraph(allocationDepth, typeUtil);
326
327       FlatMethod fm;
328       if( d instanceof MethodDescriptor ) {
329         fm = state.getMethodFlat( (MethodDescriptor) d);
330       } else {
331         assert d instanceof TaskDescriptor;
332         fm = state.getMethodFlat( (TaskDescriptor) d);
333       }
334
335       MethodContext mc = new MethodContext( d );
336       assert !mapDescriptorToAllMethodContexts.containsKey( d );
337       HashSet<MethodContext> s = new HashSet<MethodContext>();
338       s.add( mc );
339       mapDescriptorToAllMethodContexts.put( d, s );
340
341       //System.out.println("Previsiting " + mc);
342
343       og = analyzeFlatNode(mc, fm, null, og);
344       setGraphForMethodContext(mc, og);
345     }
346
347     //System.out.println("");
348
349     // as mentioned above, analyze methods one-by-one, possibly revisiting
350     // a method if the methods that it calls are updated
351     analyzeMethods();
352
353     //System.out.println("");
354
355     double timeEndAnalysis = (double) System.nanoTime();
356     double dt = (timeEndAnalysis - timeStartAnalysis)/(Math.pow( 10.0, 9.0 ) );
357     String treport = String.format( "The analysis took %.3f sec.", dt );
358     System.out.println( treport );
359
360     if( aliasFile != null ) {
361       writeAllAliases(aliasFile);
362     }
363   }
364
365   // called from the constructor to help initialize the set
366   // of methods that needs to be analyzed by ownership analysis
367   private void scheduleAllCallees(Descriptor d) {
368     if( descriptorsToAnalyze.contains(d) ) {
369       return;
370     }
371     descriptorsToAnalyze.add(d);
372
373     // start with all method calls to further schedule
374     Set moreMethodsToCheck = moreMethodsToCheck = callGraph.getMethodCalls(d);
375
376     if( d instanceof MethodDescriptor ) {
377       // see if this method has virtual dispatch
378       Set virtualMethods = callGraph.getMethods( (MethodDescriptor)d);
379       moreMethodsToCheck.addAll(virtualMethods);
380     }
381
382     // keep following any further methods identified in
383     // the call chain
384     Iterator methItr = moreMethodsToCheck.iterator();
385     while( methItr.hasNext() ) {
386       Descriptor m = (Descriptor) methItr.next();
387       scheduleAllCallees(m);
388     }
389   }
390
391
392   // manage the set of tasks and methods to be analyzed
393   // and be sure to reschedule tasks/methods when the methods
394   // they call are updated
395   private void analyzeMethods() throws java.io.IOException {
396
397     methodContextsToVisit = new HashSet<MethodContext>();    
398     Iterator<Descriptor> itrd2a = descriptorsToAnalyze.iterator();
399     while( itrd2a.hasNext() ) {
400       HashSet<MethodContext> mcs = mapDescriptorToAllMethodContexts.get( itrd2a.next() );
401       assert mcs != null;
402
403       Iterator<MethodContext> itrmc = mcs.iterator();
404       while( itrmc.hasNext() ) {
405         methodContextsToVisit.add( itrmc.next() );
406       }
407     }
408
409     while( !methodContextsToVisit.isEmpty() ) {
410       MethodContext mc = methodContextsToVisit.iterator().next();
411       methodContextsToVisit.remove(mc);
412
413
414       // because the task or method descriptor just extracted
415       // was in the "to visit" set it either hasn't been analyzed
416       // yet, or some method that it depends on has been
417       // updated.  Recompute a complete ownership graph for
418       // this task/method and compare it to any previous result.
419       // If there is a change detected, add any methods/tasks
420       // that depend on this one to the "to visit" set.
421
422       System.out.println("Analyzing " + mc);
423
424       Descriptor d = mc.getDescriptor();
425       FlatMethod fm;
426       if( d instanceof MethodDescriptor ) {
427         fm = state.getMethodFlat( (MethodDescriptor) d);
428       } else {
429         assert d instanceof TaskDescriptor;
430         fm = state.getMethodFlat( (TaskDescriptor) d);
431       }
432
433       OwnershipGraph og = analyzeFlatMethod(mc, fm);
434       OwnershipGraph ogPrev = mapMethodContextToCompleteOwnershipGraph.get(mc);
435       if( !og.equals(ogPrev) ) {
436         setGraphForMethodContext(mc, og);
437
438         // only methods have dependents, tasks cannot
439         // be invoked by any user program calls
440         if( d instanceof MethodDescriptor ) {
441           MethodDescriptor md = (MethodDescriptor) d;
442           Set dependents = callGraph.getCallerSet(md);
443           if( dependents != null ) {
444             Iterator depItr = dependents.iterator();
445             while( depItr.hasNext() ) {
446               Descriptor dependent = (Descriptor) depItr.next();
447               if( descriptorsToAnalyze.contains(dependent) ) {
448                 
449                 HashSet<MethodContext> mcs = mapDescriptorToAllMethodContexts.get( dependent );
450                 assert mcs != null;
451                 
452                 Iterator<MethodContext> itrmc = mcs.iterator();
453                 while( itrmc.hasNext() ) {
454                   methodContextsToVisit.add( itrmc.next() );
455                 }
456               }
457             }
458           }
459         }
460       }
461     }
462
463   }
464
465
466   // keep passing the Descriptor of the method along for debugging
467   // and dot file writing
468   private OwnershipGraph
469   analyzeFlatMethod(MethodContext mc,
470                     FlatMethod flatm) throws java.io.IOException {
471
472     // initialize flat nodes to visit as the flat method
473     // because it is the entry point
474
475     flatNodesToVisit = new HashSet<FlatNode>();
476     flatNodesToVisit.add(flatm);
477
478     // initilize the mapping of flat nodes in this flat method to
479     // ownership graph results to an empty mapping
480     mapFlatNodeToOwnershipGraph = new Hashtable<FlatNode, OwnershipGraph>();
481
482     // initialize the set of return nodes that will be combined as
483     // the final ownership graph result to return as an empty set
484     returnNodesToCombineForCompleteOwnershipGraph = new HashSet<FlatReturnNode>();
485
486
487     while( !flatNodesToVisit.isEmpty() ) {
488       FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
489       flatNodesToVisit.remove(fn);
490
491       //System.out.println( "  "+fn );
492
493       // perform this node's contributions to the ownership
494       // graph on a new copy, then compare it to the old graph
495       // at this node to see if anything was updated.
496       OwnershipGraph og = new OwnershipGraph(allocationDepth, typeUtil);
497
498       // start by merging all node's parents' graphs
499       for( int i = 0; i < fn.numPrev(); ++i ) {
500         FlatNode pn = fn.getPrev(i);
501         if( mapFlatNodeToOwnershipGraph.containsKey(pn) ) {
502           OwnershipGraph ogParent = mapFlatNodeToOwnershipGraph.get(pn);
503           og.merge(ogParent);
504         }
505       }
506
507       // apply the analysis of the flat node to the
508       // ownership graph made from the merge of the
509       // parent graphs
510       og = analyzeFlatNode(mc,
511                            fn,
512                            returnNodesToCombineForCompleteOwnershipGraph,
513                            og);
514
515
516       //debugSnapshot(og,fn);
517
518
519
520       // if the results of the new graph are different from
521       // the current graph at this node, replace the graph
522       // with the update and enqueue the children for
523       // processing
524       OwnershipGraph ogPrev = mapFlatNodeToOwnershipGraph.get(fn);
525       if( !og.equals(ogPrev) ) {
526         mapFlatNodeToOwnershipGraph.put(fn, og);
527
528         for( int i = 0; i < fn.numNext(); i++ ) {
529           FlatNode nn = fn.getNext(i);
530           flatNodesToVisit.add(nn);
531         }
532       }
533     }
534
535     // end by merging all return nodes into a complete
536     // ownership graph that represents all possible heap
537     // states after the flat method returns
538     OwnershipGraph completeGraph = new OwnershipGraph(allocationDepth, typeUtil);
539     Iterator retItr = returnNodesToCombineForCompleteOwnershipGraph.iterator();
540     while( retItr.hasNext() ) {
541       FlatReturnNode frn = (FlatReturnNode) retItr.next();
542       assert mapFlatNodeToOwnershipGraph.containsKey(frn);
543       OwnershipGraph ogr = mapFlatNodeToOwnershipGraph.get(frn);
544       completeGraph.merge(ogr);
545     }
546
547     return completeGraph;
548   }
549
550
551   private OwnershipGraph
552   analyzeFlatNode(MethodContext mc,
553                   FlatNode fn,
554                   HashSet<FlatReturnNode> setRetNodes,
555                   OwnershipGraph og) throws java.io.IOException {
556
557     TempDescriptor lhs;
558     TempDescriptor rhs;
559     FieldDescriptor fld;
560
561     // use node type to decide what alterations to make
562     // to the ownership graph
563     switch( fn.kind() ) {
564
565     case FKind.FlatMethod:
566       FlatMethod fm = (FlatMethod) fn;
567
568       // there should only be one FlatMethod node as the
569       // parent of all other FlatNode objects, so take
570       // the opportunity to construct the initial graph by
571       // adding parameters labels to new heap regions
572       // AND this should be done once globally so that the
573       // parameter IDs are consistent between analysis
574       // iterations, so if this step has been done already
575       // just merge in the cached version
576       OwnershipGraph ogInitParamAlloc = mapMethodContextToInitialParamAllocGraph.get(mc);
577       if( ogInitParamAlloc == null ) {
578
579         // if the method context has aliased parameters, make sure
580         // there is a blob region for all those param labels to
581         // reference
582         Set<Integer> aliasedParamIndices = mc.getAliasedParamIndices();
583         if( !aliasedParamIndices.isEmpty() ) {
584           og.makeAliasedParamHeapRegionNode( tdAliasedParams );
585         }
586
587         // set up each parameter
588         for( int i = 0; i < fm.numParameters(); ++i ) {
589           TempDescriptor tdParam = fm.getParameter( i );
590           Integer        paramIndex = new Integer( i );
591
592           if( aliasedParamIndices.contains( paramIndex ) ) {
593             // just point this one to the alias blob
594             og.assignTempEqualToAliasedParam( tdParam,
595                                               tdAliasedParams,
596                                               paramIndex );         
597           } else {
598             // this parameter is not aliased to others, give it
599             // a fresh parameter heap region
600             
601             og.assignTempEqualToParamAlloc(tdParam,
602                                            mc.getDescriptor() instanceof TaskDescriptor,
603                                            paramIndex );
604           }
605         }
606         
607         // cache the graph
608         OwnershipGraph ogResult = new OwnershipGraph(allocationDepth, typeUtil);
609         ogResult.merge(og);
610         mapMethodContextToInitialParamAllocGraph.put(mc, ogResult);
611
612       } else {
613         // or just leverage the cached copy
614         og.merge(ogInitParamAlloc);
615       }
616       break;
617
618     case FKind.FlatOpNode:
619       FlatOpNode fon = (FlatOpNode) fn;
620       if( fon.getOp().getOp() == Operation.ASSIGN ) {
621         lhs = fon.getDest();
622         rhs = fon.getLeft();
623         og.assignTempXEqualToTempY(lhs, rhs);
624       }
625       break;
626
627     case FKind.FlatFieldNode:
628       FlatFieldNode ffn = (FlatFieldNode) fn;
629       lhs = ffn.getDst();
630       rhs = ffn.getSrc();
631       fld = ffn.getField();
632       if( !fld.getType().isImmutable() ) {
633         og.assignTempXEqualToTempYFieldF(lhs, rhs, fld);
634       }
635       break;
636
637     case FKind.FlatSetFieldNode:
638       FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
639       lhs = fsfn.getDst();
640       fld = fsfn.getField();
641       rhs = fsfn.getSrc();
642       if( !fld.getType().isImmutable() ) {
643         og.assignTempXFieldFEqualToTempY(lhs, fld, rhs);
644       }
645       break;
646
647     case FKind.FlatElementNode:
648       FlatElementNode fen = (FlatElementNode) fn;
649       lhs = fen.getDst();
650       rhs = fen.getSrc();
651       if( !lhs.getType().isImmutable() ) {
652         og.assignTempXEqualToTempYFieldF(lhs, rhs, fdElement);
653       }
654       break;
655
656     case FKind.FlatSetElementNode:
657       FlatSetElementNode fsen = (FlatSetElementNode) fn;
658       lhs = fsen.getDst();
659       rhs = fsen.getSrc();
660       if( !rhs.getType().isImmutable() ) {
661         og.assignTempXFieldFEqualToTempY(lhs, fdElement, rhs);
662       }
663       break;
664
665     case FKind.FlatNew:
666       FlatNew fnn = (FlatNew) fn;
667       lhs = fnn.getDst();
668       if( !lhs.getType().isImmutable() ) {
669         AllocationSite as = getAllocationSiteFromFlatNewPRIVATE(fnn);
670         og.assignTempEqualToNewAlloc(lhs, as);
671       }
672       break;
673
674     case FKind.FlatCall:
675       FlatCall fc = (FlatCall) fn;
676       MethodDescriptor md = fc.getMethod();
677       FlatMethod flatm = state.getMethodFlat(md);
678       OwnershipGraph ogMergeOfAllPossibleCalleeResults = new OwnershipGraph(allocationDepth, typeUtil);
679
680       if( md.isStatic() ) {
681         // a static method is simply always the same, makes life easy
682         ogMergeOfAllPossibleCalleeResults = og;
683
684         Set<Integer> aliasedParamIndices = 
685           ogMergeOfAllPossibleCalleeResults.calculateAliasedParamSet(fc, md.isStatic(), flatm);
686         MethodContext mcNew = new MethodContext( md, aliasedParamIndices );      
687         OwnershipGraph onlyPossibleCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
688
689         if( onlyPossibleCallee == null ) {
690           // if this method context has never been analyzed just schedule it for analysis
691           // and skip over this call site for now
692           methodContextsToVisit.add( mcNew );
693           
694         } else {
695           ogMergeOfAllPossibleCalleeResults.resolveMethodCall(fc, md.isStatic(), flatm, onlyPossibleCallee);
696         }
697
698       } else {
699         // if the method descriptor is virtual, then there could be a
700         // set of possible methods that will actually be invoked, so
701         // find all of them and merge all of their results together
702         TypeDescriptor typeDesc = fc.getThis().getType();
703         Set possibleCallees = callGraph.getMethods(md, typeDesc);
704
705         Iterator i = possibleCallees.iterator();
706         while( i.hasNext() ) {
707           MethodDescriptor possibleMd = (MethodDescriptor) i.next();
708           FlatMethod pflatm = state.getMethodFlat(possibleMd);
709
710           // don't alter the working graph (og) until we compute a result for every
711           // possible callee, merge them all together, then set og to that
712           OwnershipGraph ogCopy = new OwnershipGraph(allocationDepth, typeUtil);
713           ogCopy.merge(og);
714
715           Set<Integer> aliasedParamIndices = 
716             ogCopy.calculateAliasedParamSet(fc, possibleMd.isStatic(), pflatm);
717           MethodContext mcNew = new MethodContext( possibleMd, aliasedParamIndices );
718           OwnershipGraph ogPotentialCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
719
720           if( ogPotentialCallee == null ) {
721             // if this method context has never been analyzed just schedule it for analysis
722             // and skip over this call site for now
723             methodContextsToVisit.add( mcNew );
724             
725           } else {
726             ogCopy.resolveMethodCall(fc, possibleMd.isStatic(), pflatm, ogPotentialCallee);
727           }
728
729           ogMergeOfAllPossibleCalleeResults.merge(ogCopy);
730         }
731       }
732
733       og = ogMergeOfAllPossibleCalleeResults;
734       break;
735
736     case FKind.FlatReturnNode:
737       FlatReturnNode frn = (FlatReturnNode) fn;
738       rhs = frn.getReturnTemp();
739       if( rhs != null && !rhs.getType().isImmutable() ) {
740         og.assignReturnEqualToTemp(rhs);
741       }
742       setRetNodes.add(frn);
743       break;
744     }
745
746     return og;
747   }
748
749
750   // insert a call to debugSnapshot() somewhere in the analysis to get
751   // successive captures of the analysis state
752   int debugCounter        = 0;
753   int numStartCountReport = 0;
754   int freqCountReport     = 1000;
755   int iterStartCapture    = 20000;
756   int numIterToCapture    = 400;
757   void debugSnapshot(OwnershipGraph og, FlatNode fn) {
758     ++debugCounter;
759     if( debugCounter > numStartCountReport &&
760         debugCounter % freqCountReport == 0 ) {
761       System.out.println("    @@@ debug counter = "+debugCounter);
762     }
763     if( debugCounter > iterStartCapture ) {
764       System.out.println("    @@@ capturing debug "+(debugCounter-iterStartCapture)+" @@@");
765       String graphName = String.format("snap%04d",debugCounter-iterStartCapture);
766       if( fn != null ) {
767         graphName = graphName+fn;
768       }
769       try {
770         og.writeGraph(graphName, true, true, false, false, false);
771       } catch( Exception e ) {
772         System.out.println("Error writing debug capture.");
773         System.exit(0);
774       }
775     }
776     if( debugCounter == iterStartCapture + numIterToCapture ) {
777       System.out.println("Stopping analysis after debug captures.");
778       System.exit(0);
779     }
780   }
781
782
783
784   // this method should generate integers strictly greater than zero!
785   // special "shadow" regions are made from a heap region by negating
786   // the ID
787   static public Integer generateUniqueHeapRegionNodeID() {
788     ++uniqueIDcount;
789     return new Integer(uniqueIDcount);
790   }
791
792
793   private void setGraphForMethodContext(MethodContext mc, OwnershipGraph og)
794   throws IOException {
795
796     mapMethodContextToCompleteOwnershipGraph.put(mc, og);
797
798     // arguments to writeGraph are:
799     // boolean writeLabels,
800     // boolean labelSelect,
801     // boolean pruneGarbage,
802     // boolean writeReferencers
803     // boolean writeParamMappings
804
805     if( writeDOTs ) {
806
807       if( !writeAllDOTs ) {
808         og.writeGraph(mc, true, true, true, false, false);
809
810       } else {
811         if( !mapMethodContextToNumUpdates.containsKey(mc) ) {
812           mapMethodContextToNumUpdates.put(mc, new Integer(0) );
813         }
814         Integer n = mapMethodContextToNumUpdates.get(mc);
815         og.writeGraph(mc, n, true, true, true, false, false);
816         mapMethodContextToNumUpdates.put(mc, n + 1);
817       }
818     }
819   }
820
821
822   // return just the allocation site associated with one FlatNew node
823   private AllocationSite getAllocationSiteFromFlatNewPRIVATE(FlatNew fn) {
824
825     if( !mapFlatNewToAllocationSite.containsKey(fn) ) {
826       AllocationSite as = new AllocationSite(allocationDepth, fn, fn.isDisjoint());
827
828       // the newest nodes are single objects
829       for( int i = 0; i < allocationDepth; ++i ) {
830         Integer id = generateUniqueHeapRegionNodeID();
831         as.setIthOldest(i, id);
832       }
833
834       // the oldest node is a summary node
835       Integer idSummary = generateUniqueHeapRegionNodeID();
836       as.setSummary(idSummary);
837
838       mapFlatNewToAllocationSite.put(fn, as);
839     }
840
841     return mapFlatNewToAllocationSite.get(fn);
842   }
843
844
845   // return all allocation sites in the method (there is one allocation
846   // site per FlatNew node in a method)
847   private HashSet<AllocationSite> getAllocationSiteSet(Descriptor d) {
848     if( !mapDescriptorToAllocationSiteSet.containsKey(d) ) {
849       buildAllocationSiteSet(d);
850     }
851
852     return mapDescriptorToAllocationSiteSet.get(d);
853
854   }
855
856   private void buildAllocationSiteSet(Descriptor d) {
857     HashSet<AllocationSite> s = new HashSet<AllocationSite>();
858
859     FlatMethod fm;
860     if( d instanceof MethodDescriptor ) {
861       fm = state.getMethodFlat( (MethodDescriptor) d);
862     } else {
863       assert d instanceof TaskDescriptor;
864       fm = state.getMethodFlat( (TaskDescriptor) d);
865     }
866
867     // visit every node in this FlatMethod's IR graph
868     // and make a set of the allocation sites from the
869     // FlatNew node's visited
870     HashSet<FlatNode> visited = new HashSet<FlatNode>();
871     HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
872     toVisit.add(fm);
873
874     while( !toVisit.isEmpty() ) {
875       FlatNode n = toVisit.iterator().next();
876
877       if( n instanceof FlatNew ) {
878         s.add(getAllocationSiteFromFlatNewPRIVATE( (FlatNew) n) );
879       }
880
881       toVisit.remove(n);
882       visited.add(n);
883
884       for( int i = 0; i < n.numNext(); ++i ) {
885         FlatNode child = n.getNext(i);
886         if( !visited.contains(child) ) {
887           toVisit.add(child);
888         }
889       }
890     }
891
892     mapDescriptorToAllocationSiteSet.put(d, s);
893   }
894
895
896   private HashSet<AllocationSite>
897   getFlaggedAllocationSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
898
899     HashSet<AllocationSite> asSetTotal = new HashSet<AllocationSite>();
900     HashSet<Descriptor>     toVisit    = new HashSet<Descriptor>();
901     HashSet<Descriptor>     visited    = new HashSet<Descriptor>();
902
903     toVisit.add(td);
904
905     // traverse this task and all methods reachable from this task
906     while( !toVisit.isEmpty() ) {
907       Descriptor d = toVisit.iterator().next();
908       toVisit.remove(d);
909       visited.add(d);
910
911       HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
912       Iterator asItr = asSet.iterator();
913       while( asItr.hasNext() ) {
914         AllocationSite as = (AllocationSite) asItr.next();
915         TypeDescriptor typed = as.getType();
916         if( typed != null ) {
917           ClassDescriptor cd = typed.getClassDesc();
918           if( cd != null && cd.hasFlags() ) {
919             asSetTotal.add(as);
920           }
921         }
922       }
923
924       // enqueue callees of this method to be searched for
925       // allocation sites also
926       Set callees = callGraph.getCalleeSet(d);
927       if( callees != null ) {
928         Iterator methItr = callees.iterator();
929         while( methItr.hasNext() ) {
930           MethodDescriptor md = (MethodDescriptor) methItr.next();
931
932           if( !visited.contains(md) ) {
933             toVisit.add(md);
934           }
935         }
936       }
937     }
938
939
940     return asSetTotal;
941   }
942 }