system stable, call site transform wipes out graphs without rebuilding correctly...
[IRC.git] / Robust / src / Analysis / Disjoint / DisjointAnalysis.java
1 package Analysis.Disjoint;
2
3 import Analysis.CallGraph.*;
4 import Analysis.Liveness;
5 import Analysis.ArrayReferencees;
6 import IR.*;
7 import IR.Flat.*;
8 import IR.Tree.Modifiers;
9 import java.util.*;
10 import java.io.*;
11
12
13 public class DisjointAnalysis {
14
15
16   // data from the compiler
17   public State            state;
18   public CallGraph        callGraph;
19   public Liveness         liveness;
20   public ArrayReferencees arrayReferencees;
21   public TypeUtil         typeUtil;
22   public int              allocationDepth;
23
24
25   // used to identify HeapRegionNode objects
26   // A unique ID equates an object in one
27   // ownership graph with an object in another
28   // graph that logically represents the same
29   // heap region
30   // start at 10 and increment to reserve some
31   // IDs for special purposes
32   static protected int uniqueIDcount = 10;
33
34
35   // An out-of-scope method created by the
36   // analysis that has no parameters, and
37   // appears to allocate the command line
38   // arguments, then invoke the source code's
39   // main method.  The purpose of this is to
40   // provide the analysis with an explicit
41   // top-level context with no parameters
42   protected MethodDescriptor mdAnalysisEntry;
43   protected FlatMethod       fmAnalysisEntry;
44
45   // main method defined by source program
46   protected MethodDescriptor mdSourceEntry;
47
48   // the set of task and/or method descriptors
49   // reachable in call graph
50   protected Set<Descriptor> 
51     descriptorsToAnalyze;
52
53   // current descriptors to visit in fixed-point
54   // interprocedural analysis, prioritized by
55   // dependency in the call graph
56   protected PriorityQueue<DescriptorQWrapper> 
57     descriptorsToVisitQ;
58   
59   // a duplication of the above structure, but
60   // for efficient testing of inclusion
61   protected HashSet<Descriptor> 
62     descriptorsToVisitSet;
63
64   // storage for priorities (doesn't make sense)
65   // to add it to the Descriptor class, just in
66   // this analysis
67   protected Hashtable<Descriptor, Integer> 
68     mapDescriptorToPriority;
69
70
71   // maps a descriptor to its current partial result
72   // from the intraprocedural fixed-point analysis--
73   // then the interprocedural analysis settles, this
74   // mapping will have the final results for each
75   // method descriptor
76   protected Hashtable<Descriptor, ReachGraph> 
77     mapDescriptorToCompleteReachGraph;
78
79   // maps a descriptor to its known dependents: namely
80   // methods or tasks that call the descriptor's method
81   // AND are part of this analysis (reachable from main)
82   protected Hashtable< Descriptor, Set<Descriptor> >
83     mapDescriptorToSetDependents;
84
85   // maps each flat new to one analysis abstraction
86   // allocate site object, these exist outside reach graphs
87   protected Hashtable<FlatNew, AllocSite>
88     mapFlatNewToAllocSite;
89
90   // maps intergraph heap region IDs to intergraph
91   // allocation sites that created them, a redundant
92   // structure for efficiency in some operations
93   protected Hashtable<Integer, AllocSite>
94     mapHrnIdToAllocSite;
95
96   // maps a method to its initial heap model (IHM) that
97   // is the set of reachability graphs from every caller
98   // site, all merged together.  The reason that we keep
99   // them separate is that any one call site's contribution
100   // to the IHM may changed along the path to the fixed point
101   protected Hashtable< Descriptor, Hashtable< FlatCall, ReachGraph > >
102     mapDescriptorToIHMcontributions;
103
104   // TODO -- CHANGE EDGE/TYPE/FIELD storage!
105   public static final String arrayElementFieldName = "___element_";
106   static protected Hashtable<TypeDescriptor, FieldDescriptor>
107     mapTypeToArrayField;
108
109   // for controlling DOT file output
110   protected boolean writeFinalDOTs;
111   protected boolean writeAllIncrementalDOTs;
112
113   // supporting DOT output--when we want to write every
114   // partial method result, keep a tally for generating
115   // unique filenames
116   protected Hashtable<Descriptor, Integer>
117     mapDescriptorToNumUpdates;
118
119
120   // allocate various structures that are not local
121   // to a single class method--should be done once
122   protected void allocateStructures() {    
123     descriptorsToAnalyze = new HashSet<Descriptor>();
124
125     mapDescriptorToCompleteReachGraph =
126       new Hashtable<Descriptor, ReachGraph>();
127
128     mapDescriptorToNumUpdates =
129       new Hashtable<Descriptor, Integer>();
130
131     mapDescriptorToSetDependents =
132       new Hashtable< Descriptor, Set<Descriptor> >();
133
134     mapFlatNewToAllocSite = 
135       new Hashtable<FlatNew, AllocSite>();
136
137     mapDescriptorToIHMcontributions =
138       new Hashtable< Descriptor, Hashtable< FlatCall, ReachGraph > >();
139
140     mapHrnIdToAllocSite =
141       new Hashtable<Integer, AllocSite>();
142
143     mapTypeToArrayField = 
144       new Hashtable <TypeDescriptor, FieldDescriptor>();
145
146     descriptorsToVisitQ =
147       new PriorityQueue<DescriptorQWrapper>();
148
149     descriptorsToVisitSet =
150       new HashSet<Descriptor>();
151
152     mapDescriptorToPriority =
153       new Hashtable<Descriptor, Integer>();
154   }
155
156
157
158   // this analysis generates a disjoint reachability
159   // graph for every reachable method in the program
160   public DisjointAnalysis( State            s,
161                            TypeUtil         tu,
162                            CallGraph        cg,
163                            Liveness         l,
164                            ArrayReferencees ar
165                            ) throws java.io.IOException {
166     init( s, tu, cg, l, ar );
167   }
168   
169   protected void init( State            state,
170                        TypeUtil         typeUtil,
171                        CallGraph        callGraph,
172                        Liveness         liveness,
173                        ArrayReferencees arrayReferencees
174                        ) throws java.io.IOException {
175     
176     this.state                   = state;
177     this.typeUtil                = typeUtil;
178     this.callGraph               = callGraph;
179     this.liveness                = liveness;
180     this.arrayReferencees        = arrayReferencees;
181     this.allocationDepth         = state.DISJOINTALLOCDEPTH;
182     this.writeFinalDOTs          = state.DISJOINTWRITEDOTS && !state.DISJOINTWRITEALL;
183     this.writeAllIncrementalDOTs = state.DISJOINTWRITEDOTS &&  state.DISJOINTWRITEALL;
184             
185     // set some static configuration for ReachGraphs
186     ReachGraph.allocationDepth = allocationDepth;
187     ReachGraph.typeUtil        = typeUtil;
188
189     allocateStructures();
190
191     double timeStartAnalysis = (double) System.nanoTime();
192
193     // start interprocedural fixed-point computation
194     analyzeMethods();
195
196     double timeEndAnalysis = (double) System.nanoTime();
197     double dt = (timeEndAnalysis - timeStartAnalysis)/(Math.pow( 10.0, 9.0 ) );
198     String treport = String.format( "The reachability analysis took %.3f sec.", dt );
199     String justtime = String.format( "%.2f", dt );
200     System.out.println( treport );
201
202     if( writeFinalDOTs && !writeAllIncrementalDOTs ) {
203       writeFinalGraphs();      
204     }
205
206     if( state.DISJOINTWRITEIHMS ) {
207       writeFinalIHMs();
208     }
209
210     if( state.DISJOINTALIASFILE != null ) {
211       if( state.TASK ) {
212         // not supporting tasks yet...
213       } else {
214         /*
215         writeAllAliasesJava( aliasFile, 
216                              treport, 
217                              justtime, 
218                              state.DISJOINTALIASTAB, 
219                              state.lines );
220         */
221       }
222     }
223   }
224
225
226   // fixed-point computation over the call graph--when a
227   // method's callees are updated, it must be reanalyzed
228   protected void analyzeMethods() throws java.io.IOException {  
229
230     if( state.TASK ) {
231       // This analysis does not support Bamboo at the moment,
232       // but if it does in the future we would initialize the
233       // set of descriptors to analyze as the program-reachable
234       // tasks and the methods callable by them.  For Java,
235       // just methods reachable from the main method.
236       System.out.println( "No Bamboo support yet..." );
237       System.exit( -1 );
238
239     } else {
240       // add all methods transitively reachable from the
241       // source's main to set for analysis
242       mdSourceEntry = typeUtil.getMain();
243       descriptorsToAnalyze.add( mdSourceEntry );
244       descriptorsToAnalyze.addAll( 
245         callGraph.getAllMethods( mdSourceEntry ) 
246                                    );
247
248       // fabricate an empty calling context that will call
249       // the source's main, but call graph doesn't know
250       // about it, so explicitly add it
251       makeAnalysisEntryMethod( mdSourceEntry );
252       descriptorsToAnalyze.add( mdAnalysisEntry );
253     }
254
255     // topologically sort according to the call graph so 
256     // leaf calls are ordered first, smarter analysis order
257     LinkedList<Descriptor> sortedDescriptors = 
258       topologicalSort( descriptorsToAnalyze );
259
260     // add sorted descriptors to priority queue, and duplicate
261     // the queue as a set for efficiently testing whether some
262     // method is marked for analysis
263     int p = 0;
264     Iterator<Descriptor> dItr = sortedDescriptors.iterator();
265     while( dItr.hasNext() ) {
266       Descriptor d = dItr.next();
267       mapDescriptorToPriority.put( d, new Integer( p ) );
268       descriptorsToVisitQ.add( new DescriptorQWrapper( p, d ) );
269       descriptorsToVisitSet.add( d );
270       ++p;
271     }
272
273     // analyze methods from the priority queue until it is empty
274     while( !descriptorsToVisitQ.isEmpty() ) {
275       Descriptor d = descriptorsToVisitQ.poll().getDescriptor();
276       assert descriptorsToVisitSet.contains( d );
277       descriptorsToVisitSet.remove( d );
278
279       // because the task or method descriptor just extracted
280       // was in the "to visit" set it either hasn't been analyzed
281       // yet, or some method that it depends on has been
282       // updated.  Recompute a complete reachability graph for
283       // this task/method and compare it to any previous result.
284       // If there is a change detected, add any methods/tasks
285       // that depend on this one to the "to visit" set.
286
287       System.out.println( "Analyzing " + d );
288
289       ReachGraph rg     = analyzeMethod( d );
290       ReachGraph rgPrev = getPartial( d );
291
292       if( !rg.equals( rgPrev ) ) {
293         setPartial( d, rg );
294
295         // results for d changed, so enqueue dependents
296         // of d for further analysis
297         Iterator<Descriptor> depsItr = getDependents( d ).iterator();
298         while( depsItr.hasNext() ) {
299           Descriptor dNext = depsItr.next();
300           enqueue( dNext );
301         }
302       }      
303     }
304   }
305
306
307   protected ReachGraph analyzeMethod( Descriptor d ) 
308     throws java.io.IOException {
309
310     // get the flat code for this descriptor
311     FlatMethod fm;
312     if( d == mdAnalysisEntry ) {
313       fm = fmAnalysisEntry;
314     } else {
315       fm = state.getMethodFlat( d );
316     }
317       
318     // intraprocedural work set
319     Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
320     flatNodesToVisit.add( fm );
321     
322     // mapping of current partial results
323     Hashtable<FlatNode, ReachGraph> mapFlatNodeToReachGraph =
324       new Hashtable<FlatNode, ReachGraph>();
325
326     // the set of return nodes partial results that will be combined as
327     // the final, conservative approximation of the entire method
328     HashSet<FlatReturnNode> setReturns = new HashSet<FlatReturnNode>();
329
330     while( !flatNodesToVisit.isEmpty() ) {
331       FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
332       flatNodesToVisit.remove( fn );
333
334       //System.out.println( "  "+fn );
335
336       // effect transfer function defined by this node,
337       // then compare it to the old graph at this node
338       // to see if anything was updated.
339
340       ReachGraph rg = new ReachGraph();
341
342       // start by merging all node's parents' graphs
343       for( int i = 0; i < fn.numPrev(); ++i ) {
344         FlatNode pn = fn.getPrev( i );
345         if( mapFlatNodeToReachGraph.containsKey( pn ) ) {
346           ReachGraph rgParent = mapFlatNodeToReachGraph.get( pn );
347           rg.merge( rgParent );
348         }
349       }
350
351       // modify rg with appropriate transfer function
352       analyzeFlatNode( d, fm, fn, setReturns, rg );
353           
354
355       if( takeDebugSnapshots && 
356           d.getSymbol().equals( descSymbolDebug ) ) {
357         debugSnapshot( rg, fn );
358       }
359
360
361       // if the results of the new graph are different from
362       // the current graph at this node, replace the graph
363       // with the update and enqueue the children
364       ReachGraph rgPrev = mapFlatNodeToReachGraph.get( fn );
365       if( !rg.equals( rgPrev ) ) {
366         mapFlatNodeToReachGraph.put( fn, rg );
367
368         for( int i = 0; i < fn.numNext(); i++ ) {
369           FlatNode nn = fn.getNext( i );
370           flatNodesToVisit.add( nn );
371         }
372       }
373     }
374
375     // end by merging all return nodes into a complete
376     // ownership graph that represents all possible heap
377     // states after the flat method returns
378     ReachGraph completeGraph = new ReachGraph();
379
380     assert !setReturns.isEmpty();
381     Iterator retItr = setReturns.iterator();
382     while( retItr.hasNext() ) {
383       FlatReturnNode frn = (FlatReturnNode) retItr.next();
384
385       assert mapFlatNodeToReachGraph.containsKey( frn );
386       ReachGraph rgRet = mapFlatNodeToReachGraph.get( frn );
387
388       completeGraph.merge( rgRet );
389     }
390     
391     return completeGraph;
392   }
393
394   
395   protected void
396     analyzeFlatNode( Descriptor              d,
397                      FlatMethod              fmContaining,
398                      FlatNode                fn,
399                      HashSet<FlatReturnNode> setRetNodes,
400                      ReachGraph              rg
401                      ) throws java.io.IOException {
402
403     
404     // any variables that are no longer live should be
405     // nullified in the graph to reduce edges
406     //rg.nullifyDeadVars( liveness.getLiveInTemps( fmContaining, fn ) );
407
408           
409     TempDescriptor  lhs;
410     TempDescriptor  rhs;
411     FieldDescriptor fld;
412
413     // use node type to decide what transfer function
414     // to apply to the reachability graph
415     switch( fn.kind() ) {
416
417     case FKind.FlatMethod: {
418       // construct this method's initial heap model (IHM)
419       // since we're working on the FlatMethod, we know
420       // the incoming ReachGraph 'rg' is empty
421
422       Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
423         getIHMcontributions( d );
424
425       Set entrySet = heapsFromCallers.entrySet();
426       Iterator itr = entrySet.iterator();
427       while( itr.hasNext() ) {
428         Map.Entry  me        = (Map.Entry)  itr.next();
429         FlatCall   fc        = (FlatCall)   me.getKey();
430         ReachGraph rgContrib = (ReachGraph) me.getValue();
431
432         assert fc.getMethod().equals( d );
433
434         // some call sites are in same method context though,
435         // and all of them should be merged together first,
436         // then heaps from different contexts should be merged
437         // THIS ASSUMES DIFFERENT CONTEXTS NEED SPECIAL CONSIDERATION!
438         // such as, do allocation sites need to be aged?
439
440         rg.merge_diffMethodContext( rgContrib );
441       }      
442     } break;
443       
444     case FKind.FlatOpNode:
445       FlatOpNode fon = (FlatOpNode) fn;
446       if( fon.getOp().getOp() == Operation.ASSIGN ) {
447         lhs = fon.getDest();
448         rhs = fon.getLeft();
449         rg.assignTempXEqualToTempY( lhs, rhs );
450       }
451       break;
452
453     case FKind.FlatCastNode:
454       FlatCastNode fcn = (FlatCastNode) fn;
455       lhs = fcn.getDst();
456       rhs = fcn.getSrc();
457
458       TypeDescriptor td = fcn.getType();
459       assert td != null;
460       
461       rg.assignTempXEqualToCastedTempY( lhs, rhs, td );
462       break;
463
464     case FKind.FlatFieldNode:
465       FlatFieldNode ffn = (FlatFieldNode) fn;
466       lhs = ffn.getDst();
467       rhs = ffn.getSrc();
468       fld = ffn.getField();
469       if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
470         rg.assignTempXEqualToTempYFieldF( lhs, rhs, fld );
471       }          
472       break;
473
474     case FKind.FlatSetFieldNode:
475       FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
476       lhs = fsfn.getDst();
477       fld = fsfn.getField();
478       rhs = fsfn.getSrc();
479       if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
480         rg.assignTempXFieldFEqualToTempY( lhs, fld, rhs );
481       }           
482       break;
483
484     case FKind.FlatElementNode:
485       FlatElementNode fen = (FlatElementNode) fn;
486       lhs = fen.getDst();
487       rhs = fen.getSrc();
488       if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
489
490         assert rhs.getType() != null;
491         assert rhs.getType().isArray();
492         
493         TypeDescriptor  tdElement = rhs.getType().dereference();
494         FieldDescriptor fdElement = getArrayField( tdElement );
495   
496         rg.assignTempXEqualToTempYFieldF( lhs, rhs, fdElement );
497       }
498       break;
499
500     case FKind.FlatSetElementNode:
501       FlatSetElementNode fsen = (FlatSetElementNode) fn;
502
503       if( arrayReferencees.doesNotCreateNewReaching( fsen ) ) {
504         // skip this node if it cannot create new reachability paths
505         break;
506       }
507
508       lhs = fsen.getDst();
509       rhs = fsen.getSrc();
510       if( !rhs.getType().isImmutable() || rhs.getType().isArray() ) {
511
512         assert lhs.getType() != null;
513         assert lhs.getType().isArray();
514         
515         TypeDescriptor  tdElement = lhs.getType().dereference();
516         FieldDescriptor fdElement = getArrayField( tdElement );
517
518         rg.assignTempXFieldFEqualToTempY( lhs, fdElement, rhs );
519       }
520       break;
521       
522     case FKind.FlatNew:
523       FlatNew fnn = (FlatNew) fn;
524       lhs = fnn.getDst();
525       if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
526         AllocSite as = getAllocSiteFromFlatNewPRIVATE( fnn );   
527         rg.assignTempEqualToNewAlloc( lhs, as );
528       }
529       break;
530
531     case FKind.FlatCall: {
532       FlatCall         fc       = (FlatCall) fn;
533       MethodDescriptor mdCallee = fc.getMethod();
534       FlatMethod       fmCallee = state.getMethodFlat( mdCallee );
535
536
537       // calculate the heap this call site can reach--note this is
538       // not used for the current call site transform, we are
539       // grabbing this heap model for future analysis of the callees,
540       // so if different results emerge we will return to this site
541       ReachGraph heapForThisCall_old = 
542         getIHMcontribution( mdCallee, fc );
543
544       // the computation of the callee-reachable heap
545       // is useful for making the callee starting point
546       // and for applying the call site transfer function
547       Set<HeapRegionNode> callerNodesCopiedToCallee = 
548         new HashSet<HeapRegionNode>();
549       Set<RefEdge> callerEdgesCopiedToCallee = 
550         new HashSet<RefEdge>();
551
552       ReachGraph heapForThisCall_cur = 
553         rg.makeCalleeView( fc, 
554                            fmCallee,
555                            callerNodesCopiedToCallee,
556                            callerEdgesCopiedToCallee
557                            );
558
559       if( !heapForThisCall_cur.equals( heapForThisCall_old ) ) {        
560         // if heap at call site changed, update the contribution,
561         // and reschedule the callee for analysis
562         addIHMcontribution( mdCallee, fc, heapForThisCall_cur );        
563         enqueue( mdCallee );
564       }
565
566
567
568
569       // the transformation for a call site should update the
570       // current heap abstraction with any effects from the callee,
571       // or if the method is virtual, the effects from any possible
572       // callees, so find the set of callees...
573       Set<MethodDescriptor> setPossibleCallees =
574         new HashSet<MethodDescriptor>();
575
576       if( mdCallee.isStatic() ) {        
577         setPossibleCallees.add( mdCallee );
578       } else {
579         TypeDescriptor typeDesc = fc.getThis().getType();
580         setPossibleCallees.addAll( callGraph.getMethods( mdCallee, 
581                                                          typeDesc )
582                                    );
583       }
584
585       ReachGraph rgMergeOfEffects = new ReachGraph();
586
587       Iterator<MethodDescriptor> mdItr = setPossibleCallees.iterator();
588       while( mdItr.hasNext() ) {
589         MethodDescriptor mdPossible = mdItr.next();
590         FlatMethod       fmPossible = state.getMethodFlat( mdPossible );
591
592         addDependent( mdPossible, // callee
593                       d );        // caller
594
595         // don't alter the working graph (rg) until we compute a 
596         // result for every possible callee, merge them all together,
597         // then set rg to that
598         ReachGraph rgCopy = new ReachGraph();
599         rgCopy.merge( rg );             
600                 
601         ReachGraph rgEffect = getPartial( mdPossible );
602
603         if( rgEffect == null ) {
604           // if this method has never been analyzed just schedule it 
605           // for analysis and skip over this call site for now
606           enqueue( mdPossible );
607         } else {
608           rgCopy.resolveMethodCall( fc, 
609                                     fmPossible, 
610                                     rgEffect,
611                                     callerNodesCopiedToCallee,
612                                     callerEdgesCopiedToCallee
613                                     );
614         }
615         
616         rgMergeOfEffects.merge( rgCopy );        
617       }
618
619         
620
621
622       // now that we've taken care of building heap models for
623       // callee analysis, finish this transformation
624       rg = rgMergeOfEffects;
625     } break;
626       
627
628     case FKind.FlatReturnNode:
629       FlatReturnNode frn = (FlatReturnNode) fn;
630       rhs = frn.getReturnTemp();
631       if( rhs != null && !rhs.getType().isImmutable() ) {
632         rg.assignReturnEqualToTemp( rhs );
633       }
634       setRetNodes.add( frn );
635       break;
636
637     } // end switch
638
639     
640     // dead variables were removed before the above transfer function
641     // was applied, so eliminate heap regions and edges that are no
642     // longer part of the abstractly-live heap graph, and sweep up
643     // and reachability effects that are altered by the reduction
644     //rg.abstractGarbageCollect();
645     //rg.globalSweep();
646
647
648     // at this point rg should be the correct update
649     // by an above transfer function, or untouched if
650     // the flat node type doesn't affect the heap
651   }
652
653   
654   // this method should generate integers strictly greater than zero!
655   // special "shadow" regions are made from a heap region by negating
656   // the ID
657   static public Integer generateUniqueHeapRegionNodeID() {
658     ++uniqueIDcount;
659     return new Integer( uniqueIDcount );
660   }
661
662
663   
664   static public FieldDescriptor getArrayField( TypeDescriptor tdElement ) {
665     FieldDescriptor fdElement = mapTypeToArrayField.get( tdElement );
666     if( fdElement == null ) {
667       fdElement = new FieldDescriptor( new Modifiers( Modifiers.PUBLIC ),
668                                        tdElement,
669                                        arrayElementFieldName,
670                                        null,
671                                        false );
672       mapTypeToArrayField.put( tdElement, fdElement );
673     }
674     return fdElement;
675   }
676
677   
678   
679   private void writeFinalGraphs() {
680     Set entrySet = mapDescriptorToCompleteReachGraph.entrySet();
681     Iterator itr = entrySet.iterator();
682     while( itr.hasNext() ) {
683       Map.Entry  me = (Map.Entry)  itr.next();
684       Descriptor  d = (Descriptor) me.getKey();
685       ReachGraph rg = (ReachGraph) me.getValue();
686
687       try {        
688         rg.writeGraph( "COMPLETE"+d,
689                        true,   // write labels (variables)
690                        true,   // selectively hide intermediate temp vars
691                        true,   // prune unreachable heap regions
692                        false,  // show back edges to confirm graph validity
693                        true,   // hide subset reachability states
694                        true ); // hide edge taints
695       } catch( IOException e ) {}    
696     }
697   }
698
699   private void writeFinalIHMs() {
700     Iterator d2IHMsItr = mapDescriptorToIHMcontributions.entrySet().iterator();
701     while( d2IHMsItr.hasNext() ) {
702       Map.Entry                        me1 = (Map.Entry)                       d2IHMsItr.next();
703       Descriptor                         d = (Descriptor)                      me1.getKey();
704       Hashtable<FlatCall, ReachGraph> IHMs = (Hashtable<FlatCall, ReachGraph>) me1.getValue();
705
706       Iterator fc2rgItr = IHMs.entrySet().iterator();
707       while( fc2rgItr.hasNext() ) {
708         Map.Entry  me2 = (Map.Entry)  fc2rgItr.next();
709         FlatCall   fc  = (FlatCall)   me2.getKey();
710         ReachGraph rg  = (ReachGraph) me2.getValue();
711                 
712         try {        
713           rg.writeGraph( "IHMPARTFOR"+d+"FROM"+fc,
714                          true,   // write labels (variables)
715                          false,  // selectively hide intermediate temp vars
716                          false,  // prune unreachable heap regions
717                          false,  // show back edges to confirm graph validity
718                          true,   // hide subset reachability states
719                          true ); // hide edge taints
720         } catch( IOException e ) {}    
721       }
722     }
723   }
724    
725
726
727
728   // return just the allocation site associated with one FlatNew node
729   protected AllocSite getAllocSiteFromFlatNewPRIVATE( FlatNew fnew ) {
730
731     if( !mapFlatNewToAllocSite.containsKey( fnew ) ) {
732       AllocSite as = 
733         (AllocSite) Canonical.makeCanonical( new AllocSite( allocationDepth, 
734                                                             fnew, 
735                                                             fnew.getDisjointId() 
736                                                             )
737                                              );
738
739       // the newest nodes are single objects
740       for( int i = 0; i < allocationDepth; ++i ) {
741         Integer id = generateUniqueHeapRegionNodeID();
742         as.setIthOldest( i, id );
743         mapHrnIdToAllocSite.put( id, as );
744       }
745
746       // the oldest node is a summary node
747       as.setSummary( generateUniqueHeapRegionNodeID() );
748
749       // and one special node is older than all
750       // nodes and shadow nodes for the site
751       as.setSiteSummary( generateUniqueHeapRegionNodeID() );
752
753       mapFlatNewToAllocSite.put( fnew, as );
754     }
755
756     return mapFlatNewToAllocSite.get( fnew );
757   }
758
759
760   /*
761   // return all allocation sites in the method (there is one allocation
762   // site per FlatNew node in a method)
763   protected HashSet<AllocSite> getAllocSiteSet(Descriptor d) {
764     if( !mapDescriptorToAllocSiteSet.containsKey(d) ) {
765       buildAllocSiteSet(d);
766     }
767
768     return mapDescriptorToAllocSiteSet.get(d);
769
770   }
771   */
772
773   /*
774   protected void buildAllocSiteSet(Descriptor d) {
775     HashSet<AllocSite> s = new HashSet<AllocSite>();
776
777     FlatMethod fm = state.getMethodFlat( d );
778
779     // visit every node in this FlatMethod's IR graph
780     // and make a set of the allocation sites from the
781     // FlatNew node's visited
782     HashSet<FlatNode> visited = new HashSet<FlatNode>();
783     HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
784     toVisit.add( fm );
785
786     while( !toVisit.isEmpty() ) {
787       FlatNode n = toVisit.iterator().next();
788
789       if( n instanceof FlatNew ) {
790         s.add(getAllocSiteFromFlatNewPRIVATE( (FlatNew) n) );
791       }
792
793       toVisit.remove( n );
794       visited.add( n );
795
796       for( int i = 0; i < n.numNext(); ++i ) {
797         FlatNode child = n.getNext( i );
798         if( !visited.contains( child ) ) {
799           toVisit.add( child );
800         }
801       }
802     }
803
804     mapDescriptorToAllocSiteSet.put( d, s );
805   }
806   */
807   /*
808   protected HashSet<AllocSite> getFlaggedAllocSites(Descriptor dIn) {
809     
810     HashSet<AllocSite> out     = new HashSet<AllocSite>();
811     HashSet<Descriptor>     toVisit = new HashSet<Descriptor>();
812     HashSet<Descriptor>     visited = new HashSet<Descriptor>();
813
814     toVisit.add(dIn);
815
816     while( !toVisit.isEmpty() ) {
817       Descriptor d = toVisit.iterator().next();
818       toVisit.remove(d);
819       visited.add(d);
820
821       HashSet<AllocSite> asSet = getAllocSiteSet(d);
822       Iterator asItr = asSet.iterator();
823       while( asItr.hasNext() ) {
824         AllocSite as = (AllocSite) asItr.next();
825         if( as.getDisjointAnalysisId() != null ) {
826           out.add(as);
827         }
828       }
829
830       // enqueue callees of this method to be searched for
831       // allocation sites also
832       Set callees = callGraph.getCalleeSet(d);
833       if( callees != null ) {
834         Iterator methItr = callees.iterator();
835         while( methItr.hasNext() ) {
836           MethodDescriptor md = (MethodDescriptor) methItr.next();
837
838           if( !visited.contains(md) ) {
839             toVisit.add(md);
840           }
841         }
842       }
843     }
844     
845     return out;
846   }
847   */
848
849   /*
850   protected HashSet<AllocSite>
851   getFlaggedAllocSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
852
853     HashSet<AllocSite> asSetTotal = new HashSet<AllocSite>();
854     HashSet<Descriptor>     toVisit    = new HashSet<Descriptor>();
855     HashSet<Descriptor>     visited    = new HashSet<Descriptor>();
856
857     toVisit.add(td);
858
859     // traverse this task and all methods reachable from this task
860     while( !toVisit.isEmpty() ) {
861       Descriptor d = toVisit.iterator().next();
862       toVisit.remove(d);
863       visited.add(d);
864
865       HashSet<AllocSite> asSet = getAllocSiteSet(d);
866       Iterator asItr = asSet.iterator();
867       while( asItr.hasNext() ) {
868         AllocSite as = (AllocSite) asItr.next();
869         TypeDescriptor typed = as.getType();
870         if( typed != null ) {
871           ClassDescriptor cd = typed.getClassDesc();
872           if( cd != null && cd.hasFlags() ) {
873             asSetTotal.add(as);
874           }
875         }
876       }
877
878       // enqueue callees of this method to be searched for
879       // allocation sites also
880       Set callees = callGraph.getCalleeSet(d);
881       if( callees != null ) {
882         Iterator methItr = callees.iterator();
883         while( methItr.hasNext() ) {
884           MethodDescriptor md = (MethodDescriptor) methItr.next();
885
886           if( !visited.contains(md) ) {
887             toVisit.add(md);
888           }
889         }
890       }
891     }
892
893
894     return asSetTotal;
895   }
896   */
897
898
899   /*
900   protected String computeAliasContextHistogram() {
901     
902     Hashtable<Integer, Integer> mapNumContexts2NumDesc = 
903       new Hashtable<Integer, Integer>();
904   
905     Iterator itr = mapDescriptorToAllDescriptors.entrySet().iterator();
906     while( itr.hasNext() ) {
907       Map.Entry me = (Map.Entry) itr.next();
908       HashSet<Descriptor> s = (HashSet<Descriptor>) me.getValue();
909       
910       Integer i = mapNumContexts2NumDesc.get( s.size() );
911       if( i == null ) {
912         i = new Integer( 0 );
913       }
914       mapNumContexts2NumDesc.put( s.size(), i + 1 );
915     }   
916
917     String s = "";
918     int total = 0;
919
920     itr = mapNumContexts2NumDesc.entrySet().iterator();
921     while( itr.hasNext() ) {
922       Map.Entry me = (Map.Entry) itr.next();
923       Integer c0 = (Integer) me.getKey();
924       Integer d0 = (Integer) me.getValue();
925       total += d0;
926       s += String.format( "%4d methods had %4d unique alias contexts.\n", d0, c0 );
927     }
928
929     s += String.format( "\n%4d total methods analayzed.\n", total );
930
931     return s;
932   }
933
934   protected int numMethodsAnalyzed() {    
935     return descriptorsToAnalyze.size();
936   }
937   */
938
939   
940   
941   
942   // Take in source entry which is the program's compiled entry and
943   // create a new analysis entry, a method that takes no parameters
944   // and appears to allocate the command line arguments and call the
945   // source entry with them.  The purpose of this analysis entry is
946   // to provide a top-level method context with no parameters left.
947   protected void makeAnalysisEntryMethod( MethodDescriptor mdSourceEntry ) {
948
949     Modifiers mods = new Modifiers();
950     mods.addModifier( Modifiers.PUBLIC );
951     mods.addModifier( Modifiers.STATIC );
952
953     TypeDescriptor returnType = 
954       new TypeDescriptor( TypeDescriptor.VOID );
955
956     this.mdAnalysisEntry = 
957       new MethodDescriptor( mods,
958                             returnType,
959                             "analysisEntryMethod"
960                             );
961
962     TempDescriptor cmdLineArgs = 
963       new TempDescriptor( "args",
964                           mdSourceEntry.getParamType( 0 )
965                           );
966
967     FlatNew fn = 
968       new FlatNew( mdSourceEntry.getParamType( 0 ),
969                    cmdLineArgs,
970                    false // is global 
971                    );
972     
973     TempDescriptor[] sourceEntryArgs = new TempDescriptor[1];
974     sourceEntryArgs[0] = cmdLineArgs;
975     
976     FlatCall fc = 
977       new FlatCall( mdSourceEntry,
978                     null, // dst temp
979                     null, // this temp
980                     sourceEntryArgs
981                     );
982
983     FlatReturnNode frn = new FlatReturnNode( null );
984
985     FlatExit fe = new FlatExit();
986
987     this.fmAnalysisEntry = 
988       new FlatMethod( mdAnalysisEntry, 
989                       fe
990                       );
991
992     this.fmAnalysisEntry.addNext( fn );
993     fn.addNext( fc );
994     fc.addNext( frn );
995     frn.addNext( fe );
996   }
997
998
999   protected LinkedList<Descriptor> topologicalSort( Set<Descriptor> toSort ) {
1000
1001     Set       <Descriptor> discovered = new HashSet   <Descriptor>();
1002     LinkedList<Descriptor> sorted     = new LinkedList<Descriptor>();
1003   
1004     Iterator<Descriptor> itr = toSort.iterator();
1005     while( itr.hasNext() ) {
1006       Descriptor d = itr.next();
1007           
1008       if( !discovered.contains( d ) ) {
1009         dfsVisit( d, toSort, sorted, discovered );
1010       }
1011     }
1012     
1013     return sorted;
1014   }
1015   
1016   // While we're doing DFS on call graph, remember
1017   // dependencies for efficient queuing of methods
1018   // during interprocedural analysis:
1019   //
1020   // a dependent of a method decriptor d for this analysis is:
1021   //  1) a method or task that invokes d
1022   //  2) in the descriptorsToAnalyze set
1023   protected void dfsVisit( Descriptor             d,
1024                            Set       <Descriptor> toSort,                        
1025                            LinkedList<Descriptor> sorted,
1026                            Set       <Descriptor> discovered ) {
1027     discovered.add( d );
1028     
1029     // only methods have callers, tasks never do
1030     if( d instanceof MethodDescriptor ) {
1031
1032       MethodDescriptor md = (MethodDescriptor) d;
1033
1034       // the call graph is not aware that we have a fabricated
1035       // analysis entry that calls the program source's entry
1036       if( md == mdSourceEntry ) {
1037         if( !discovered.contains( mdAnalysisEntry ) ) {
1038           addDependent( mdSourceEntry,  // callee
1039                         mdAnalysisEntry // caller
1040                         );
1041           dfsVisit( mdAnalysisEntry, toSort, sorted, discovered );
1042         }
1043       }
1044
1045       // otherwise call graph guides DFS
1046       Iterator itr = callGraph.getCallerSet( md ).iterator();
1047       while( itr.hasNext() ) {
1048         Descriptor dCaller = (Descriptor) itr.next();
1049         
1050         // only consider callers in the original set to analyze
1051         if( !toSort.contains( dCaller ) ) {
1052           continue;
1053         }
1054           
1055         if( !discovered.contains( dCaller ) ) {
1056           addDependent( md,     // callee
1057                         dCaller // caller
1058                         );
1059
1060           dfsVisit( dCaller, toSort, sorted, discovered );
1061         }
1062       }
1063     }
1064     
1065     sorted.addFirst( d );
1066   }
1067
1068
1069   protected void enqueue( Descriptor d ) {
1070     if( !descriptorsToVisitSet.contains( d ) ) {
1071       Integer priority = mapDescriptorToPriority.get( d );
1072       descriptorsToVisitQ.add( new DescriptorQWrapper( priority, 
1073                                                        d ) 
1074                                );
1075       descriptorsToVisitSet.add( d );
1076     }
1077   }
1078
1079
1080   protected ReachGraph getPartial( Descriptor d ) {
1081     return mapDescriptorToCompleteReachGraph.get( d );
1082   }
1083
1084   protected void setPartial( Descriptor d, ReachGraph rg ) {
1085     mapDescriptorToCompleteReachGraph.put( d, rg );
1086
1087     // when the flag for writing out every partial
1088     // result is set, we should spit out the graph,
1089     // but in order to give it a unique name we need
1090     // to track how many partial results for this
1091     // descriptor we've already written out
1092     if( writeAllIncrementalDOTs ) {
1093       if( !mapDescriptorToNumUpdates.containsKey( d ) ) {
1094         mapDescriptorToNumUpdates.put( d, new Integer( 0 ) );
1095       }
1096       Integer n = mapDescriptorToNumUpdates.get( d );
1097       /*
1098       try {
1099         rg.writeGraph( d+"COMPLETE"+String.format( "%05d", n ),
1100                        true,  // write labels (variables)
1101                        true,  // selectively hide intermediate temp vars
1102                        true,  // prune unreachable heap regions
1103                        false, // show back edges to confirm graph validity
1104                        false, // show parameter indices (unmaintained!)
1105                        true,  // hide subset reachability states
1106                        true); // hide edge taints
1107       } catch( IOException e ) {}
1108       */
1109       mapDescriptorToNumUpdates.put( d, n + 1 );
1110     }
1111   }
1112
1113
1114   // a dependent of a method decriptor d for this analysis is:
1115   //  1) a method or task that invokes d
1116   //  2) in the descriptorsToAnalyze set
1117   protected void addDependent( Descriptor callee, Descriptor caller ) {
1118     Set<Descriptor> deps = mapDescriptorToSetDependents.get( callee );
1119     if( deps == null ) {
1120       deps = new HashSet<Descriptor>();
1121     }
1122     deps.add( caller );
1123     mapDescriptorToSetDependents.put( callee, deps );
1124   }
1125   
1126   protected Set<Descriptor> getDependents( Descriptor callee ) {
1127     Set<Descriptor> deps = mapDescriptorToSetDependents.get( callee );
1128     if( deps == null ) {
1129       deps = new HashSet<Descriptor>();
1130       mapDescriptorToSetDependents.put( callee, deps );
1131     }
1132     return deps;
1133   }
1134
1135   
1136   public Hashtable<FlatCall, ReachGraph> getIHMcontributions( Descriptor d ) {
1137
1138     Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
1139       mapDescriptorToIHMcontributions.get( d );
1140     
1141     if( heapsFromCallers == null ) {
1142       heapsFromCallers = new Hashtable<FlatCall, ReachGraph>();
1143       mapDescriptorToIHMcontributions.put( d, heapsFromCallers );
1144     }
1145     
1146     return heapsFromCallers;
1147   }
1148
1149   public ReachGraph getIHMcontribution( Descriptor d, 
1150                                         FlatCall   fc
1151                                         ) {
1152     Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
1153       getIHMcontributions( d );
1154
1155     if( !heapsFromCallers.containsKey( fc ) ) {
1156       heapsFromCallers.put( fc, new ReachGraph() );
1157     }
1158
1159     return heapsFromCallers.get( fc );
1160   }
1161
1162   public void addIHMcontribution( Descriptor d,
1163                                   FlatCall   fc,
1164                                   ReachGraph rg
1165                                   ) {
1166     Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
1167       getIHMcontributions( d );
1168
1169     heapsFromCallers.put( fc, rg );
1170   }
1171
1172
1173
1174   
1175   
1176   // get successive captures of the analysis state
1177   boolean takeDebugSnapshots = false;
1178   String descSymbolDebug = "addSomething";
1179   boolean stopAfterCapture = true;
1180
1181   // increments every visit to debugSnapshot, don't fiddle with it
1182   int debugCounter = 0;
1183
1184   // the value of debugCounter to start reporting the debugCounter
1185   // to the screen to let user know what debug iteration we're at
1186   int numStartCountReport = 0;
1187
1188   // the frequency of debugCounter values to print out, 0 no report
1189   int freqCountReport = 0;
1190
1191   // the debugCounter value at which to start taking snapshots
1192   int iterStartCapture = 0;
1193
1194   // the number of snapshots to take
1195   int numIterToCapture = 300;
1196
1197   void debugSnapshot( ReachGraph rg, FlatNode fn ) {
1198     if( debugCounter > iterStartCapture + numIterToCapture ) {
1199       return;
1200     }
1201
1202     ++debugCounter;
1203     if( debugCounter    > numStartCountReport &&
1204         freqCountReport > 0                   &&
1205         debugCounter % freqCountReport == 0 
1206         ) {
1207       System.out.println( "    @@@ debug counter = "+
1208                           debugCounter );
1209     }
1210     if( debugCounter > iterStartCapture ) {
1211       System.out.println( "    @@@ capturing debug "+
1212                           (debugCounter - iterStartCapture)+
1213                           " @@@" );
1214       String graphName = 
1215         String.format( "snap%04d",
1216                        debugCounter - iterStartCapture );
1217       if( fn != null ) {
1218         graphName = graphName + fn;
1219       }
1220       try {
1221         rg.writeGraph( graphName,
1222                        true,  // write labels (variables)
1223                        true,  // selectively hide intermediate temp vars
1224                        false, // prune unreachable heap regions
1225                        false, // show back edges to confirm graph validity
1226                        true,  // hide subset reachability states
1227                        true );// hide edge taints
1228       } catch( Exception e ) {
1229         System.out.println( "Error writing debug capture." );
1230         System.exit( 0 );
1231       }
1232     }
1233
1234     if( debugCounter == iterStartCapture + numIterToCapture && 
1235         stopAfterCapture 
1236         ) {
1237       System.out.println( "Stopping analysis after debug captures." );
1238       System.exit( 0 );
1239     }
1240   }
1241
1242 }