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