bug fix.
[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( "Bamboo..." );
238       Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
239       
240       while (taskItr.hasNext()) {
241           TaskDescriptor td = (TaskDescriptor) taskItr.next();
242           if (!descriptorsToAnalyze.contains(td)) {           
243               descriptorsToAnalyze.add(td);
244               descriptorsToAnalyze.addAll(callGraph.getAllMethods(td));
245           }       
246       }
247
248     } else {
249       // add all methods transitively reachable from the
250       // source's main to set for analysis
251       mdSourceEntry = typeUtil.getMain();
252       descriptorsToAnalyze.add( mdSourceEntry );
253       descriptorsToAnalyze.addAll( 
254         callGraph.getAllMethods( mdSourceEntry ) 
255                                    );
256
257       // fabricate an empty calling context that will call
258       // the source's main, but call graph doesn't know
259       // about it, so explicitly add it
260       makeAnalysisEntryMethod( mdSourceEntry );
261       descriptorsToAnalyze.add( mdAnalysisEntry );
262     }
263
264     // topologically sort according to the call graph so 
265     // leaf calls are ordered first, smarter analysis order
266     LinkedList<Descriptor> sortedDescriptors = 
267       topologicalSort( descriptorsToAnalyze );
268
269     // add sorted descriptors to priority queue, and duplicate
270     // the queue as a set for efficiently testing whether some
271     // method is marked for analysis
272     int p = 0;
273     Iterator<Descriptor> dItr = sortedDescriptors.iterator();
274     while( dItr.hasNext() ) {
275       Descriptor d = dItr.next();
276       mapDescriptorToPriority.put( d, new Integer( p ) );
277       descriptorsToVisitQ.add( new DescriptorQWrapper( p, d ) );
278       descriptorsToVisitSet.add( d );
279       ++p;
280     }
281
282     // analyze methods from the priority queue until it is empty
283     while( !descriptorsToVisitQ.isEmpty() ) {
284       Descriptor d = descriptorsToVisitQ.poll().getDescriptor();
285       assert descriptorsToVisitSet.contains( d );
286       descriptorsToVisitSet.remove( d );
287
288       // because the task or method descriptor just extracted
289       // was in the "to visit" set it either hasn't been analyzed
290       // yet, or some method that it depends on has been
291       // updated.  Recompute a complete reachability graph for
292       // this task/method and compare it to any previous result.
293       // If there is a change detected, add any methods/tasks
294       // that depend on this one to the "to visit" set.
295
296       System.out.println( "Analyzing " + d );
297
298       ReachGraph rg     = analyzeMethod( d );
299       ReachGraph rgPrev = getPartial( d );
300
301       if( !rg.equals( rgPrev ) ) {
302         setPartial( d, rg );
303
304         // results for d changed, so enqueue dependents
305         // of d for further analysis
306         Iterator<Descriptor> depsItr = getDependents( d ).iterator();
307         while( depsItr.hasNext() ) {
308           Descriptor dNext = depsItr.next();
309           enqueue( dNext );
310         }
311       }      
312     }
313   }
314
315   protected ReachGraph analyzeMethod( Descriptor d ) 
316     throws java.io.IOException {
317
318     // get the flat code for this descriptor
319     FlatMethod fm;
320     if( d == mdAnalysisEntry ) {
321       fm = fmAnalysisEntry;
322     } else {
323       fm = state.getMethodFlat( d );
324     }
325       
326     // intraprocedural work set
327     Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
328     flatNodesToVisit.add( fm );
329     
330     // mapping of current partial results
331     Hashtable<FlatNode, ReachGraph> mapFlatNodeToReachGraph =
332       new Hashtable<FlatNode, ReachGraph>();
333
334     // the set of return nodes partial results that will be combined as
335     // the final, conservative approximation of the entire method
336     HashSet<FlatReturnNode> setReturns = new HashSet<FlatReturnNode>();
337
338     while( !flatNodesToVisit.isEmpty() ) {
339       FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
340       flatNodesToVisit.remove( fn );
341
342       //System.out.println( "  "+fn );
343
344       // effect transfer function defined by this node,
345       // then compare it to the old graph at this node
346       // to see if anything was updated.
347
348       ReachGraph rg = new ReachGraph();
349       
350       if(fn instanceof FlatMethod && ((FlatMethod)fn).getTask()!=null){
351           // create initial reach graph for a task
352           rg=createInitialTaskReachGraph((FlatMethod)fn);
353       }
354
355       // start by merging all node's parents' graphs
356       for( int i = 0; i < fn.numPrev(); ++i ) {
357         FlatNode pn = fn.getPrev( i );
358         if( mapFlatNodeToReachGraph.containsKey( pn ) ) {
359           ReachGraph rgParent = mapFlatNodeToReachGraph.get( pn );
360           rg.merge( rgParent );
361         }
362       }
363
364       if( takeDebugSnapshots && 
365           d.getSymbol().equals( descSymbolDebug ) 
366           ) {
367         debugSnapshot( rg, fn, true );
368       }
369
370       // modify rg with appropriate transfer function
371       rg = analyzeFlatNode( d, fm, fn, setReturns, rg );
372           
373       if( takeDebugSnapshots && 
374           d.getSymbol().equals( descSymbolDebug ) 
375           ) {
376         debugSnapshot( rg, fn, false );
377       }
378
379
380       // if the results of the new graph are different from
381       // the current graph at this node, replace the graph
382       // with the update and enqueue the children
383       ReachGraph rgPrev = mapFlatNodeToReachGraph.get( fn );
384       if( !rg.equals( rgPrev ) ) {
385         mapFlatNodeToReachGraph.put( fn, rg );
386
387         for( int i = 0; i < fn.numNext(); i++ ) {
388           FlatNode nn = fn.getNext( i );
389           flatNodesToVisit.add( nn );
390         }
391       }
392     }
393
394     // end by merging all return nodes into a complete
395     // ownership graph that represents all possible heap
396     // states after the flat method returns
397     ReachGraph completeGraph = new ReachGraph();
398
399     assert !setReturns.isEmpty();
400     Iterator retItr = setReturns.iterator();
401     while( retItr.hasNext() ) {
402       FlatReturnNode frn = (FlatReturnNode) retItr.next();
403
404       assert mapFlatNodeToReachGraph.containsKey( frn );
405       ReachGraph rgRet = mapFlatNodeToReachGraph.get( frn );
406
407       completeGraph.merge( rgRet );
408     }
409     
410     return completeGraph;
411   }
412
413   
414   protected ReachGraph
415     analyzeFlatNode( Descriptor              d,
416                      FlatMethod              fmContaining,
417                      FlatNode                fn,
418                      HashSet<FlatReturnNode> setRetNodes,
419                      ReachGraph              rg
420                      ) throws java.io.IOException {
421
422     
423     // any variables that are no longer live should be
424     // nullified in the graph to reduce edges
425     //rg.nullifyDeadVars( liveness.getLiveInTemps( fmContaining, fn ) );
426
427           
428     TempDescriptor  lhs;
429     TempDescriptor  rhs;
430     FieldDescriptor fld;
431
432     // use node type to decide what transfer function
433     // to apply to the reachability graph
434     switch( fn.kind() ) {
435
436     case FKind.FlatMethod: {
437       // construct this method's initial heap model (IHM)
438       // since we're working on the FlatMethod, we know
439       // the incoming ReachGraph 'rg' is empty
440
441       Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
442         getIHMcontributions( d );
443
444       Set entrySet = heapsFromCallers.entrySet();
445       Iterator itr = entrySet.iterator();
446       while( itr.hasNext() ) {
447         Map.Entry  me        = (Map.Entry)  itr.next();
448         FlatCall   fc        = (FlatCall)   me.getKey();
449         ReachGraph rgContrib = (ReachGraph) me.getValue();
450
451         assert fc.getMethod().equals( d );
452
453         // some call sites are in same method context though,
454         // and all of them should be merged together first,
455         // then heaps from different contexts should be merged
456         // THIS ASSUMES DIFFERENT CONTEXTS NEED SPECIAL CONSIDERATION!
457         // such as, do allocation sites need to be aged?
458
459         rg.merge_diffMethodContext( rgContrib );
460       }      
461     } break;
462       
463     case FKind.FlatOpNode:
464       FlatOpNode fon = (FlatOpNode) fn;
465       if( fon.getOp().getOp() == Operation.ASSIGN ) {
466         lhs = fon.getDest();
467         rhs = fon.getLeft();
468         rg.assignTempXEqualToTempY( lhs, rhs );
469       }
470       break;
471
472     case FKind.FlatCastNode:
473       FlatCastNode fcn = (FlatCastNode) fn;
474       lhs = fcn.getDst();
475       rhs = fcn.getSrc();
476
477       TypeDescriptor td = fcn.getType();
478       assert td != null;
479       
480       rg.assignTempXEqualToCastedTempY( lhs, rhs, td );
481       break;
482
483     case FKind.FlatFieldNode:
484       FlatFieldNode ffn = (FlatFieldNode) fn;
485       lhs = ffn.getDst();
486       rhs = ffn.getSrc();
487       fld = ffn.getField();
488       if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
489         rg.assignTempXEqualToTempYFieldF( lhs, rhs, fld );
490       }          
491       break;
492
493     case FKind.FlatSetFieldNode:
494       FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
495       lhs = fsfn.getDst();
496       fld = fsfn.getField();
497       rhs = fsfn.getSrc();
498       if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
499         rg.assignTempXFieldFEqualToTempY( lhs, fld, rhs );
500       }           
501       break;
502
503     case FKind.FlatElementNode:
504       FlatElementNode fen = (FlatElementNode) fn;
505       lhs = fen.getDst();
506       rhs = fen.getSrc();
507       if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
508
509         assert rhs.getType() != null;
510         assert rhs.getType().isArray();
511         
512         TypeDescriptor  tdElement = rhs.getType().dereference();
513         FieldDescriptor fdElement = getArrayField( tdElement );
514   
515         rg.assignTempXEqualToTempYFieldF( lhs, rhs, fdElement );
516       }
517       break;
518
519     case FKind.FlatSetElementNode:
520       FlatSetElementNode fsen = (FlatSetElementNode) fn;
521
522       if( arrayReferencees.doesNotCreateNewReaching( fsen ) ) {
523         // skip this node if it cannot create new reachability paths
524         break;
525       }
526
527       lhs = fsen.getDst();
528       rhs = fsen.getSrc();
529       if( !rhs.getType().isImmutable() || rhs.getType().isArray() ) {
530
531         assert lhs.getType() != null;
532         assert lhs.getType().isArray();
533         
534         TypeDescriptor  tdElement = lhs.getType().dereference();
535         FieldDescriptor fdElement = getArrayField( tdElement );
536
537         rg.assignTempXFieldFEqualToTempY( lhs, fdElement, rhs );
538       }
539       break;
540       
541     case FKind.FlatNew:
542       FlatNew fnn = (FlatNew) fn;
543       lhs = fnn.getDst();
544       if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
545         AllocSite as = getAllocSiteFromFlatNewPRIVATE( fnn );   
546         rg.assignTempEqualToNewAlloc( lhs, as );
547       }
548       break;
549
550     case FKind.FlatCall: {
551       //TODO: temporal fix for task descriptor case
552       //MethodDescriptor mdCaller = fmContaining.getMethod();
553       Descriptor mdCaller;
554       if(fmContaining.getMethod()!=null){
555           mdCaller  = fmContaining.getMethod();
556       }else{
557           mdCaller = fmContaining.getTask();
558       }      
559       FlatCall         fc       = (FlatCall) fn;
560       MethodDescriptor mdCallee = fc.getMethod();
561       FlatMethod       fmCallee = state.getMethodFlat( mdCallee );
562
563       boolean writeDebugDOTs = 
564         mdCaller.getSymbol().equals( state.DISJOINTDEBUGCALLER ) &&
565         mdCallee.getSymbol().equals( state.DISJOINTDEBUGCALLEE );      
566
567
568       // calculate the heap this call site can reach--note this is
569       // not used for the current call site transform, we are
570       // grabbing this heap model for future analysis of the callees,
571       // so if different results emerge we will return to this site
572       ReachGraph heapForThisCall_old = 
573         getIHMcontribution( mdCallee, fc );
574
575       // the computation of the callee-reachable heap
576       // is useful for making the callee starting point
577       // and for applying the call site transfer function
578       Set<Integer> callerNodeIDsCopiedToCallee = 
579         new HashSet<Integer>();
580
581       ReachGraph heapForThisCall_cur = 
582         rg.makeCalleeView( fc, 
583                            fmCallee,
584                            callerNodeIDsCopiedToCallee,
585                            writeDebugDOTs
586                            );
587
588       if( !heapForThisCall_cur.equals( heapForThisCall_old ) ) {        
589         // if heap at call site changed, update the contribution,
590         // and reschedule the callee for analysis
591         addIHMcontribution( mdCallee, fc, heapForThisCall_cur );        
592         enqueue( mdCallee );
593       }
594
595
596
597
598       // the transformation for a call site should update the
599       // current heap abstraction with any effects from the callee,
600       // or if the method is virtual, the effects from any possible
601       // callees, so find the set of callees...
602       Set<MethodDescriptor> setPossibleCallees =
603         new HashSet<MethodDescriptor>();
604
605       if( mdCallee.isStatic() ) {        
606         setPossibleCallees.add( mdCallee );
607       } else {
608         TypeDescriptor typeDesc = fc.getThis().getType();
609         setPossibleCallees.addAll( callGraph.getMethods( mdCallee, 
610                                                          typeDesc )
611                                    );
612       }
613
614       ReachGraph rgMergeOfEffects = new ReachGraph();
615
616       Iterator<MethodDescriptor> mdItr = setPossibleCallees.iterator();
617       while( mdItr.hasNext() ) {
618         MethodDescriptor mdPossible = mdItr.next();
619         FlatMethod       fmPossible = state.getMethodFlat( mdPossible );
620
621         addDependent( mdPossible, // callee
622                       d );        // caller
623
624         // don't alter the working graph (rg) until we compute a 
625         // result for every possible callee, merge them all together,
626         // then set rg to that
627         ReachGraph rgCopy = new ReachGraph();
628         rgCopy.merge( rg );             
629                 
630         ReachGraph rgEffect = getPartial( mdPossible );
631
632         if( rgEffect == null ) {
633           // if this method has never been analyzed just schedule it 
634           // for analysis and skip over this call site for now
635           enqueue( mdPossible );
636         } else {
637           rgCopy.resolveMethodCall( fc, 
638                                     fmPossible, 
639                                     rgEffect,
640                                     callerNodeIDsCopiedToCallee,
641                                     writeDebugDOTs
642                                     );
643         }
644         
645         rgMergeOfEffects.merge( rgCopy );
646       }
647
648
649       // now that we've taken care of building heap models for
650       // callee analysis, finish this transformation
651       rg = rgMergeOfEffects;
652     } break;
653       
654
655     case FKind.FlatReturnNode:
656       FlatReturnNode frn = (FlatReturnNode) fn;
657       rhs = frn.getReturnTemp();
658       if( rhs != null && !rhs.getType().isImmutable() ) {
659         rg.assignReturnEqualToTemp( rhs );
660       }
661       setRetNodes.add( frn );
662       break;
663
664     } // end switch
665
666     
667     // dead variables were removed before the above transfer function
668     // was applied, so eliminate heap regions and edges that are no
669     // longer part of the abstractly-live heap graph, and sweep up
670     // and reachability effects that are altered by the reduction
671     //rg.abstractGarbageCollect();
672     //rg.globalSweep();
673
674
675     // at this point rg should be the correct update
676     // by an above transfer function, or untouched if
677     // the flat node type doesn't affect the heap
678     return rg;
679   }
680
681   
682   // this method should generate integers strictly greater than zero!
683   // special "shadow" regions are made from a heap region by negating
684   // the ID
685   static public Integer generateUniqueHeapRegionNodeID() {
686     ++uniqueIDcount;
687     return new Integer( uniqueIDcount );
688   }
689
690
691   
692   static public FieldDescriptor getArrayField( TypeDescriptor tdElement ) {
693     FieldDescriptor fdElement = mapTypeToArrayField.get( tdElement );
694     if( fdElement == null ) {
695       fdElement = new FieldDescriptor( new Modifiers( Modifiers.PUBLIC ),
696                                        tdElement,
697                                        arrayElementFieldName,
698                                        null,
699                                        false );
700       mapTypeToArrayField.put( tdElement, fdElement );
701     }
702     return fdElement;
703   }
704
705   
706   
707   private void writeFinalGraphs() {
708     Set entrySet = mapDescriptorToCompleteReachGraph.entrySet();
709     Iterator itr = entrySet.iterator();
710     while( itr.hasNext() ) {
711       Map.Entry  me = (Map.Entry)  itr.next();
712       Descriptor  d = (Descriptor) me.getKey();
713       ReachGraph rg = (ReachGraph) me.getValue();
714
715       try {        
716         rg.writeGraph( "COMPLETE"+d,
717                        true,   // write labels (variables)
718                        true,   // selectively hide intermediate temp vars
719                        true,   // prune unreachable heap regions
720                        false,  // show back edges to confirm graph validity
721                        true,   // hide subset reachability states
722                        true ); // hide edge taints
723       } catch( IOException e ) {}    
724     }
725   }
726
727   private void writeFinalIHMs() {
728     Iterator d2IHMsItr = mapDescriptorToIHMcontributions.entrySet().iterator();
729     while( d2IHMsItr.hasNext() ) {
730       Map.Entry                        me1 = (Map.Entry)                       d2IHMsItr.next();
731       Descriptor                         d = (Descriptor)                      me1.getKey();
732       Hashtable<FlatCall, ReachGraph> IHMs = (Hashtable<FlatCall, ReachGraph>) me1.getValue();
733
734       Iterator fc2rgItr = IHMs.entrySet().iterator();
735       while( fc2rgItr.hasNext() ) {
736         Map.Entry  me2 = (Map.Entry)  fc2rgItr.next();
737         FlatCall   fc  = (FlatCall)   me2.getKey();
738         ReachGraph rg  = (ReachGraph) me2.getValue();
739                 
740         try {        
741           rg.writeGraph( "IHMPARTFOR"+d+"FROM"+fc,
742                          true,   // write labels (variables)
743                          false,  // selectively hide intermediate temp vars
744                          false,  // prune unreachable heap regions
745                          false,  // show back edges to confirm graph validity
746                          true,   // hide subset reachability states
747                          true ); // hide edge taints
748         } catch( IOException e ) {}    
749       }
750     }
751   }
752    
753
754
755
756   // return just the allocation site associated with one FlatNew node
757   protected AllocSite getAllocSiteFromFlatNewPRIVATE( FlatNew fnew ) {
758
759     if( !mapFlatNewToAllocSite.containsKey( fnew ) ) {
760       AllocSite as = 
761         (AllocSite) Canonical.makeCanonical( new AllocSite( allocationDepth, 
762                                                             fnew, 
763                                                             fnew.getDisjointId() 
764                                                             )
765                                              );
766
767       // the newest nodes are single objects
768       for( int i = 0; i < allocationDepth; ++i ) {
769         Integer id = generateUniqueHeapRegionNodeID();
770         as.setIthOldest( i, id );
771         mapHrnIdToAllocSite.put( id, as );
772       }
773
774       // the oldest node is a summary node
775       as.setSummary( generateUniqueHeapRegionNodeID() );
776
777       mapFlatNewToAllocSite.put( fnew, as );
778     }
779
780     return mapFlatNewToAllocSite.get( fnew );
781   }
782
783
784   /*
785   // return all allocation sites in the method (there is one allocation
786   // site per FlatNew node in a method)
787   protected HashSet<AllocSite> getAllocSiteSet(Descriptor d) {
788     if( !mapDescriptorToAllocSiteSet.containsKey(d) ) {
789       buildAllocSiteSet(d);
790     }
791
792     return mapDescriptorToAllocSiteSet.get(d);
793
794   }
795   */
796
797   /*
798   protected void buildAllocSiteSet(Descriptor d) {
799     HashSet<AllocSite> s = new HashSet<AllocSite>();
800
801     FlatMethod fm = state.getMethodFlat( d );
802
803     // visit every node in this FlatMethod's IR graph
804     // and make a set of the allocation sites from the
805     // FlatNew node's visited
806     HashSet<FlatNode> visited = new HashSet<FlatNode>();
807     HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
808     toVisit.add( fm );
809
810     while( !toVisit.isEmpty() ) {
811       FlatNode n = toVisit.iterator().next();
812
813       if( n instanceof FlatNew ) {
814         s.add(getAllocSiteFromFlatNewPRIVATE( (FlatNew) n) );
815       }
816
817       toVisit.remove( n );
818       visited.add( n );
819
820       for( int i = 0; i < n.numNext(); ++i ) {
821         FlatNode child = n.getNext( i );
822         if( !visited.contains( child ) ) {
823           toVisit.add( child );
824         }
825       }
826     }
827
828     mapDescriptorToAllocSiteSet.put( d, s );
829   }
830   */
831   /*
832   protected HashSet<AllocSite> getFlaggedAllocSites(Descriptor dIn) {
833     
834     HashSet<AllocSite> out     = new HashSet<AllocSite>();
835     HashSet<Descriptor>     toVisit = new HashSet<Descriptor>();
836     HashSet<Descriptor>     visited = new HashSet<Descriptor>();
837
838     toVisit.add(dIn);
839
840     while( !toVisit.isEmpty() ) {
841       Descriptor d = toVisit.iterator().next();
842       toVisit.remove(d);
843       visited.add(d);
844
845       HashSet<AllocSite> asSet = getAllocSiteSet(d);
846       Iterator asItr = asSet.iterator();
847       while( asItr.hasNext() ) {
848         AllocSite as = (AllocSite) asItr.next();
849         if( as.getDisjointAnalysisId() != null ) {
850           out.add(as);
851         }
852       }
853
854       // enqueue callees of this method to be searched for
855       // allocation sites also
856       Set callees = callGraph.getCalleeSet(d);
857       if( callees != null ) {
858         Iterator methItr = callees.iterator();
859         while( methItr.hasNext() ) {
860           MethodDescriptor md = (MethodDescriptor) methItr.next();
861
862           if( !visited.contains(md) ) {
863             toVisit.add(md);
864           }
865         }
866       }
867     }
868     
869     return out;
870   }
871   */
872
873   /*
874   protected HashSet<AllocSite>
875   getFlaggedAllocSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
876
877     HashSet<AllocSite> asSetTotal = new HashSet<AllocSite>();
878     HashSet<Descriptor>     toVisit    = new HashSet<Descriptor>();
879     HashSet<Descriptor>     visited    = new HashSet<Descriptor>();
880
881     toVisit.add(td);
882
883     // traverse this task and all methods reachable from this task
884     while( !toVisit.isEmpty() ) {
885       Descriptor d = toVisit.iterator().next();
886       toVisit.remove(d);
887       visited.add(d);
888
889       HashSet<AllocSite> asSet = getAllocSiteSet(d);
890       Iterator asItr = asSet.iterator();
891       while( asItr.hasNext() ) {
892         AllocSite as = (AllocSite) asItr.next();
893         TypeDescriptor typed = as.getType();
894         if( typed != null ) {
895           ClassDescriptor cd = typed.getClassDesc();
896           if( cd != null && cd.hasFlags() ) {
897             asSetTotal.add(as);
898           }
899         }
900       }
901
902       // enqueue callees of this method to be searched for
903       // allocation sites also
904       Set callees = callGraph.getCalleeSet(d);
905       if( callees != null ) {
906         Iterator methItr = callees.iterator();
907         while( methItr.hasNext() ) {
908           MethodDescriptor md = (MethodDescriptor) methItr.next();
909
910           if( !visited.contains(md) ) {
911             toVisit.add(md);
912           }
913         }
914       }
915     }
916
917
918     return asSetTotal;
919   }
920   */
921
922
923   /*
924   protected String computeAliasContextHistogram() {
925     
926     Hashtable<Integer, Integer> mapNumContexts2NumDesc = 
927       new Hashtable<Integer, Integer>();
928   
929     Iterator itr = mapDescriptorToAllDescriptors.entrySet().iterator();
930     while( itr.hasNext() ) {
931       Map.Entry me = (Map.Entry) itr.next();
932       HashSet<Descriptor> s = (HashSet<Descriptor>) me.getValue();
933       
934       Integer i = mapNumContexts2NumDesc.get( s.size() );
935       if( i == null ) {
936         i = new Integer( 0 );
937       }
938       mapNumContexts2NumDesc.put( s.size(), i + 1 );
939     }   
940
941     String s = "";
942     int total = 0;
943
944     itr = mapNumContexts2NumDesc.entrySet().iterator();
945     while( itr.hasNext() ) {
946       Map.Entry me = (Map.Entry) itr.next();
947       Integer c0 = (Integer) me.getKey();
948       Integer d0 = (Integer) me.getValue();
949       total += d0;
950       s += String.format( "%4d methods had %4d unique alias contexts.\n", d0, c0 );
951     }
952
953     s += String.format( "\n%4d total methods analayzed.\n", total );
954
955     return s;
956   }
957
958   protected int numMethodsAnalyzed() {    
959     return descriptorsToAnalyze.size();
960   }
961   */
962
963   
964   
965   
966   // Take in source entry which is the program's compiled entry and
967   // create a new analysis entry, a method that takes no parameters
968   // and appears to allocate the command line arguments and call the
969   // source entry with them.  The purpose of this analysis entry is
970   // to provide a top-level method context with no parameters left.
971   protected void makeAnalysisEntryMethod( MethodDescriptor mdSourceEntry ) {
972
973     Modifiers mods = new Modifiers();
974     mods.addModifier( Modifiers.PUBLIC );
975     mods.addModifier( Modifiers.STATIC );
976
977     TypeDescriptor returnType = 
978       new TypeDescriptor( TypeDescriptor.VOID );
979
980     this.mdAnalysisEntry = 
981       new MethodDescriptor( mods,
982                             returnType,
983                             "analysisEntryMethod"
984                             );
985
986     TempDescriptor cmdLineArgs = 
987       new TempDescriptor( "args",
988                           mdSourceEntry.getParamType( 0 )
989                           );
990
991     FlatNew fn = 
992       new FlatNew( mdSourceEntry.getParamType( 0 ),
993                    cmdLineArgs,
994                    false // is global 
995                    );
996     
997     TempDescriptor[] sourceEntryArgs = new TempDescriptor[1];
998     sourceEntryArgs[0] = cmdLineArgs;
999     
1000     FlatCall fc = 
1001       new FlatCall( mdSourceEntry,
1002                     null, // dst temp
1003                     null, // this temp
1004                     sourceEntryArgs
1005                     );
1006
1007     FlatReturnNode frn = new FlatReturnNode( null );
1008
1009     FlatExit fe = new FlatExit();
1010
1011     this.fmAnalysisEntry = 
1012       new FlatMethod( mdAnalysisEntry, 
1013                       fe
1014                       );
1015
1016     this.fmAnalysisEntry.addNext( fn );
1017     fn.addNext( fc );
1018     fc.addNext( frn );
1019     frn.addNext( fe );
1020   }
1021
1022
1023   protected LinkedList<Descriptor> topologicalSort( Set<Descriptor> toSort ) {
1024
1025     Set       <Descriptor> discovered = new HashSet   <Descriptor>();
1026     LinkedList<Descriptor> sorted     = new LinkedList<Descriptor>();
1027   
1028     Iterator<Descriptor> itr = toSort.iterator();
1029     while( itr.hasNext() ) {
1030       Descriptor d = itr.next();
1031           
1032       if( !discovered.contains( d ) ) {
1033         dfsVisit( d, toSort, sorted, discovered );
1034       }
1035     }
1036     
1037     return sorted;
1038   }
1039   
1040   // While we're doing DFS on call graph, remember
1041   // dependencies for efficient queuing of methods
1042   // during interprocedural analysis:
1043   //
1044   // a dependent of a method decriptor d for this analysis is:
1045   //  1) a method or task that invokes d
1046   //  2) in the descriptorsToAnalyze set
1047   protected void dfsVisit( Descriptor             d,
1048                            Set       <Descriptor> toSort,                        
1049                            LinkedList<Descriptor> sorted,
1050                            Set       <Descriptor> discovered ) {
1051     discovered.add( d );
1052     
1053     // only methods have callers, tasks never do
1054     if( d instanceof MethodDescriptor ) {
1055
1056       MethodDescriptor md = (MethodDescriptor) d;
1057
1058       // the call graph is not aware that we have a fabricated
1059       // analysis entry that calls the program source's entry
1060       if( md == mdSourceEntry ) {
1061         if( !discovered.contains( mdAnalysisEntry ) ) {
1062           addDependent( mdSourceEntry,  // callee
1063                         mdAnalysisEntry // caller
1064                         );
1065           dfsVisit( mdAnalysisEntry, toSort, sorted, discovered );
1066         }
1067       }
1068
1069       // otherwise call graph guides DFS
1070       Iterator itr = callGraph.getCallerSet( md ).iterator();
1071       while( itr.hasNext() ) {
1072         Descriptor dCaller = (Descriptor) itr.next();
1073         
1074         // only consider callers in the original set to analyze
1075         if( !toSort.contains( dCaller ) ) {
1076           continue;
1077         }
1078           
1079         if( !discovered.contains( dCaller ) ) {
1080           addDependent( md,     // callee
1081                         dCaller // caller
1082                         );
1083
1084           dfsVisit( dCaller, toSort, sorted, discovered );
1085         }
1086       }
1087     }
1088     
1089     sorted.addFirst( d );
1090   }
1091
1092
1093   protected void enqueue( Descriptor d ) {
1094     if( !descriptorsToVisitSet.contains( d ) ) {
1095       Integer priority = mapDescriptorToPriority.get( d );
1096       descriptorsToVisitQ.add( new DescriptorQWrapper( priority, 
1097                                                        d ) 
1098                                );
1099       descriptorsToVisitSet.add( d );
1100     }
1101   }
1102
1103
1104   protected ReachGraph getPartial( Descriptor d ) {
1105     return mapDescriptorToCompleteReachGraph.get( d );
1106   }
1107
1108   protected void setPartial( Descriptor d, ReachGraph rg ) {
1109     mapDescriptorToCompleteReachGraph.put( d, rg );
1110
1111     // when the flag for writing out every partial
1112     // result is set, we should spit out the graph,
1113     // but in order to give it a unique name we need
1114     // to track how many partial results for this
1115     // descriptor we've already written out
1116     if( writeAllIncrementalDOTs ) {
1117       if( !mapDescriptorToNumUpdates.containsKey( d ) ) {
1118         mapDescriptorToNumUpdates.put( d, new Integer( 0 ) );
1119       }
1120       Integer n = mapDescriptorToNumUpdates.get( d );
1121       /*
1122       try {
1123         rg.writeGraph( d+"COMPLETE"+String.format( "%05d", n ),
1124                        true,  // write labels (variables)
1125                        true,  // selectively hide intermediate temp vars
1126                        true,  // prune unreachable heap regions
1127                        false, // show back edges to confirm graph validity
1128                        false, // show parameter indices (unmaintained!)
1129                        true,  // hide subset reachability states
1130                        true); // hide edge taints
1131       } catch( IOException e ) {}
1132       */
1133       mapDescriptorToNumUpdates.put( d, n + 1 );
1134     }
1135   }
1136
1137
1138   // a dependent of a method decriptor d for this analysis is:
1139   //  1) a method or task that invokes d
1140   //  2) in the descriptorsToAnalyze set
1141   protected void addDependent( Descriptor callee, Descriptor caller ) {
1142     Set<Descriptor> deps = mapDescriptorToSetDependents.get( callee );
1143     if( deps == null ) {
1144       deps = new HashSet<Descriptor>();
1145     }
1146     deps.add( caller );
1147     mapDescriptorToSetDependents.put( callee, deps );
1148   }
1149   
1150   protected Set<Descriptor> getDependents( Descriptor callee ) {
1151     Set<Descriptor> deps = mapDescriptorToSetDependents.get( callee );
1152     if( deps == null ) {
1153       deps = new HashSet<Descriptor>();
1154       mapDescriptorToSetDependents.put( callee, deps );
1155     }
1156     return deps;
1157   }
1158
1159   
1160   public Hashtable<FlatCall, ReachGraph> getIHMcontributions( Descriptor d ) {
1161
1162     Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
1163       mapDescriptorToIHMcontributions.get( d );
1164     
1165     if( heapsFromCallers == null ) {
1166       heapsFromCallers = new Hashtable<FlatCall, ReachGraph>();
1167       mapDescriptorToIHMcontributions.put( d, heapsFromCallers );
1168     }
1169     
1170     return heapsFromCallers;
1171   }
1172
1173   public ReachGraph getIHMcontribution( Descriptor d, 
1174                                         FlatCall   fc
1175                                         ) {
1176     Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
1177       getIHMcontributions( d );
1178
1179     if( !heapsFromCallers.containsKey( fc ) ) {
1180       heapsFromCallers.put( fc, new ReachGraph() );
1181     }
1182
1183     return heapsFromCallers.get( fc );
1184   }
1185
1186   public void addIHMcontribution( Descriptor d,
1187                                   FlatCall   fc,
1188                                   ReachGraph rg
1189                                   ) {
1190     Hashtable<FlatCall, ReachGraph> heapsFromCallers = 
1191       getIHMcontributions( d );
1192
1193     heapsFromCallers.put( fc, rg );
1194   }
1195
1196 private AllocSite createParameterAllocSite(ReachGraph rg, TempDescriptor tempDesc) {
1197     
1198     // create temp descriptor for each parameter variable
1199     FlatNew flatNew = new FlatNew(tempDesc.getType(), tempDesc, false);
1200     // create allocation site
1201     AllocSite as = (AllocSite) Canonical.makeCanonical(new AllocSite( allocationDepth, flatNew, flatNew.getDisjointId()));
1202     for (int i = 0; i < allocationDepth; ++i) {
1203         Integer id = generateUniqueHeapRegionNodeID();
1204         as.setIthOldest(i, id);
1205         mapHrnIdToAllocSite.put(id, as);
1206     }
1207     // the oldest node is a summary node
1208     as.setSummary( generateUniqueHeapRegionNodeID() );
1209     
1210     rg.age(as);
1211     
1212     return as;
1213     
1214 }
1215     
1216 private ReachGraph createInitialTaskReachGraph(FlatMethod fm) {
1217     ReachGraph rg = new ReachGraph();
1218     TaskDescriptor taskDesc = fm.getTask();
1219     
1220     for (int idx = 0; idx < taskDesc.numParameters(); idx++) {
1221         Descriptor paramDesc = taskDesc.getParameter(idx);
1222         TypeDescriptor paramTypeDesc = taskDesc.getParamType(idx);
1223         
1224         // setup data structure
1225         Set<HashMap<HeapRegionNode, FieldDescriptor>> workSet = 
1226             new HashSet<HashMap<HeapRegionNode, FieldDescriptor>>();
1227         Hashtable<TypeDescriptor, HeapRegionNode> mapTypeToExistingSummaryNode = 
1228             new Hashtable<TypeDescriptor, HeapRegionNode>();
1229         Set<String> doneSet = new HashSet<String>();
1230         
1231         TempDescriptor tempDesc = new TempDescriptor(paramDesc.getSymbol(),
1232                                                      paramTypeDesc);
1233         
1234         AllocSite as = createParameterAllocSite(rg, tempDesc);
1235         VariableNode lnX = rg.getVariableNodeFromTemp(tempDesc);
1236         
1237         Integer idNewest = as.getIthOldest(0);
1238         HeapRegionNode hrnNewest = rg.id2hrn.get(idNewest);
1239         // make a new reference to allocated node
1240         RefEdge edgeNew = new RefEdge(lnX, // source
1241                                       hrnNewest, // dest
1242                                       taskDesc.getParamType(idx), // type
1243                                       null, // field name
1244                                       hrnNewest.getAlpha(), // beta
1245                                       ExistPredSet.factory(rg.predTrue) // predicates
1246                                       );
1247         rg.addRefEdge(lnX, hrnNewest, edgeNew);
1248         
1249         // set-up a work set for class field
1250         ClassDescriptor classDesc = paramTypeDesc.getClassDesc();
1251         for (Iterator it = classDesc.getFields(); it.hasNext();) {
1252             FieldDescriptor fd = (FieldDescriptor) it.next();
1253             TypeDescriptor fieldType = fd.getType();
1254             if (!fieldType.isImmutable()) {
1255                 HashMap<HeapRegionNode, FieldDescriptor> newMap = new HashMap<HeapRegionNode, FieldDescriptor>();
1256                 newMap.put(hrnNewest, fd);
1257                 workSet.add(newMap);
1258             }
1259         }
1260         
1261         int uniqueIdentifier = 0;
1262         while (!workSet.isEmpty()) {
1263             HashMap<HeapRegionNode, FieldDescriptor> map = workSet
1264                 .iterator().next();
1265             workSet.remove(map);
1266             
1267             Set<HeapRegionNode> key = map.keySet();
1268             HeapRegionNode srcHRN = key.iterator().next();
1269             FieldDescriptor fd = map.get(srcHRN);
1270             TypeDescriptor type = fd.getType();
1271             String doneSetIdentifier = srcHRN.getIDString() + "_" + fd;
1272             
1273             if (!doneSet.contains(doneSetIdentifier)) {
1274                 doneSet.add(doneSetIdentifier);
1275                 if (!mapTypeToExistingSummaryNode.containsKey(type)) {
1276                     // create new summary Node
1277                     TempDescriptor td = new TempDescriptor("temp"
1278                                                            + uniqueIdentifier, type);
1279                     
1280                     AllocSite allocSite;
1281                     if(type.equals(paramTypeDesc)){
1282                     //corresponding allocsite has already been created for a parameter variable.
1283                         allocSite=as;
1284                     }else{
1285                         allocSite = createParameterAllocSite(rg, td);
1286                     }
1287                     String strDesc = allocSite.toStringForDOT()
1288                         + "\\nsummary";
1289                     HeapRegionNode hrnSummary = 
1290                         rg.createNewHeapRegionNode(allocSite.getSummary(), // id or null to generate a new one
1291                                                    false, // single object?
1292                                                    true, // summary?
1293                                                    false, // flagged?
1294                                                    false, // out-of-context?
1295                                                    allocSite.getType(), // type
1296                                                    allocSite, // allocation site
1297                                                    null, // inherent reach
1298                                                    srcHRN.getAlpha(), // current reach
1299                                                    ExistPredSet.factory(), // predicates
1300                                                    strDesc // description
1301                                                    );
1302                     
1303                     // make a new reference to summary node
1304                     RefEdge edgeToSummary = new RefEdge(srcHRN, // source
1305                                                         hrnSummary, // dest
1306                                                         fd.getType(), // type
1307                                                         fd.getSymbol(), // field name
1308                                                         srcHRN.getAlpha(), // beta
1309                                                         ExistPredSet.factory(rg.predTrue) // predicates
1310                                                         );
1311                     
1312                     rg.addRefEdge(srcHRN, hrnSummary, edgeToSummary);
1313                     
1314                     uniqueIdentifier++;
1315                     
1316                     mapTypeToExistingSummaryNode.put(type, hrnSummary);
1317                     
1318                     // set-up a work set for  fields of the class
1319                     classDesc = type.getClassDesc();
1320                     for (Iterator it = classDesc.getFields(); it.hasNext();) {
1321                         FieldDescriptor typeFieldDesc = (FieldDescriptor) it.next();
1322                         TypeDescriptor fieldType = typeFieldDesc.getType();
1323                         if (!fieldType.isImmutable()) {
1324                             doneSetIdentifier = hrnSummary.getIDString() + "_" + typeFieldDesc;                                                          
1325                             if(!doneSet.contains(doneSetIdentifier)){
1326                                 // add new work item
1327                                 HashMap<HeapRegionNode, FieldDescriptor> newMap = 
1328                                     new HashMap<HeapRegionNode, FieldDescriptor>();
1329                                 newMap.put(hrnSummary, typeFieldDesc);
1330                                 workSet.add(newMap);
1331                             }
1332                         }
1333                     }
1334                     
1335                 }else{
1336                     // if there exists corresponding summary node
1337                     HeapRegionNode hrnDst=mapTypeToExistingSummaryNode.get(type);
1338                     
1339                     RefEdge edgeToSummary = new RefEdge(srcHRN, // source
1340                                                         hrnDst, // dest
1341                                                         fd.getType(), // type
1342                                                         fd.getSymbol(), // field name
1343                                                         srcHRN.getAlpha(), // beta
1344                                                         ExistPredSet.factory(rg.predTrue) // predicates
1345                                                         );
1346                     rg.addRefEdge(srcHRN, hrnDst, edgeToSummary);
1347                     
1348                 }               
1349             }       
1350         }           
1351     }   
1352 //    debugSnapshot(rg, fm, true);
1353     return rg;
1354 }
1355     
1356
1357
1358
1359   int zzz = 0;
1360
1361
1362   
1363   
1364   // get successive captures of the analysis state
1365   boolean takeDebugSnapshots = false;
1366   String descSymbolDebug = "addBar";
1367   boolean stopAfterCapture = true;
1368
1369   // increments every visit to debugSnapshot, don't fiddle with it
1370   int debugCounter = 0;
1371
1372   // the value of debugCounter to start reporting the debugCounter
1373   // to the screen to let user know what debug iteration we're at
1374   int numStartCountReport = 0;
1375
1376   // the frequency of debugCounter values to print out, 0 no report
1377   int freqCountReport = 0;
1378
1379   // the debugCounter value at which to start taking snapshots
1380   int iterStartCapture = 0;
1381
1382   // the number of snapshots to take
1383   int numIterToCapture = 300;
1384
1385   void debugSnapshot( ReachGraph rg, FlatNode fn, boolean in ) {
1386     if( debugCounter > iterStartCapture + numIterToCapture ) {
1387       return;
1388     }
1389
1390     if( in ) {
1391       ++debugCounter;
1392     }
1393
1394     if( debugCounter    > numStartCountReport &&
1395         freqCountReport > 0                   &&
1396         debugCounter % freqCountReport == 0 
1397         ) {
1398       System.out.println( "    @@@ debug counter = "+
1399                           debugCounter );
1400     }
1401
1402     if( debugCounter > iterStartCapture ) {
1403       System.out.println( "    @@@ capturing debug "+
1404                           (debugCounter - iterStartCapture)+
1405                           " @@@" );
1406       String graphName;
1407       if( in ) {
1408         graphName = String.format( "snap%04din",
1409                                    debugCounter - iterStartCapture );
1410       } else {
1411         graphName = String.format( "snap%04dout",
1412                                    debugCounter - iterStartCapture );
1413       }
1414       if( fn != null ) {
1415         graphName = graphName + fn;
1416       }
1417       try {
1418         rg.writeGraph( graphName,
1419                        true,  // write labels (variables)
1420                        false, // selectively hide intermediate temp vars
1421                        false, // prune unreachable heap regions
1422                        false, // show back edges to confirm graph validity
1423                        true,  // hide subset reachability states
1424                        true );// hide edge taints
1425       } catch( Exception e ) {
1426         System.out.println( "Error writing debug capture." );
1427         System.exit( 0 );
1428       }
1429     }
1430
1431     if( debugCounter == iterStartCapture + numIterToCapture && 
1432         stopAfterCapture 
1433         ) {
1434       System.out.println( "Stopping analysis after debug captures." );
1435       System.exit( 0 );
1436     }
1437   }
1438
1439 }