reach propagation not working, getting null states...
[IRC.git] / Robust / src / Analysis / Disjoint / ReachGraph.java
1 package Analysis.Disjoint;
2
3 import IR.*;
4 import IR.Flat.*;
5 import Util.UtilAlgorithms;
6 import java.util.*;
7 import java.io.*;
8
9 public class ReachGraph {
10
11   // use to disable improvements for comparison
12   protected static final boolean DISABLE_STRONG_UPDATES = false;
13   protected static final boolean DISABLE_GLOBAL_SWEEP   = false;
14                    
15   // a special out-of-scope temp
16   protected static final TempDescriptor tdReturn = new TempDescriptor( "_Return___" );
17                    
18   // some frequently used reachability constants
19   protected static final ReachState rstateEmpty        = ReachState.factory();
20   protected static final ReachSet   rsetEmpty          = ReachSet.factory();
21   protected static final ReachSet   rsetWithEmptyState = ReachSet.factory( rstateEmpty );
22
23   // predicate constants
24   protected static final ExistPred    predTrue   = ExistPred.factory(); // if no args, true
25   protected static final ExistPredSet predsEmpty = ExistPredSet.factory();
26   protected static final ExistPredSet predsTrue  = ExistPredSet.factory( predTrue );
27
28
29   // from DisjointAnalysis for convenience
30   protected static int      allocationDepth   = -1;
31   protected static TypeUtil typeUtil          = null;
32
33
34   // variable and heap region nodes indexed by unique ID
35   public Hashtable<Integer,        HeapRegionNode> id2hrn;
36   public Hashtable<TempDescriptor, VariableNode  > td2vn;
37
38   // convenient set of alloc sites for all heap regions
39   // present in the graph without having to search
40   public HashSet<AllocSite> allocSites;  
41
42   public ReachGraph() {
43     id2hrn     = new Hashtable<Integer,        HeapRegionNode>();
44     td2vn      = new Hashtable<TempDescriptor, VariableNode  >();
45     allocSites = new HashSet<AllocSite>();
46   }
47
48   
49   // temp descriptors are globally unique and map to
50   // exactly one variable node, easy
51   protected VariableNode getVariableNodeFromTemp( TempDescriptor td ) {
52     assert td != null;
53
54     if( !td2vn.containsKey( td ) ) {
55       td2vn.put( td, new VariableNode( td ) );
56     }
57
58     return td2vn.get( td );
59   }
60
61   public boolean hasVariable( TempDescriptor td ) {
62     return td2vn.containsKey( td );
63   }
64
65
66   // this suite of methods can be used to assert a
67   // very important property of ReachGraph objects:
68   // some element, HeapRegionNode, RefEdge etc.
69   // should be referenced by at most ONE ReachGraph!!
70   // If a heap region or edge or variable should be
71   // in another graph, make a new object with
72   // equivalent properties for a new graph
73   public boolean belongsToThis( RefSrcNode rsn ) {
74     if( rsn instanceof VariableNode ) {
75       VariableNode vn = (VariableNode) rsn;
76       return this.td2vn.get( vn.getTempDescriptor() ) == vn;
77     }
78     HeapRegionNode hrn = (HeapRegionNode) rsn;
79     return this.id2hrn.get( hrn.getID() ) == hrn;
80   }
81
82
83
84   // the reason for this method is to have the option
85   // of creating new heap regions with specific IDs, or
86   // duplicating heap regions with specific IDs (especially
87   // in the merge() operation) or to create new heap
88   // regions with a new unique ID
89   protected HeapRegionNode
90     createNewHeapRegionNode( Integer        id,
91                              boolean        isSingleObject,
92                              boolean        isNewSummary,
93                              boolean        isFlagged,
94                              boolean        isOutOfContext,
95                              TypeDescriptor type,
96                              AllocSite      allocSite,
97                              ReachSet       inherent,
98                              ReachSet       alpha,
99                              ExistPredSet   preds,
100                              String         description
101                              ) {
102
103     boolean markForAnalysis = isFlagged;
104
105     TypeDescriptor typeToUse = null;
106     if( allocSite != null ) {
107       typeToUse = allocSite.getType();
108       allocSites.add( allocSite );
109     } else {
110       typeToUse = type;
111     }
112
113     if( allocSite != null && allocSite.getDisjointAnalysisId() != null ) {
114       markForAnalysis = true;
115     }
116
117     if( id == null ) {
118       id = DisjointAnalysis.generateUniqueHeapRegionNodeID();
119     }
120
121     if( inherent == null ) {
122       if( markForAnalysis ) {
123         inherent = 
124           ReachSet.factory(
125                            ReachState.factory(
126                                               ReachTuple.factory( id,
127                                                                   !isSingleObject,
128                                                                   ReachTuple.ARITY_ONE,
129                                                                   false // out-of-context
130                                                                   )
131                                               )
132                            );
133       } else {
134         inherent = rsetWithEmptyState;
135       }
136     }
137
138     if( alpha == null ) {
139       alpha = inherent;
140     }
141
142     if( preds == null ) {
143       // TODO: do this right?  For out-of-context nodes?
144       preds = ExistPredSet.factory();
145     }
146     
147     HeapRegionNode hrn = new HeapRegionNode( id,
148                                              isSingleObject,
149                                              markForAnalysis,
150                                              isNewSummary,
151                                              isOutOfContext,
152                                              typeToUse,
153                                              allocSite,
154                                              inherent,
155                                              alpha,
156                                              preds,
157                                              description );
158     id2hrn.put( id, hrn );
159     return hrn;
160   }
161
162
163
164   ////////////////////////////////////////////////
165   //
166   //  Low-level referencee and referencer methods
167   //
168   //  These methods provide the lowest level for
169   //  creating references between reachability nodes
170   //  and handling the details of maintaining both
171   //  list of referencers and referencees.
172   //
173   ////////////////////////////////////////////////
174   protected void addRefEdge( RefSrcNode     referencer,
175                              HeapRegionNode referencee,
176                              RefEdge        edge ) {
177     assert referencer != null;
178     assert referencee != null;
179     assert edge       != null;
180     assert edge.getSrc() == referencer;
181     assert edge.getDst() == referencee;
182     assert belongsToThis( referencer );
183     assert belongsToThis( referencee );
184
185     // edges are getting added twice to graphs now, the
186     // kind that should have abstract facts merged--use
187     // this check to prevent that
188     assert referencer.getReferenceTo( referencee,
189                                       edge.getType(),
190                                       edge.getField()
191                                       ) == null;
192
193     referencer.addReferencee( edge );
194     referencee.addReferencer( edge );
195   }
196
197   protected void removeRefEdge( RefEdge e ) {
198     removeRefEdge( e.getSrc(),
199                    e.getDst(),
200                    e.getType(),
201                    e.getField() );
202   }
203
204   protected void removeRefEdge( RefSrcNode     referencer,
205                                 HeapRegionNode referencee,
206                                 TypeDescriptor type,
207                                 String         field ) {
208     assert referencer != null;
209     assert referencee != null;
210     
211     RefEdge edge = referencer.getReferenceTo( referencee,
212                                               type,
213                                               field );
214     assert edge != null;
215     assert edge == referencee.getReferenceFrom( referencer,
216                                                 type,
217                                                 field );
218        
219     referencer.removeReferencee( edge );
220     referencee.removeReferencer( edge );
221   }
222
223   protected void clearRefEdgesFrom( RefSrcNode     referencer,
224                                     TypeDescriptor type,
225                                     String         field,
226                                     boolean        removeAll ) {
227     assert referencer != null;
228
229     // get a copy of the set to iterate over, otherwise
230     // we will be trying to take apart the set as we
231     // are iterating over it, which won't work
232     Iterator<RefEdge> i = referencer.iteratorToReferenceesClone();
233     while( i.hasNext() ) {
234       RefEdge edge = i.next();
235
236       if( removeAll                                          || 
237           (edge.typeEquals( type ) && edge.fieldEquals( field ))
238         ){
239
240         HeapRegionNode referencee = edge.getDst();
241         
242         removeRefEdge( referencer,
243                        referencee,
244                        edge.getType(),
245                        edge.getField() );
246       }
247     }
248   }
249
250   protected void clearRefEdgesTo( HeapRegionNode referencee,
251                                   TypeDescriptor type,
252                                   String         field,
253                                   boolean        removeAll ) {
254     assert referencee != null;
255
256     // get a copy of the set to iterate over, otherwise
257     // we will be trying to take apart the set as we
258     // are iterating over it, which won't work
259     Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
260     while( i.hasNext() ) {
261       RefEdge edge = i.next();
262
263       if( removeAll                                          || 
264           (edge.typeEquals( type ) && edge.fieldEquals( field ))
265         ){
266
267         RefSrcNode referencer = edge.getSrc();
268
269         removeRefEdge( referencer,
270                        referencee,
271                        edge.getType(),
272                        edge.getField() );
273       }
274     }
275   }
276
277   protected void clearNonVarRefEdgesTo( HeapRegionNode referencee ) {
278     assert referencee != null;
279
280     // get a copy of the set to iterate over, otherwise
281     // we will be trying to take apart the set as we
282     // are iterating over it, which won't work
283     Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
284     while( i.hasNext() ) {
285       RefEdge edge = i.next();
286       RefSrcNode referencer = edge.getSrc();
287       if( !(referencer instanceof VariableNode) ) {
288         removeRefEdge( referencer,
289                        referencee,
290                        edge.getType(),
291                        edge.getField() );
292       }
293     }
294   }
295
296
297   ////////////////////////////////////////////////////
298   //
299   //  Assignment Operation Methods
300   //
301   //  These methods are high-level operations for
302   //  modeling program assignment statements using
303   //  the low-level reference create/remove methods
304   //  above.
305   //
306   ////////////////////////////////////////////////////
307
308   public void assignTempXEqualToTempY( TempDescriptor x,
309                                        TempDescriptor y ) {
310     assignTempXEqualToCastedTempY( x, y, null );
311   }
312
313   public void assignTempXEqualToCastedTempY( TempDescriptor x,
314                                              TempDescriptor y,
315                                              TypeDescriptor tdCast ) {
316
317     VariableNode lnX = getVariableNodeFromTemp( x );
318     VariableNode lnY = getVariableNodeFromTemp( y );
319     
320     clearRefEdgesFrom( lnX, null, null, true );
321
322     // note it is possible that the types of temps in the
323     // flat node to analyze will reveal that some typed
324     // edges in the reachability graph are impossible
325     Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
326
327     Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
328     while( itrYhrn.hasNext() ) {
329       RefEdge        edgeY      = itrYhrn.next();
330       HeapRegionNode referencee = edgeY.getDst();
331       RefEdge        edgeNew    = edgeY.copy();
332
333       if( !isSuperiorType( x.getType(), edgeY.getType() ) ) {
334         impossibleEdges.add( edgeY );
335         continue;
336       }
337
338       edgeNew.setSrc( lnX );
339       
340       if( tdCast == null ) {
341         edgeNew.setType( mostSpecificType( y.getType(),                           
342                                            edgeY.getType(), 
343                                            referencee.getType() 
344                                            ) 
345                          );
346       } else {
347         edgeNew.setType( mostSpecificType( y.getType(),
348                                            edgeY.getType(), 
349                                            referencee.getType(),
350                                            tdCast
351                                            ) 
352                          );
353       }
354
355       edgeNew.setField( null );
356
357       addRefEdge( lnX, referencee, edgeNew );
358     }
359
360     Iterator<RefEdge> itrImp = impossibleEdges.iterator();
361     while( itrImp.hasNext() ) {
362       RefEdge edgeImp = itrImp.next();
363       removeRefEdge( edgeImp );
364     }
365   }
366
367
368   public void assignTempXEqualToTempYFieldF( TempDescriptor  x,
369                                              TempDescriptor  y,
370                                              FieldDescriptor f ) {
371     VariableNode lnX = getVariableNodeFromTemp( x );
372     VariableNode lnY = getVariableNodeFromTemp( y );
373
374     clearRefEdgesFrom( lnX, null, null, true );
375
376     // note it is possible that the types of temps in the
377     // flat node to analyze will reveal that some typed
378     // edges in the reachability graph are impossible
379     Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
380
381     Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
382     while( itrYhrn.hasNext() ) {
383       RefEdge        edgeY = itrYhrn.next();
384       HeapRegionNode hrnY  = edgeY.getDst();
385       ReachSet       betaY = edgeY.getBeta();
386
387       Iterator<RefEdge> itrHrnFhrn = hrnY.iteratorToReferencees();
388       while( itrHrnFhrn.hasNext() ) {
389         RefEdge        edgeHrn = itrHrnFhrn.next();
390         HeapRegionNode hrnHrn  = edgeHrn.getDst();
391         ReachSet       betaHrn = edgeHrn.getBeta();
392
393         // prune edges that are not a matching field
394         if( edgeHrn.getType() != null &&                    
395             !edgeHrn.getField().equals( f.getSymbol() )     
396             ) {
397           continue;
398         }
399
400         // check for impossible edges
401         if( !isSuperiorType( x.getType(), edgeHrn.getType() ) ) {
402           impossibleEdges.add( edgeHrn );
403           continue;
404         }
405
406         TypeDescriptor tdNewEdge =
407           mostSpecificType( edgeHrn.getType(), 
408                             hrnHrn.getType() 
409                             );
410           
411         RefEdge edgeNew = new RefEdge( lnX,
412                                        hrnHrn,
413                                        tdNewEdge,
414                                        null,
415                                        Canonical.intersection( betaY, betaHrn ),
416                                        predsTrue
417                                        );
418         
419         addRefEdge( lnX, hrnHrn, edgeNew );
420       }
421     }
422
423     Iterator<RefEdge> itrImp = impossibleEdges.iterator();
424     while( itrImp.hasNext() ) {
425       RefEdge edgeImp = itrImp.next();
426       removeRefEdge( edgeImp );
427     }
428
429     // anytime you might remove edges between heap regions
430     // you must global sweep to clean up broken reachability    
431     if( !impossibleEdges.isEmpty() ) {
432       if( !DISABLE_GLOBAL_SWEEP ) {
433         globalSweep();
434       }
435     }    
436   }
437
438
439   public void assignTempXFieldFEqualToTempY( TempDescriptor  x,
440                                              FieldDescriptor f,
441                                              TempDescriptor  y ) {
442
443     VariableNode lnX = getVariableNodeFromTemp( x );
444     VariableNode lnY = getVariableNodeFromTemp( y );
445
446     HashSet<HeapRegionNode> nodesWithNewAlpha = new HashSet<HeapRegionNode>();
447     HashSet<RefEdge>        edgesWithNewBeta  = new HashSet<RefEdge>();
448
449     // note it is possible that the types of temps in the
450     // flat node to analyze will reveal that some typed
451     // edges in the reachability graph are impossible
452     Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
453
454     // first look for possible strong updates and remove those edges
455     boolean strongUpdate = false;
456
457     Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
458     while( itrXhrn.hasNext() ) {
459       RefEdge        edgeX = itrXhrn.next();
460       HeapRegionNode hrnX  = edgeX.getDst();
461
462       // we can do a strong update here if one of two cases holds       
463       if( f != null &&
464           f != DisjointAnalysis.getArrayField( f.getType() ) &&     
465           (   (hrnX.getNumReferencers()                         == 1) || // case 1
466               (hrnX.isSingleObject() && lnX.getNumReferencees() == 1)    // case 2
467               )
468           ) {
469         if( !DISABLE_STRONG_UPDATES ) {
470           strongUpdate = true;
471           clearRefEdgesFrom( hrnX, f.getType(), f.getSymbol(), false );
472         }
473       }
474     }
475     
476     // then do all token propagation
477     itrXhrn = lnX.iteratorToReferencees();
478     while( itrXhrn.hasNext() ) {
479       RefEdge        edgeX = itrXhrn.next();
480       HeapRegionNode hrnX  = edgeX.getDst();
481       ReachSet       betaX = edgeX.getBeta();
482       ReachSet       R     = Canonical.intersection( hrnX.getAlpha(),
483                                                      edgeX.getBeta() 
484                                                      );
485
486       Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
487       while( itrYhrn.hasNext() ) {
488         RefEdge        edgeY = itrYhrn.next();
489         HeapRegionNode hrnY  = edgeY.getDst();
490         ReachSet       O     = edgeY.getBeta();
491
492         // check for impossible edges
493         if( !isSuperiorType( f.getType(), edgeY.getType() ) ) {
494           impossibleEdges.add( edgeY );
495           continue;
496         }
497
498         // propagate tokens over nodes starting from hrnSrc, and it will
499         // take care of propagating back up edges from any touched nodes
500         ChangeSet Cy = Canonical.unionUpArityToChangeSet( O, R );
501         propagateTokensOverNodes( hrnY, Cy, nodesWithNewAlpha, edgesWithNewBeta );
502
503         // then propagate back just up the edges from hrn
504         ChangeSet Cx = Canonical.unionUpArityToChangeSet( R, O );
505         HashSet<RefEdge> todoEdges = new HashSet<RefEdge>();
506
507         Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
508           new Hashtable<RefEdge, ChangeSet>();
509
510         Iterator<RefEdge> referItr = hrnX.iteratorToReferencers();
511         while( referItr.hasNext() ) {
512           RefEdge edgeUpstream = referItr.next();
513           todoEdges.add( edgeUpstream );
514           edgePlannedChanges.put( edgeUpstream, Cx );
515         }
516
517         propagateTokensOverEdges( todoEdges,
518                                   edgePlannedChanges,
519                                   edgesWithNewBeta );
520       }
521     }
522
523
524     // apply the updates to reachability
525     Iterator<HeapRegionNode> nodeItr = nodesWithNewAlpha.iterator();
526     while( nodeItr.hasNext() ) {
527       nodeItr.next().applyAlphaNew();
528     }
529
530     Iterator<RefEdge> edgeItr = edgesWithNewBeta.iterator();
531     while( edgeItr.hasNext() ) {
532       edgeItr.next().applyBetaNew();
533     }
534
535
536     // then go back through and add the new edges
537     itrXhrn = lnX.iteratorToReferencees();
538     while( itrXhrn.hasNext() ) {
539       RefEdge        edgeX = itrXhrn.next();
540       HeapRegionNode hrnX  = edgeX.getDst();
541       
542       Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
543       while( itrYhrn.hasNext() ) {
544         RefEdge        edgeY = itrYhrn.next();
545         HeapRegionNode hrnY  = edgeY.getDst();
546
547         // skip impossible edges here, we already marked them
548         // when computing reachability propagations above
549         if( !isSuperiorType( f.getType(), edgeY.getType() ) ) {
550           continue;
551         }
552         
553         // prepare the new reference edge hrnX.f -> hrnY
554         TypeDescriptor tdNewEdge =      
555           mostSpecificType( y.getType(),
556                             edgeY.getType(), 
557                             hrnY.getType()
558                             );  
559
560         RefEdge edgeNew = new RefEdge( hrnX,
561                                        hrnY,
562                                        tdNewEdge,
563                                        f.getSymbol(),
564                                        Canonical.pruneBy( edgeY.getBeta(),
565                                                           hrnX.getAlpha() 
566                                                           ),
567                                        predsTrue
568                                        );
569
570         // look to see if an edge with same field exists
571         // and merge with it, otherwise just add the edge
572         RefEdge edgeExisting = hrnX.getReferenceTo( hrnY, 
573                                                     tdNewEdge,
574                                                     f.getSymbol() );
575         
576         if( edgeExisting != null ) {
577           edgeExisting.setBeta(
578                                Canonical.union( edgeExisting.getBeta(),
579                                                 edgeNew.getBeta()
580                                                 )
581                                );
582           edgeExisting.setPreds(
583                                 Canonical.join( edgeExisting.getPreds(),
584                                                 edgeNew.getPreds()
585                                                 )
586                                 );
587         
588         } else {                          
589           addRefEdge( hrnX, hrnY, edgeNew );
590         }
591       }
592     }
593
594     Iterator<RefEdge> itrImp = impossibleEdges.iterator();
595     while( itrImp.hasNext() ) {
596       RefEdge edgeImp = itrImp.next();
597       removeRefEdge( edgeImp );
598     }
599
600     // if there was a strong update, make sure to improve
601     // reachability with a global sweep    
602     if( strongUpdate || !impossibleEdges.isEmpty() ) {    
603       if( !DISABLE_GLOBAL_SWEEP ) {
604         globalSweep();
605       }
606     }    
607   }
608
609
610   public void assignReturnEqualToTemp( TempDescriptor x ) {
611
612     VariableNode lnR = getVariableNodeFromTemp( tdReturn );
613     VariableNode lnX = getVariableNodeFromTemp( x );
614
615     clearRefEdgesFrom( lnR, null, null, true );
616
617     Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
618     while( itrXhrn.hasNext() ) {
619       RefEdge        edgeX      = itrXhrn.next();
620       HeapRegionNode referencee = edgeX.getDst();
621       RefEdge        edgeNew    = edgeX.copy();
622       edgeNew.setSrc( lnR );
623
624       addRefEdge( lnR, referencee, edgeNew );
625     }
626   }
627
628
629   public void assignTempEqualToNewAlloc( TempDescriptor x,
630                                          AllocSite      as ) {
631     assert x  != null;
632     assert as != null;
633
634     age( as );
635
636     // after the age operation the newest (or zero-ith oldest)
637     // node associated with the allocation site should have
638     // no references to it as if it were a newly allocated
639     // heap region
640     Integer        idNewest   = as.getIthOldest( 0 );
641     HeapRegionNode hrnNewest  = id2hrn.get( idNewest );
642     assert         hrnNewest != null;
643
644     VariableNode lnX = getVariableNodeFromTemp( x );
645     clearRefEdgesFrom( lnX, null, null, true );
646
647     // make a new reference to allocated node
648     TypeDescriptor type = as.getType();
649
650     RefEdge edgeNew =
651       new RefEdge( lnX,                  // source
652                    hrnNewest,            // dest
653                    type,                 // type
654                    null,                 // field name
655                    hrnNewest.getAlpha(), // beta
656                    predsTrue             // predicates
657                    );
658
659     addRefEdge( lnX, hrnNewest, edgeNew );
660   }
661
662
663   // use the allocation site (unique to entire analysis) to
664   // locate the heap region nodes in this reachability graph
665   // that should be aged.  The process models the allocation
666   // of new objects and collects all the oldest allocations
667   // in a summary node to allow for a finite analysis
668   //
669   // There is an additional property of this method.  After
670   // running it on a particular reachability graph (many graphs
671   // may have heap regions related to the same allocation site)
672   // the heap region node objects in this reachability graph will be
673   // allocated.  Therefore, after aging a graph for an allocation
674   // site, attempts to retrieve the heap region nodes using the
675   // integer id's contained in the allocation site should always
676   // return non-null heap regions.
677   public void age( AllocSite as ) {
678
679     // keep track of allocation sites that are represented 
680     // in this graph for efficiency with other operations
681     allocSites.add( as );
682
683     // if there is a k-th oldest node, it merges into
684     // the summary node
685     Integer idK = as.getOldest();
686     if( id2hrn.containsKey( idK ) ) {
687       HeapRegionNode hrnK = id2hrn.get( idK );
688
689       // retrieve the summary node, or make it
690       // from scratch
691       HeapRegionNode hrnSummary = getSummaryNode( as, false );      
692       
693       mergeIntoSummary( hrnK, hrnSummary );
694     }
695
696     // move down the line of heap region nodes
697     // clobbering the ith and transferring all references
698     // to and from i-1 to node i.
699     for( int i = allocationDepth - 1; i > 0; --i ) {
700
701       // only do the transfer if the i-1 node exists
702       Integer idImin1th = as.getIthOldest( i - 1 );
703       if( id2hrn.containsKey( idImin1th ) ) {
704         HeapRegionNode hrnImin1 = id2hrn.get( idImin1th );
705         if( hrnImin1.isWiped() ) {
706           // there is no info on this node, just skip
707           continue;
708         }
709
710         // either retrieve or make target of transfer
711         HeapRegionNode hrnI = getIthNode( as, i, false );
712
713         transferOnto( hrnImin1, hrnI );
714       }
715
716     }
717
718     // as stated above, the newest node should have had its
719     // references moved over to the second oldest, so we wipe newest
720     // in preparation for being the new object to assign something to
721     HeapRegionNode hrn0 = getIthNode( as, 0, false );
722     wipeOut( hrn0, true );
723
724     // now tokens in reachability sets need to "age" also
725     Iterator itrAllVariableNodes = td2vn.entrySet().iterator();
726     while( itrAllVariableNodes.hasNext() ) {
727       Map.Entry    me = (Map.Entry)    itrAllVariableNodes.next();
728       VariableNode ln = (VariableNode) me.getValue();
729
730       Iterator<RefEdge> itrEdges = ln.iteratorToReferencees();
731       while( itrEdges.hasNext() ) {
732         ageTuplesFrom( as, itrEdges.next() );
733       }
734     }
735
736     Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
737     while( itrAllHRNodes.hasNext() ) {
738       Map.Entry      me       = (Map.Entry)      itrAllHRNodes.next();
739       HeapRegionNode hrnToAge = (HeapRegionNode) me.getValue();
740
741       ageTuplesFrom( as, hrnToAge );
742
743       Iterator<RefEdge> itrEdges = hrnToAge.iteratorToReferencees();
744       while( itrEdges.hasNext() ) {
745         ageTuplesFrom( as, itrEdges.next() );
746       }
747     }
748
749
750     // after tokens have been aged, reset newest node's reachability
751     // and a brand new node has a "true" predicate
752     hrn0.setAlpha( hrn0.getInherent() );
753     hrn0.setPreds( predsTrue );
754   }
755
756
757   // either retrieve or create the needed heap region node
758   protected HeapRegionNode getSummaryNode( AllocSite as, 
759                                            boolean   shadow ) {
760
761     Integer idSummary;
762     if( shadow ) {
763       idSummary = as.getSummaryShadow();
764     } else {
765       idSummary = as.getSummary();
766     }
767
768     HeapRegionNode hrnSummary = id2hrn.get( idSummary );
769
770     if( hrnSummary == null ) {
771
772       boolean hasFlags = false;
773       if( as.getType().isClass() ) {
774         hasFlags = as.getType().getClassDesc().hasFlags();
775       }
776       
777       if( as.getFlag() ){
778         hasFlags = as.getFlag();
779       }
780
781       String strDesc = as.toStringForDOT()+"\\nsummary";
782       if( shadow ) {
783         strDesc += " shadow";
784       }
785
786       hrnSummary = 
787         createNewHeapRegionNode( idSummary,    // id or null to generate a new one 
788                                  false,        // single object?                 
789                                  true,         // summary?       
790                                  hasFlags,     // flagged?
791                                  false,        // out-of-context?
792                                  as.getType(), // type                           
793                                  as,           // allocation site                        
794                                  null,         // inherent reach
795                                  null,         // current reach                 
796                                  predsEmpty,   // predicates
797                                  strDesc       // description
798                                  );                                
799     }
800   
801     return hrnSummary;
802   }
803
804   // either retrieve or create the needed heap region node
805   protected HeapRegionNode getIthNode( AllocSite as, 
806                                        Integer   i, 
807                                        boolean   shadow ) {
808
809     Integer idIth;
810     if( shadow ) {
811       idIth = as.getIthOldestShadow( i );
812     } else {
813       idIth = as.getIthOldest( i );
814     }
815     
816     HeapRegionNode hrnIth = id2hrn.get( idIth );
817     
818     if( hrnIth == null ) {
819
820       boolean hasFlags = false;
821       if( as.getType().isClass() ) {
822         hasFlags = as.getType().getClassDesc().hasFlags();
823       }
824       
825       if( as.getFlag() ){
826         hasFlags = as.getFlag();
827       }
828
829       String strDesc = as.toStringForDOT()+"\\n"+i+" oldest";
830       if( shadow ) {
831         strDesc += " shadow";
832       }
833
834       hrnIth = createNewHeapRegionNode( idIth,        // id or null to generate a new one 
835                                         true,         // single object?                  
836                                         false,        // summary?                        
837                                         hasFlags,     // flagged?                        
838                                         false,        // out-of-context?
839                                         as.getType(), // type                            
840                                         as,           // allocation site                         
841                                         null,         // inherent reach
842                                         null,         // current reach
843                                         predsEmpty,   // predicates
844                                         strDesc       // description
845                                         );
846     }
847
848     return hrnIth;
849   }
850
851
852   protected void mergeIntoSummary( HeapRegionNode hrn, 
853                                    HeapRegionNode hrnSummary ) {
854     assert hrnSummary.isNewSummary();
855
856     // assert that these nodes belong to THIS graph
857     assert belongsToThis( hrn );
858     assert belongsToThis( hrnSummary );
859
860     assert hrn != hrnSummary;
861
862     // transfer references _from_ hrn over to hrnSummary
863     Iterator<RefEdge> itrReferencee = hrn.iteratorToReferencees();
864     while( itrReferencee.hasNext() ) {
865       RefEdge edge       = itrReferencee.next();
866       RefEdge edgeMerged = edge.copy();
867       edgeMerged.setSrc( hrnSummary );
868
869       HeapRegionNode hrnReferencee = edge.getDst();
870       RefEdge        edgeSummary   = 
871         hrnSummary.getReferenceTo( hrnReferencee, 
872                                    edge.getType(),
873                                    edge.getField() 
874                                    );
875       
876       if( edgeSummary == null ) {
877         // the merge is trivial, nothing to be done
878         addRefEdge( hrnSummary, hrnReferencee, edgeMerged );
879
880       } else {
881         // otherwise an edge from the referencer to hrnSummary exists already
882         // and the edge referencer->hrn should be merged with it
883         edgeSummary.setBeta( 
884                             Canonical.union( edgeMerged.getBeta(),
885                                              edgeSummary.getBeta() 
886                                              ) 
887                              );
888         edgeSummary.setPreds( 
889                              Canonical.join( edgeMerged.getPreds(),
890                                              edgeSummary.getPreds() 
891                                              )
892                               );
893       }
894     }
895
896     // next transfer references _to_ hrn over to hrnSummary
897     Iterator<RefEdge> itrReferencer = hrn.iteratorToReferencers();
898     while( itrReferencer.hasNext() ) {
899       RefEdge edge         = itrReferencer.next();
900       RefEdge edgeMerged   = edge.copy();
901       edgeMerged.setDst( hrnSummary );
902
903       RefSrcNode onReferencer = edge.getSrc();
904       RefEdge    edgeSummary  =
905         onReferencer.getReferenceTo( hrnSummary, 
906                                      edge.getType(),
907                                      edge.getField() 
908                                      );
909
910       if( edgeSummary == null ) {
911         // the merge is trivial, nothing to be done
912         addRefEdge( onReferencer, hrnSummary, edgeMerged );
913
914       } else {
915         // otherwise an edge from the referencer to alpha_S exists already
916         // and the edge referencer->alpha_K should be merged with it
917         edgeSummary.setBeta( 
918                             Canonical.union( edgeMerged.getBeta(),
919                                              edgeSummary.getBeta() 
920                                              ) 
921                              );
922         edgeSummary.setPreds( 
923                              Canonical.join( edgeMerged.getPreds(),
924                                              edgeSummary.getPreds() 
925                                              )
926                               );
927       }
928     }
929
930     // then merge hrn reachability into hrnSummary
931     hrnSummary.setAlpha( 
932                         Canonical.union( hrnSummary.getAlpha(),
933                                          hrn.getAlpha() 
934                                          )
935                          );
936
937     hrnSummary.setPreds(
938                         Canonical.join( hrnSummary.getPreds(),
939                                         hrn.getPreds()
940                                         )
941                         );
942     
943     // and afterward, this node is gone
944     wipeOut( hrn, true );
945   }
946
947
948   protected void transferOnto( HeapRegionNode hrnA, 
949                                HeapRegionNode hrnB ) {
950
951     assert belongsToThis( hrnA );
952     assert belongsToThis( hrnB );
953     assert hrnA != hrnB;
954
955     // clear references in and out of node b?
956     assert hrnB.isWiped();
957
958     // copy each: (edge in and out of A) to B
959     Iterator<RefEdge> itrReferencee = hrnA.iteratorToReferencees();
960     while( itrReferencee.hasNext() ) {
961       RefEdge        edge          = itrReferencee.next();
962       HeapRegionNode hrnReferencee = edge.getDst();
963       RefEdge        edgeNew       = edge.copy();
964       edgeNew.setSrc( hrnB );
965       edgeNew.setDst( hrnReferencee );
966
967       addRefEdge( hrnB, hrnReferencee, edgeNew );
968     }
969
970     Iterator<RefEdge> itrReferencer = hrnA.iteratorToReferencers();
971     while( itrReferencer.hasNext() ) {
972       RefEdge    edge          = itrReferencer.next();
973       RefSrcNode rsnReferencer = edge.getSrc();
974       RefEdge    edgeNew       = edge.copy();
975       edgeNew.setSrc( rsnReferencer );
976       edgeNew.setDst( hrnB );
977
978       addRefEdge( rsnReferencer, hrnB, edgeNew );
979     }
980
981     // replace hrnB reachability and preds with hrnA's
982     hrnB.setAlpha( hrnA.getAlpha() );
983     hrnB.setPreds( hrnA.getPreds() );
984
985     // after transfer, wipe out source
986     wipeOut( hrnA, true );
987   }
988
989
990   // the purpose of this method is to conceptually "wipe out"
991   // a heap region from the graph--purposefully not called REMOVE
992   // because the node is still hanging around in the graph, just
993   // not mechanically connected or have any reach or predicate
994   // information on it anymore--lots of ops can use this
995   protected void wipeOut( HeapRegionNode hrn,
996                           boolean        wipeVariableReferences ) {
997
998     assert belongsToThis( hrn );
999
1000     clearRefEdgesFrom( hrn, null, null, true );
1001
1002     if( wipeVariableReferences ) {
1003       clearRefEdgesTo( hrn, null, null, true );
1004     } else {
1005       clearNonVarRefEdgesTo( hrn );
1006     }
1007
1008     hrn.setAlpha( rsetEmpty );
1009     hrn.setPreds( predsEmpty );
1010   }
1011
1012
1013   protected void ageTuplesFrom( AllocSite as, RefEdge edge ) {
1014     edge.setBeta( 
1015                  Canonical.ageTuplesFrom( edge.getBeta(),
1016                                           as
1017                                           )
1018                   );
1019   }
1020
1021   protected void ageTuplesFrom( AllocSite as, HeapRegionNode hrn ) {
1022     hrn.setAlpha( 
1023                  Canonical.ageTuplesFrom( hrn.getAlpha(),
1024                                           as
1025                                           )
1026                   );
1027   }
1028
1029
1030
1031   protected void propagateTokensOverNodes( HeapRegionNode          nPrime,
1032                                            ChangeSet               c0,
1033                                            HashSet<HeapRegionNode> nodesWithNewAlpha,
1034                                            HashSet<RefEdge>        edgesWithNewBeta ) {
1035
1036     HashSet<HeapRegionNode> todoNodes
1037       = new HashSet<HeapRegionNode>();
1038     todoNodes.add( nPrime );
1039     
1040     HashSet<RefEdge> todoEdges
1041       = new HashSet<RefEdge>();
1042     
1043     Hashtable<HeapRegionNode, ChangeSet> nodePlannedChanges
1044       = new Hashtable<HeapRegionNode, ChangeSet>();
1045     nodePlannedChanges.put( nPrime, c0 );
1046
1047     Hashtable<RefEdge, ChangeSet> edgePlannedChanges
1048       = new Hashtable<RefEdge, ChangeSet>();
1049
1050     // first propagate change sets everywhere they can go
1051     while( !todoNodes.isEmpty() ) {
1052       HeapRegionNode n = todoNodes.iterator().next();
1053       ChangeSet      C = nodePlannedChanges.get( n );
1054
1055       Iterator<RefEdge> referItr = n.iteratorToReferencers();
1056       while( referItr.hasNext() ) {
1057         RefEdge edge = referItr.next();
1058         todoEdges.add( edge );
1059
1060         if( !edgePlannedChanges.containsKey( edge ) ) {
1061           edgePlannedChanges.put( edge, 
1062                                   ChangeSet.factory()
1063                                   );
1064         }
1065
1066         edgePlannedChanges.put( edge, 
1067                                 Canonical.union( edgePlannedChanges.get( edge ),
1068                                                  C
1069                                                  )
1070                                 );
1071       }
1072
1073       Iterator<RefEdge> refeeItr = n.iteratorToReferencees();
1074       while( refeeItr.hasNext() ) {
1075         RefEdge        edgeF = refeeItr.next();
1076         HeapRegionNode m     = edgeF.getDst();
1077
1078         ChangeSet changesToPass = ChangeSet.factory();
1079
1080         Iterator<ChangeTuple> itrCprime = C.iterator();
1081         while( itrCprime.hasNext() ) {
1082           ChangeTuple c = itrCprime.next();
1083           if( edgeF.getBeta().contains( c.getSetToMatch() ) ) {
1084             changesToPass = Canonical.union( changesToPass, c );
1085           }
1086         }
1087
1088         if( !changesToPass.isEmpty() ) {
1089           if( !nodePlannedChanges.containsKey( m ) ) {
1090             nodePlannedChanges.put( m, ChangeSet.factory() );
1091           }
1092
1093           ChangeSet currentChanges = nodePlannedChanges.get( m );
1094
1095           if( !changesToPass.isSubset( currentChanges ) ) {
1096
1097             nodePlannedChanges.put( m, 
1098                                     Canonical.union( currentChanges,
1099                                                      changesToPass
1100                                                      )
1101                                     );
1102             todoNodes.add( m );
1103           }
1104         }
1105       }
1106
1107       todoNodes.remove( n );
1108     }
1109
1110     // then apply all of the changes for each node at once
1111     Iterator itrMap = nodePlannedChanges.entrySet().iterator();
1112     while( itrMap.hasNext() ) {
1113       Map.Entry      me = (Map.Entry)      itrMap.next();
1114       HeapRegionNode n  = (HeapRegionNode) me.getKey();
1115       ChangeSet      C  = (ChangeSet)      me.getValue();
1116
1117       // this propagation step is with respect to one change,
1118       // so we capture the full change from the old alpha:
1119       ReachSet localDelta = Canonical.applyChangeSet( n.getAlpha(),
1120                                                       C,
1121                                                       true 
1122                                                       );
1123       // but this propagation may be only one of many concurrent
1124       // possible changes, so keep a running union with the node's
1125       // partially updated new alpha set
1126       n.setAlphaNew( Canonical.union( n.getAlphaNew(),
1127                                       localDelta 
1128                                       )
1129                      );
1130
1131       nodesWithNewAlpha.add( n );
1132     }
1133
1134     propagateTokensOverEdges( todoEdges, 
1135                               edgePlannedChanges, 
1136                               edgesWithNewBeta
1137                               );
1138   }
1139
1140
1141   protected void propagateTokensOverEdges( HashSet  <RefEdge>            todoEdges,
1142                                            Hashtable<RefEdge, ChangeSet> edgePlannedChanges,
1143                                            HashSet  <RefEdge>            edgesWithNewBeta ) {
1144     
1145     // first propagate all change tuples everywhere they can go
1146     while( !todoEdges.isEmpty() ) {
1147       RefEdge edgeE = todoEdges.iterator().next();
1148       todoEdges.remove( edgeE );
1149
1150       if( !edgePlannedChanges.containsKey( edgeE ) ) {
1151         edgePlannedChanges.put( edgeE, 
1152                                 ChangeSet.factory()
1153                                 );
1154       }
1155
1156       ChangeSet C = edgePlannedChanges.get( edgeE );
1157
1158       ChangeSet changesToPass = ChangeSet.factory();
1159
1160       Iterator<ChangeTuple> itrC = C.iterator();
1161       while( itrC.hasNext() ) {
1162         ChangeTuple c = itrC.next();
1163         if( edgeE.getBeta().contains( c.getSetToMatch() ) ) {
1164           changesToPass = Canonical.union( changesToPass, c );
1165         }
1166       }
1167
1168       RefSrcNode rsn = edgeE.getSrc();
1169
1170       if( !changesToPass.isEmpty() && rsn instanceof HeapRegionNode ) {
1171         HeapRegionNode n = (HeapRegionNode) rsn;
1172
1173         Iterator<RefEdge> referItr = n.iteratorToReferencers();
1174         while( referItr.hasNext() ) {
1175           RefEdge edgeF = referItr.next();
1176
1177           if( !edgePlannedChanges.containsKey( edgeF ) ) {
1178             edgePlannedChanges.put( edgeF,
1179                                     ChangeSet.factory()
1180                                     );
1181           }
1182
1183           ChangeSet currentChanges = edgePlannedChanges.get( edgeF );
1184
1185           if( !changesToPass.isSubset( currentChanges ) ) {
1186             todoEdges.add( edgeF );
1187             edgePlannedChanges.put( edgeF,
1188                                     Canonical.union( currentChanges,
1189                                                      changesToPass
1190                                                      )
1191                                     );
1192           }
1193         }
1194       }
1195     }
1196
1197     // then apply all of the changes for each edge at once
1198     Iterator itrMap = edgePlannedChanges.entrySet().iterator();
1199     while( itrMap.hasNext() ) {
1200       Map.Entry me = (Map.Entry) itrMap.next();
1201       RefEdge   e  = (RefEdge)   me.getKey();
1202       ChangeSet C  = (ChangeSet) me.getValue();
1203
1204       // this propagation step is with respect to one change,
1205       // so we capture the full change from the old beta:
1206       ReachSet localDelta =
1207         Canonical.applyChangeSet( e.getBeta(),
1208                                   C,
1209                                   true 
1210                                   );
1211
1212       // but this propagation may be only one of many concurrent
1213       // possible changes, so keep a running union with the edge's
1214       // partially updated new beta set
1215       e.setBetaNew( Canonical.union( e.getBetaNew(),
1216                                      localDelta  
1217                                      )
1218                     );
1219       
1220       edgesWithNewBeta.add( e );
1221     }
1222   }
1223
1224
1225   // used in makeCalleeView below to decide if there is
1226   // already an appropriate out-of-context edge in a callee
1227   // view graph for merging, or null if a new one will be added
1228   protected RefEdge
1229     getOutOfContextReferenceTo( HeapRegionNode hrn,
1230                                 TypeDescriptor srcType,
1231                                 TypeDescriptor refType,
1232                                 String         refField ) {
1233     assert belongsToThis( hrn );
1234
1235     HeapRegionNode hrnInContext = id2hrn.get( hrn.getID() );
1236     if( hrnInContext == null ) {
1237       return null;
1238     }
1239
1240     Iterator<RefEdge> refItr = hrnInContext.iteratorToReferencers();
1241     while( refItr.hasNext() ) {
1242       RefEdge re = refItr.next();
1243
1244       assert belongsToThis( re.getSrc() );
1245       assert belongsToThis( re.getDst() );
1246
1247       if( !(re.getSrc() instanceof HeapRegionNode) ) {
1248         continue;
1249       }
1250
1251       HeapRegionNode hrnSrc = (HeapRegionNode) re.getSrc();
1252       if( !hrnSrc.isOutOfContext() ) {
1253         continue;
1254       }
1255       
1256       if( srcType == null ) {
1257         if( hrnSrc.getType() != null ) {
1258           continue;
1259         }
1260       } else {
1261         if( !srcType.equals( hrnSrc.getType() ) ) {
1262           continue;
1263         }
1264       }
1265
1266       if( !re.typeEquals( refType ) ) {
1267         continue;
1268       }
1269
1270       if( !re.fieldEquals( refField ) ) {
1271         continue;
1272       }
1273
1274       // tada!  We found it!
1275       return re;
1276     }
1277     
1278     return null;
1279   }
1280
1281   // used below to convert a ReachSet to its callee-context
1282   // equivalent with respect to allocation sites in this graph
1283   protected ReachSet toCalleeContext( Set<ReachTuple> oocTuples,
1284                                       ReachSet        rs,
1285                                       Integer         hrnID,
1286                                       TempDescriptor  tdSrc,
1287                                       Integer         hrnSrcID,
1288                                       Integer         hrnDstID,
1289                                       TypeDescriptor  type,
1290                                       String          field,
1291                                       boolean         outOfContext
1292                                       ) {
1293     ReachSet out = ReachSet.factory();
1294    
1295     Iterator<ReachState> itr = rs.iterator();
1296     while( itr.hasNext() ) {
1297       ReachState stateCaller = itr.next();
1298     
1299       ReachState stateCallee = stateCaller;
1300
1301       Iterator<AllocSite> asItr = allocSites.iterator();
1302       while( asItr.hasNext() ) {
1303         AllocSite as = asItr.next();
1304
1305         ReachState stateNew = ReachState.factory();
1306         Iterator<ReachTuple> rtItr = stateCallee.iterator();
1307         while( rtItr.hasNext() ) {
1308           ReachTuple rt = rtItr.next();
1309
1310           // only translate this tuple if it is in the out-context bag
1311           if( !oocTuples.contains( rt ) ) {
1312             stateNew = Canonical.union( stateNew, rt );
1313             continue;
1314           }
1315
1316           int age = as.getAgeCategory( rt.getHrnID() );
1317           
1318           // this is the current mapping, where 0, 1, 2S were allocated
1319           // in the current context, 0?, 1? and 2S? were allocated in a
1320           // previous context, and we're translating to a future context
1321           //
1322           // 0    -> 0?
1323           // 1    -> 1?
1324           // 2S   -> 2S?
1325           // 2S*  -> 2S?*
1326           //
1327           // 0?   -> 2S?
1328           // 1?   -> 2S?
1329           // 2S?  -> 2S?
1330           // 2S?* -> 2S?*
1331       
1332           if( age == AllocSite.AGE_notInThisSite ) {
1333             // things not from the site just go back in
1334             stateNew = Canonical.union( stateNew, rt );
1335
1336           } else if( age == AllocSite.AGE_summary ||
1337                      rt.isOutOfContext()
1338                      ) {
1339             // the in-context summary and all existing out-of-context
1340             // stuff all become
1341             stateNew = Canonical.union( stateNew,
1342                                         ReachTuple.factory( as.getSummary(),
1343                                                             true, // multi
1344                                                             rt.getArity(),
1345                                                             true  // out-of-context
1346                                                             )
1347                                         );
1348           } else {
1349             // otherwise everything else just goes to an out-of-context
1350             // version, everything else the same
1351             Integer I = as.getAge( rt.getHrnID() );
1352             assert I != null;
1353
1354             assert !rt.isMultiObject();
1355
1356             stateNew = Canonical.union( stateNew,
1357                                         ReachTuple.factory( rt.getHrnID(),
1358                                                             rt.isMultiObject(),
1359                                                             rt.getArity(),
1360                                                             true  // out-of-context
1361                                                             )
1362                                         );        
1363           }
1364         }
1365         
1366         stateCallee = stateNew;
1367       }
1368
1369
1370       ExistPredSet preds;
1371
1372       if( outOfContext ) {
1373         preds = predsEmpty;
1374       } else {
1375         ExistPred pred;
1376         if( hrnID != null ) {
1377           assert tdSrc    == null;
1378           assert hrnSrcID == null;
1379           assert hrnDstID == null;
1380           pred = ExistPred.factory( hrnID, 
1381                                     stateCaller );
1382         } else {
1383           assert tdSrc != null || hrnSrcID != null;
1384           assert hrnDstID != null;
1385           pred = ExistPred.factory( tdSrc,
1386                                     hrnSrcID,
1387                                     hrnDstID,
1388                                     type,
1389                                     field,
1390                                     stateCaller,
1391                                     false );
1392         }
1393         preds = ExistPredSet.factory( pred );
1394       }
1395       
1396       stateCallee = Canonical.attach( stateCallee,
1397                                       preds );
1398
1399       out = Canonical.add( out,
1400                            stateCallee
1401                            );
1402
1403     }
1404     assert out.isCanonical();
1405     return out;
1406   }
1407
1408   // used below to convert a ReachSet to its caller-context
1409   // equivalent with respect to allocation sites in this graph
1410   protected ReachSet 
1411     toCallerContext( ReachSet                            rs,
1412                      Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied 
1413                      ) {
1414     ReachSet out = ReachSet.factory();
1415
1416     Iterator<ReachState> itr = rs.iterator();
1417     while( itr.hasNext() ) {
1418       ReachState stateCallee = itr.next();
1419
1420       if( calleeStatesSatisfied.containsKey( stateCallee ) ) {
1421
1422         // starting from one callee state...
1423         ReachSet rsCaller = ReachSet.factory( stateCallee );
1424
1425         // possibly branch it into many states, which any
1426         // allocation site might do, so lots of derived states
1427         Iterator<AllocSite> asItr = allocSites.iterator();
1428         while( asItr.hasNext() ) {
1429           AllocSite as = asItr.next();
1430           rsCaller = Canonical.toCallerContext( rs, as );
1431         }
1432         
1433         // then before adding each derived, now caller-context
1434         // states to the output, attach the appropriate pred
1435         // based on the source callee state
1436         Iterator<ReachState> stateItr = rsCaller.iterator();
1437         while( stateItr.hasNext() ) {
1438           ReachState stateCaller = stateItr.next();
1439           stateCaller = Canonical.attach( stateCaller,
1440                                           calleeStatesSatisfied.get( stateCallee )
1441                                           );        
1442           out = Canonical.union( out,
1443                                  stateCaller
1444                                  );
1445         }
1446       }
1447     }
1448     
1449     assert out.isCanonical();
1450     return out;
1451   }
1452
1453   // used below to convert a ReachSet to an equivalent
1454   // version with shadow IDs merged into unshadowed IDs
1455   protected ReachSet unshadow( ReachSet rs ) {
1456     ReachSet out = rs;
1457     Iterator<AllocSite> asItr = allocSites.iterator();
1458     while( asItr.hasNext() ) {
1459       AllocSite as = asItr.next();
1460       out = Canonical.unshadow( out, as );
1461     }
1462     assert out.isCanonical();
1463     return out;
1464   }
1465
1466
1467   // use this method to make a new reach graph that is
1468   // what heap the FlatMethod callee from the FlatCall 
1469   // would start with reaching from its arguments in
1470   // this reach graph
1471   public ReachGraph 
1472     makeCalleeView( FlatCall     fc,
1473                     FlatMethod   fmCallee,
1474                     Set<Integer> callerNodeIDsCopiedToCallee,
1475                     boolean      writeDebugDOTs
1476                     ) {
1477
1478     // first traverse this context to find nodes and edges
1479     //  that will be callee-reachable
1480     Set<HeapRegionNode> reachableCallerNodes =
1481       new HashSet<HeapRegionNode>();
1482
1483     Set<RefEdge> reachableCallerEdges =
1484       new HashSet<RefEdge>();
1485
1486     Set<RefEdge> oocCallerEdges =
1487       new HashSet<RefEdge>();
1488
1489     for( int i = 0; i < fmCallee.numParameters(); ++i ) {
1490
1491       TempDescriptor tdArg = fc.getArgMatchingParamIndex( fmCallee, i );
1492       VariableNode vnCaller = this.getVariableNodeFromTemp( tdArg );
1493
1494       Set<RefSrcNode> toVisitInCaller = new HashSet<RefSrcNode>();
1495       Set<RefSrcNode> visitedInCaller = new HashSet<RefSrcNode>();
1496
1497       toVisitInCaller.add( vnCaller );
1498       
1499       while( !toVisitInCaller.isEmpty() ) {
1500         RefSrcNode rsnCaller = toVisitInCaller.iterator().next();
1501         toVisitInCaller.remove( rsnCaller );
1502         visitedInCaller.add( rsnCaller );
1503
1504         Iterator<RefEdge> itrRefEdges = rsnCaller.iteratorToReferencees();
1505         while( itrRefEdges.hasNext() ) {
1506           RefEdge        reCaller  = itrRefEdges.next();
1507           HeapRegionNode hrnCaller = reCaller.getDst();
1508
1509           callerNodeIDsCopiedToCallee.add( hrnCaller.getID() );
1510           reachableCallerNodes.add( hrnCaller );
1511
1512           if( reCaller.getSrc() instanceof HeapRegionNode ) {
1513             reachableCallerEdges.add( reCaller );
1514           } else {
1515             oocCallerEdges.add( reCaller );
1516           }
1517
1518           if( !visitedInCaller.contains( hrnCaller ) ) {
1519             toVisitInCaller.add( hrnCaller );
1520           }
1521           
1522         } // end edge iteration
1523       } // end visiting heap nodes in caller
1524     } // end iterating over parameters as starting points
1525
1526
1527     // now collect out-of-context reach tuples and 
1528     // more out-of-context edges
1529     Set<ReachTuple> oocTuples = new HashSet<ReachTuple>();
1530
1531     Iterator<Integer> itrInContext = 
1532       callerNodeIDsCopiedToCallee.iterator();
1533     while( itrInContext.hasNext() ) {
1534       Integer        hrnID                 = itrInContext.next();
1535       HeapRegionNode hrnCallerAndInContext = id2hrn.get( hrnID );
1536       
1537       Iterator<RefEdge> itrMightCross =
1538         hrnCallerAndInContext.iteratorToReferencers();
1539       while( itrMightCross.hasNext() ) {
1540         RefEdge edgeMightCross = itrMightCross.next();        
1541
1542         RefSrcNode rsnCallerAndOutContext =
1543           edgeMightCross.getSrc();
1544         
1545         if( rsnCallerAndOutContext instanceof VariableNode ) {
1546           // variables do not have out-of-context reach states,
1547           // so jump out now
1548           oocCallerEdges.add( edgeMightCross );
1549           continue;
1550         }
1551           
1552         HeapRegionNode hrnCallerAndOutContext = 
1553           (HeapRegionNode) rsnCallerAndOutContext;
1554
1555         // is this source node out-of-context?
1556         if( callerNodeIDsCopiedToCallee.contains( hrnCallerAndOutContext.getID() ) ) {
1557           // no, skip this edge
1558           continue;
1559         }
1560
1561         // okay, we got one
1562         oocCallerEdges.add( edgeMightCross );
1563
1564         // add all reach tuples on the node to list
1565         // of things that are out-of-context: insight
1566         // if this node is reachable from someting that WAS
1567         // in-context, then this node should already be in-context
1568         Iterator<ReachState> stateItr = hrnCallerAndOutContext.getAlpha().iterator();
1569         while( stateItr.hasNext() ) {
1570           ReachState state = stateItr.next();
1571
1572           Iterator<ReachTuple> rtItr = state.iterator();
1573           while( rtItr.hasNext() ) {
1574             ReachTuple rt = rtItr.next();
1575
1576             oocTuples.add( rt );
1577           }
1578         }
1579       }
1580     }
1581
1582
1583     // the callee view is a new graph: DON'T MODIFY *THIS* graph
1584     ReachGraph rg = new ReachGraph();
1585
1586     // add nodes to callee graph
1587     Iterator<HeapRegionNode> hrnItr = reachableCallerNodes.iterator();
1588     while( hrnItr.hasNext() ) {
1589       HeapRegionNode hrnCaller = hrnItr.next();
1590
1591       assert callerNodeIDsCopiedToCallee.contains( hrnCaller.getID() );
1592       assert !rg.id2hrn.containsKey( hrnCaller.getID() );
1593             
1594       ExistPred    pred  = ExistPred.factory( hrnCaller.getID(), null );
1595       ExistPredSet preds = ExistPredSet.factory( pred );
1596       
1597       rg.createNewHeapRegionNode( hrnCaller.getID(),
1598                                   hrnCaller.isSingleObject(),
1599                                   hrnCaller.isNewSummary(),
1600                                   hrnCaller.isFlagged(),
1601                                   false, // out-of-context?
1602                                   hrnCaller.getType(),
1603                                   hrnCaller.getAllocSite(),
1604                                   toCalleeContext( oocTuples,
1605                                                    hrnCaller.getInherent(),      // in state
1606                                                    hrnCaller.getID(),            // node pred
1607                                                    null, null, null, null, null, // edge pred
1608                                                    false ),                      // ooc pred
1609                                   toCalleeContext( oocTuples,
1610                                                    hrnCaller.getAlpha(),         // in state
1611                                                    hrnCaller.getID(),            // node pred
1612                                                    null, null, null, null, null, // edge pred
1613                                                    false ),                      // ooc pred
1614                                   preds,
1615                                   hrnCaller.getDescription()
1616                                   );
1617     }
1618
1619     // add in-context edges to callee graph
1620     Iterator<RefEdge> reItr = reachableCallerEdges.iterator();
1621     while( reItr.hasNext() ) {
1622       RefEdge    reCaller  = reItr.next();
1623       RefSrcNode rsnCaller = reCaller.getSrc();
1624       assert rsnCaller instanceof HeapRegionNode;
1625       HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1626       HeapRegionNode hrnDstCaller = reCaller.getDst();
1627
1628       HeapRegionNode hrnSrcCallee = rg.id2hrn.get( hrnSrcCaller.getID() );
1629       HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1630       assert hrnSrcCallee != null;
1631       assert hrnDstCallee != null;
1632
1633       ExistPred pred =
1634         ExistPred.factory( null, 
1635                            hrnSrcCallee.getID(), 
1636                            hrnDstCallee.getID(),
1637                            reCaller.getType(),
1638                            reCaller.getField(),
1639                            null,
1640                            false ); // out-of-context
1641       
1642       ExistPredSet preds = 
1643         ExistPredSet.factory( pred );
1644       
1645       RefEdge reCallee = 
1646         new RefEdge( hrnSrcCallee,
1647                      hrnDstCallee,
1648                      reCaller.getType(),
1649                      reCaller.getField(),
1650                      toCalleeContext( oocTuples,
1651                                       reCaller.getBeta(),   // in state
1652                                       null,                 // node pred
1653                                       null,                 // edge pred
1654                                       hrnSrcCallee.getID(), // edge pred
1655                                       hrnDstCallee.getID(), // edge pred
1656                                       reCaller.getType(),   // edge pred
1657                                       reCaller.getField(),  // edge pred
1658                                       false ),              // ooc pred
1659                      preds
1660                      );
1661       
1662       rg.addRefEdge( hrnSrcCallee,
1663                      hrnDstCallee,
1664                      reCallee
1665                      );        
1666     }
1667
1668     // add out-of-context edges to callee graph
1669     reItr = oocCallerEdges.iterator();
1670     while( reItr.hasNext() ) {
1671       RefEdge        reCaller     = reItr.next();
1672       RefSrcNode     rsnCaller    = reCaller.getSrc();
1673       HeapRegionNode hrnDstCaller = reCaller.getDst();
1674       HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1675       assert hrnDstCallee != null;
1676
1677       TypeDescriptor oocNodeType;
1678       ReachSet       oocReach;
1679       TempDescriptor oocPredSrcTemp = null;
1680       Integer        oocPredSrcID   = null;
1681
1682       if( rsnCaller instanceof VariableNode ) {
1683         VariableNode vnCaller = (VariableNode) rsnCaller;
1684         oocNodeType    = null;
1685         oocReach       = rsetEmpty;
1686         oocPredSrcTemp = vnCaller.getTempDescriptor();
1687
1688       } else {
1689         HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1690         assert !callerNodeIDsCopiedToCallee.contains( hrnSrcCaller.getID() );
1691         oocNodeType  = hrnSrcCaller.getType();
1692         oocReach     = hrnSrcCaller.getAlpha(); 
1693         oocPredSrcID = hrnSrcCaller.getID();        
1694       }
1695
1696       ExistPred pred =
1697         ExistPred.factory( oocPredSrcTemp, 
1698                            oocPredSrcID, 
1699                            hrnDstCallee.getID(),
1700                            reCaller.getType(),
1701                            reCaller.getField(),
1702                            null,
1703                            true ); // out-of-context
1704
1705       ExistPredSet preds = 
1706         ExistPredSet.factory( pred );
1707         
1708       RefEdge oocEdgeExisting =
1709         rg.getOutOfContextReferenceTo( hrnDstCallee,
1710                                        oocNodeType,
1711                                        reCaller.getType(),
1712                                        reCaller.getField()
1713                                        );
1714
1715       if( oocEdgeExisting == null ) {          
1716           // for consistency, map one out-of-context "identifier"
1717           // to one heap region node id, otherwise no convergence
1718         String oocid = "oocid"+
1719           fmCallee+
1720           hrnDstCallee.getIDString()+
1721           oocNodeType+
1722           reCaller.getType()+
1723           reCaller.getField();
1724           
1725         Integer oocHrnID = oocid2hrnid.get( oocid );
1726
1727         HeapRegionNode hrnCalleeAndOutContext;
1728
1729         if( oocHrnID == null ) {
1730
1731           hrnCalleeAndOutContext =
1732             rg.createNewHeapRegionNode( null,  // ID
1733                                         false, // single object?
1734                                         false, // new summary?
1735                                         false, // flagged?
1736                                         true,  // out-of-context?
1737                                         oocNodeType,
1738                                         null,  // alloc site, shouldn't be used
1739                                         toCalleeContext( oocTuples, 
1740                                                          oocReach,                     // in state
1741                                                          null,                         // node pred
1742                                                          null, null, null, null, null, // edge pred
1743                                                          true                          // ooc pred
1744                                                          ), // inherent
1745                                         toCalleeContext( oocTuples,
1746                                                          oocReach,                     // in state
1747                                                          null,                         // node pred
1748                                                          null, null, null, null, null, // edge pred
1749                                                          true                          // ooc pred
1750                                                          ), // alpha
1751                                         preds,
1752                                         "out-of-context"
1753                                         );       
1754           
1755           oocid2hrnid.put( oocid, hrnCalleeAndOutContext.getID() );
1756           
1757         } else {
1758
1759           // the mapping already exists, so see if node is there
1760           hrnCalleeAndOutContext = rg.id2hrn.get( oocHrnID );
1761
1762           if( hrnCalleeAndOutContext == null ) {
1763             // nope, make it
1764             hrnCalleeAndOutContext =
1765               rg.createNewHeapRegionNode( oocHrnID,  // ID
1766                                           false, // single object?
1767                                           false, // new summary?
1768                                           false, // flagged?
1769                                           true,  // out-of-context?
1770                                           oocNodeType,
1771                                           null,  // alloc site, shouldn't be used
1772                                           toCalleeContext( oocTuples,
1773                                                            oocReach,                     // in state
1774                                                            null,                         // node pred
1775                                                            null, null, null, null, null, // edge pred
1776                                                            true                          // ooc pred
1777                                                            ), // inherent
1778                                           toCalleeContext( oocTuples,
1779                                                            oocReach,                     // in state
1780                                                            null,                         // node pred
1781                                                            null, null, null, null, null, // edge pred
1782                                                            true                          // ooc pred
1783                                                            ), // alpha
1784                                           preds,
1785                                           "out-of-context"
1786                                           );       
1787           }
1788         }
1789
1790         rg.addRefEdge( hrnCalleeAndOutContext,
1791                        hrnDstCallee,
1792                        new RefEdge( hrnCalleeAndOutContext,
1793                                     hrnDstCallee,
1794                                     reCaller.getType(),
1795                                     reCaller.getField(),
1796                                     toCalleeContext( oocTuples,
1797                                                      reCaller.getBeta(),   // in state
1798                                                      null,                 // node pred
1799                                                      oocPredSrcTemp,       // edge pred
1800                                                      oocPredSrcID,         // edge pred
1801                                                      hrnDstCaller.getID(), // edge pred
1802                                                      reCaller.getType(),   // edge pred
1803                                                      reCaller.getField(),  // edge pred
1804                                                      false                 // ooc pred
1805                                                      ),
1806                                     preds
1807                                     )
1808                        );              
1809         
1810         } else {
1811         // the out-of-context edge already exists
1812         oocEdgeExisting.setBeta( Canonical.union( oocEdgeExisting.getBeta(),
1813                                                   toCalleeContext( oocTuples,
1814                                                                    reCaller.getBeta(),   // in state
1815                                                                    null,                 // node pred
1816                                                                    oocPredSrcTemp,       // edge pred
1817                                                                    oocPredSrcID,         // edge pred
1818                                                                    hrnDstCaller.getID(), // edge pred
1819                                                                    reCaller.getType(),   // edge pred
1820                                                                    reCaller.getField(),  // edge pred
1821                                                                    false                 // ooc pred
1822                                                                    )
1823                                                   )
1824                                  );         
1825           
1826         oocEdgeExisting.setPreds( Canonical.join( oocEdgeExisting.getPreds(),
1827                                                   reCaller.getPreds()
1828                                                   )
1829                                   );          
1830         
1831       }                
1832     }
1833
1834
1835     if( writeDebugDOTs ) {    
1836       try {
1837         rg.writeGraph( "calleeview", true, false, false, false, true, true );
1838       } catch( IOException e ) {}
1839     }
1840
1841     return rg;
1842   }  
1843
1844   private static Hashtable<String, Integer> oocid2hrnid = 
1845     new Hashtable<String, Integer>();
1846
1847
1848
1849   public void 
1850     resolveMethodCall( FlatCall     fc,        
1851                        FlatMethod   fmCallee,        
1852                        ReachGraph   rgCallee,
1853                        Set<Integer> callerNodeIDsCopiedToCallee,
1854                        boolean      writeDebugDOTs
1855                        ) {
1856
1857
1858     if( writeDebugDOTs ) {
1859       try {
1860         rgCallee.writeGraph( "callee", 
1861                              true, false, false, false, true, true );
1862         writeGraph( "caller00In", 
1863                     true, false, false, false, true, true, 
1864                     callerNodeIDsCopiedToCallee );
1865       } catch( IOException e ) {}
1866     }
1867
1868
1869     // method call transfer function steps:
1870     // 1. Use current callee-reachable heap (CRH) to test callee 
1871     //    predicates and mark what will be coming in.
1872     // 2. Wipe CRH out of caller.
1873     // 3. Transplant marked callee parts in:
1874     //    a) bring in nodes
1875     //    b) bring in callee -> callee edges
1876     //    c) resolve out-of-context -> callee edges
1877     //    d) assign return value
1878     // 4. Collapse shadow nodes down
1879     // 5. Global sweep it.
1880
1881
1882
1883     // 1. mark what callee elements have satisfied predicates
1884     Hashtable<HeapRegionNode, ExistPredSet> calleeNodesSatisfied =
1885       new Hashtable<HeapRegionNode, ExistPredSet>();
1886     
1887     Hashtable<RefEdge, ExistPredSet> calleeEdgesSatisfied =
1888       new Hashtable<RefEdge, ExistPredSet>();
1889
1890     Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied =
1891       new Hashtable<ReachState, ExistPredSet>();
1892
1893     Hashtable< RefEdge, Set<RefSrcNode> > calleeEdges2oocCallerSrcMatches =
1894       new Hashtable< RefEdge, Set<RefSrcNode> >();
1895
1896     Iterator meItr = rgCallee.id2hrn.entrySet().iterator();
1897     while( meItr.hasNext() ) {
1898       Map.Entry      me        = (Map.Entry)      meItr.next();
1899       Integer        id        = (Integer)        me.getKey();
1900       HeapRegionNode hrnCallee = (HeapRegionNode) me.getValue();
1901
1902       // if a callee element's predicates are satisfied then a set
1903       // of CALLER predicates is returned: they are the predicates
1904       // that the callee element moved into the caller context
1905       // should have, and it is inefficient to find this again later
1906       ExistPredSet predsIfSatis = 
1907         hrnCallee.getPreds().isSatisfiedBy( this,
1908                                             callerNodeIDsCopiedToCallee
1909                                             );
1910       if( predsIfSatis != null ) {
1911         calleeNodesSatisfied.put( hrnCallee, predsIfSatis );
1912       } else {
1913         // otherwise don't bother looking at edges to this node
1914         continue;
1915       }
1916       
1917       // since the node is coming over, find out which reach
1918       // states on it should come over, too
1919       Iterator<ReachState> stateItr = hrnCallee.getAlpha().iterator();
1920       while( stateItr.hasNext() ) {
1921         ReachState stateCallee = stateItr.next();
1922
1923         predsIfSatis = 
1924           stateCallee.getPreds().isSatisfiedBy( this,
1925                                                 callerNodeIDsCopiedToCallee
1926                                                 );
1927         if( predsIfSatis != null ) {
1928           calleeStatesSatisfied.put( stateCallee, predsIfSatis );
1929         } 
1930       }
1931
1932       // then look at edges to the node
1933       Iterator<RefEdge> reItr = hrnCallee.iteratorToReferencers();
1934       while( reItr.hasNext() ) {
1935         RefEdge    reCallee  = reItr.next();
1936         RefSrcNode rsnCallee = reCallee.getSrc();
1937
1938         // (caller local variables to in-context heap regions)
1939         // have an (out-of-context heap region -> in-context heap region)
1940         // abstraction in the callEE, so its true we never need to
1941         // look at a (var node -> heap region) edge in callee to bring
1942         // those over for the call site transfer.  What about (param var->heap region)
1943         // edges in callee? They are dealt with below this loop.
1944         // So, yes, at this point skip (var->region) edges in callee
1945         if( rsnCallee instanceof VariableNode ) {
1946           continue;
1947         }        
1948
1949         // first see if the source is out-of-context, and only
1950         // proceed with this edge if we find some caller-context
1951         // matches
1952         HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
1953         boolean matchedOutOfContext = false;
1954
1955         if( hrnSrcCallee.isOutOfContext() ) {          
1956
1957           assert !calleeEdges2oocCallerSrcMatches.containsKey( reCallee );
1958           Set<RefSrcNode> rsnCallers = new HashSet<RefSrcNode>();            
1959
1960           HeapRegionNode hrnDstCaller = this.id2hrn.get( hrnCallee.getID() );
1961           Iterator<RefEdge> reDstItr = hrnDstCaller.iteratorToReferencers();
1962           while( reDstItr.hasNext() ) {
1963             // the edge and field (either possibly null) must match
1964             RefEdge reCaller = reDstItr.next();
1965
1966             if( !reCaller.typeEquals ( reCallee.getType()  ) ||
1967                 !reCaller.fieldEquals( reCallee.getField() ) 
1968                 ) {
1969               continue;
1970             }
1971             
1972             RefSrcNode rsnCaller = reCaller.getSrc();
1973             if( rsnCaller instanceof VariableNode ) {
1974               // a variable node matches an OOC region with null type
1975               if( hrnSrcCallee.getType() != null ) {
1976                 continue;
1977               }
1978
1979             } else {
1980               // otherwise types should match
1981               HeapRegionNode hrnCallerSrc = (HeapRegionNode) rsnCaller;
1982               if( hrnSrcCallee.getType() == null ) {
1983                 if( hrnCallerSrc.getType() != null ) {
1984                   continue;
1985                 }
1986               } else {
1987                 if( !hrnSrcCallee.getType().equals( hrnCallerSrc.getType() ) ) {
1988                   continue;
1989                 }
1990               }
1991             }
1992
1993             rsnCallers.add( rsnCaller );
1994             matchedOutOfContext = true;
1995           }
1996
1997           if( !rsnCallers.isEmpty() ) {
1998             calleeEdges2oocCallerSrcMatches.put( reCallee, rsnCallers );
1999           }
2000         }
2001
2002         if( hrnSrcCallee.isOutOfContext() &&
2003             !matchedOutOfContext ) {
2004           continue;
2005         }
2006         
2007         predsIfSatis = 
2008           reCallee.getPreds().isSatisfiedBy( this,
2009                                              callerNodeIDsCopiedToCallee
2010                                              );
2011         if( predsIfSatis != null ) {
2012           calleeEdgesSatisfied.put( reCallee, predsIfSatis );
2013
2014           // since the edge is coming over, find out which reach
2015           // states on it should come over, too
2016           stateItr = reCallee.getBeta().iterator();
2017           while( stateItr.hasNext() ) {
2018             ReachState stateCallee = stateItr.next();
2019             
2020             predsIfSatis = 
2021               stateCallee.getPreds().isSatisfiedBy( this,
2022                                                     callerNodeIDsCopiedToCallee
2023                                                     );
2024             if( predsIfSatis != null ) {
2025               calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2026             } 
2027           }
2028
2029         }        
2030
2031       }
2032     }
2033
2034     // test param -> HRN edges, also
2035     for( int i = 0; i < fmCallee.numParameters(); ++i ) {
2036
2037       // parameter defined here is the symbol in the callee
2038       TempDescriptor tdParam  = fmCallee.getParameter( i );
2039       VariableNode   vnCallee = rgCallee.getVariableNodeFromTemp( tdParam );
2040
2041       Iterator<RefEdge> reItr = vnCallee.iteratorToReferencees();
2042       while( reItr.hasNext() ) {
2043         RefEdge reCallee = reItr.next();
2044         
2045         ExistPredSet ifDst = 
2046           reCallee.getDst().getPreds().isSatisfiedBy( this,
2047                                                       callerNodeIDsCopiedToCallee
2048                                                       );
2049         if( ifDst == null ) {
2050           continue;
2051         }
2052         
2053         ExistPredSet predsIfSatis = 
2054           reCallee.getPreds().isSatisfiedBy( this,
2055                                              callerNodeIDsCopiedToCallee
2056                                              );
2057         if( predsIfSatis != null ) {
2058           calleeEdgesSatisfied.put( reCallee, predsIfSatis );
2059
2060           // since the edge is coming over, find out which reach
2061           // states on it should come over, too
2062           Iterator<ReachState> stateItr = reCallee.getBeta().iterator();
2063           while( stateItr.hasNext() ) {
2064             ReachState stateCallee = stateItr.next();
2065             
2066             predsIfSatis = 
2067               stateCallee.getPreds().isSatisfiedBy( this,
2068                                                     callerNodeIDsCopiedToCallee
2069                                                     );
2070             if( predsIfSatis != null ) {
2071               calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2072             } 
2073           }
2074
2075         }        
2076       }
2077     }
2078
2079
2080
2081
2082     if( writeDebugDOTs ) {
2083       try {
2084         writeGraph( "caller20BeforeWipe", 
2085                     true, false, false, false, true, true );
2086       } catch( IOException e ) {}
2087     }
2088
2089
2090     // 2. predicates tested, ok to wipe out caller part
2091     Iterator<Integer> hrnItr = callerNodeIDsCopiedToCallee.iterator();
2092     while( hrnItr.hasNext() ) {
2093       Integer        hrnID     = hrnItr.next();
2094       HeapRegionNode hrnCaller = id2hrn.get( hrnID );
2095       assert hrnCaller != null;
2096
2097       // when clearing off nodes, also eliminate variable
2098       // references
2099       wipeOut( hrnCaller, true );
2100     }
2101
2102
2103
2104     if( writeDebugDOTs ) {
2105       try {
2106         writeGraph( "caller30BeforeAddingNodes", 
2107                     true, false, false, false, true, true );
2108       } catch( IOException e ) {}
2109     }
2110
2111
2112     // 3. callee elements with satisfied preds come in, note that
2113     //    the mapping of elements satisfied to preds is like this:
2114     //    A callee element EE has preds EEp that are satisfied by
2115     //    some caller element ER.  We bring EE into the caller
2116     //    context as ERee with the preds of ER, namely ERp, which
2117     //    in the following algorithm is the value in the mapping
2118
2119     // 3.a) nodes
2120     Iterator satisItr = calleeNodesSatisfied.entrySet().iterator();
2121     while( satisItr.hasNext() ) {
2122       Map.Entry      me        = (Map.Entry)      satisItr.next();
2123       HeapRegionNode hrnCallee = (HeapRegionNode) me.getKey();
2124       ExistPredSet   preds     = (ExistPredSet)   me.getValue();
2125
2126       // TODO: I think its true that the current implementation uses
2127       // the type of the OOC region and the predicates OF THE EDGE from
2128       // it to link everything up in caller context, so that's why we're
2129       // skipping this... maybe that's a sillier way to do it?
2130       if( hrnCallee.isOutOfContext() ) {
2131         continue;
2132       }
2133
2134       AllocSite as = hrnCallee.getAllocSite();  
2135       allocSites.add( as );
2136
2137       Integer hrnIDshadow = as.getShadowIDfromID( hrnCallee.getID() );
2138
2139       HeapRegionNode hrnCaller = id2hrn.get( hrnIDshadow );
2140       if( hrnCaller == null ) {
2141         hrnCaller =
2142           createNewHeapRegionNode( hrnIDshadow,                // id or null to generate a new one 
2143                                    hrnCallee.isSingleObject(), // single object?                 
2144                                    hrnCallee.isNewSummary(),   // summary?       
2145                                    hrnCallee.isFlagged(),      // flagged?
2146                                    false,                      // out-of-context?
2147                                    hrnCallee.getType(),        // type                           
2148                                    hrnCallee.getAllocSite(),   // allocation site                        
2149                                    toCallerContext( hrnCallee.getInherent(),
2150                                                     calleeStatesSatisfied  ),    // inherent reach
2151                                    null,                       // current reach                 
2152                                    predsEmpty,                 // predicates
2153                                    hrnCallee.getDescription()  // description
2154                                    );                                        
2155       } else {
2156         assert hrnCaller.isWiped();
2157       }
2158
2159       hrnCaller.setAlpha( toCallerContext( hrnCallee.getAlpha(),
2160                                            calleeStatesSatisfied 
2161                                            )
2162                           );
2163
2164       hrnCaller.setPreds( preds );
2165     }
2166
2167
2168
2169     if( writeDebugDOTs ) {
2170       try {
2171         writeGraph( "caller31BeforeAddingEdges", 
2172                     true, false, false, false, true, true );
2173       } catch( IOException e ) {}
2174     }
2175
2176
2177     // set these up during the next procedure so after
2178     // the caller has all of its nodes and edges put
2179     // back together we can propagate the callee's
2180     // reach changes backwards into the caller graph
2181     HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
2182
2183     Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
2184       new Hashtable<RefEdge, ChangeSet>();
2185
2186
2187     // 3.b) callee -> callee edges AND out-of-context -> callee
2188     satisItr = calleeEdgesSatisfied.entrySet().iterator();
2189     while( satisItr.hasNext() ) {
2190       Map.Entry    me       = (Map.Entry)    satisItr.next();
2191       RefEdge      reCallee = (RefEdge)      me.getKey();
2192       ExistPredSet preds    = (ExistPredSet) me.getValue();
2193
2194       HeapRegionNode hrnDstCallee = reCallee.getDst();
2195       AllocSite      asDst        = hrnDstCallee.getAllocSite();
2196       allocSites.add( asDst );
2197
2198       Integer hrnIDDstShadow = 
2199         asDst.getShadowIDfromID( hrnDstCallee.getID() );
2200       
2201       HeapRegionNode hrnDstCaller = id2hrn.get( hrnIDDstShadow );
2202       assert hrnDstCaller != null;
2203       
2204       
2205       RefSrcNode rsnCallee = reCallee.getSrc();
2206
2207       Set<RefSrcNode> rsnCallers =
2208         new HashSet<RefSrcNode>();
2209       
2210       Set<RefSrcNode> oocCallers = 
2211         calleeEdges2oocCallerSrcMatches.get( reCallee );
2212
2213       boolean oocEdges = false;
2214       
2215       if( oocCallers == null ) {
2216         // there are no out-of-context matches, so it's
2217         // either a param/arg var or one in-context heap region
2218         if( rsnCallee instanceof VariableNode ) {
2219           // variable -> node in the callee should only
2220           // come into the caller if its from a param var
2221           VariableNode   vnCallee = (VariableNode) rsnCallee;
2222           TempDescriptor tdParam  = vnCallee.getTempDescriptor();
2223           TempDescriptor tdArg    = fc.getArgMatchingParam( fmCallee,
2224                                                             tdParam );
2225           if( tdArg == null ) {
2226             // this means the variable isn't a parameter, its local
2227             // to the callee so we ignore it in call site transfer
2228             // shouldn't this NEVER HAPPEN?
2229             assert false;
2230           }
2231           rsnCallers.add( this.getVariableNodeFromTemp( tdArg ) );
2232           oocEdges = true;
2233
2234         } else {
2235           // otherwise source is in context, one region
2236           HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2237
2238           // translate an in-context node to shadow
2239           AllocSite asSrc = hrnSrcCallee.getAllocSite();
2240           allocSites.add( asSrc );
2241           
2242           Integer hrnIDSrcShadow = 
2243             asSrc.getShadowIDfromID( hrnSrcCallee.getID() );
2244
2245           HeapRegionNode hrnSrcCallerShadow =
2246             this.id2hrn.get( hrnIDSrcShadow );
2247           
2248           if( hrnSrcCallerShadow == null ) {
2249             hrnSrcCallerShadow =
2250               createNewHeapRegionNode( hrnIDSrcShadow,                // id or null to generate a new one 
2251                                        hrnSrcCallee.isSingleObject(), // single object?          
2252                                        hrnSrcCallee.isNewSummary(),   // summary?        
2253                                        hrnSrcCallee.isFlagged(),      // flagged?
2254                                        false,                         // out-of-context?
2255                                        hrnSrcCallee.getType(),        // type                            
2256                                        hrnSrcCallee.getAllocSite(),   // allocation site                         
2257                                        toCallerContext( hrnSrcCallee.getInherent(),
2258                                                         calleeStatesSatisfied ),    // inherent reach
2259                                        toCallerContext( hrnSrcCallee.getAlpha(),
2260                                                         calleeStatesSatisfied ),       // current reach                 
2261                                        predsEmpty,                    // predicates
2262                                        hrnSrcCallee.getDescription()  // description
2263                                        );                                        
2264           }
2265           
2266           rsnCallers.add( hrnSrcCallerShadow );
2267         }
2268
2269       } else {
2270         // otherwise we have a set of out-of-context srcs
2271         // that should NOT be translated to shadow nodes
2272         assert !oocCallers.isEmpty();
2273         rsnCallers.addAll( oocCallers );
2274         oocEdges = true;
2275       }
2276
2277       // now make all caller edges we've identified from
2278       // this callee edge with a satisfied predicate
2279       assert !rsnCallers.isEmpty();
2280       Iterator<RefSrcNode> rsnItr = rsnCallers.iterator();
2281       while( rsnItr.hasNext() ) {
2282         RefSrcNode rsnCaller = rsnItr.next();
2283         
2284         // TODO: beta rewrites
2285         RefEdge reCaller = new RefEdge( rsnCaller,
2286                                         hrnDstCaller,
2287                                         reCallee.getType(),
2288                                         reCallee.getField(),
2289                                         toCallerContext( reCallee.getBeta(),
2290                                                          calleeStatesSatisfied ),
2291                                         preds
2292                                         );
2293
2294         ChangeSet cs = ChangeSet.factory();
2295         Iterator<ReachState> rsItr = reCaller.getBeta().iterator();
2296         while( rsItr.hasNext() ) {
2297           ReachState   state = rsItr.next();
2298           ExistPredSet preds2 = state.getPreds();
2299           assert preds2.preds.size() == 1;
2300
2301           ExistPred pred = preds2.preds.iterator().next();
2302           ReachState old = pred.ne_state;
2303           assert old != null;
2304
2305           cs = Canonical.union( cs,
2306                                 ChangeTuple.factory( old,
2307                                                      state
2308                                                      )
2309                                 );
2310         }
2311         
2312         // look to see if an edge with same field exists
2313         // and merge with it, otherwise just add the edge
2314         RefEdge edgeExisting = rsnCaller.getReferenceTo( hrnDstCaller,
2315                                                          reCallee.getType(),
2316                                                          reCallee.getField()
2317                                                          );     
2318         if( edgeExisting != null ) {
2319           edgeExisting.setBeta(
2320                                Canonical.union( edgeExisting.getBeta(),
2321                                                 reCaller.getBeta()
2322                                                 )
2323                                );
2324           edgeExisting.setPreds(
2325                                 Canonical.join( edgeExisting.getPreds(),
2326                                                 reCaller.getPreds()
2327                                                 )
2328                                 );
2329
2330           // for reach propagation
2331           edgePlannedChanges.put( 
2332                                  edgeExisting, 
2333                                  Canonical.union( edgePlannedChanges.get( edgeExisting ),
2334                                                   cs
2335                                                   ) 
2336                                   );
2337           
2338         } else {                          
2339           addRefEdge( rsnCaller, hrnDstCaller, reCaller );      
2340
2341           // for reach propagation
2342           edgesForPropagation.add( reCaller );
2343           assert !edgePlannedChanges.containsKey( reCaller );
2344           edgePlannedChanges.put( reCaller, cs );
2345         }
2346       }
2347     }
2348
2349
2350
2351
2352
2353     if( writeDebugDOTs ) {
2354       try {
2355         writeGraph( "caller35BeforeAssignReturnValue", 
2356                     true, false, false, false, true, true );
2357       } catch( IOException e ) {}
2358     }
2359
2360
2361
2362     // TODO: WAIT! THIS SHOULD BE MERGED INTO OTHER PARTS, BECAUSE
2363     // AS IT IS WE'RE NOT VERIFYING PREDICATES OF RETURN VALUE
2364     // EDGES, JUST BRINGING THEM ALL!  It'll work for now, over approximation
2365     
2366     // 3.d) handle return value assignment if needed
2367     TempDescriptor returnTemp = fc.getReturnTemp();
2368     if( returnTemp != null && !returnTemp.getType().isImmutable() ) {
2369
2370       VariableNode vnLhsCaller = getVariableNodeFromTemp( returnTemp );
2371       clearRefEdgesFrom( vnLhsCaller, null, null, true );
2372
2373       VariableNode vnReturnCallee = rgCallee.getVariableNodeFromTemp( tdReturn );
2374       Iterator<RefEdge> reCalleeItr = vnReturnCallee.iteratorToReferencees();
2375       while( reCalleeItr.hasNext() ) {
2376         RefEdge        reCallee     = reCalleeItr.next();
2377         HeapRegionNode hrnDstCallee = reCallee.getDst();
2378
2379         // some edge types are not possible return values when we can
2380         // see what type variable we are assigning it to
2381         if( !isSuperiorType( returnTemp.getType(), reCallee.getType() ) ) {
2382           System.out.println( "*** NOT EXPECTING TO SEE THIS: Throwing out "+
2383                               reCallee+" for return temp "+returnTemp );
2384           // prune
2385           continue;
2386         }       
2387
2388         AllocSite asDst = hrnDstCallee.getAllocSite();
2389         allocSites.add( asDst );
2390
2391         Integer hrnIDDstShadow = asDst.getShadowIDfromID( hrnDstCallee.getID() );
2392
2393         HeapRegionNode hrnDstCaller = id2hrn.get( hrnIDDstShadow );
2394         if( hrnDstCaller == null ) {
2395           hrnDstCaller =
2396             createNewHeapRegionNode( hrnIDDstShadow,                // id or null to generate a new one 
2397                                      hrnDstCallee.isSingleObject(), // single object?            
2398                                      hrnDstCallee.isNewSummary(),   // summary?  
2399                                      hrnDstCallee.isFlagged(),      // flagged?
2400                                      false,                         // out-of-context?
2401                                      hrnDstCallee.getType(),        // type                              
2402                                      hrnDstCallee.getAllocSite(),   // allocation site                   
2403                                      toCallerContext( hrnDstCallee.getInherent(),
2404                                                       calleeStatesSatisfied  ),    // inherent reach
2405                                      toCallerContext( hrnDstCallee.getAlpha(),
2406                                                       calleeStatesSatisfied  ),    // current reach                 
2407                                      predsTrue,                     // predicates
2408                                      hrnDstCallee.getDescription()  // description
2409                                      );                                        
2410         } else {
2411           assert hrnDstCaller.isWiped();
2412         }
2413
2414         TypeDescriptor tdNewEdge =
2415           mostSpecificType( reCallee.getType(),
2416                             hrnDstCallee.getType(),
2417                             hrnDstCaller.getType()
2418                             );        
2419
2420         RefEdge reCaller = new RefEdge( vnLhsCaller,
2421                                         hrnDstCaller,
2422                                         tdNewEdge,
2423                                         null,
2424                                         toCallerContext( reCallee.getBeta(),
2425                                                          calleeStatesSatisfied ),
2426                                         predsTrue
2427                                         );
2428
2429         addRefEdge( vnLhsCaller, hrnDstCaller, reCaller );
2430       }
2431     }
2432
2433
2434
2435     if( writeDebugDOTs ) {
2436       try {
2437         writeGraph( "caller38propagateReach", 
2438                     true, false, false, false, true, true );
2439       } catch( IOException e ) {}
2440     }
2441
2442     // propagate callee reachability changes to the rest
2443     // of the caller graph edges
2444
2445
2446     /*
2447     if( !cts.isEmpty() ) {
2448       Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
2449       while( incidentEdgeItr.hasNext() ) {
2450         RefEdge incidentEdge = incidentEdgeItr.next();
2451         
2452         edgesForPropagation.add( incidentEdge );
2453         
2454         if( edgePlannedChanges.get( incidentEdge ) == null ) {
2455           edgePlannedChanges.put( incidentEdge, cts );
2456         } else {            
2457           edgePlannedChanges.put( 
2458                                  incidentEdge, 
2459                                    Canonical.union( edgePlannedChanges.get( incidentEdge ),
2460                                                     cts
2461                                                     ) 
2462                                   );
2463         }
2464       }
2465     }
2466     */
2467   
2468     HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
2469   
2470     propagateTokensOverEdges( edgesForPropagation, // source edges
2471                               edgePlannedChanges,  // map src edge to change set
2472                               edgesUpdated );      // list of updated edges
2473     
2474     // commit beta' (beta<-betaNew)
2475     Iterator<RefEdge> edgeItr = edgesUpdated.iterator();
2476     while( edgeItr.hasNext() ) {
2477       edgeItr.next().applyBetaNew();
2478     }
2479
2480
2481
2482
2483
2484
2485     if( writeDebugDOTs ) {
2486       try {
2487         writeGraph( "caller40BeforeShadowMerge", 
2488                     true, false, false, false, true, true );
2489       } catch( IOException e ) {}
2490     }
2491     
2492
2493     // 4) merge shadow nodes so alloc sites are back to k
2494     Iterator<AllocSite> asItr = rgCallee.allocSites.iterator();
2495     while( asItr.hasNext() ) {
2496       // for each allocation site do the following to merge
2497       // shadow nodes (newest from callee) with any existing
2498       // look for the newest normal and newest shadow "slot"
2499       // not being used, transfer normal to shadow.  Keep
2500       // doing this until there are no more normal nodes, or
2501       // no empty shadow slots: then merge all remaining normal
2502       // nodes into the shadow summary.  Finally, convert all
2503       // shadow to their normal versions.
2504       AllocSite as = asItr.next();
2505       int ageNorm = 0;
2506       int ageShad = 0;
2507       while( ageNorm < allocationDepth &&
2508              ageShad < allocationDepth ) {
2509
2510         // first, are there any normal nodes left?
2511         Integer        idNorm  = as.getIthOldest( ageNorm );
2512         HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2513         if( hrnNorm == null ) {
2514           // no, this age of normal node not in the caller graph
2515           ageNorm++;
2516           continue;
2517         }
2518
2519         // yes, a normal node exists, is there an empty shadow
2520         // "slot" to transfer it onto?
2521         HeapRegionNode hrnShad = getIthNode( as, ageShad, true );        
2522         if( !hrnShad.isWiped() ) {
2523           // no, this age of shadow node is not empty
2524           ageShad++;
2525           continue;
2526         }
2527  
2528         // yes, this shadow node is empty
2529         transferOnto( hrnNorm, hrnShad );
2530         ageNorm++;
2531         ageShad++;
2532       }
2533
2534       // now, while there are still normal nodes but no shadow
2535       // slots, merge normal nodes into the shadow summary
2536       while( ageNorm < allocationDepth ) {
2537
2538         // first, are there any normal nodes left?
2539         Integer        idNorm  = as.getIthOldest( ageNorm );
2540         HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2541         if( hrnNorm == null ) {
2542           // no, this age of normal node not in the caller graph
2543           ageNorm++;
2544           continue;
2545         }
2546
2547         // yes, a normal node exists, so get the shadow summary
2548         HeapRegionNode summShad = getSummaryNode( as, true );
2549         mergeIntoSummary( hrnNorm, summShad );
2550         ageNorm++;
2551       }
2552
2553       // if there is a normal summary, merge it into shadow summary
2554       Integer        idNorm   = as.getSummary();
2555       HeapRegionNode summNorm = id2hrn.get( idNorm );
2556       if( summNorm != null ) {
2557         HeapRegionNode summShad = getSummaryNode( as, true );
2558         mergeIntoSummary( summNorm, summShad );
2559       }
2560       
2561       // finally, flip all existing shadow nodes onto the normal
2562       for( int i = 0; i < allocationDepth; ++i ) {
2563         Integer        idShad  = as.getIthOldestShadow( i );
2564         HeapRegionNode hrnShad = id2hrn.get( idShad );
2565         if( hrnShad != null ) {
2566           // flip it
2567           HeapRegionNode hrnNorm = getIthNode( as, i, false );
2568           assert hrnNorm.isWiped();
2569           transferOnto( hrnShad, hrnNorm );
2570         }
2571       }
2572       
2573       Integer        idShad   = as.getSummaryShadow();
2574       HeapRegionNode summShad = id2hrn.get( idShad );
2575       if( summShad != null ) {
2576         summNorm = getSummaryNode( as, false );
2577         transferOnto( summShad, summNorm );
2578       }      
2579     }
2580
2581
2582     if( writeDebugDOTs ) {
2583       try {
2584         writeGraph( "caller45BeforeUnshadow", 
2585                     true, false, false, false, true, true );
2586       } catch( IOException e ) {}
2587     }
2588     
2589     
2590     Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
2591     while( itrAllHRNodes.hasNext() ) {
2592       Map.Entry      me  = (Map.Entry)      itrAllHRNodes.next();
2593       HeapRegionNode hrn = (HeapRegionNode) me.getValue();
2594       
2595       hrn.setAlpha( unshadow( hrn.getAlpha() ) );
2596       
2597       Iterator<RefEdge> itrEdges = hrn.iteratorToReferencers();
2598       while( itrEdges.hasNext() ) {
2599         RefEdge re = itrEdges.next();
2600         re.setBeta( unshadow( re.getBeta() ) );
2601       }
2602     }
2603     
2604
2605
2606     if( writeDebugDOTs ) {
2607       try {
2608         writeGraph( "caller50BeforeGlobalSweep", 
2609                     true, false, false, false, true, true );
2610       } catch( IOException e ) {}
2611     }
2612
2613
2614     // 5.
2615     if( !DISABLE_GLOBAL_SWEEP ) {
2616       globalSweep();
2617     }
2618     
2619
2620
2621     if( writeDebugDOTs ) {
2622       try {
2623         writeGraph( "caller90AfterTransfer", 
2624                     true, false, false, false, true, true );
2625       } catch( IOException e ) {}
2626     }
2627   } 
2628
2629   
2630
2631   ////////////////////////////////////////////////////
2632   //
2633   //  Abstract garbage collection simply removes
2634   //  heap region nodes that are not mechanically
2635   //  reachable from a root set.  This step is
2636   //  essential for testing node and edge existence
2637   //  predicates efficiently
2638   //
2639   ////////////////////////////////////////////////////
2640   public void abstractGarbageCollect( Set<TempDescriptor> liveSet ) {
2641
2642     // calculate a root set, will be different for Java
2643     // version of analysis versus Bamboo version
2644     Set<RefSrcNode> toVisit = new HashSet<RefSrcNode>();
2645
2646     // visit every variable in graph while building root
2647     // set, and do iterating on a copy, so we can remove
2648     // dead variables while we're at this
2649     Iterator makeCopyItr = td2vn.entrySet().iterator();
2650     Set      entrysCopy  = new HashSet();
2651     while( makeCopyItr.hasNext() ) {
2652       entrysCopy.add( makeCopyItr.next() );
2653     }
2654     
2655     Iterator eItr = entrysCopy.iterator();
2656     while( eItr.hasNext() ) {
2657       Map.Entry      me = (Map.Entry)      eItr.next();
2658       TempDescriptor td = (TempDescriptor) me.getKey();
2659       VariableNode   vn = (VariableNode)   me.getValue();
2660
2661       if( liveSet.contains( td ) ) {
2662         toVisit.add( vn );
2663
2664       } else {
2665         // dead var, remove completely from graph
2666         td2vn.remove( td );
2667         clearRefEdgesFrom( vn, null, null, true );
2668       }
2669     }
2670
2671     // everything visited in a traversal is
2672     // considered abstractly live
2673     Set<RefSrcNode> visited = new HashSet<RefSrcNode>();
2674     
2675     while( !toVisit.isEmpty() ) {
2676       RefSrcNode rsn = toVisit.iterator().next();
2677       toVisit.remove( rsn );
2678       visited.add( rsn );
2679       
2680       Iterator<RefEdge> hrnItr = rsn.iteratorToReferencees();
2681       while( hrnItr.hasNext() ) {
2682         RefEdge        edge = hrnItr.next();
2683         HeapRegionNode hrn  = edge.getDst();
2684         
2685         if( !visited.contains( hrn ) ) {
2686           toVisit.add( hrn );
2687         }
2688       }
2689     }
2690
2691     // get a copy of the set to iterate over because
2692     // we're going to monkey with the graph when we
2693     // identify a garbage node
2694     Set<HeapRegionNode> hrnAllPrior = new HashSet<HeapRegionNode>();
2695     Iterator<HeapRegionNode> hrnItr = id2hrn.values().iterator();
2696     while( hrnItr.hasNext() ) {
2697       hrnAllPrior.add( hrnItr.next() );
2698     }
2699
2700     Iterator<HeapRegionNode> hrnAllItr = hrnAllPrior.iterator();
2701     while( hrnAllItr.hasNext() ) {
2702       HeapRegionNode hrn = hrnAllItr.next();
2703
2704       if( !visited.contains( hrn ) ) {
2705
2706         // heap region nodes are compared across ReachGraph
2707         // objects by their integer ID, so when discarding
2708         // garbage nodes we must also discard entries in
2709         // the ID -> heap region hashtable.
2710         id2hrn.remove( hrn.getID() );
2711
2712         // RefEdge objects are two-way linked between
2713         // nodes, so when a node is identified as garbage,
2714         // actively clear references to and from it so
2715         // live nodes won't have dangling RefEdge's
2716         wipeOut( hrn, true );
2717
2718         // if we just removed the last node from an allocation
2719         // site, it should be taken out of the ReachGraph's list
2720         AllocSite as = hrn.getAllocSite();
2721         if( !hasNodesOf( as ) ) {
2722           allocSites.remove( as );
2723         }
2724       }
2725     }
2726   }
2727
2728   protected boolean hasNodesOf( AllocSite as ) {
2729     if( id2hrn.containsKey( as.getSummary() ) ) {
2730       return true;
2731     }
2732
2733     for( int i = 0; i < allocationDepth; ++i ) {
2734       if( id2hrn.containsKey( as.getIthOldest( i ) ) ) {
2735         return true;
2736       }      
2737     }
2738     return false;
2739   }
2740
2741
2742   ////////////////////////////////////////////////////
2743   //
2744   //  This global sweep is an optional step to prune
2745   //  reachability sets that are not internally
2746   //  consistent with the global graph.  It should be
2747   //  invoked after strong updates or method calls.
2748   //
2749   ////////////////////////////////////////////////////
2750   public void globalSweep() {
2751
2752     // boldB is part of the phase 1 sweep
2753     // it has an in-context table and an out-of-context table
2754     Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBic =
2755       new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();    
2756
2757     Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBooc =
2758       new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();    
2759
2760     // visit every heap region to initialize alphaNew and betaNew,
2761     // and make a map of every hrnID to the source nodes it should
2762     // propagate forward from.  In-context flagged hrnID's propagate
2763     // from only the in-context node they name, but out-of-context
2764     // ID's may propagate from several out-of-context nodes
2765     Hashtable< Integer, Set<HeapRegionNode> > icID2srcs = 
2766       new Hashtable< Integer, Set<HeapRegionNode> >();
2767
2768     Hashtable< Integer, Set<HeapRegionNode> > oocID2srcs =
2769       new Hashtable< Integer, Set<HeapRegionNode> >();
2770
2771
2772     Iterator itrHrns = id2hrn.entrySet().iterator();
2773     while( itrHrns.hasNext() ) {
2774       Map.Entry      me    = (Map.Entry)      itrHrns.next();
2775       Integer        hrnID = (Integer)        me.getKey();
2776       HeapRegionNode hrn   = (HeapRegionNode) me.getValue();
2777     
2778       // assert that this node and incoming edges have clean alphaNew
2779       // and betaNew sets, respectively
2780       assert rsetEmpty.equals( hrn.getAlphaNew() );
2781
2782       Iterator<RefEdge> itrRers = hrn.iteratorToReferencers();
2783       while( itrRers.hasNext() ) {
2784         RefEdge edge = itrRers.next();
2785         assert rsetEmpty.equals( edge.getBetaNew() );
2786       }      
2787
2788       // calculate boldB for this flagged node, or out-of-context node
2789       if( hrn.isFlagged() ) {
2790         assert !hrn.isOutOfContext();
2791         assert !icID2srcs.containsKey( hrn.getID() );
2792         Set<HeapRegionNode> srcs = new HashSet<HeapRegionNode>();
2793         srcs.add( hrn );
2794         icID2srcs.put( hrn.getID(), srcs );
2795       }
2796
2797       if( hrn.isOutOfContext() ) {
2798         assert !hrn.isFlagged();
2799
2800         Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
2801         while( stateItr.hasNext() ) {
2802           ReachState state = stateItr.next();
2803
2804           Iterator<ReachTuple> rtItr = state.iterator();
2805           while( rtItr.hasNext() ) {
2806             ReachTuple rt = rtItr.next();
2807             assert rt.isOutOfContext();
2808
2809             Set<HeapRegionNode> srcs = oocID2srcs.get( rt.getHrnID() );
2810             if( srcs == null ) {
2811               srcs = new HashSet<HeapRegionNode>();
2812             }
2813             srcs.add( hrn );
2814             oocID2srcs.put( rt.getHrnID(), srcs );
2815           }
2816         }
2817       }
2818     }
2819
2820     // calculate boldB for all hrnIDs identified by the above
2821     // node traversal, propagating from every source
2822     while( !icID2srcs.isEmpty() || !oocID2srcs.isEmpty() ) {
2823
2824       Integer             hrnID;
2825       Set<HeapRegionNode> srcs;
2826       boolean             inContext;
2827
2828       if( !icID2srcs.isEmpty() ) {
2829         Map.Entry me = (Map.Entry) icID2srcs.entrySet().iterator().next();
2830         hrnID = (Integer)             me.getKey();
2831         srcs  = (Set<HeapRegionNode>) me.getValue();
2832         inContext = true;
2833         icID2srcs.remove( hrnID );
2834
2835       } else {
2836         assert !oocID2srcs.isEmpty();
2837
2838         Map.Entry me = (Map.Entry) oocID2srcs.entrySet().iterator().next();
2839         hrnID = (Integer)             me.getKey();
2840         srcs  = (Set<HeapRegionNode>) me.getValue();
2841         inContext = false;
2842         oocID2srcs.remove( hrnID );
2843       }
2844
2845
2846       Hashtable<RefEdge, ReachSet> boldB_f =
2847         new Hashtable<RefEdge, ReachSet>();
2848         
2849       Set<RefEdge> workSetEdges = new HashSet<RefEdge>();
2850
2851       Iterator<HeapRegionNode> hrnItr = srcs.iterator();
2852       while( hrnItr.hasNext() ) {
2853         HeapRegionNode hrn = hrnItr.next();
2854
2855         assert workSetEdges.isEmpty();
2856         
2857         // initial boldB_f constraints
2858         Iterator<RefEdge> itrRees = hrn.iteratorToReferencees();
2859         while( itrRees.hasNext() ) {
2860           RefEdge edge = itrRees.next();
2861           
2862           assert !boldB_f.containsKey( edge );
2863           boldB_f.put( edge, edge.getBeta() );
2864           
2865           assert !workSetEdges.contains( edge );
2866           workSetEdges.add( edge );
2867         }       
2868       
2869         // enforce the boldB_f constraint at edges until we reach a fixed point
2870         while( !workSetEdges.isEmpty() ) {
2871           RefEdge edge = workSetEdges.iterator().next();
2872           workSetEdges.remove( edge );   
2873           
2874           Iterator<RefEdge> itrPrime = edge.getDst().iteratorToReferencees();
2875           while( itrPrime.hasNext() ) {
2876             RefEdge edgePrime = itrPrime.next();            
2877           
2878             ReachSet prevResult   = boldB_f.get( edgePrime );
2879             ReachSet intersection = Canonical.intersection( boldB_f.get( edge ),
2880                                                             edgePrime.getBeta()
2881                                                             );
2882           
2883             if( prevResult == null || 
2884                 Canonical.union( prevResult,
2885                                  intersection ).size() > prevResult.size() ) {
2886             
2887               if( prevResult == null ) {
2888                 boldB_f.put( edgePrime, 
2889                              Canonical.union( edgePrime.getBeta(),
2890                                               intersection 
2891                                               )
2892                              );
2893               } else {
2894                 boldB_f.put( edgePrime, 
2895                              Canonical.union( prevResult,
2896                                               intersection 
2897                                               )
2898                              );
2899               }
2900               workSetEdges.add( edgePrime );    
2901             }
2902           }
2903         }
2904       }
2905       
2906       if( inContext ) {
2907         boldBic.put( hrnID, boldB_f );
2908       } else {
2909         boldBooc.put( hrnID, boldB_f );
2910       }
2911     }
2912
2913
2914     // use boldB to prune hrnIDs from alpha states that are impossible
2915     // and propagate the differences backwards across edges
2916     HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
2917
2918     Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
2919       new Hashtable<RefEdge, ChangeSet>();
2920
2921
2922     itrHrns = id2hrn.entrySet().iterator();
2923     while( itrHrns.hasNext() ) {
2924       Map.Entry      me    = (Map.Entry)      itrHrns.next();
2925       Integer        hrnID = (Integer)        me.getKey();
2926       HeapRegionNode hrn   = (HeapRegionNode) me.getValue();
2927       
2928       // out-of-context nodes don't participate in the 
2929       // global sweep, they serve as sources for the pass
2930       // performed above
2931       if( hrn.isOutOfContext() ) {
2932         continue;
2933       }
2934
2935       // the inherent states of a region are the exception
2936       // to removal as the global sweep prunes
2937       ReachTuple rtException = ReachTuple.factory( hrnID,
2938                                                    !hrn.isSingleObject(),    
2939                                                    ReachTuple.ARITY_ONE,
2940                                                    false // out-of-context
2941                                                    );
2942
2943       ChangeSet cts = ChangeSet.factory();
2944
2945       // mark hrnIDs for removal
2946       Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
2947       while( stateItr.hasNext() ) {
2948         ReachState stateOld = stateItr.next();
2949
2950         ReachState markedHrnIDs = ReachState.factory();
2951
2952         Iterator<ReachTuple> rtItr = stateOld.iterator();
2953         while( rtItr.hasNext() ) {
2954           ReachTuple rtOld = rtItr.next();
2955
2956           // never remove the inherent hrnID from a flagged region
2957           // because it is trivially satisfied
2958           if( hrn.isFlagged() ) {       
2959             if( rtOld == rtException ) {
2960               continue;
2961             }
2962           }
2963
2964           // does boldB allow this hrnID?
2965           boolean foundState = false;
2966           Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
2967           while( incidentEdgeItr.hasNext() ) {
2968             RefEdge incidentEdge = incidentEdgeItr.next();
2969
2970             Hashtable<RefEdge, ReachSet> B; 
2971             if( rtOld.isOutOfContext() ) {
2972               B = boldBooc.get( rtOld.getHrnID() ); 
2973             } else {
2974               assert id2hrn.containsKey( rtOld.getHrnID() );
2975               B = boldBic.get( rtOld.getHrnID() ); 
2976             }
2977
2978             if( B != null ) {            
2979               ReachSet boldB_rtOld_incident = B.get( incidentEdge );
2980               if( boldB_rtOld_incident != null &&
2981                   boldB_rtOld_incident.contains( stateOld ) ) {
2982                 foundState = true;
2983               }
2984             }
2985           }
2986           
2987           if( !foundState ) {
2988             markedHrnIDs = Canonical.add( markedHrnIDs, rtOld );          
2989           }
2990         }
2991
2992         // if there is nothing marked, just move on
2993         if( markedHrnIDs.isEmpty() ) {
2994           hrn.setAlphaNew( Canonical.union( hrn.getAlphaNew(),
2995                                             stateOld
2996                                             )
2997                            );
2998           continue;
2999         }
3000
3001         // remove all marked hrnIDs and establish a change set that should
3002         // propagate backwards over edges from this node
3003         ReachState statePruned = ReachState.factory();
3004         rtItr = stateOld.iterator();
3005         while( rtItr.hasNext() ) {
3006           ReachTuple rtOld = rtItr.next();
3007
3008           if( !markedHrnIDs.containsTuple( rtOld ) ) {
3009             statePruned = Canonical.union( statePruned, rtOld );
3010           }
3011         }
3012         assert !stateOld.equals( statePruned );
3013
3014         hrn.setAlphaNew( Canonical.union( hrn.getAlphaNew(),
3015                                           statePruned
3016                                           )
3017                          );
3018         ChangeTuple ct = ChangeTuple.factory( stateOld,
3019                                               statePruned
3020                                               );
3021         cts = Canonical.union( cts, ct );
3022       }
3023
3024       // throw change tuple set on all incident edges
3025       if( !cts.isEmpty() ) {
3026         Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3027         while( incidentEdgeItr.hasNext() ) {
3028           RefEdge incidentEdge = incidentEdgeItr.next();
3029                   
3030           edgesForPropagation.add( incidentEdge );
3031
3032           if( edgePlannedChanges.get( incidentEdge ) == null ) {
3033             edgePlannedChanges.put( incidentEdge, cts );
3034           } else {          
3035             edgePlannedChanges.put( 
3036                                    incidentEdge, 
3037                                    Canonical.union( edgePlannedChanges.get( incidentEdge ),
3038                                                     cts
3039                                                     ) 
3040                                     );
3041           }
3042         }
3043       }
3044     }
3045     
3046     HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
3047
3048     propagateTokensOverEdges( edgesForPropagation,
3049                               edgePlannedChanges,
3050                               edgesUpdated );
3051
3052     // at the end of the 1st phase reference edges have
3053     // beta, betaNew that correspond to beta and betaR
3054     //
3055     // commit beta<-betaNew, so beta=betaR and betaNew
3056     // will represent the beta' calculation in 2nd phase
3057     //
3058     // commit alpha<-alphaNew because it won't change
3059     HashSet<RefEdge> res = new HashSet<RefEdge>();
3060
3061     Iterator<HeapRegionNode> nodeItr = id2hrn.values().iterator();
3062     while( nodeItr.hasNext() ) {
3063       HeapRegionNode hrn = nodeItr.next();
3064       hrn.applyAlphaNew();
3065       Iterator<RefEdge> itrRes = hrn.iteratorToReferencers();
3066       while( itrRes.hasNext() ) {
3067         res.add( itrRes.next() );
3068       }
3069     }
3070
3071
3072     // 2nd phase    
3073     Iterator<RefEdge> edgeItr = res.iterator();
3074     while( edgeItr.hasNext() ) {
3075       RefEdge        edge = edgeItr.next();
3076       HeapRegionNode hrn  = edge.getDst();
3077
3078       // commit results of last phase
3079       if( edgesUpdated.contains( edge ) ) {
3080         edge.applyBetaNew();
3081       }
3082
3083       // compute intial condition of 2nd phase
3084       edge.setBetaNew( Canonical.intersection( edge.getBeta(),
3085                                                hrn.getAlpha() 
3086                                                )
3087                        );
3088     }
3089         
3090     // every edge in the graph is the initial workset
3091     Set<RefEdge> edgeWorkSet = (Set) res.clone();
3092     while( !edgeWorkSet.isEmpty() ) {
3093       RefEdge edgePrime = edgeWorkSet.iterator().next();
3094       edgeWorkSet.remove( edgePrime );
3095
3096       RefSrcNode rsn = edgePrime.getSrc();
3097       if( !(rsn instanceof HeapRegionNode) ) {
3098         continue;
3099       }
3100       HeapRegionNode hrn = (HeapRegionNode) rsn;
3101
3102       Iterator<RefEdge> itrEdge = hrn.iteratorToReferencers();
3103       while( itrEdge.hasNext() ) {
3104         RefEdge edge = itrEdge.next();      
3105
3106         ReachSet prevResult = edge.getBetaNew();
3107         assert prevResult != null;
3108
3109         ReachSet intersection = 
3110           Canonical.intersection( edge.getBeta(),
3111                                   edgePrime.getBetaNew() 
3112                                   );
3113                     
3114         if( Canonical.union( prevResult,
3115                              intersection
3116                              ).size() > prevResult.size() ) {
3117           edge.setBetaNew( 
3118                           Canonical.union( prevResult,
3119                                            intersection 
3120                                            )
3121                            );
3122           edgeWorkSet.add( edge );
3123         }       
3124       }      
3125     }
3126
3127     // commit beta' (beta<-betaNew)
3128     edgeItr = res.iterator();
3129     while( edgeItr.hasNext() ) {
3130       edgeItr.next().applyBetaNew();
3131     } 
3132   }  
3133
3134
3135
3136   ////////////////////////////////////////////////////
3137   // high-level merge operations
3138   ////////////////////////////////////////////////////
3139   public void merge_sameMethodContext( ReachGraph rg ) {
3140     // when merging two graphs that abstract the heap
3141     // of the same method context, we just call the
3142     // basic merge operation
3143     merge( rg );
3144   }
3145
3146   public void merge_diffMethodContext( ReachGraph rg ) {
3147     // when merging graphs for abstract heaps in
3148     // different method contexts we should:
3149     // 1) age the allocation sites?
3150     merge( rg );
3151   }
3152
3153   ////////////////////////////////////////////////////
3154   // in merge() and equals() methods the suffix A
3155   // represents the passed in graph and the suffix
3156   // B refers to the graph in this object
3157   // Merging means to take the incoming graph A and
3158   // merge it into B, so after the operation graph B
3159   // is the final result.
3160   ////////////////////////////////////////////////////
3161   protected void merge( ReachGraph rg ) {
3162
3163     if( rg == null ) {
3164       return;
3165     }
3166
3167     mergeNodes     ( rg );
3168     mergeRefEdges  ( rg );
3169     mergeAllocSites( rg );
3170   }
3171   
3172   protected void mergeNodes( ReachGraph rg ) {
3173
3174     // start with heap region nodes
3175     Set      sA = rg.id2hrn.entrySet();
3176     Iterator iA = sA.iterator();
3177     while( iA.hasNext() ) {
3178       Map.Entry      meA  = (Map.Entry)      iA.next();
3179       Integer        idA  = (Integer)        meA.getKey();
3180       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3181
3182       // if this graph doesn't have a node the
3183       // incoming graph has, allocate it
3184       if( !id2hrn.containsKey( idA ) ) {
3185         HeapRegionNode hrnB = hrnA.copy();
3186         id2hrn.put( idA, hrnB );
3187
3188       } else {
3189         // otherwise this is a node present in both graphs
3190         // so make the new reachability set a union of the
3191         // nodes' reachability sets
3192         HeapRegionNode hrnB = id2hrn.get( idA );
3193         hrnB.setAlpha( Canonical.union( hrnB.getAlpha(),
3194                                         hrnA.getAlpha() 
3195                                         )
3196                        );
3197
3198         // if hrnB is already dirty or hrnA is dirty,
3199         // the hrnB should end up dirty: TODO
3200         /*
3201         if( !hrnA.isClean() ) {
3202           hrnB.setIsClean( false );
3203         }
3204         */
3205       }
3206     }
3207
3208     // now add any variable nodes that are in graph B but
3209     // not in A
3210     sA = rg.td2vn.entrySet();
3211     iA = sA.iterator();
3212     while( iA.hasNext() ) {
3213       Map.Entry      meA = (Map.Entry)      iA.next();
3214       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3215       VariableNode   lnA = (VariableNode)   meA.getValue();
3216
3217       // if the variable doesn't exist in B, allocate and add it
3218       VariableNode lnB = getVariableNodeFromTemp( tdA );
3219     }
3220   }
3221
3222   protected void mergeRefEdges( ReachGraph rg ) {
3223
3224     // between heap regions
3225     Set      sA = rg.id2hrn.entrySet();
3226     Iterator iA = sA.iterator();
3227     while( iA.hasNext() ) {
3228       Map.Entry      meA  = (Map.Entry)      iA.next();
3229       Integer        idA  = (Integer)        meA.getKey();
3230       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3231
3232       Iterator<RefEdge> heapRegionsItrA = hrnA.iteratorToReferencees();
3233       while( heapRegionsItrA.hasNext() ) {
3234         RefEdge        edgeA     = heapRegionsItrA.next();
3235         HeapRegionNode hrnChildA = edgeA.getDst();
3236         Integer        idChildA  = hrnChildA.getID();
3237
3238         // at this point we know an edge in graph A exists
3239         // idA -> idChildA, does this exist in B?
3240         assert id2hrn.containsKey( idA );
3241         HeapRegionNode hrnB        = id2hrn.get( idA );
3242         RefEdge        edgeToMerge = null;
3243
3244         Iterator<RefEdge> heapRegionsItrB = hrnB.iteratorToReferencees();
3245         while( heapRegionsItrB.hasNext() &&
3246                edgeToMerge == null          ) {
3247
3248           RefEdge        edgeB     = heapRegionsItrB.next();
3249           HeapRegionNode hrnChildB = edgeB.getDst();
3250           Integer        idChildB  = hrnChildB.getID();
3251
3252           // don't use the RefEdge.equals() here because
3253           // we're talking about existence between graphs,
3254           // not intragraph equal
3255           if( idChildB.equals( idChildA ) &&
3256               edgeB.typeAndFieldEquals( edgeA ) ) {
3257
3258             edgeToMerge = edgeB;
3259           }
3260         }
3261
3262         // if the edge from A was not found in B,
3263         // add it to B.
3264         if( edgeToMerge == null ) {
3265           assert id2hrn.containsKey( idChildA );
3266           HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3267           edgeToMerge = edgeA.copy();
3268           edgeToMerge.setSrc( hrnB );
3269           edgeToMerge.setDst( hrnChildB );
3270           addRefEdge( hrnB, hrnChildB, edgeToMerge );
3271         }
3272         // otherwise, the edge already existed in both graphs
3273         // so merge their reachability sets
3274         else {
3275           // just replace this beta set with the union
3276           assert edgeToMerge != null;
3277           edgeToMerge.setBeta(
3278                               Canonical.union( edgeToMerge.getBeta(),
3279                                                edgeA.getBeta() 
3280                                                )
3281                               );
3282           // TODO: what?
3283           /*
3284           if( !edgeA.isClean() ) {
3285             edgeToMerge.setIsClean( false );
3286           }
3287           */
3288         }
3289       }
3290     }
3291
3292     // and then again from variable nodes
3293     sA = rg.td2vn.entrySet();
3294     iA = sA.iterator();
3295     while( iA.hasNext() ) {
3296       Map.Entry      meA = (Map.Entry)      iA.next();
3297       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3298       VariableNode   vnA = (VariableNode)   meA.getValue();
3299
3300       Iterator<RefEdge> heapRegionsItrA = vnA.iteratorToReferencees();
3301       while( heapRegionsItrA.hasNext() ) {
3302         RefEdge        edgeA     = heapRegionsItrA.next();
3303         HeapRegionNode hrnChildA = edgeA.getDst();
3304         Integer        idChildA  = hrnChildA.getID();
3305
3306         // at this point we know an edge in graph A exists
3307         // tdA -> idChildA, does this exist in B?
3308         assert td2vn.containsKey( tdA );
3309         VariableNode vnB         = td2vn.get( tdA );
3310         RefEdge      edgeToMerge = null;
3311
3312         Iterator<RefEdge> heapRegionsItrB = vnB.iteratorToReferencees();
3313         while( heapRegionsItrB.hasNext() &&
3314                edgeToMerge == null          ) {
3315
3316           RefEdge        edgeB     = heapRegionsItrB.next();
3317           HeapRegionNode hrnChildB = edgeB.getDst();
3318           Integer        idChildB  = hrnChildB.getID();
3319
3320           // don't use the RefEdge.equals() here because
3321           // we're talking about existence between graphs
3322           if( idChildB.equals( idChildA ) &&
3323               edgeB.typeAndFieldEquals( edgeA ) ) {
3324
3325             edgeToMerge = edgeB;
3326           }
3327         }
3328
3329         // if the edge from A was not found in B,
3330         // add it to B.
3331         if( edgeToMerge == null ) {
3332           assert id2hrn.containsKey( idChildA );
3333           HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3334           edgeToMerge = edgeA.copy();
3335           edgeToMerge.setSrc( vnB );
3336           edgeToMerge.setDst( hrnChildB );
3337           addRefEdge( vnB, hrnChildB, edgeToMerge );
3338         }
3339         // otherwise, the edge already existed in both graphs
3340         // so merge their reachability sets
3341         else {
3342           // just replace this beta set with the union
3343           edgeToMerge.setBeta( Canonical.union( edgeToMerge.getBeta(),
3344                                                 edgeA.getBeta()
3345                                                 )
3346                                );
3347           // TODO: what?
3348           /*
3349           if( !edgeA.isClean() ) {
3350             edgeToMerge.setIsClean( false );
3351           }
3352           */
3353         }
3354       }
3355     }
3356   }
3357
3358   protected void mergeAllocSites( ReachGraph rg ) {
3359     allocSites.addAll( rg.allocSites );
3360   }
3361
3362
3363   // it is necessary in the equals() member functions
3364   // to "check both ways" when comparing the data
3365   // structures of two graphs.  For instance, if all
3366   // edges between heap region nodes in graph A are
3367   // present and equal in graph B it is not sufficient
3368   // to say the graphs are equal.  Consider that there
3369   // may be edges in graph B that are not in graph A.
3370   // the only way to know that all edges in both graphs
3371   // are equally present is to iterate over both data
3372   // structures and compare against the other graph.
3373   public boolean equals( ReachGraph rg ) {
3374
3375     if( rg == null ) {
3376       return false;
3377     }
3378     
3379     if( !areHeapRegionNodesEqual( rg ) ) {
3380       return false;
3381     }
3382
3383     if( !areVariableNodesEqual( rg ) ) {
3384       return false;
3385     }
3386
3387     if( !areRefEdgesEqual( rg ) ) {
3388       return false;
3389     }
3390
3391     // if everything is equal up to this point,
3392     // assert that allocSites is also equal--
3393     // this data is redundant but kept for efficiency
3394     assert allocSites.equals( rg.allocSites );
3395
3396     return true;
3397   }
3398
3399   
3400   protected boolean areHeapRegionNodesEqual( ReachGraph rg ) {
3401
3402     if( !areallHRNinAalsoinBandequal( this, rg ) ) {
3403       return false;
3404     }
3405
3406     if( !areallHRNinAalsoinBandequal( rg, this ) ) {
3407       return false;
3408     }
3409
3410     return true;
3411   }
3412
3413   static protected boolean areallHRNinAalsoinBandequal( ReachGraph rgA,
3414                                                         ReachGraph rgB ) {
3415     Set      sA = rgA.id2hrn.entrySet();
3416     Iterator iA = sA.iterator();
3417     while( iA.hasNext() ) {
3418       Map.Entry      meA  = (Map.Entry)      iA.next();
3419       Integer        idA  = (Integer)        meA.getKey();
3420       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3421
3422       if( !rgB.id2hrn.containsKey( idA ) ) {
3423         return false;
3424       }
3425
3426       HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3427       if( !hrnA.equalsIncludingAlphaAndPreds( hrnB ) ) {
3428         return false;
3429       }
3430     }
3431     
3432     return true;
3433   }
3434   
3435
3436   protected boolean areVariableNodesEqual( ReachGraph rg ) {
3437
3438     if( !areallVNinAalsoinBandequal( this, rg ) ) {
3439       return false;
3440     }
3441
3442     if( !areallVNinAalsoinBandequal( rg, this ) ) {
3443       return false;
3444     }
3445
3446     return true;
3447   }
3448
3449   static protected boolean areallVNinAalsoinBandequal( ReachGraph rgA,
3450                                                        ReachGraph rgB ) {
3451     Set      sA = rgA.td2vn.entrySet();
3452     Iterator iA = sA.iterator();
3453     while( iA.hasNext() ) {
3454       Map.Entry      meA = (Map.Entry)      iA.next();
3455       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3456
3457       if( !rgB.td2vn.containsKey( tdA ) ) {
3458         return false;
3459       }
3460     }
3461
3462     return true;
3463   }
3464
3465
3466   protected boolean areRefEdgesEqual( ReachGraph rg ) {
3467     if( !areallREinAandBequal( this, rg ) ) {
3468       return false;
3469     }
3470
3471     return true;
3472   }
3473
3474   static protected boolean areallREinAandBequal( ReachGraph rgA,
3475                                                  ReachGraph rgB ) {
3476
3477     // check all the heap region->heap region edges
3478     Set      sA = rgA.id2hrn.entrySet();
3479     Iterator iA = sA.iterator();
3480     while( iA.hasNext() ) {
3481       Map.Entry      meA  = (Map.Entry)      iA.next();
3482       Integer        idA  = (Integer)        meA.getKey();
3483       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3484
3485       // we should have already checked that the same
3486       // heap regions exist in both graphs
3487       assert rgB.id2hrn.containsKey( idA );
3488
3489       if( !areallREfromAequaltoB( rgA, hrnA, rgB ) ) {
3490         return false;
3491       }
3492
3493       // then check every edge in B for presence in A, starting
3494       // from the same parent HeapRegionNode
3495       HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3496
3497       if( !areallREfromAequaltoB( rgB, hrnB, rgA ) ) {
3498         return false;
3499       }
3500     }
3501
3502     // then check all the variable->heap region edges
3503     sA = rgA.td2vn.entrySet();
3504     iA = sA.iterator();
3505     while( iA.hasNext() ) {
3506       Map.Entry      meA = (Map.Entry)      iA.next();
3507       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3508       VariableNode   vnA = (VariableNode)   meA.getValue();
3509
3510       // we should have already checked that the same
3511       // label nodes exist in both graphs
3512       assert rgB.td2vn.containsKey( tdA );
3513
3514       if( !areallREfromAequaltoB( rgA, vnA, rgB ) ) {
3515         return false;
3516       }
3517
3518       // then check every edge in B for presence in A, starting
3519       // from the same parent VariableNode
3520       VariableNode vnB = rgB.td2vn.get( tdA );
3521
3522       if( !areallREfromAequaltoB( rgB, vnB, rgA ) ) {
3523         return false;
3524       }
3525     }
3526
3527     return true;
3528   }
3529
3530
3531   static protected boolean areallREfromAequaltoB( ReachGraph rgA,
3532                                                   RefSrcNode rnA,
3533                                                   ReachGraph rgB ) {
3534
3535     Iterator<RefEdge> itrA = rnA.iteratorToReferencees();
3536     while( itrA.hasNext() ) {
3537       RefEdge        edgeA     = itrA.next();
3538       HeapRegionNode hrnChildA = edgeA.getDst();
3539       Integer        idChildA  = hrnChildA.getID();
3540
3541       assert rgB.id2hrn.containsKey( idChildA );
3542
3543       // at this point we know an edge in graph A exists
3544       // rnA -> idChildA, does this exact edge exist in B?
3545       boolean edgeFound = false;
3546
3547       RefSrcNode rnB = null;
3548       if( rnA instanceof HeapRegionNode ) {
3549         HeapRegionNode hrnA = (HeapRegionNode) rnA;
3550         rnB = rgB.id2hrn.get( hrnA.getID() );
3551       } else {
3552         VariableNode vnA = (VariableNode) rnA;
3553         rnB = rgB.td2vn.get( vnA.getTempDescriptor() );
3554       }
3555
3556       Iterator<RefEdge> itrB = rnB.iteratorToReferencees();
3557       while( itrB.hasNext() ) {
3558         RefEdge        edgeB     = itrB.next();
3559         HeapRegionNode hrnChildB = edgeB.getDst();
3560         Integer        idChildB  = hrnChildB.getID();
3561
3562         if( idChildA.equals( idChildB ) &&
3563             edgeA.typeAndFieldEquals( edgeB ) ) {
3564
3565           // there is an edge in the right place with the right field,
3566           // but do they have the same attributes?
3567           if( edgeA.getBeta().equals( edgeB.getBeta() ) &&
3568               edgeA.equalsPreds( edgeB )
3569               ) {
3570             edgeFound = true;
3571           }
3572         }
3573       }
3574       
3575       if( !edgeFound ) {
3576         return false;
3577       }
3578     }
3579
3580     return true;
3581   }
3582
3583
3584
3585   // this analysis no longer has the "match anything"
3586   // type which was represented by null
3587   protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3588                                              TypeDescriptor td2 ) {
3589     assert td1 != null;
3590     assert td2 != null;
3591
3592     if( td1.isNull() ) {
3593       return td2;
3594     }
3595     if( td2.isNull() ) {
3596       return td1;
3597     }
3598     return typeUtil.mostSpecific( td1, td2 );
3599   }
3600   
3601   protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3602                                              TypeDescriptor td2,
3603                                              TypeDescriptor td3 ) {
3604     
3605     return mostSpecificType( td1, 
3606                              mostSpecificType( td2, td3 )
3607                              );
3608   }  
3609   
3610   protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3611                                              TypeDescriptor td2,
3612                                              TypeDescriptor td3,
3613                                              TypeDescriptor td4 ) {
3614     
3615     return mostSpecificType( mostSpecificType( td1, td2 ), 
3616                              mostSpecificType( td3, td4 )
3617                              );
3618   }  
3619
3620   protected boolean isSuperiorType( TypeDescriptor possibleSuper,
3621                                     TypeDescriptor possibleChild ) {
3622     assert possibleSuper != null;
3623     assert possibleChild != null;
3624     
3625     if( possibleSuper.isNull() ||
3626         possibleChild.isNull() ) {
3627       return true;
3628     }
3629
3630     return typeUtil.isSuperorType( possibleSuper, possibleChild );
3631   }
3632
3633
3634   protected boolean hasMatchingField( HeapRegionNode src, 
3635                                       RefEdge        edge ) {
3636
3637     TypeDescriptor tdSrc = src.getType();    
3638     assert tdSrc != null;
3639
3640     if( tdSrc.isArray() ) {
3641       TypeDescriptor td = edge.getType();
3642       assert td != null;
3643
3644       TypeDescriptor tdSrcDeref = tdSrc.dereference();
3645       assert tdSrcDeref != null;
3646
3647       if( !typeUtil.isSuperorType( tdSrcDeref, td ) ) {
3648         return false;
3649       }
3650
3651       return edge.getField().equals( DisjointAnalysis.arrayElementFieldName );
3652     }
3653
3654     // if it's not a class, it doesn't have any fields to match
3655     if( !tdSrc.isClass() ) {
3656       return false;
3657     }
3658
3659     ClassDescriptor cd = tdSrc.getClassDesc();
3660     while( cd != null ) {      
3661       Iterator fieldItr = cd.getFields();
3662
3663       while( fieldItr.hasNext() ) {     
3664         FieldDescriptor fd = (FieldDescriptor) fieldItr.next();
3665
3666         if( fd.getType().equals( edge.getType() ) &&
3667             fd.getSymbol().equals( edge.getField() ) ) {
3668           return true;
3669         }
3670       }
3671       
3672       cd = cd.getSuperDesc();
3673     }
3674     
3675     // otherwise it is a class with fields
3676     // but we didn't find a match
3677     return false;
3678   }
3679
3680   protected boolean hasMatchingType( RefEdge        edge, 
3681                                      HeapRegionNode dst  ) {
3682     
3683     // if the region has no type, matches everything
3684     TypeDescriptor tdDst = dst.getType();
3685     assert tdDst != null;
3686  
3687     // if the type is not a class or an array, don't
3688     // match because primitives are copied, no aliases
3689     ClassDescriptor cdDst = tdDst.getClassDesc();
3690     if( cdDst == null && !tdDst.isArray() ) {
3691       return false;
3692     }
3693  
3694     // if the edge type is null, it matches everything
3695     TypeDescriptor tdEdge = edge.getType();
3696     assert tdEdge != null;
3697  
3698     return typeUtil.isSuperorType( tdEdge, tdDst );
3699   }
3700   
3701
3702
3703   public void writeGraph( String  graphName,
3704                           boolean writeLabels,
3705                           boolean labelSelect,
3706                           boolean pruneGarbage,
3707                           boolean writeReferencers,
3708                           boolean hideSubsetReachability,
3709                           boolean hideEdgeTaints
3710                           ) throws java.io.IOException {
3711     writeGraph( graphName,
3712                 writeLabels,
3713                 labelSelect,
3714                 pruneGarbage,
3715                 writeReferencers,
3716                 hideSubsetReachability,
3717                 hideEdgeTaints,
3718                 null );
3719   }
3720
3721   public void writeGraph( String       graphName,
3722                           boolean      writeLabels,
3723                           boolean      labelSelect,
3724                           boolean      pruneGarbage,
3725                           boolean      writeReferencers,
3726                           boolean      hideSubsetReachability,
3727                           boolean      hideEdgeTaints,
3728                           Set<Integer> callerNodeIDsCopiedToCallee
3729                           ) throws java.io.IOException {
3730     
3731     // remove all non-word characters from the graph name so
3732     // the filename and identifier in dot don't cause errors
3733     graphName = graphName.replaceAll( "[\\W]", "" );
3734
3735     BufferedWriter bw = 
3736       new BufferedWriter( new FileWriter( graphName+".dot" ) );
3737
3738     bw.write( "digraph "+graphName+" {\n" );
3739
3740
3741     // this is an optional step to form the callee-reachable
3742     // "cut-out" into a DOT cluster for visualization
3743     if( callerNodeIDsCopiedToCallee != null ) {
3744
3745       bw.write( "  subgraph cluster0 {\n" );
3746       bw.write( "    color=blue;\n" );
3747
3748       Iterator i = id2hrn.entrySet().iterator();
3749       while( i.hasNext() ) {
3750         Map.Entry      me  = (Map.Entry)      i.next();
3751         HeapRegionNode hrn = (HeapRegionNode) me.getValue();      
3752         
3753         if( callerNodeIDsCopiedToCallee.contains( hrn.getID() ) ) {
3754           bw.write( "    "+hrn.toString()+
3755                     hrn.toStringDOT( hideSubsetReachability )+
3756                     ";\n" );
3757           
3758         }
3759       }
3760
3761       bw.write( "  }\n" );
3762     }
3763
3764
3765     Set<HeapRegionNode> visited = new HashSet<HeapRegionNode>();
3766
3767     // then visit every heap region node    
3768     Iterator i = id2hrn.entrySet().iterator();
3769     while( i.hasNext() ) {
3770       Map.Entry      me  = (Map.Entry)      i.next();
3771       HeapRegionNode hrn = (HeapRegionNode) me.getValue();      
3772
3773       // only visit nodes worth writing out--for instance
3774       // not every node at an allocation is referenced
3775       // (think of it as garbage-collected), etc.
3776       if( !pruneGarbage                              ||
3777           (hrn.isFlagged() && hrn.getID() > 0)       ||
3778           hrn.getDescription().startsWith( "param" ) ||
3779           hrn.isOutOfContext()
3780           ) {
3781
3782         if( !visited.contains( hrn ) ) {
3783           traverseHeapRegionNodes( hrn,
3784                                    bw,
3785                                    null,
3786                                    visited,
3787                                    writeReferencers,
3788                                    hideSubsetReachability,
3789                                    hideEdgeTaints,
3790                                    callerNodeIDsCopiedToCallee );
3791         }
3792       }
3793     }
3794
3795     bw.write( "  graphTitle[label=\""+graphName+"\",shape=box];\n" );
3796   
3797
3798     // then visit every label node, useful for debugging
3799     if( writeLabels ) {
3800       i = td2vn.entrySet().iterator();
3801       while( i.hasNext() ) {
3802         Map.Entry    me = (Map.Entry)    i.next();
3803         VariableNode vn = (VariableNode) me.getValue();
3804         
3805         if( labelSelect ) {
3806           String labelStr = vn.getTempDescriptorString();
3807           if( labelStr.startsWith("___temp") ||
3808               labelStr.startsWith("___dst") ||
3809               labelStr.startsWith("___srctmp") ||
3810               labelStr.startsWith("___neverused")
3811               ) {
3812             continue;
3813           }
3814         }
3815         
3816         Iterator<RefEdge> heapRegionsItr = vn.iteratorToReferencees();
3817         while( heapRegionsItr.hasNext() ) {
3818           RefEdge        edge = heapRegionsItr.next();
3819           HeapRegionNode hrn  = edge.getDst();
3820           
3821           if( pruneGarbage && !visited.contains( hrn ) ) {
3822             traverseHeapRegionNodes( hrn,
3823                                      bw,
3824                                      null,
3825                                      visited,
3826                                      writeReferencers,
3827                                      hideSubsetReachability,
3828                                      hideEdgeTaints,
3829                                      callerNodeIDsCopiedToCallee );
3830           }
3831           
3832           bw.write( "  "+vn.toString()+
3833                     " -> "+hrn.toString()+
3834                     edge.toStringDOT( hideSubsetReachability, "" )+
3835                     ";\n" );
3836         }
3837       }
3838     }
3839     
3840     bw.write( "}\n" );
3841     bw.close();
3842   }
3843
3844   protected void traverseHeapRegionNodes( HeapRegionNode      hrn,
3845                                           BufferedWriter      bw,
3846                                           TempDescriptor      td,
3847                                           Set<HeapRegionNode> visited,
3848                                           boolean             writeReferencers,
3849                                           boolean             hideSubsetReachability,
3850                                           boolean             hideEdgeTaints,
3851                                           Set<Integer>        callerNodeIDsCopiedToCallee
3852                                           ) throws java.io.IOException {
3853
3854     if( visited.contains( hrn ) ) {
3855       return;
3856     }
3857     visited.add( hrn );
3858
3859     // if we're drawing the callee-view subgraph, only
3860     // write out the node info if it hasn't already been
3861     // written
3862     if( callerNodeIDsCopiedToCallee == null ||
3863         !callerNodeIDsCopiedToCallee.contains( hrn.getID() ) 
3864         ) {
3865       bw.write( "  "+hrn.toString()+
3866                 hrn.toStringDOT( hideSubsetReachability )+
3867                 ";\n" );
3868     }
3869
3870     Iterator<RefEdge> childRegionsItr = hrn.iteratorToReferencees();
3871     while( childRegionsItr.hasNext() ) {
3872       RefEdge        edge     = childRegionsItr.next();
3873       HeapRegionNode hrnChild = edge.getDst();
3874
3875       if( callerNodeIDsCopiedToCallee != null &&
3876           (edge.getSrc() instanceof HeapRegionNode) ) {
3877         HeapRegionNode hrnSrc = (HeapRegionNode) edge.getSrc();
3878         if( callerNodeIDsCopiedToCallee.contains( hrnSrc.getID()        ) &&
3879             callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
3880             ) {
3881           bw.write( "  "+hrn.toString()+
3882                     " -> "+hrnChild.toString()+
3883                     edge.toStringDOT( hideSubsetReachability, ",color=blue" )+
3884                     ";\n");
3885         } else if( !callerNodeIDsCopiedToCallee.contains( hrnSrc.getID()       ) &&
3886                    callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
3887                    ) {
3888           bw.write( "  "+hrn.toString()+
3889                     " -> "+hrnChild.toString()+
3890                     edge.toStringDOT( hideSubsetReachability, ",color=blue,style=dashed" )+
3891                     ";\n");
3892         } else {
3893           bw.write( "  "+hrn.toString()+
3894                     " -> "+hrnChild.toString()+
3895                     edge.toStringDOT( hideSubsetReachability, "" )+
3896                     ";\n");
3897         }
3898       } else {
3899         bw.write( "  "+hrn.toString()+
3900                   " -> "+hrnChild.toString()+
3901                   edge.toStringDOT( hideSubsetReachability, "" )+
3902                   ";\n");
3903       }
3904       
3905       traverseHeapRegionNodes( hrnChild,
3906                                bw,
3907                                td,
3908                                visited,
3909                                writeReferencers,
3910                                hideSubsetReachability,
3911                                hideEdgeTaints,
3912                                callerNodeIDsCopiedToCallee );
3913     }
3914   }  
3915
3916 }