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