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