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