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