make sure straight union of reach states or reach sets is never done, reach tuples...
[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.unionORpreds( 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.unionORpreds( 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.unionORpreds( 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.unionORpreds( 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.unionORpreds( 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.unionORpreds( 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.add( 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.add( 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.add( 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.add( 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( rsCaller, 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.add( 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
1479     // first traverse this context to find nodes and edges
1480     //  that will be callee-reachable
1481     Set<HeapRegionNode> reachableCallerNodes =
1482       new HashSet<HeapRegionNode>();
1483
1484     // caller edges between callee-reachable nodes
1485     Set<RefEdge> reachableCallerEdges =
1486       new HashSet<RefEdge>();
1487
1488     // caller edges from arg vars, and the matching param index
1489     // because these become a special edge in callee
1490     Hashtable<RefEdge, Integer> reachableCallerArgEdges2paramIndex =
1491       new Hashtable<RefEdge, Integer>();
1492
1493     // caller edges from local vars or callee-unreachable nodes
1494     // (out-of-context sources) to callee-reachable nodes
1495     Set<RefEdge> oocCallerEdges =
1496       new HashSet<RefEdge>();
1497
1498
1499     for( int i = 0; i < fmCallee.numParameters(); ++i ) {
1500
1501       TempDescriptor tdArg = fc.getArgMatchingParamIndex( fmCallee, i );
1502       VariableNode vnArgCaller = this.getVariableNodeFromTemp( tdArg );
1503
1504       Set<RefSrcNode> toVisitInCaller = new HashSet<RefSrcNode>();
1505       Set<RefSrcNode> visitedInCaller = new HashSet<RefSrcNode>();
1506
1507       toVisitInCaller.add( vnArgCaller );
1508       
1509       while( !toVisitInCaller.isEmpty() ) {
1510         RefSrcNode rsnCaller = toVisitInCaller.iterator().next();
1511         toVisitInCaller.remove( rsnCaller );
1512         visitedInCaller.add( rsnCaller );
1513
1514         Iterator<RefEdge> itrRefEdges = rsnCaller.iteratorToReferencees();
1515         while( itrRefEdges.hasNext() ) {
1516           RefEdge        reCaller  = itrRefEdges.next();
1517           HeapRegionNode hrnCaller = reCaller.getDst();
1518
1519           callerNodeIDsCopiedToCallee.add( hrnCaller.getID() );
1520           reachableCallerNodes.add( hrnCaller );
1521
1522           if( reCaller.getSrc() instanceof HeapRegionNode ) {
1523             reachableCallerEdges.add( reCaller );
1524           } else {
1525             if( rsnCaller.equals( vnArgCaller ) ) {
1526               reachableCallerArgEdges2paramIndex.put( reCaller, i );
1527             } else {
1528               oocCallerEdges.add( reCaller );
1529             }
1530           }
1531
1532           if( !visitedInCaller.contains( hrnCaller ) ) {
1533             toVisitInCaller.add( hrnCaller );
1534           }
1535           
1536         } // end edge iteration
1537       } // end visiting heap nodes in caller
1538     } // end iterating over parameters as starting points
1539
1540
1541     // now collect out-of-context reach tuples and 
1542     // more out-of-context edges
1543     Set<ReachTuple> oocTuples = new HashSet<ReachTuple>();
1544
1545     Iterator<Integer> itrInContext = 
1546       callerNodeIDsCopiedToCallee.iterator();
1547     while( itrInContext.hasNext() ) {
1548       Integer        hrnID                 = itrInContext.next();
1549       HeapRegionNode hrnCallerAndInContext = id2hrn.get( hrnID );
1550       
1551       Iterator<RefEdge> itrMightCross =
1552         hrnCallerAndInContext.iteratorToReferencers();
1553       while( itrMightCross.hasNext() ) {
1554         RefEdge edgeMightCross = itrMightCross.next();        
1555
1556         RefSrcNode rsnCallerAndOutContext =
1557           edgeMightCross.getSrc();
1558         
1559         if( rsnCallerAndOutContext instanceof VariableNode ) {
1560           // variables do not have out-of-context reach states,
1561           // so jump out now
1562           oocCallerEdges.add( edgeMightCross );
1563           continue;
1564         }
1565           
1566         HeapRegionNode hrnCallerAndOutContext = 
1567           (HeapRegionNode) rsnCallerAndOutContext;
1568
1569         // is this source node out-of-context?
1570         if( callerNodeIDsCopiedToCallee.contains( hrnCallerAndOutContext.getID() ) ) {
1571           // no, skip this edge
1572           continue;
1573         }
1574
1575         // okay, we got one
1576         oocCallerEdges.add( edgeMightCross );
1577
1578         // add all reach tuples on the node to list
1579         // of things that are out-of-context: insight
1580         // if this node is reachable from someting that WAS
1581         // in-context, then this node should already be in-context
1582         Iterator<ReachState> stateItr = hrnCallerAndOutContext.getAlpha().iterator();
1583         while( stateItr.hasNext() ) {
1584           ReachState state = stateItr.next();
1585
1586           Iterator<ReachTuple> rtItr = state.iterator();
1587           while( rtItr.hasNext() ) {
1588             ReachTuple rt = rtItr.next();
1589
1590             oocTuples.add( rt );
1591           }
1592         }
1593       }
1594     }
1595
1596
1597     // the callee view is a new graph: DON'T MODIFY *THIS* graph
1598     ReachGraph rg = new ReachGraph();
1599
1600     // add nodes to callee graph
1601     Iterator<HeapRegionNode> hrnItr = reachableCallerNodes.iterator();
1602     while( hrnItr.hasNext() ) {
1603       HeapRegionNode hrnCaller = hrnItr.next();
1604
1605       assert callerNodeIDsCopiedToCallee.contains( hrnCaller.getID() );
1606       assert !rg.id2hrn.containsKey( hrnCaller.getID() );
1607             
1608       ExistPred    pred  = ExistPred.factory( hrnCaller.getID(), null );
1609       ExistPredSet preds = ExistPredSet.factory( pred );
1610       
1611       rg.createNewHeapRegionNode( hrnCaller.getID(),
1612                                   hrnCaller.isSingleObject(),
1613                                   hrnCaller.isNewSummary(),
1614                                   hrnCaller.isFlagged(),
1615                                   false, // out-of-context?
1616                                   hrnCaller.getType(),
1617                                   hrnCaller.getAllocSite(),
1618                                   toCalleeContext( oocTuples,
1619                                                    hrnCaller.getInherent(),      // in state
1620                                                    hrnCaller.getID(),            // node pred
1621                                                    null, null, null, null, null, // edge pred
1622                                                    false ),                      // ooc pred
1623                                   toCalleeContext( oocTuples,
1624                                                    hrnCaller.getAlpha(),         // in state
1625                                                    hrnCaller.getID(),            // node pred
1626                                                    null, null, null, null, null, // edge pred
1627                                                    false ),                      // ooc pred
1628                                   preds,
1629                                   hrnCaller.getDescription()
1630                                   );
1631     }
1632
1633     // add param edges to callee graph
1634     Iterator argEdges = 
1635       reachableCallerArgEdges2paramIndex.entrySet().iterator();
1636     while( argEdges.hasNext() ) {
1637       Map.Entry me    = (Map.Entry) argEdges.next();
1638       RefEdge   reArg = (RefEdge)   me.getKey();
1639       Integer   index = (Integer)   me.getValue();
1640       
1641       TempDescriptor arg = fmCallee.getParameter( index );
1642       
1643       VariableNode vnCallee = 
1644         rg.getVariableNodeFromTemp( arg );
1645       
1646       HeapRegionNode hrnDstCaller = reArg.getDst();
1647       HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1648       assert hrnDstCallee != null;
1649       
1650       ExistPred pred =
1651         ExistPred.factory( arg,
1652                            null, 
1653                            hrnDstCallee.getID(),
1654                            reArg.getType(),
1655                            reArg.getField(),
1656                            null,
1657                            false ); // out-of-context
1658       
1659       ExistPredSet preds = 
1660         ExistPredSet.factory( pred );
1661       
1662       RefEdge reCallee = 
1663         new RefEdge( vnCallee,
1664                      hrnDstCallee,
1665                      reArg.getType(),
1666                      reArg.getField(),
1667                      toCalleeContext( oocTuples,
1668                                       reArg.getBeta(),      // in state
1669                                       null,                 // node pred
1670                                       arg,                  // edge pred
1671                                       null,                 // edge pred
1672                                       hrnDstCallee.getID(), // edge pred
1673                                       reArg.getType(),      // edge pred
1674                                       reArg.getField(),     // edge pred
1675                                       false ),              // ooc pred
1676                      preds
1677                      );
1678       
1679       rg.addRefEdge( vnCallee,
1680                      hrnDstCallee,
1681                      reCallee
1682                      );      
1683     }
1684
1685     // add in-context edges to callee graph
1686     Iterator<RefEdge> reItr = reachableCallerEdges.iterator();
1687     while( reItr.hasNext() ) {
1688       RefEdge    reCaller  = reItr.next();
1689       RefSrcNode rsnCaller = reCaller.getSrc();
1690       assert rsnCaller instanceof HeapRegionNode;
1691       HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1692       HeapRegionNode hrnDstCaller = reCaller.getDst();
1693
1694       HeapRegionNode hrnSrcCallee = rg.id2hrn.get( hrnSrcCaller.getID() );
1695       HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1696       assert hrnSrcCallee != null;
1697       assert hrnDstCallee != null;
1698
1699       ExistPred pred =
1700         ExistPred.factory( null, 
1701                            hrnSrcCallee.getID(), 
1702                            hrnDstCallee.getID(),
1703                            reCaller.getType(),
1704                            reCaller.getField(),
1705                            null,
1706                            false ); // out-of-context
1707       
1708       ExistPredSet preds = 
1709         ExistPredSet.factory( pred );
1710       
1711       RefEdge reCallee = 
1712         new RefEdge( hrnSrcCallee,
1713                      hrnDstCallee,
1714                      reCaller.getType(),
1715                      reCaller.getField(),
1716                      toCalleeContext( oocTuples,
1717                                       reCaller.getBeta(),   // in state
1718                                       null,                 // node pred
1719                                       null,                 // edge pred
1720                                       hrnSrcCallee.getID(), // edge pred
1721                                       hrnDstCallee.getID(), // edge pred
1722                                       reCaller.getType(),   // edge pred
1723                                       reCaller.getField(),  // edge pred
1724                                       false ),              // ooc pred
1725                      preds
1726                      );
1727       
1728       rg.addRefEdge( hrnSrcCallee,
1729                      hrnDstCallee,
1730                      reCallee
1731                      );        
1732     }
1733
1734     // add out-of-context edges to callee graph
1735     reItr = oocCallerEdges.iterator();
1736     while( reItr.hasNext() ) {
1737       RefEdge        reCaller     = reItr.next();
1738       RefSrcNode     rsnCaller    = reCaller.getSrc();
1739       HeapRegionNode hrnDstCaller = reCaller.getDst();
1740       HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1741       assert hrnDstCallee != null;
1742
1743       TypeDescriptor oocNodeType;
1744       ReachSet       oocReach;
1745       TempDescriptor oocPredSrcTemp = null;
1746       Integer        oocPredSrcID   = null;
1747
1748       if( rsnCaller instanceof VariableNode ) {
1749         VariableNode vnCaller = (VariableNode) rsnCaller;
1750         oocNodeType    = null;
1751         oocReach       = rsetEmpty;
1752         oocPredSrcTemp = vnCaller.getTempDescriptor();
1753
1754       } else {
1755         HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1756         assert !callerNodeIDsCopiedToCallee.contains( hrnSrcCaller.getID() );
1757         oocNodeType  = hrnSrcCaller.getType();
1758         oocReach     = hrnSrcCaller.getAlpha(); 
1759         oocPredSrcID = hrnSrcCaller.getID();        
1760       }
1761
1762       ExistPred pred =
1763         ExistPred.factory( oocPredSrcTemp, 
1764                            oocPredSrcID, 
1765                            hrnDstCallee.getID(),
1766                            reCaller.getType(),
1767                            reCaller.getField(),
1768                            null,
1769                            true ); // out-of-context
1770
1771       ExistPredSet preds = 
1772         ExistPredSet.factory( pred );
1773         
1774       RefEdge oocEdgeExisting =
1775         rg.getOutOfContextReferenceTo( hrnDstCallee,
1776                                        oocNodeType,
1777                                        reCaller.getType(),
1778                                        reCaller.getField()
1779                                        );
1780
1781       if( oocEdgeExisting == null ) {          
1782           // for consistency, map one out-of-context "identifier"
1783           // to one heap region node id, otherwise no convergence
1784         String oocid = "oocid"+
1785           fmCallee+
1786           hrnDstCallee.getIDString()+
1787           oocNodeType+
1788           reCaller.getType()+
1789           reCaller.getField();
1790           
1791         Integer oocHrnID = oocid2hrnid.get( oocid );
1792
1793         HeapRegionNode hrnCalleeAndOutContext;
1794
1795         if( oocHrnID == null ) {
1796
1797           hrnCalleeAndOutContext =
1798             rg.createNewHeapRegionNode( null,  // ID
1799                                         false, // single object?
1800                                         false, // new summary?
1801                                         false, // flagged?
1802                                         true,  // out-of-context?
1803                                         oocNodeType,
1804                                         null,  // alloc site, shouldn't be used
1805                                         toCalleeContext( oocTuples, 
1806                                                          oocReach,                     // in state
1807                                                          null,                         // node pred
1808                                                          null, null, null, null, null, // edge pred
1809                                                          true                          // ooc pred
1810                                                          ), // inherent
1811                                         toCalleeContext( oocTuples,
1812                                                          oocReach,                     // in state
1813                                                          null,                         // node pred
1814                                                          null, null, null, null, null, // edge pred
1815                                                          true                          // ooc pred
1816                                                          ), // alpha
1817                                         preds,
1818                                         "out-of-context"
1819                                         );       
1820           
1821           oocid2hrnid.put( oocid, hrnCalleeAndOutContext.getID() );
1822           
1823         } else {
1824
1825           // the mapping already exists, so see if node is there
1826           hrnCalleeAndOutContext = rg.id2hrn.get( oocHrnID );
1827
1828           if( hrnCalleeAndOutContext == null ) {
1829             // nope, make it
1830             hrnCalleeAndOutContext =
1831               rg.createNewHeapRegionNode( oocHrnID,  // ID
1832                                           false, // single object?
1833                                           false, // new summary?
1834                                           false, // flagged?
1835                                           true,  // out-of-context?
1836                                           oocNodeType,
1837                                           null,  // alloc site, shouldn't be used
1838                                           toCalleeContext( oocTuples,
1839                                                            oocReach,                     // in state
1840                                                            null,                         // node pred
1841                                                            null, null, null, null, null, // edge pred
1842                                                            true                          // ooc pred
1843                                                            ), // inherent
1844                                           toCalleeContext( oocTuples,
1845                                                            oocReach,                     // in state
1846                                                            null,                         // node pred
1847                                                            null, null, null, null, null, // edge pred
1848                                                            true                          // ooc pred
1849                                                            ), // alpha
1850                                           preds,
1851                                           "out-of-context"
1852                                           );       
1853           }
1854         }
1855
1856         rg.addRefEdge( hrnCalleeAndOutContext,
1857                        hrnDstCallee,
1858                        new RefEdge( hrnCalleeAndOutContext,
1859                                     hrnDstCallee,
1860                                     reCaller.getType(),
1861                                     reCaller.getField(),
1862                                     toCalleeContext( oocTuples,
1863                                                      reCaller.getBeta(),   // in state
1864                                                      null,                 // node pred
1865                                                      oocPredSrcTemp,       // edge pred
1866                                                      oocPredSrcID,         // edge pred
1867                                                      hrnDstCaller.getID(), // edge pred
1868                                                      reCaller.getType(),   // edge pred
1869                                                      reCaller.getField(),  // edge pred
1870                                                      false                 // ooc pred
1871                                                      ),
1872                                     preds
1873                                     )
1874                        );              
1875         
1876         } else {
1877         // the out-of-context edge already exists
1878         oocEdgeExisting.setBeta( Canonical.unionORpreds( oocEdgeExisting.getBeta(),
1879                                                   toCalleeContext( oocTuples,
1880                                                                    reCaller.getBeta(),   // in state
1881                                                                    null,                 // node pred
1882                                                                    oocPredSrcTemp,       // edge pred
1883                                                                    oocPredSrcID,         // edge pred
1884                                                                    hrnDstCaller.getID(), // edge pred
1885                                                                    reCaller.getType(),   // edge pred
1886                                                                    reCaller.getField(),  // edge pred
1887                                                                    false                 // ooc pred
1888                                                                    )
1889                                                   )
1890                                  );         
1891           
1892         oocEdgeExisting.setPreds( Canonical.join( oocEdgeExisting.getPreds(),
1893                                                   reCaller.getPreds()
1894                                                   )
1895                                   );          
1896         
1897       }                
1898     }
1899
1900
1901     if( writeDebugDOTs ) {    
1902       try {
1903         rg.writeGraph( "calleeview", 
1904                        true,   // write labels (variables)                
1905                        true,   // selectively hide intermediate temp vars 
1906                        true,   // prune unreachable heap regions          
1907                        true,   // hide subset reachability states         
1908                        true ); // hide edge taints                        
1909       } catch( IOException e ) {}
1910     }
1911
1912     return rg;
1913   }  
1914
1915   private static Hashtable<String, Integer> oocid2hrnid = 
1916     new Hashtable<String, Integer>();
1917
1918
1919
1920   public void 
1921     resolveMethodCall( FlatCall     fc,        
1922                        FlatMethod   fmCallee,        
1923                        ReachGraph   rgCallee,
1924                        Set<Integer> callerNodeIDsCopiedToCallee,
1925                        boolean      writeDebugDOTs
1926                        ) {
1927
1928
1929     if( writeDebugDOTs ) {
1930       try {
1931         rgCallee.writeGraph( "callee", 
1932                              true,   // write labels (variables)                  
1933                              true,   // selectively hide intermediate temp vars   
1934                              true,   // prune unreachable heap regions            
1935                              true,   // hide subset reachability states           
1936                              true ); // hide edge taints                        
1937         writeGraph( "caller00In", 
1938                     true,  // write labels (variables)                
1939                     true,  // selectively hide intermediate temp vars 
1940                     true,  // prune unreachable heap regions          
1941                     true,  // hide subset reachability states         
1942                     true,  // hide edge taints                        
1943                     callerNodeIDsCopiedToCallee );
1944       } catch( IOException e ) {}
1945     }
1946
1947
1948     // method call transfer function steps:
1949     // 1. Use current callee-reachable heap (CRH) to test callee 
1950     //    predicates and mark what will be coming in.
1951     // 2. Wipe CRH out of caller.
1952     // 3. Transplant marked callee parts in:
1953     //    a) bring in nodes
1954     //    b) bring in callee -> callee edges
1955     //    c) resolve out-of-context -> callee edges
1956     //    d) assign return value
1957     // 4. Collapse shadow nodes down
1958     // 5. Global sweep it.
1959
1960
1961
1962     // 1. mark what callee elements have satisfied predicates
1963     Hashtable<HeapRegionNode, ExistPredSet> calleeNodesSatisfied =
1964       new Hashtable<HeapRegionNode, ExistPredSet>();
1965     
1966     Hashtable<RefEdge, ExistPredSet> calleeEdgesSatisfied =
1967       new Hashtable<RefEdge, ExistPredSet>();
1968
1969     Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied =
1970       new Hashtable<ReachState, ExistPredSet>();
1971
1972     Hashtable< RefEdge, Set<RefSrcNode> > calleeEdges2oocCallerSrcMatches =
1973       new Hashtable< RefEdge, Set<RefSrcNode> >();
1974
1975     Iterator meItr = rgCallee.id2hrn.entrySet().iterator();
1976     while( meItr.hasNext() ) {
1977       Map.Entry      me        = (Map.Entry)      meItr.next();
1978       Integer        id        = (Integer)        me.getKey();
1979       HeapRegionNode hrnCallee = (HeapRegionNode) me.getValue();
1980
1981       // if a callee element's predicates are satisfied then a set
1982       // of CALLER predicates is returned: they are the predicates
1983       // that the callee element moved into the caller context
1984       // should have, and it is inefficient to find this again later
1985       ExistPredSet predsIfSatis = 
1986         hrnCallee.getPreds().isSatisfiedBy( this,
1987                                             callerNodeIDsCopiedToCallee
1988                                             );
1989       if( predsIfSatis != null ) {
1990         calleeNodesSatisfied.put( hrnCallee, predsIfSatis );
1991       } else {
1992         // otherwise don't bother looking at edges to this node
1993         continue;
1994       }
1995       
1996       // since the node is coming over, find out which reach
1997       // states on it should come over, too
1998       Iterator<ReachState> stateItr = hrnCallee.getAlpha().iterator();
1999       while( stateItr.hasNext() ) {
2000         ReachState stateCallee = stateItr.next();
2001
2002         predsIfSatis = 
2003           stateCallee.getPreds().isSatisfiedBy( this,
2004                                                 callerNodeIDsCopiedToCallee
2005                                                 );
2006         if( predsIfSatis != null ) {
2007           calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2008         } 
2009       }
2010
2011       // then look at edges to the node
2012       Iterator<RefEdge> reItr = hrnCallee.iteratorToReferencers();
2013       while( reItr.hasNext() ) {
2014         RefEdge    reCallee  = reItr.next();
2015         RefSrcNode rsnCallee = reCallee.getSrc();
2016
2017         // (caller local variables to in-context heap regions)
2018         // have an (out-of-context heap region -> in-context heap region)
2019         // abstraction in the callEE, so its true we never need to
2020         // look at a (var node -> heap region) edge in callee to bring
2021         // those over for the call site transfer.  What about (param var->heap region)
2022         // edges in callee? They are dealt with below this loop.
2023         // So, yes, at this point skip (var->region) edges in callee
2024         if( rsnCallee instanceof VariableNode ) {
2025           continue;
2026         }        
2027
2028         // first see if the source is out-of-context, and only
2029         // proceed with this edge if we find some caller-context
2030         // matches
2031         HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2032         boolean matchedOutOfContext = false;
2033
2034         if( hrnSrcCallee.isOutOfContext() ) {          
2035
2036           assert !calleeEdges2oocCallerSrcMatches.containsKey( reCallee );
2037           Set<RefSrcNode> rsnCallers = new HashSet<RefSrcNode>();            
2038
2039           HeapRegionNode hrnDstCaller = this.id2hrn.get( hrnCallee.getID() );
2040           Iterator<RefEdge> reDstItr = hrnDstCaller.iteratorToReferencers();
2041           while( reDstItr.hasNext() ) {
2042             // the edge and field (either possibly null) must match
2043             RefEdge reCaller = reDstItr.next();
2044
2045             if( !reCaller.typeEquals ( reCallee.getType()  ) ||
2046                 !reCaller.fieldEquals( reCallee.getField() ) 
2047                 ) {
2048               continue;
2049             }
2050             
2051             RefSrcNode rsnCaller = reCaller.getSrc();
2052             if( rsnCaller instanceof VariableNode ) {
2053               // a variable node matches an OOC region with null type
2054               if( hrnSrcCallee.getType() != null ) {
2055                 continue;
2056               }
2057
2058             } else {
2059               // otherwise types should match
2060               HeapRegionNode hrnCallerSrc = (HeapRegionNode) rsnCaller;
2061               if( hrnSrcCallee.getType() == null ) {
2062                 if( hrnCallerSrc.getType() != null ) {
2063                   continue;
2064                 }
2065               } else {
2066                 if( !hrnSrcCallee.getType().equals( hrnCallerSrc.getType() ) ) {
2067                   continue;
2068                 }
2069               }
2070             }
2071
2072             rsnCallers.add( rsnCaller );
2073             matchedOutOfContext = true;
2074           }
2075
2076           if( !rsnCallers.isEmpty() ) {
2077             calleeEdges2oocCallerSrcMatches.put( reCallee, rsnCallers );
2078           }
2079         }
2080
2081         if( hrnSrcCallee.isOutOfContext() &&
2082             !matchedOutOfContext ) {
2083           continue;
2084         }
2085         
2086         predsIfSatis = 
2087           reCallee.getPreds().isSatisfiedBy( this,
2088                                              callerNodeIDsCopiedToCallee
2089                                              );
2090         if( predsIfSatis != null ) {
2091           calleeEdgesSatisfied.put( reCallee, predsIfSatis );
2092
2093           // since the edge is coming over, find out which reach
2094           // states on it should come over, too
2095           stateItr = reCallee.getBeta().iterator();
2096           while( stateItr.hasNext() ) {
2097             ReachState stateCallee = stateItr.next();
2098             
2099             predsIfSatis = 
2100               stateCallee.getPreds().isSatisfiedBy( this,
2101                                                     callerNodeIDsCopiedToCallee
2102                                                     );
2103             if( predsIfSatis != null ) {
2104               calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2105             } 
2106           }
2107
2108         }        
2109
2110       }
2111     }
2112
2113     // test param -> HRN edges, also
2114     for( int i = 0; i < fmCallee.numParameters(); ++i ) {
2115
2116       // parameter defined here is the symbol in the callee
2117       TempDescriptor tdParam  = fmCallee.getParameter( i );
2118       VariableNode   vnCallee = rgCallee.getVariableNodeFromTemp( tdParam );
2119
2120       Iterator<RefEdge> reItr = vnCallee.iteratorToReferencees();
2121       while( reItr.hasNext() ) {
2122         RefEdge reCallee = reItr.next();
2123         
2124         ExistPredSet ifDst = 
2125           reCallee.getDst().getPreds().isSatisfiedBy( this,
2126                                                       callerNodeIDsCopiedToCallee
2127                                                       );
2128         if( ifDst == null ) {
2129           continue;
2130         }
2131         
2132         ExistPredSet predsIfSatis = 
2133           reCallee.getPreds().isSatisfiedBy( this,
2134                                              callerNodeIDsCopiedToCallee
2135                                              );
2136         if( predsIfSatis != null ) {
2137           calleeEdgesSatisfied.put( reCallee, predsIfSatis );
2138
2139           // since the edge is coming over, find out which reach
2140           // states on it should come over, too
2141           Iterator<ReachState> stateItr = reCallee.getBeta().iterator();
2142           while( stateItr.hasNext() ) {
2143             ReachState stateCallee = stateItr.next();
2144             
2145             predsIfSatis = 
2146               stateCallee.getPreds().isSatisfiedBy( this,
2147                                                     callerNodeIDsCopiedToCallee
2148                                                     );
2149             if( predsIfSatis != null ) {
2150               calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2151             } 
2152           }
2153
2154         }        
2155       }
2156     }
2157
2158
2159
2160
2161     if( writeDebugDOTs ) {
2162       try {
2163         writeGraph( "caller20BeforeWipe", 
2164                     true,   // write labels (variables)                
2165                     true,   // selectively hide intermediate temp vars 
2166                     true,   // prune unreachable heap regions          
2167                     true,   // hide subset reachability states         
2168                     true ); // hide edge taints                        
2169       } catch( IOException e ) {}
2170     }
2171
2172
2173     // 2. predicates tested, ok to wipe out caller part
2174     Iterator<Integer> hrnItr = callerNodeIDsCopiedToCallee.iterator();
2175     while( hrnItr.hasNext() ) {
2176       Integer        hrnID     = hrnItr.next();
2177       HeapRegionNode hrnCaller = id2hrn.get( hrnID );
2178       assert hrnCaller != null;
2179
2180       // when clearing off nodes, also eliminate variable
2181       // references
2182       wipeOut( hrnCaller, true );
2183     }
2184
2185
2186
2187     if( writeDebugDOTs ) {
2188       try {
2189         writeGraph( "caller30BeforeAddingNodes", 
2190                     true,   // write labels (variables)                
2191                     true,   // selectively hide intermediate temp vars 
2192                     true,   // prune unreachable heap regions          
2193                     true,   // hide subset reachability states         
2194                     true ); // hide edge taints                        
2195       } catch( IOException e ) {}
2196     }
2197
2198
2199     // 3. callee elements with satisfied preds come in, note that
2200     //    the mapping of elements satisfied to preds is like this:
2201     //    A callee element EE has preds EEp that are satisfied by
2202     //    some caller element ER.  We bring EE into the caller
2203     //    context as ERee with the preds of ER, namely ERp, which
2204     //    in the following algorithm is the value in the mapping
2205
2206     // 3.a) nodes
2207     Iterator satisItr = calleeNodesSatisfied.entrySet().iterator();
2208     while( satisItr.hasNext() ) {
2209       Map.Entry      me        = (Map.Entry)      satisItr.next();
2210       HeapRegionNode hrnCallee = (HeapRegionNode) me.getKey();
2211       ExistPredSet   preds     = (ExistPredSet)   me.getValue();
2212
2213       // TODO: I think its true that the current implementation uses
2214       // the type of the OOC region and the predicates OF THE EDGE from
2215       // it to link everything up in caller context, so that's why we're
2216       // skipping this... maybe that's a sillier way to do it?
2217       if( hrnCallee.isOutOfContext() ) {
2218         continue;
2219       }
2220
2221       AllocSite as = hrnCallee.getAllocSite();  
2222       allocSites.add( as );
2223
2224       Integer hrnIDshadow = as.getShadowIDfromID( hrnCallee.getID() );
2225
2226       HeapRegionNode hrnCaller = id2hrn.get( hrnIDshadow );
2227       if( hrnCaller == null ) {
2228         hrnCaller =
2229           createNewHeapRegionNode( hrnIDshadow,                // id or null to generate a new one 
2230                                    hrnCallee.isSingleObject(), // single object?                 
2231                                    hrnCallee.isNewSummary(),   // summary?       
2232                                    hrnCallee.isFlagged(),      // flagged?
2233                                    false,                      // out-of-context?
2234                                    hrnCallee.getType(),        // type                           
2235                                    hrnCallee.getAllocSite(),   // allocation site                        
2236                                    toCallerContext( hrnCallee.getInherent(),
2237                                                     calleeStatesSatisfied  ),    // inherent reach
2238                                    null,                       // current reach                 
2239                                    predsEmpty,                 // predicates
2240                                    hrnCallee.getDescription()  // description
2241                                    );                                        
2242       } else {
2243         assert hrnCaller.isWiped();
2244       }
2245
2246       hrnCaller.setAlpha( toCallerContext( hrnCallee.getAlpha(),
2247                                            calleeStatesSatisfied 
2248                                            )
2249                           );
2250
2251       hrnCaller.setPreds( preds );
2252     }
2253
2254
2255
2256     if( writeDebugDOTs ) {
2257       try {
2258         writeGraph( "caller31BeforeAddingEdges", 
2259                     true,   // write labels (variables)                
2260                     true,   // selectively hide intermediate temp vars 
2261                     true,   // prune unreachable heap regions          
2262                     true,   // hide subset reachability states         
2263                     true ); // hide edge taints                        
2264       } catch( IOException e ) {}
2265     }
2266
2267
2268     // set these up during the next procedure so after
2269     // the caller has all of its nodes and edges put
2270     // back together we can propagate the callee's
2271     // reach changes backwards into the caller graph
2272     HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
2273
2274     Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
2275       new Hashtable<RefEdge, ChangeSet>();
2276
2277
2278     // 3.b) callee -> callee edges AND out-of-context -> callee
2279     satisItr = calleeEdgesSatisfied.entrySet().iterator();
2280     while( satisItr.hasNext() ) {
2281       Map.Entry    me       = (Map.Entry)    satisItr.next();
2282       RefEdge      reCallee = (RefEdge)      me.getKey();
2283       ExistPredSet preds    = (ExistPredSet) me.getValue();
2284
2285       HeapRegionNode hrnDstCallee = reCallee.getDst();
2286       AllocSite      asDst        = hrnDstCallee.getAllocSite();
2287       allocSites.add( asDst );
2288
2289       Integer hrnIDDstShadow = 
2290         asDst.getShadowIDfromID( hrnDstCallee.getID() );
2291       
2292       HeapRegionNode hrnDstCaller = id2hrn.get( hrnIDDstShadow );
2293       assert hrnDstCaller != null;
2294       
2295       
2296       RefSrcNode rsnCallee = reCallee.getSrc();
2297
2298       Set<RefSrcNode> rsnCallers =
2299         new HashSet<RefSrcNode>();
2300       
2301       Set<RefSrcNode> oocCallers = 
2302         calleeEdges2oocCallerSrcMatches.get( reCallee );
2303
2304       boolean oocEdges = false;
2305       
2306       if( oocCallers == null ) {
2307         // there are no out-of-context matches, so it's
2308         // either a param/arg var or one in-context heap region
2309         if( rsnCallee instanceof VariableNode ) {
2310           // variable -> node in the callee should only
2311           // come into the caller if its from a param var
2312           VariableNode   vnCallee = (VariableNode) rsnCallee;
2313           TempDescriptor tdParam  = vnCallee.getTempDescriptor();
2314           TempDescriptor tdArg    = fc.getArgMatchingParam( fmCallee,
2315                                                             tdParam );
2316           if( tdArg == null ) {
2317             // this means the variable isn't a parameter, its local
2318             // to the callee so we ignore it in call site transfer
2319             // shouldn't this NEVER HAPPEN?
2320             assert false;
2321           }
2322           rsnCallers.add( this.getVariableNodeFromTemp( tdArg ) );
2323           oocEdges = true;
2324
2325         } else {
2326           // otherwise source is in context, one region
2327           HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2328
2329           // translate an in-context node to shadow
2330           AllocSite asSrc = hrnSrcCallee.getAllocSite();
2331           allocSites.add( asSrc );
2332           
2333           Integer hrnIDSrcShadow = 
2334             asSrc.getShadowIDfromID( hrnSrcCallee.getID() );
2335
2336           HeapRegionNode hrnSrcCallerShadow =
2337             this.id2hrn.get( hrnIDSrcShadow );
2338           
2339           if( hrnSrcCallerShadow == null ) {
2340             hrnSrcCallerShadow =
2341               createNewHeapRegionNode( hrnIDSrcShadow,                // id or null to generate a new one 
2342                                        hrnSrcCallee.isSingleObject(), // single object?          
2343                                        hrnSrcCallee.isNewSummary(),   // summary?        
2344                                        hrnSrcCallee.isFlagged(),      // flagged?
2345                                        false,                         // out-of-context?
2346                                        hrnSrcCallee.getType(),        // type                            
2347                                        hrnSrcCallee.getAllocSite(),   // allocation site                         
2348                                        toCallerContext( hrnSrcCallee.getInherent(),
2349                                                         calleeStatesSatisfied ),    // inherent reach
2350                                        toCallerContext( hrnSrcCallee.getAlpha(),
2351                                                         calleeStatesSatisfied ),       // current reach                 
2352                                        predsEmpty,                    // predicates
2353                                        hrnSrcCallee.getDescription()  // description
2354                                        );                                        
2355           }
2356           
2357           rsnCallers.add( hrnSrcCallerShadow );
2358         }
2359
2360       } else {
2361         // otherwise we have a set of out-of-context srcs
2362         // that should NOT be translated to shadow nodes
2363         assert !oocCallers.isEmpty();
2364         rsnCallers.addAll( oocCallers );
2365         oocEdges = true;
2366       }
2367
2368       // now make all caller edges we've identified from
2369       // this callee edge with a satisfied predicate
2370       assert !rsnCallers.isEmpty();
2371       Iterator<RefSrcNode> rsnItr = rsnCallers.iterator();
2372       while( rsnItr.hasNext() ) {
2373         RefSrcNode rsnCaller = rsnItr.next();
2374         
2375         RefEdge reCaller = new RefEdge( rsnCaller,
2376                                         hrnDstCaller,
2377                                         reCallee.getType(),
2378                                         reCallee.getField(),
2379                                         toCallerContext( reCallee.getBeta(),
2380                                                          calleeStatesSatisfied ),
2381                                         preds
2382                                         );
2383
2384         ChangeSet cs = ChangeSet.factory();
2385         Iterator<ReachState> rsItr = reCaller.getBeta().iterator();
2386         while( rsItr.hasNext() ) {
2387           ReachState   state = rsItr.next();
2388           ExistPredSet preds2 = state.getPreds();
2389           assert preds2.preds.size() == 1;
2390
2391           if( state.isEmpty() ) {
2392             continue;
2393           }
2394
2395           ExistPred pred = preds2.preds.iterator().next();
2396           ReachState old = pred.ne_state;
2397
2398           if( old == null ) {
2399             old = rstateEmpty;
2400           }
2401
2402           assert old != null;
2403
2404           cs = Canonical.union( cs,
2405                                 ChangeTuple.factory( old,
2406                                                      state
2407                                                      )
2408                                 );
2409         }
2410         
2411         // look to see if an edge with same field exists
2412         // and merge with it, otherwise just add the edge
2413         RefEdge edgeExisting = rsnCaller.getReferenceTo( hrnDstCaller,
2414                                                          reCallee.getType(),
2415                                                          reCallee.getField()
2416                                                          );     
2417         if( edgeExisting != null ) {
2418           edgeExisting.setBeta(
2419                                Canonical.unionORpreds( edgeExisting.getBeta(),
2420                                                 reCaller.getBeta()
2421                                                 )
2422                                );
2423           edgeExisting.setPreds(
2424                                 Canonical.join( edgeExisting.getPreds(),
2425                                                 reCaller.getPreds()
2426                                                 )
2427                                 );
2428
2429           // for reach propagation
2430           if( !cs.isEmpty() ) {
2431             edgePlannedChanges.put( 
2432                                    edgeExisting, 
2433                                    Canonical.union( edgePlannedChanges.get( edgeExisting ),
2434                                                     cs
2435                                                     ) 
2436                                     );
2437           }
2438           
2439         } else {                          
2440           addRefEdge( rsnCaller, hrnDstCaller, reCaller );      
2441
2442           // for reach propagation
2443           if( !cs.isEmpty() ) {
2444             edgesForPropagation.add( reCaller );
2445             assert !edgePlannedChanges.containsKey( reCaller );
2446             edgePlannedChanges.put( reCaller, cs );
2447           }
2448         }
2449       }
2450     }
2451
2452
2453
2454
2455
2456     if( writeDebugDOTs ) {
2457       try {
2458         writeGraph( "caller35BeforeAssignReturnValue", 
2459                     true,   // write labels (variables)                
2460                     true,   // selectively hide intermediate temp vars 
2461                     true,   // prune unreachable heap regions          
2462                     true,   // hide subset reachability states         
2463                     true ); // hide edge taints                        
2464       } catch( IOException e ) {}
2465     }
2466
2467
2468
2469     // TODO: WAIT! THIS SHOULD BE MERGED INTO OTHER PARTS, BECAUSE
2470     // AS IT IS WE'RE NOT VERIFYING PREDICATES OF RETURN VALUE
2471     // EDGES, JUST BRINGING THEM ALL!  It'll work for now, over approximation
2472     
2473     // 3.d) handle return value assignment if needed
2474     TempDescriptor returnTemp = fc.getReturnTemp();
2475     if( returnTemp != null && !returnTemp.getType().isImmutable() ) {
2476
2477       VariableNode vnLhsCaller = getVariableNodeFromTemp( returnTemp );
2478       clearRefEdgesFrom( vnLhsCaller, null, null, true );
2479
2480       VariableNode vnReturnCallee = rgCallee.getVariableNodeFromTemp( tdReturn );
2481       Iterator<RefEdge> reCalleeItr = vnReturnCallee.iteratorToReferencees();
2482       while( reCalleeItr.hasNext() ) {
2483         RefEdge        reCallee     = reCalleeItr.next();
2484         HeapRegionNode hrnDstCallee = reCallee.getDst();
2485
2486         // some edge types are not possible return values when we can
2487         // see what type variable we are assigning it to
2488         if( !isSuperiorType( returnTemp.getType(), reCallee.getType() ) ) {
2489           System.out.println( "*** NOT EXPECTING TO SEE THIS: Throwing out "+
2490                               reCallee+" for return temp "+returnTemp );
2491           // prune
2492           continue;
2493         }       
2494
2495         AllocSite asDst = hrnDstCallee.getAllocSite();
2496         allocSites.add( asDst );
2497
2498         Integer hrnIDDstShadow = asDst.getShadowIDfromID( hrnDstCallee.getID() );
2499
2500         HeapRegionNode hrnDstCaller = id2hrn.get( hrnIDDstShadow );
2501         if( hrnDstCaller == null ) {
2502           hrnDstCaller =
2503             createNewHeapRegionNode( hrnIDDstShadow,                // id or null to generate a new one 
2504                                      hrnDstCallee.isSingleObject(), // single object?            
2505                                      hrnDstCallee.isNewSummary(),   // summary?  
2506                                      hrnDstCallee.isFlagged(),      // flagged?
2507                                      false,                         // out-of-context?
2508                                      hrnDstCallee.getType(),        // type                              
2509                                      hrnDstCallee.getAllocSite(),   // allocation site                   
2510                                      toCallerContext( hrnDstCallee.getInherent(),
2511                                                       calleeStatesSatisfied  ),    // inherent reach
2512                                      toCallerContext( hrnDstCallee.getAlpha(),
2513                                                       calleeStatesSatisfied  ),    // current reach                 
2514                                      predsTrue,                     // predicates
2515                                      hrnDstCallee.getDescription()  // description
2516                                      );                                        
2517         } else {
2518           assert hrnDstCaller.isWiped();
2519         }
2520
2521         TypeDescriptor tdNewEdge =
2522           mostSpecificType( reCallee.getType(),
2523                             hrnDstCallee.getType(),
2524                             hrnDstCaller.getType()
2525                             );        
2526
2527         RefEdge reCaller = new RefEdge( vnLhsCaller,
2528                                         hrnDstCaller,
2529                                         tdNewEdge,
2530                                         null,
2531                                         toCallerContext( reCallee.getBeta(),
2532                                                          calleeStatesSatisfied ),
2533                                         predsTrue
2534                                         );
2535
2536         addRefEdge( vnLhsCaller, hrnDstCaller, reCaller );
2537       }
2538     }
2539
2540
2541
2542     if( writeDebugDOTs ) {
2543       try {
2544         writeGraph( "caller38propagateReach", 
2545                     true,   // write labels (variables)                
2546                     true,   // selectively hide intermediate temp vars 
2547                     true,   // prune unreachable heap regions          
2548                     true,   // hide subset reachability states         
2549                     true ); // hide edge taints                        
2550       } catch( IOException e ) {}
2551     }
2552
2553     // propagate callee reachability changes to the rest
2554     // of the caller graph edges
2555     HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
2556   
2557     propagateTokensOverEdges( edgesForPropagation, // source edges
2558                               edgePlannedChanges,  // map src edge to change set
2559                               edgesUpdated );      // list of updated edges
2560     
2561     // commit beta' (beta<-betaNew)
2562     Iterator<RefEdge> edgeItr = edgesUpdated.iterator();
2563     while( edgeItr.hasNext() ) {
2564       edgeItr.next().applyBetaNew();
2565     }
2566
2567
2568
2569
2570
2571
2572     if( writeDebugDOTs ) {
2573       try {
2574         writeGraph( "caller40BeforeShadowMerge", 
2575                     true,   // write labels (variables)                
2576                     true,   // selectively hide intermediate temp vars 
2577                     true,   // prune unreachable heap regions          
2578                     true,   // hide subset reachability states         
2579                     true ); // hide edge taints                        
2580       } catch( IOException e ) {}
2581     }
2582     
2583
2584     // 4) merge shadow nodes so alloc sites are back to k
2585     Iterator<AllocSite> asItr = rgCallee.allocSites.iterator();
2586     while( asItr.hasNext() ) {
2587       // for each allocation site do the following to merge
2588       // shadow nodes (newest from callee) with any existing
2589       // look for the newest normal and newest shadow "slot"
2590       // not being used, transfer normal to shadow.  Keep
2591       // doing this until there are no more normal nodes, or
2592       // no empty shadow slots: then merge all remaining normal
2593       // nodes into the shadow summary.  Finally, convert all
2594       // shadow to their normal versions.
2595       AllocSite as = asItr.next();
2596       int ageNorm = 0;
2597       int ageShad = 0;
2598       while( ageNorm < allocationDepth &&
2599              ageShad < allocationDepth ) {
2600
2601         // first, are there any normal nodes left?
2602         Integer        idNorm  = as.getIthOldest( ageNorm );
2603         HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2604         if( hrnNorm == null ) {
2605           // no, this age of normal node not in the caller graph
2606           ageNorm++;
2607           continue;
2608         }
2609
2610         // yes, a normal node exists, is there an empty shadow
2611         // "slot" to transfer it onto?
2612         HeapRegionNode hrnShad = getIthNode( as, ageShad, true );        
2613         if( !hrnShad.isWiped() ) {
2614           // no, this age of shadow node is not empty
2615           ageShad++;
2616           continue;
2617         }
2618  
2619         // yes, this shadow node is empty
2620         transferOnto( hrnNorm, hrnShad );
2621         ageNorm++;
2622         ageShad++;
2623       }
2624
2625       // now, while there are still normal nodes but no shadow
2626       // slots, merge normal nodes into the shadow summary
2627       while( ageNorm < allocationDepth ) {
2628
2629         // first, are there any normal nodes left?
2630         Integer        idNorm  = as.getIthOldest( ageNorm );
2631         HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2632         if( hrnNorm == null ) {
2633           // no, this age of normal node not in the caller graph
2634           ageNorm++;
2635           continue;
2636         }
2637
2638         // yes, a normal node exists, so get the shadow summary
2639         HeapRegionNode summShad = getSummaryNode( as, true );
2640         mergeIntoSummary( hrnNorm, summShad );
2641         ageNorm++;
2642       }
2643
2644       // if there is a normal summary, merge it into shadow summary
2645       Integer        idNorm   = as.getSummary();
2646       HeapRegionNode summNorm = id2hrn.get( idNorm );
2647       if( summNorm != null ) {
2648         HeapRegionNode summShad = getSummaryNode( as, true );
2649         mergeIntoSummary( summNorm, summShad );
2650       }
2651       
2652       // finally, flip all existing shadow nodes onto the normal
2653       for( int i = 0; i < allocationDepth; ++i ) {
2654         Integer        idShad  = as.getIthOldestShadow( i );
2655         HeapRegionNode hrnShad = id2hrn.get( idShad );
2656         if( hrnShad != null ) {
2657           // flip it
2658           HeapRegionNode hrnNorm = getIthNode( as, i, false );
2659           assert hrnNorm.isWiped();
2660           transferOnto( hrnShad, hrnNorm );
2661         }
2662       }
2663       
2664       Integer        idShad   = as.getSummaryShadow();
2665       HeapRegionNode summShad = id2hrn.get( idShad );
2666       if( summShad != null ) {
2667         summNorm = getSummaryNode( as, false );
2668         transferOnto( summShad, summNorm );
2669       }      
2670     }
2671
2672
2673     if( writeDebugDOTs ) {
2674       try {
2675         writeGraph( "caller45BeforeUnshadow", 
2676                     true,   // write labels (variables)                
2677                     true,   // selectively hide intermediate temp vars 
2678                     true,   // prune unreachable heap regions          
2679                     true,   // hide subset reachability states         
2680                     true ); // hide edge taints                        
2681       } catch( IOException e ) {}
2682     }
2683     
2684     
2685     Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
2686     while( itrAllHRNodes.hasNext() ) {
2687       Map.Entry      me  = (Map.Entry)      itrAllHRNodes.next();
2688       HeapRegionNode hrn = (HeapRegionNode) me.getValue();
2689       
2690       hrn.setAlpha( unshadow( hrn.getAlpha() ) );
2691       
2692       Iterator<RefEdge> itrEdges = hrn.iteratorToReferencers();
2693       while( itrEdges.hasNext() ) {
2694         RefEdge re = itrEdges.next();
2695         re.setBeta( unshadow( re.getBeta() ) );
2696       }
2697     }
2698     
2699
2700
2701     if( writeDebugDOTs ) {
2702       try {
2703         writeGraph( "caller50BeforeGlobalSweep", 
2704                     true,   // write labels (variables)                
2705                     true,   // selectively hide intermediate temp vars 
2706                     true,   // prune unreachable heap regions          
2707                     true,   // hide subset reachability states         
2708                     true ); // hide edge taints                        
2709       } catch( IOException e ) {}
2710     }
2711
2712
2713     // 5.
2714     if( !DISABLE_GLOBAL_SWEEP ) {
2715       globalSweep();
2716     }
2717     
2718
2719
2720     if( writeDebugDOTs ) {
2721       try {
2722         writeGraph( "caller90AfterTransfer", 
2723                     true,   // write labels (variables)                
2724                     true,   // selectively hide intermediate temp vars 
2725                     true,   // prune unreachable heap regions          
2726                     true,   // hide subset reachability states         
2727                     true ); // hide edge taints                        
2728       } catch( IOException e ) {}
2729     }
2730   } 
2731
2732   
2733
2734   ////////////////////////////////////////////////////
2735   //
2736   //  Abstract garbage collection simply removes
2737   //  heap region nodes that are not mechanically
2738   //  reachable from a root set.  This step is
2739   //  essential for testing node and edge existence
2740   //  predicates efficiently
2741   //
2742   ////////////////////////////////////////////////////
2743   public void abstractGarbageCollect( Set<TempDescriptor> liveSet ) {
2744
2745     // calculate a root set, will be different for Java
2746     // version of analysis versus Bamboo version
2747     Set<RefSrcNode> toVisit = new HashSet<RefSrcNode>();
2748
2749     // visit every variable in graph while building root
2750     // set, and do iterating on a copy, so we can remove
2751     // dead variables while we're at this
2752     Iterator makeCopyItr = td2vn.entrySet().iterator();
2753     Set      entrysCopy  = new HashSet();
2754     while( makeCopyItr.hasNext() ) {
2755       entrysCopy.add( makeCopyItr.next() );
2756     }
2757     
2758     Iterator eItr = entrysCopy.iterator();
2759     while( eItr.hasNext() ) {
2760       Map.Entry      me = (Map.Entry)      eItr.next();
2761       TempDescriptor td = (TempDescriptor) me.getKey();
2762       VariableNode   vn = (VariableNode)   me.getValue();
2763
2764       if( liveSet.contains( td ) ) {
2765         toVisit.add( vn );
2766
2767       } else {
2768         // dead var, remove completely from graph
2769         td2vn.remove( td );
2770         clearRefEdgesFrom( vn, null, null, true );
2771       }
2772     }
2773
2774     // everything visited in a traversal is
2775     // considered abstractly live
2776     Set<RefSrcNode> visited = new HashSet<RefSrcNode>();
2777     
2778     while( !toVisit.isEmpty() ) {
2779       RefSrcNode rsn = toVisit.iterator().next();
2780       toVisit.remove( rsn );
2781       visited.add( rsn );
2782       
2783       Iterator<RefEdge> hrnItr = rsn.iteratorToReferencees();
2784       while( hrnItr.hasNext() ) {
2785         RefEdge        edge = hrnItr.next();
2786         HeapRegionNode hrn  = edge.getDst();
2787         
2788         if( !visited.contains( hrn ) ) {
2789           toVisit.add( hrn );
2790         }
2791       }
2792     }
2793
2794     // get a copy of the set to iterate over because
2795     // we're going to monkey with the graph when we
2796     // identify a garbage node
2797     Set<HeapRegionNode> hrnAllPrior = new HashSet<HeapRegionNode>();
2798     Iterator<HeapRegionNode> hrnItr = id2hrn.values().iterator();
2799     while( hrnItr.hasNext() ) {
2800       hrnAllPrior.add( hrnItr.next() );
2801     }
2802
2803     Iterator<HeapRegionNode> hrnAllItr = hrnAllPrior.iterator();
2804     while( hrnAllItr.hasNext() ) {
2805       HeapRegionNode hrn = hrnAllItr.next();
2806
2807       if( !visited.contains( hrn ) ) {
2808
2809         // heap region nodes are compared across ReachGraph
2810         // objects by their integer ID, so when discarding
2811         // garbage nodes we must also discard entries in
2812         // the ID -> heap region hashtable.
2813         id2hrn.remove( hrn.getID() );
2814
2815         // RefEdge objects are two-way linked between
2816         // nodes, so when a node is identified as garbage,
2817         // actively clear references to and from it so
2818         // live nodes won't have dangling RefEdge's
2819         wipeOut( hrn, true );
2820
2821         // if we just removed the last node from an allocation
2822         // site, it should be taken out of the ReachGraph's list
2823         AllocSite as = hrn.getAllocSite();
2824         if( !hasNodesOf( as ) ) {
2825           allocSites.remove( as );
2826         }
2827       }
2828     }
2829   }
2830
2831   protected boolean hasNodesOf( AllocSite as ) {
2832     if( id2hrn.containsKey( as.getSummary() ) ) {
2833       return true;
2834     }
2835
2836     for( int i = 0; i < allocationDepth; ++i ) {
2837       if( id2hrn.containsKey( as.getIthOldest( i ) ) ) {
2838         return true;
2839       }      
2840     }
2841     return false;
2842   }
2843
2844
2845   ////////////////////////////////////////////////////
2846   //
2847   //  This global sweep is an optional step to prune
2848   //  reachability sets that are not internally
2849   //  consistent with the global graph.  It should be
2850   //  invoked after strong updates or method calls.
2851   //
2852   ////////////////////////////////////////////////////
2853   public void globalSweep() {
2854
2855     // boldB is part of the phase 1 sweep
2856     // it has an in-context table and an out-of-context table
2857     Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBic =
2858       new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();    
2859
2860     Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBooc =
2861       new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();    
2862
2863     // visit every heap region to initialize alphaNew and betaNew,
2864     // and make a map of every hrnID to the source nodes it should
2865     // propagate forward from.  In-context flagged hrnID's propagate
2866     // from only the in-context node they name, but out-of-context
2867     // ID's may propagate from several out-of-context nodes
2868     Hashtable< Integer, Set<HeapRegionNode> > icID2srcs = 
2869       new Hashtable< Integer, Set<HeapRegionNode> >();
2870
2871     Hashtable< Integer, Set<HeapRegionNode> > oocID2srcs =
2872       new Hashtable< Integer, Set<HeapRegionNode> >();
2873
2874
2875     Iterator itrHrns = id2hrn.entrySet().iterator();
2876     while( itrHrns.hasNext() ) {
2877       Map.Entry      me    = (Map.Entry)      itrHrns.next();
2878       Integer        hrnID = (Integer)        me.getKey();
2879       HeapRegionNode hrn   = (HeapRegionNode) me.getValue();
2880     
2881       // assert that this node and incoming edges have clean alphaNew
2882       // and betaNew sets, respectively
2883       assert rsetEmpty.equals( hrn.getAlphaNew() );
2884
2885       Iterator<RefEdge> itrRers = hrn.iteratorToReferencers();
2886       while( itrRers.hasNext() ) {
2887         RefEdge edge = itrRers.next();
2888         assert rsetEmpty.equals( edge.getBetaNew() );
2889       }      
2890
2891       // calculate boldB for this flagged node, or out-of-context node
2892       if( hrn.isFlagged() ) {
2893         assert !hrn.isOutOfContext();
2894         assert !icID2srcs.containsKey( hrn.getID() );
2895         Set<HeapRegionNode> srcs = new HashSet<HeapRegionNode>();
2896         srcs.add( hrn );
2897         icID2srcs.put( hrn.getID(), srcs );
2898       }
2899
2900       if( hrn.isOutOfContext() ) {
2901         assert !hrn.isFlagged();
2902
2903         Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
2904         while( stateItr.hasNext() ) {
2905           ReachState state = stateItr.next();
2906
2907           Iterator<ReachTuple> rtItr = state.iterator();
2908           while( rtItr.hasNext() ) {
2909             ReachTuple rt = rtItr.next();
2910             assert rt.isOutOfContext();
2911
2912             Set<HeapRegionNode> srcs = oocID2srcs.get( rt.getHrnID() );
2913             if( srcs == null ) {
2914               srcs = new HashSet<HeapRegionNode>();
2915             }
2916             srcs.add( hrn );
2917             oocID2srcs.put( rt.getHrnID(), srcs );
2918           }
2919         }
2920       }
2921     }
2922
2923     // calculate boldB for all hrnIDs identified by the above
2924     // node traversal, propagating from every source
2925     while( !icID2srcs.isEmpty() || !oocID2srcs.isEmpty() ) {
2926
2927       Integer             hrnID;
2928       Set<HeapRegionNode> srcs;
2929       boolean             inContext;
2930
2931       if( !icID2srcs.isEmpty() ) {
2932         Map.Entry me = (Map.Entry) icID2srcs.entrySet().iterator().next();
2933         hrnID = (Integer)             me.getKey();
2934         srcs  = (Set<HeapRegionNode>) me.getValue();
2935         inContext = true;
2936         icID2srcs.remove( hrnID );
2937
2938       } else {
2939         assert !oocID2srcs.isEmpty();
2940
2941         Map.Entry me = (Map.Entry) oocID2srcs.entrySet().iterator().next();
2942         hrnID = (Integer)             me.getKey();
2943         srcs  = (Set<HeapRegionNode>) me.getValue();
2944         inContext = false;
2945         oocID2srcs.remove( hrnID );
2946       }
2947
2948
2949       Hashtable<RefEdge, ReachSet> boldB_f =
2950         new Hashtable<RefEdge, ReachSet>();
2951         
2952       Set<RefEdge> workSetEdges = new HashSet<RefEdge>();
2953
2954       Iterator<HeapRegionNode> hrnItr = srcs.iterator();
2955       while( hrnItr.hasNext() ) {
2956         HeapRegionNode hrn = hrnItr.next();
2957
2958         assert workSetEdges.isEmpty();
2959         
2960         // initial boldB_f constraints
2961         Iterator<RefEdge> itrRees = hrn.iteratorToReferencees();
2962         while( itrRees.hasNext() ) {
2963           RefEdge edge = itrRees.next();
2964           
2965           assert !boldB_f.containsKey( edge );
2966           boldB_f.put( edge, edge.getBeta() );
2967           
2968           assert !workSetEdges.contains( edge );
2969           workSetEdges.add( edge );
2970         }       
2971       
2972         // enforce the boldB_f constraint at edges until we reach a fixed point
2973         while( !workSetEdges.isEmpty() ) {
2974           RefEdge edge = workSetEdges.iterator().next();
2975           workSetEdges.remove( edge );   
2976           
2977           Iterator<RefEdge> itrPrime = edge.getDst().iteratorToReferencees();
2978           while( itrPrime.hasNext() ) {
2979             RefEdge edgePrime = itrPrime.next();            
2980           
2981             ReachSet prevResult   = boldB_f.get( edgePrime );
2982             ReachSet intersection = Canonical.intersection( boldB_f.get( edge ),
2983                                                             edgePrime.getBeta()
2984                                                             );
2985           
2986             if( prevResult == null || 
2987                 Canonical.unionORpreds( prevResult,
2988                                  intersection ).size() > prevResult.size() ) {
2989             
2990               if( prevResult == null ) {
2991                 boldB_f.put( edgePrime, 
2992                              Canonical.unionORpreds( edgePrime.getBeta(),
2993                                               intersection 
2994                                               )
2995                              );
2996               } else {
2997                 boldB_f.put( edgePrime, 
2998                              Canonical.unionORpreds( prevResult,
2999                                               intersection 
3000                                               )
3001                              );
3002               }
3003               workSetEdges.add( edgePrime );    
3004             }
3005           }
3006         }
3007       }
3008       
3009       if( inContext ) {
3010         boldBic.put( hrnID, boldB_f );
3011       } else {
3012         boldBooc.put( hrnID, boldB_f );
3013       }
3014     }
3015
3016
3017     // use boldB to prune hrnIDs from alpha states that are impossible
3018     // and propagate the differences backwards across edges
3019     HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
3020
3021     Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
3022       new Hashtable<RefEdge, ChangeSet>();
3023
3024
3025     itrHrns = id2hrn.entrySet().iterator();
3026     while( itrHrns.hasNext() ) {
3027       Map.Entry      me    = (Map.Entry)      itrHrns.next();
3028       Integer        hrnID = (Integer)        me.getKey();
3029       HeapRegionNode hrn   = (HeapRegionNode) me.getValue();
3030       
3031       // out-of-context nodes don't participate in the 
3032       // global sweep, they serve as sources for the pass
3033       // performed above
3034       if( hrn.isOutOfContext() ) {
3035         continue;
3036       }
3037
3038       // the inherent states of a region are the exception
3039       // to removal as the global sweep prunes
3040       ReachTuple rtException = ReachTuple.factory( hrnID,
3041                                                    !hrn.isSingleObject(),    
3042                                                    ReachTuple.ARITY_ONE,
3043                                                    false // out-of-context
3044                                                    );
3045
3046       ChangeSet cts = ChangeSet.factory();
3047
3048       // mark hrnIDs for removal
3049       Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3050       while( stateItr.hasNext() ) {
3051         ReachState stateOld = stateItr.next();
3052
3053         ReachState markedHrnIDs = ReachState.factory();
3054
3055         Iterator<ReachTuple> rtItr = stateOld.iterator();
3056         while( rtItr.hasNext() ) {
3057           ReachTuple rtOld = rtItr.next();
3058
3059           // never remove the inherent hrnID from a flagged region
3060           // because it is trivially satisfied
3061           if( hrn.isFlagged() ) {       
3062             if( rtOld == rtException ) {
3063               continue;
3064             }
3065           }
3066
3067           // does boldB allow this hrnID?
3068           boolean foundState = false;
3069           Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3070           while( incidentEdgeItr.hasNext() ) {
3071             RefEdge incidentEdge = incidentEdgeItr.next();
3072
3073             Hashtable<RefEdge, ReachSet> B; 
3074             if( rtOld.isOutOfContext() ) {
3075               B = boldBooc.get( rtOld.getHrnID() ); 
3076             } else {
3077               assert id2hrn.containsKey( rtOld.getHrnID() );
3078               B = boldBic.get( rtOld.getHrnID() ); 
3079             }
3080
3081             if( B != null ) {            
3082               ReachSet boldB_rtOld_incident = B.get( incidentEdge );
3083               if( boldB_rtOld_incident != null &&
3084                   boldB_rtOld_incident.contains( stateOld ) ) {
3085                 foundState = true;
3086               }
3087             }
3088           }
3089           
3090           if( !foundState ) {
3091             markedHrnIDs = Canonical.add( markedHrnIDs, rtOld );          
3092           }
3093         }
3094
3095         // if there is nothing marked, just move on
3096         if( markedHrnIDs.isEmpty() ) {
3097           hrn.setAlphaNew( Canonical.add( hrn.getAlphaNew(),
3098                                             stateOld
3099                                             )
3100                            );
3101           continue;
3102         }
3103
3104         // remove all marked hrnIDs and establish a change set that should
3105         // propagate backwards over edges from this node
3106         ReachState statePruned = ReachState.factory();
3107         rtItr = stateOld.iterator();
3108         while( rtItr.hasNext() ) {
3109           ReachTuple rtOld = rtItr.next();
3110
3111           if( !markedHrnIDs.containsTuple( rtOld ) ) {
3112             statePruned = Canonical.add( statePruned, rtOld );
3113           }
3114         }
3115         assert !stateOld.equals( statePruned );
3116
3117         hrn.setAlphaNew( Canonical.add( hrn.getAlphaNew(),
3118                                           statePruned
3119                                           )
3120                          );
3121         ChangeTuple ct = ChangeTuple.factory( stateOld,
3122                                               statePruned
3123                                               );
3124         cts = Canonical.union( cts, ct );
3125       }
3126
3127       // throw change tuple set on all incident edges
3128       if( !cts.isEmpty() ) {
3129         Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3130         while( incidentEdgeItr.hasNext() ) {
3131           RefEdge incidentEdge = incidentEdgeItr.next();
3132                   
3133           edgesForPropagation.add( incidentEdge );
3134
3135           if( edgePlannedChanges.get( incidentEdge ) == null ) {
3136             edgePlannedChanges.put( incidentEdge, cts );
3137           } else {          
3138             edgePlannedChanges.put( 
3139                                    incidentEdge, 
3140                                    Canonical.union( edgePlannedChanges.get( incidentEdge ),
3141                                                     cts
3142                                                     ) 
3143                                     );
3144           }
3145         }
3146       }
3147     }
3148     
3149     HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
3150
3151     propagateTokensOverEdges( edgesForPropagation,
3152                               edgePlannedChanges,
3153                               edgesUpdated );
3154
3155     // at the end of the 1st phase reference edges have
3156     // beta, betaNew that correspond to beta and betaR
3157     //
3158     // commit beta<-betaNew, so beta=betaR and betaNew
3159     // will represent the beta' calculation in 2nd phase
3160     //
3161     // commit alpha<-alphaNew because it won't change
3162     HashSet<RefEdge> res = new HashSet<RefEdge>();
3163
3164     Iterator<HeapRegionNode> nodeItr = id2hrn.values().iterator();
3165     while( nodeItr.hasNext() ) {
3166       HeapRegionNode hrn = nodeItr.next();
3167       hrn.applyAlphaNew();
3168       Iterator<RefEdge> itrRes = hrn.iteratorToReferencers();
3169       while( itrRes.hasNext() ) {
3170         res.add( itrRes.next() );
3171       }
3172     }
3173
3174
3175     // 2nd phase    
3176     Iterator<RefEdge> edgeItr = res.iterator();
3177     while( edgeItr.hasNext() ) {
3178       RefEdge        edge = edgeItr.next();
3179       HeapRegionNode hrn  = edge.getDst();
3180
3181       // commit results of last phase
3182       if( edgesUpdated.contains( edge ) ) {
3183         edge.applyBetaNew();
3184       }
3185
3186       // compute intial condition of 2nd phase
3187       edge.setBetaNew( Canonical.intersection( edge.getBeta(),
3188                                                hrn.getAlpha() 
3189                                                )
3190                        );
3191     }
3192         
3193     // every edge in the graph is the initial workset
3194     Set<RefEdge> edgeWorkSet = (Set) res.clone();
3195     while( !edgeWorkSet.isEmpty() ) {
3196       RefEdge edgePrime = edgeWorkSet.iterator().next();
3197       edgeWorkSet.remove( edgePrime );
3198
3199       RefSrcNode rsn = edgePrime.getSrc();
3200       if( !(rsn instanceof HeapRegionNode) ) {
3201         continue;
3202       }
3203       HeapRegionNode hrn = (HeapRegionNode) rsn;
3204
3205       Iterator<RefEdge> itrEdge = hrn.iteratorToReferencers();
3206       while( itrEdge.hasNext() ) {
3207         RefEdge edge = itrEdge.next();      
3208
3209         ReachSet prevResult = edge.getBetaNew();
3210         assert prevResult != null;
3211
3212         ReachSet intersection = 
3213           Canonical.intersection( edge.getBeta(),
3214                                   edgePrime.getBetaNew() 
3215                                   );
3216                     
3217         if( Canonical.unionORpreds( prevResult,
3218                                     intersection
3219                                     ).size() > prevResult.size() ) {
3220           edge.setBetaNew( 
3221                           Canonical.unionORpreds( prevResult,
3222                                                   intersection 
3223                                                   )
3224                            );
3225           edgeWorkSet.add( edge );
3226         }       
3227       }      
3228     }
3229
3230     // commit beta' (beta<-betaNew)
3231     edgeItr = res.iterator();
3232     while( edgeItr.hasNext() ) {
3233       edgeItr.next().applyBetaNew();
3234     } 
3235   }  
3236
3237
3238
3239   ////////////////////////////////////////////////////
3240   // high-level merge operations
3241   ////////////////////////////////////////////////////
3242   public void merge_sameMethodContext( ReachGraph rg ) {
3243     // when merging two graphs that abstract the heap
3244     // of the same method context, we just call the
3245     // basic merge operation
3246     merge( rg );
3247   }
3248
3249   public void merge_diffMethodContext( ReachGraph rg ) {
3250     // when merging graphs for abstract heaps in
3251     // different method contexts we should:
3252     // 1) age the allocation sites?
3253     merge( rg );
3254   }
3255
3256   ////////////////////////////////////////////////////
3257   // in merge() and equals() methods the suffix A
3258   // represents the passed in graph and the suffix
3259   // B refers to the graph in this object
3260   // Merging means to take the incoming graph A and
3261   // merge it into B, so after the operation graph B
3262   // is the final result.
3263   ////////////////////////////////////////////////////
3264   protected void merge( ReachGraph rg ) {
3265
3266     if( rg == null ) {
3267       return;
3268     }
3269
3270     mergeNodes     ( rg );
3271     mergeRefEdges  ( rg );
3272     mergeAllocSites( rg );
3273   }
3274   
3275   protected void mergeNodes( ReachGraph rg ) {
3276
3277     // start with heap region nodes
3278     Set      sA = rg.id2hrn.entrySet();
3279     Iterator iA = sA.iterator();
3280     while( iA.hasNext() ) {
3281       Map.Entry      meA  = (Map.Entry)      iA.next();
3282       Integer        idA  = (Integer)        meA.getKey();
3283       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3284
3285       // if this graph doesn't have a node the
3286       // incoming graph has, allocate it
3287       if( !id2hrn.containsKey( idA ) ) {
3288         HeapRegionNode hrnB = hrnA.copy();
3289         id2hrn.put( idA, hrnB );
3290
3291       } else {
3292         // otherwise this is a node present in both graphs
3293         // so make the new reachability set a union of the
3294         // nodes' reachability sets
3295         HeapRegionNode hrnB = id2hrn.get( idA );
3296         hrnB.setAlpha( Canonical.unionORpreds( hrnB.getAlpha(),
3297                                         hrnA.getAlpha() 
3298                                         )
3299                        );
3300
3301         hrnB.setPreds( Canonical.join( hrnB.getPreds(),
3302                                        hrnA.getPreds()
3303                                        )
3304                        );
3305       }
3306     }
3307
3308     // now add any variable nodes that are in graph B but
3309     // not in A
3310     sA = rg.td2vn.entrySet();
3311     iA = sA.iterator();
3312     while( iA.hasNext() ) {
3313       Map.Entry      meA = (Map.Entry)      iA.next();
3314       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3315       VariableNode   lnA = (VariableNode)   meA.getValue();
3316
3317       // if the variable doesn't exist in B, allocate and add it
3318       VariableNode lnB = getVariableNodeFromTemp( tdA );
3319     }
3320   }
3321
3322   protected void mergeRefEdges( ReachGraph rg ) {
3323
3324     // between heap regions
3325     Set      sA = rg.id2hrn.entrySet();
3326     Iterator iA = sA.iterator();
3327     while( iA.hasNext() ) {
3328       Map.Entry      meA  = (Map.Entry)      iA.next();
3329       Integer        idA  = (Integer)        meA.getKey();
3330       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3331
3332       Iterator<RefEdge> heapRegionsItrA = hrnA.iteratorToReferencees();
3333       while( heapRegionsItrA.hasNext() ) {
3334         RefEdge        edgeA     = heapRegionsItrA.next();
3335         HeapRegionNode hrnChildA = edgeA.getDst();
3336         Integer        idChildA  = hrnChildA.getID();
3337
3338         // at this point we know an edge in graph A exists
3339         // idA -> idChildA, does this exist in B?
3340         assert id2hrn.containsKey( idA );
3341         HeapRegionNode hrnB        = id2hrn.get( idA );
3342         RefEdge        edgeToMerge = null;
3343
3344         Iterator<RefEdge> heapRegionsItrB = hrnB.iteratorToReferencees();
3345         while( heapRegionsItrB.hasNext() &&
3346                edgeToMerge == null          ) {
3347
3348           RefEdge        edgeB     = heapRegionsItrB.next();
3349           HeapRegionNode hrnChildB = edgeB.getDst();
3350           Integer        idChildB  = hrnChildB.getID();
3351
3352           // don't use the RefEdge.equals() here because
3353           // we're talking about existence between graphs,
3354           // not intragraph equal
3355           if( idChildB.equals( idChildA ) &&
3356               edgeB.typeAndFieldEquals( edgeA ) ) {
3357
3358             edgeToMerge = edgeB;
3359           }
3360         }
3361
3362         // if the edge from A was not found in B,
3363         // add it to B.
3364         if( edgeToMerge == null ) {
3365           assert id2hrn.containsKey( idChildA );
3366           HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3367           edgeToMerge = edgeA.copy();
3368           edgeToMerge.setSrc( hrnB );
3369           edgeToMerge.setDst( hrnChildB );
3370           addRefEdge( hrnB, hrnChildB, edgeToMerge );
3371         }
3372         // otherwise, the edge already existed in both graphs
3373         // so merge their reachability sets
3374         else {
3375           // just replace this beta set with the union
3376           assert edgeToMerge != null;
3377           edgeToMerge.setBeta(
3378                               Canonical.unionORpreds( edgeToMerge.getBeta(),
3379                                                edgeA.getBeta() 
3380                                                )
3381                               );
3382           edgeToMerge.setPreds(
3383                                Canonical.join( edgeToMerge.getPreds(),
3384                                                edgeA.getPreds()
3385                                                )
3386                                );
3387         }
3388       }
3389     }
3390
3391     // and then again from variable nodes
3392     sA = rg.td2vn.entrySet();
3393     iA = sA.iterator();
3394     while( iA.hasNext() ) {
3395       Map.Entry      meA = (Map.Entry)      iA.next();
3396       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3397       VariableNode   vnA = (VariableNode)   meA.getValue();
3398
3399       Iterator<RefEdge> heapRegionsItrA = vnA.iteratorToReferencees();
3400       while( heapRegionsItrA.hasNext() ) {
3401         RefEdge        edgeA     = heapRegionsItrA.next();
3402         HeapRegionNode hrnChildA = edgeA.getDst();
3403         Integer        idChildA  = hrnChildA.getID();
3404
3405         // at this point we know an edge in graph A exists
3406         // tdA -> idChildA, does this exist in B?
3407         assert td2vn.containsKey( tdA );
3408         VariableNode vnB         = td2vn.get( tdA );
3409         RefEdge      edgeToMerge = null;
3410
3411         Iterator<RefEdge> heapRegionsItrB = vnB.iteratorToReferencees();
3412         while( heapRegionsItrB.hasNext() &&
3413                edgeToMerge == null          ) {
3414
3415           RefEdge        edgeB     = heapRegionsItrB.next();
3416           HeapRegionNode hrnChildB = edgeB.getDst();
3417           Integer        idChildB  = hrnChildB.getID();
3418
3419           // don't use the RefEdge.equals() here because
3420           // we're talking about existence between graphs
3421           if( idChildB.equals( idChildA ) &&
3422               edgeB.typeAndFieldEquals( edgeA ) ) {
3423
3424             edgeToMerge = edgeB;
3425           }
3426         }
3427
3428         // if the edge from A was not found in B,
3429         // add it to B.
3430         if( edgeToMerge == null ) {
3431           assert id2hrn.containsKey( idChildA );
3432           HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3433           edgeToMerge = edgeA.copy();
3434           edgeToMerge.setSrc( vnB );
3435           edgeToMerge.setDst( hrnChildB );
3436           addRefEdge( vnB, hrnChildB, edgeToMerge );
3437         }
3438         // otherwise, the edge already existed in both graphs
3439         // so merge their reachability sets
3440         else {
3441           // just replace this beta set with the union
3442           edgeToMerge.setBeta( Canonical.unionORpreds( edgeToMerge.getBeta(),
3443                                                 edgeA.getBeta()
3444                                                 )
3445                                );
3446           edgeToMerge.setPreds( Canonical.join( edgeToMerge.getPreds(),
3447                                                 edgeA.getPreds()
3448                                                 )
3449                                 );
3450         }
3451       }
3452     }
3453   }
3454
3455   protected void mergeAllocSites( ReachGraph rg ) {
3456     allocSites.addAll( rg.allocSites );
3457   }
3458
3459
3460   // it is necessary in the equals() member functions
3461   // to "check both ways" when comparing the data
3462   // structures of two graphs.  For instance, if all
3463   // edges between heap region nodes in graph A are
3464   // present and equal in graph B it is not sufficient
3465   // to say the graphs are equal.  Consider that there
3466   // may be edges in graph B that are not in graph A.
3467   // the only way to know that all edges in both graphs
3468   // are equally present is to iterate over both data
3469   // structures and compare against the other graph.
3470   public boolean equals( ReachGraph rg ) {
3471
3472     if( rg == null ) {
3473       return false;
3474     }
3475     
3476     if( !areHeapRegionNodesEqual( rg ) ) {
3477       return false;
3478     }
3479
3480     if( !areVariableNodesEqual( rg ) ) {
3481       return false;
3482     }
3483
3484     if( !areRefEdgesEqual( rg ) ) {
3485       return false;
3486     }
3487
3488     // if everything is equal up to this point,
3489     // assert that allocSites is also equal--
3490     // this data is redundant but kept for efficiency
3491     assert allocSites.equals( rg.allocSites );
3492
3493     return true;
3494   }
3495
3496   
3497   protected boolean areHeapRegionNodesEqual( ReachGraph rg ) {
3498
3499     if( !areallHRNinAalsoinBandequal( this, rg ) ) {
3500       return false;
3501     }
3502
3503     if( !areallHRNinAalsoinBandequal( rg, this ) ) {
3504       return false;
3505     }
3506
3507     return true;
3508   }
3509
3510   static protected boolean areallHRNinAalsoinBandequal( ReachGraph rgA,
3511                                                         ReachGraph rgB ) {
3512     Set      sA = rgA.id2hrn.entrySet();
3513     Iterator iA = sA.iterator();
3514     while( iA.hasNext() ) {
3515       Map.Entry      meA  = (Map.Entry)      iA.next();
3516       Integer        idA  = (Integer)        meA.getKey();
3517       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3518
3519       if( !rgB.id2hrn.containsKey( idA ) ) {
3520         return false;
3521       }
3522
3523       HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3524       if( !hrnA.equalsIncludingAlphaAndPreds( hrnB ) ) {
3525         return false;
3526       }
3527     }
3528     
3529     return true;
3530   }
3531   
3532
3533   protected boolean areVariableNodesEqual( ReachGraph rg ) {
3534
3535     if( !areallVNinAalsoinBandequal( this, rg ) ) {
3536       return false;
3537     }
3538
3539     if( !areallVNinAalsoinBandequal( rg, this ) ) {
3540       return false;
3541     }
3542
3543     return true;
3544   }
3545
3546   static protected boolean areallVNinAalsoinBandequal( ReachGraph rgA,
3547                                                        ReachGraph rgB ) {
3548     Set      sA = rgA.td2vn.entrySet();
3549     Iterator iA = sA.iterator();
3550     while( iA.hasNext() ) {
3551       Map.Entry      meA = (Map.Entry)      iA.next();
3552       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3553
3554       if( !rgB.td2vn.containsKey( tdA ) ) {
3555         return false;
3556       }
3557     }
3558
3559     return true;
3560   }
3561
3562
3563   protected boolean areRefEdgesEqual( ReachGraph rg ) {
3564     if( !areallREinAandBequal( this, rg ) ) {
3565       return false;
3566     }
3567
3568     return true;
3569   }
3570
3571   static protected boolean areallREinAandBequal( ReachGraph rgA,
3572                                                  ReachGraph rgB ) {
3573
3574     // check all the heap region->heap region edges
3575     Set      sA = rgA.id2hrn.entrySet();
3576     Iterator iA = sA.iterator();
3577     while( iA.hasNext() ) {
3578       Map.Entry      meA  = (Map.Entry)      iA.next();
3579       Integer        idA  = (Integer)        meA.getKey();
3580       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3581
3582       // we should have already checked that the same
3583       // heap regions exist in both graphs
3584       assert rgB.id2hrn.containsKey( idA );
3585
3586       if( !areallREfromAequaltoB( rgA, hrnA, rgB ) ) {
3587         return false;
3588       }
3589
3590       // then check every edge in B for presence in A, starting
3591       // from the same parent HeapRegionNode
3592       HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3593
3594       if( !areallREfromAequaltoB( rgB, hrnB, rgA ) ) {
3595         return false;
3596       }
3597     }
3598
3599     // then check all the variable->heap region edges
3600     sA = rgA.td2vn.entrySet();
3601     iA = sA.iterator();
3602     while( iA.hasNext() ) {
3603       Map.Entry      meA = (Map.Entry)      iA.next();
3604       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3605       VariableNode   vnA = (VariableNode)   meA.getValue();
3606
3607       // we should have already checked that the same
3608       // label nodes exist in both graphs
3609       assert rgB.td2vn.containsKey( tdA );
3610
3611       if( !areallREfromAequaltoB( rgA, vnA, rgB ) ) {
3612         return false;
3613       }
3614
3615       // then check every edge in B for presence in A, starting
3616       // from the same parent VariableNode
3617       VariableNode vnB = rgB.td2vn.get( tdA );
3618
3619       if( !areallREfromAequaltoB( rgB, vnB, rgA ) ) {
3620         return false;
3621       }
3622     }
3623
3624     return true;
3625   }
3626
3627
3628   static protected boolean areallREfromAequaltoB( ReachGraph rgA,
3629                                                   RefSrcNode rnA,
3630                                                   ReachGraph rgB ) {
3631
3632     Iterator<RefEdge> itrA = rnA.iteratorToReferencees();
3633     while( itrA.hasNext() ) {
3634       RefEdge        edgeA     = itrA.next();
3635       HeapRegionNode hrnChildA = edgeA.getDst();
3636       Integer        idChildA  = hrnChildA.getID();
3637
3638       assert rgB.id2hrn.containsKey( idChildA );
3639
3640       // at this point we know an edge in graph A exists
3641       // rnA -> idChildA, does this exact edge exist in B?
3642       boolean edgeFound = false;
3643
3644       RefSrcNode rnB = null;
3645       if( rnA instanceof HeapRegionNode ) {
3646         HeapRegionNode hrnA = (HeapRegionNode) rnA;
3647         rnB = rgB.id2hrn.get( hrnA.getID() );
3648       } else {
3649         VariableNode vnA = (VariableNode) rnA;
3650         rnB = rgB.td2vn.get( vnA.getTempDescriptor() );
3651       }
3652
3653       Iterator<RefEdge> itrB = rnB.iteratorToReferencees();
3654       while( itrB.hasNext() ) {
3655         RefEdge        edgeB     = itrB.next();
3656         HeapRegionNode hrnChildB = edgeB.getDst();
3657         Integer        idChildB  = hrnChildB.getID();
3658
3659         if( idChildA.equals( idChildB ) &&
3660             edgeA.typeAndFieldEquals( edgeB ) ) {
3661
3662           // there is an edge in the right place with the right field,
3663           // but do they have the same attributes?
3664           if( edgeA.getBeta().equals( edgeB.getBeta() ) &&
3665               edgeA.equalsPreds( edgeB )
3666               ) {
3667             edgeFound = true;
3668           }
3669         }
3670       }
3671       
3672       if( !edgeFound ) {
3673         return false;
3674       }
3675     }
3676
3677     return true;
3678   }
3679
3680
3681
3682   // this analysis no longer has the "match anything"
3683   // type which was represented by null
3684   protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3685                                              TypeDescriptor td2 ) {
3686     assert td1 != null;
3687     assert td2 != null;
3688
3689     if( td1.isNull() ) {
3690       return td2;
3691     }
3692     if( td2.isNull() ) {
3693       return td1;
3694     }
3695     return typeUtil.mostSpecific( td1, td2 );
3696   }
3697   
3698   protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3699                                              TypeDescriptor td2,
3700                                              TypeDescriptor td3 ) {
3701     
3702     return mostSpecificType( td1, 
3703                              mostSpecificType( td2, td3 )
3704                              );
3705   }  
3706   
3707   protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3708                                              TypeDescriptor td2,
3709                                              TypeDescriptor td3,
3710                                              TypeDescriptor td4 ) {
3711     
3712     return mostSpecificType( mostSpecificType( td1, td2 ), 
3713                              mostSpecificType( td3, td4 )
3714                              );
3715   }  
3716
3717   protected boolean isSuperiorType( TypeDescriptor possibleSuper,
3718                                     TypeDescriptor possibleChild ) {
3719     assert possibleSuper != null;
3720     assert possibleChild != null;
3721     
3722     if( possibleSuper.isNull() ||
3723         possibleChild.isNull() ) {
3724       return true;
3725     }
3726
3727     return typeUtil.isSuperorType( possibleSuper, possibleChild );
3728   }
3729
3730
3731   protected boolean hasMatchingField( HeapRegionNode src, 
3732                                       RefEdge        edge ) {
3733
3734     TypeDescriptor tdSrc = src.getType();    
3735     assert tdSrc != null;
3736
3737     if( tdSrc.isArray() ) {
3738       TypeDescriptor td = edge.getType();
3739       assert td != null;
3740
3741       TypeDescriptor tdSrcDeref = tdSrc.dereference();
3742       assert tdSrcDeref != null;
3743
3744       if( !typeUtil.isSuperorType( tdSrcDeref, td ) ) {
3745         return false;
3746       }
3747
3748       return edge.getField().equals( DisjointAnalysis.arrayElementFieldName );
3749     }
3750
3751     // if it's not a class, it doesn't have any fields to match
3752     if( !tdSrc.isClass() ) {
3753       return false;
3754     }
3755
3756     ClassDescriptor cd = tdSrc.getClassDesc();
3757     while( cd != null ) {      
3758       Iterator fieldItr = cd.getFields();
3759
3760       while( fieldItr.hasNext() ) {     
3761         FieldDescriptor fd = (FieldDescriptor) fieldItr.next();
3762
3763         if( fd.getType().equals( edge.getType() ) &&
3764             fd.getSymbol().equals( edge.getField() ) ) {
3765           return true;
3766         }
3767       }
3768       
3769       cd = cd.getSuperDesc();
3770     }
3771     
3772     // otherwise it is a class with fields
3773     // but we didn't find a match
3774     return false;
3775   }
3776
3777   protected boolean hasMatchingType( RefEdge        edge, 
3778                                      HeapRegionNode dst  ) {
3779     
3780     // if the region has no type, matches everything
3781     TypeDescriptor tdDst = dst.getType();
3782     assert tdDst != null;
3783  
3784     // if the type is not a class or an array, don't
3785     // match because primitives are copied, no aliases
3786     ClassDescriptor cdDst = tdDst.getClassDesc();
3787     if( cdDst == null && !tdDst.isArray() ) {
3788       return false;
3789     }
3790  
3791     // if the edge type is null, it matches everything
3792     TypeDescriptor tdEdge = edge.getType();
3793     assert tdEdge != null;
3794  
3795     return typeUtil.isSuperorType( tdEdge, tdDst );
3796   }
3797   
3798
3799
3800   public void writeGraph( String  graphName,
3801                           boolean writeLabels,
3802                           boolean labelSelect,
3803                           boolean pruneGarbage,
3804                           boolean hideSubsetReachability,
3805                           boolean hideEdgeTaints
3806                           ) throws java.io.IOException {
3807     writeGraph( graphName,
3808                 writeLabels,
3809                 labelSelect,
3810                 pruneGarbage,
3811                 hideSubsetReachability,
3812                 hideEdgeTaints,
3813                 null );
3814   }
3815
3816   public void writeGraph( String       graphName,
3817                           boolean      writeLabels,
3818                           boolean      labelSelect,
3819                           boolean      pruneGarbage,
3820                           boolean      hideSubsetReachability,
3821                           boolean      hideEdgeTaints,
3822                           Set<Integer> callerNodeIDsCopiedToCallee
3823                           ) throws java.io.IOException {
3824     
3825     // remove all non-word characters from the graph name so
3826     // the filename and identifier in dot don't cause errors
3827     graphName = graphName.replaceAll( "[\\W]", "" );
3828
3829     BufferedWriter bw = 
3830       new BufferedWriter( new FileWriter( graphName+".dot" ) );
3831
3832     bw.write( "digraph "+graphName+" {\n" );
3833
3834
3835     // this is an optional step to form the callee-reachable
3836     // "cut-out" into a DOT cluster for visualization
3837     if( callerNodeIDsCopiedToCallee != null ) {
3838
3839       bw.write( "  subgraph cluster0 {\n" );
3840       bw.write( "    color=blue;\n" );
3841
3842       Iterator i = id2hrn.entrySet().iterator();
3843       while( i.hasNext() ) {
3844         Map.Entry      me  = (Map.Entry)      i.next();
3845         HeapRegionNode hrn = (HeapRegionNode) me.getValue();      
3846         
3847         if( callerNodeIDsCopiedToCallee.contains( hrn.getID() ) ) {
3848           bw.write( "    "+hrn.toString()+
3849                     hrn.toStringDOT( hideSubsetReachability )+
3850                     ";\n" );
3851           
3852         }
3853       }
3854
3855       bw.write( "  }\n" );
3856     }
3857
3858
3859     Set<HeapRegionNode> visited = new HashSet<HeapRegionNode>();
3860
3861     // then visit every heap region node    
3862     Iterator i = id2hrn.entrySet().iterator();
3863     while( i.hasNext() ) {
3864       Map.Entry      me  = (Map.Entry)      i.next();
3865       HeapRegionNode hrn = (HeapRegionNode) me.getValue();      
3866
3867       // only visit nodes worth writing out--for instance
3868       // not every node at an allocation is referenced
3869       // (think of it as garbage-collected), etc.
3870       if( !pruneGarbage        ||
3871           hrn.isOutOfContext()
3872           ) {
3873
3874         if( !visited.contains( hrn ) ) {
3875           traverseHeapRegionNodes( hrn,
3876                                    bw,
3877                                    null,
3878                                    visited,
3879                                    hideSubsetReachability,
3880                                    hideEdgeTaints,
3881                                    callerNodeIDsCopiedToCallee );
3882         }
3883       }
3884     }
3885
3886     bw.write( "  graphTitle[label=\""+graphName+"\",shape=box];\n" );
3887   
3888
3889     // then visit every label node, useful for debugging
3890     if( writeLabels ) {
3891       i = td2vn.entrySet().iterator();
3892       while( i.hasNext() ) {
3893         Map.Entry    me = (Map.Entry)    i.next();
3894         VariableNode vn = (VariableNode) me.getValue();
3895         
3896         if( labelSelect ) {
3897           String labelStr = vn.getTempDescriptorString();
3898           if( labelStr.startsWith( "___temp" )     ||
3899               labelStr.startsWith( "___dst" )      ||
3900               labelStr.startsWith( "___srctmp" )   ||
3901               labelStr.startsWith( "___neverused" )
3902               ) {
3903             continue;
3904           }
3905         }
3906         
3907         Iterator<RefEdge> heapRegionsItr = vn.iteratorToReferencees();
3908         while( heapRegionsItr.hasNext() ) {
3909           RefEdge        edge = heapRegionsItr.next();
3910           HeapRegionNode hrn  = edge.getDst();
3911           
3912           if( !visited.contains( hrn ) ) {
3913             traverseHeapRegionNodes( hrn,
3914                                      bw,
3915                                      null,
3916                                      visited,
3917                                      hideSubsetReachability,
3918                                      hideEdgeTaints,
3919                                      callerNodeIDsCopiedToCallee );
3920           }
3921           
3922           bw.write( "  "+vn.toString()+
3923                     " -> "+hrn.toString()+
3924                     edge.toStringDOT( hideSubsetReachability, "" )+
3925                     ";\n" );
3926         }
3927       }
3928     }
3929     
3930     bw.write( "}\n" );
3931     bw.close();
3932   }
3933
3934   protected void traverseHeapRegionNodes( HeapRegionNode      hrn,
3935                                           BufferedWriter      bw,
3936                                           TempDescriptor      td,
3937                                           Set<HeapRegionNode> visited,
3938                                           boolean             hideSubsetReachability,
3939                                           boolean             hideEdgeTaints,
3940                                           Set<Integer>        callerNodeIDsCopiedToCallee
3941                                           ) throws java.io.IOException {
3942
3943     if( visited.contains( hrn ) ) {
3944       return;
3945     }
3946     visited.add( hrn );
3947
3948     // if we're drawing the callee-view subgraph, only
3949     // write out the node info if it hasn't already been
3950     // written
3951     if( callerNodeIDsCopiedToCallee == null ||
3952         !callerNodeIDsCopiedToCallee.contains( hrn.getID() ) 
3953         ) {
3954       bw.write( "  "+hrn.toString()+
3955                 hrn.toStringDOT( hideSubsetReachability )+
3956                 ";\n" );
3957     }
3958
3959     Iterator<RefEdge> childRegionsItr = hrn.iteratorToReferencees();
3960     while( childRegionsItr.hasNext() ) {
3961       RefEdge        edge     = childRegionsItr.next();
3962       HeapRegionNode hrnChild = edge.getDst();
3963
3964       if( callerNodeIDsCopiedToCallee != null &&
3965           (edge.getSrc() instanceof HeapRegionNode) ) {
3966         HeapRegionNode hrnSrc = (HeapRegionNode) edge.getSrc();
3967         if( callerNodeIDsCopiedToCallee.contains( hrnSrc.getID()        ) &&
3968             callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
3969             ) {
3970           bw.write( "  "+hrn.toString()+
3971                     " -> "+hrnChild.toString()+
3972                     edge.toStringDOT( hideSubsetReachability, ",color=blue" )+
3973                     ";\n");
3974         } else if( !callerNodeIDsCopiedToCallee.contains( hrnSrc.getID()       ) &&
3975                    callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
3976                    ) {
3977           bw.write( "  "+hrn.toString()+
3978                     " -> "+hrnChild.toString()+
3979                     edge.toStringDOT( hideSubsetReachability, ",color=blue,style=dashed" )+
3980                     ";\n");
3981         } else {
3982           bw.write( "  "+hrn.toString()+
3983                     " -> "+hrnChild.toString()+
3984                     edge.toStringDOT( hideSubsetReachability, "" )+
3985                     ";\n");
3986         }
3987       } else {
3988         bw.write( "  "+hrn.toString()+
3989                   " -> "+hrnChild.toString()+
3990                   edge.toStringDOT( hideSubsetReachability, "" )+
3991                   ";\n");
3992       }
3993       
3994       traverseHeapRegionNodes( hrnChild,
3995                                bw,
3996                                td,
3997                                visited,
3998                                hideSubsetReachability,
3999                                hideEdgeTaints,
4000                                callerNodeIDsCopiedToCallee );
4001     }
4002   }  
4003   
4004         public Set<HeapRegionNode> findCommonReachableNodes(HeapRegionNode hrn1,
4005                         HeapRegionNode hrn2) {
4006
4007                 Set<HeapRegionNode> reachableNodes1 = new HashSet<HeapRegionNode>();
4008                 Set<HeapRegionNode> reachableNodes2 = new HashSet<HeapRegionNode>();
4009
4010                 Set<HeapRegionNode> todoNodes1 = new HashSet<HeapRegionNode>();
4011                 todoNodes1.add(hrn1);
4012
4013                 Set<HeapRegionNode> todoNodes2 = new HashSet<HeapRegionNode>();
4014                 todoNodes2.add(hrn2);
4015
4016                 // follow links until all reachable nodes have been found
4017                 while (!todoNodes1.isEmpty()) {
4018                         HeapRegionNode hrn = todoNodes1.iterator().next();
4019                         todoNodes1.remove(hrn);
4020                         reachableNodes1.add(hrn);
4021
4022                         Iterator<RefEdge> edgeItr = hrn.iteratorToReferencees();
4023                         while (edgeItr.hasNext()) {
4024                                 RefEdge edge = edgeItr.next();
4025
4026                                 if (!reachableNodes1.contains(edge.getDst())) {
4027                                         todoNodes1.add(edge.getDst());
4028                                 }
4029                         }
4030                 }
4031
4032                 while (!todoNodes2.isEmpty()) {
4033                         HeapRegionNode hrn = todoNodes2.iterator().next();
4034                         todoNodes2.remove(hrn);
4035                         reachableNodes2.add(hrn);
4036
4037                         Iterator<RefEdge> edgeItr = hrn.iteratorToReferencees();
4038                         while (edgeItr.hasNext()) {
4039                                 RefEdge edge = edgeItr.next();
4040
4041                                 if (!reachableNodes2.contains(edge.getDst())) {
4042                                         todoNodes2.add(edge.getDst());
4043                                 }
4044                         }
4045                 }
4046
4047                 Set<HeapRegionNode> intersection = new HashSet<HeapRegionNode>(
4048                                 reachableNodes1);
4049
4050                 intersection.retainAll(reachableNodes2);
4051
4052                 return intersection;
4053         }
4054
4055         public Set<HeapRegionNode> mayReachSharedObjects(HeapRegionNode hrn1,
4056                         HeapRegionNode hrn2) {
4057                 assert hrn1 != null;
4058                 assert hrn2 != null;
4059
4060                 // then get the various tokens for these heap regions
4061                 ReachTuple h1 = ReachTuple.factory(hrn1.getID(),
4062                                 !hrn1.isSingleObject(), ReachTuple.ARITY_ONE, false);
4063
4064                 ReachTuple h1plus = ReachTuple.factory(hrn1.getID(), !hrn1
4065                                 .isSingleObject(), ReachTuple.ARITY_ONEORMORE, false);
4066
4067                 ReachTuple h1star = ReachTuple.factory(hrn1.getID(), !hrn1
4068                                 .isSingleObject(), ReachTuple.ARITY_ZEROORMORE, false);
4069
4070                 ReachTuple h2 = ReachTuple.factory(hrn2.getID(),
4071                                 !hrn2.isSingleObject(), ReachTuple.ARITY_ONE, false);
4072
4073                 ReachTuple h2plus = ReachTuple.factory(hrn2.getID(), !hrn2
4074                                 .isSingleObject(), ReachTuple.ARITY_ONEORMORE, false);
4075
4076                 ReachTuple h2star = ReachTuple.factory(hrn2.getID(), !hrn2
4077                                 .isSingleObject(), ReachTuple.ARITY_ZEROORMORE, false);
4078
4079                 // then get the merged beta of all out-going edges from these heap
4080                 // regions
4081
4082                 ReachSet beta1 = ReachSet.factory();
4083                 Iterator<RefEdge> itrEdge = hrn1.iteratorToReferencees();
4084                 while (itrEdge.hasNext()) {
4085                         RefEdge edge = itrEdge.next();
4086                         beta1 = Canonical.unionORpreds(beta1, edge.getBeta());
4087                 }
4088
4089                 ReachSet beta2 = ReachSet.factory();
4090                 itrEdge = hrn2.iteratorToReferencees();
4091                 while (itrEdge.hasNext()) {
4092                         RefEdge edge = itrEdge.next();
4093                         beta2 = Canonical.unionORpreds(beta2, edge.getBeta());
4094                 }
4095
4096                 boolean aliasDetected = false;
4097
4098                 // only do this one if they are different tokens
4099                 if (h1 != h2 && beta1.containsStateWithBoth(h1, h2)) {
4100                         aliasDetected = true;
4101                 }
4102                 if (beta1.containsStateWithBoth(h1plus, h2)) {
4103                         aliasDetected = true;
4104                 }
4105                 if (beta1.containsStateWithBoth(h1star, h2)) {
4106                         aliasDetected = true;
4107                 }
4108                 if (beta1.containsStateWithBoth(h1, h2plus)) {
4109                         aliasDetected = true;
4110                 }
4111                 if (beta1.containsStateWithBoth(h1plus, h2plus)) {
4112                         aliasDetected = true;
4113                 }
4114                 if (beta1.containsStateWithBoth(h1star, h2plus)) {
4115                         aliasDetected = true;
4116                 }
4117                 if (beta1.containsStateWithBoth(h1, h2star)) {
4118                         aliasDetected = true;
4119                 }
4120                 if (beta1.containsStateWithBoth(h1plus, h2star)) {
4121                         aliasDetected = true;
4122                 }
4123                 if (beta1.containsStateWithBoth(h1star, h2star)) {
4124                         aliasDetected = true;
4125                 }
4126
4127                 if (h1 != h2 && beta2.containsStateWithBoth(h1, h2)) {
4128                         aliasDetected = true;
4129                 }
4130                 if (beta2.containsStateWithBoth(h1plus, h2)) {
4131                         aliasDetected = true;
4132                 }
4133                 if (beta2.containsStateWithBoth(h1star, h2)) {
4134                         aliasDetected = true;
4135                 }
4136                 if (beta2.containsStateWithBoth(h1, h2plus)) {
4137                         aliasDetected = true;
4138                 }
4139                 if (beta2.containsStateWithBoth(h1plus, h2plus)) {
4140                         aliasDetected = true;
4141                 }
4142                 if (beta2.containsStateWithBoth(h1star, h2plus)) {
4143                         aliasDetected = true;
4144                 }
4145                 if (beta2.containsStateWithBoth(h1, h2star)) {
4146                         aliasDetected = true;
4147                 }
4148                 if (beta2.containsStateWithBoth(h1plus, h2star)) {
4149                         aliasDetected = true;
4150                 }
4151                 if (beta2.containsStateWithBoth(h1star, h2star)) {
4152                         aliasDetected = true;
4153                 }
4154
4155                 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4156                 if (aliasDetected) {
4157                         common = findCommonReachableNodes(hrn1, hrn2);
4158                         if (!(DISABLE_STRONG_UPDATES || DISABLE_GLOBAL_SWEEP)) {
4159                                 assert !common.isEmpty();
4160                         }
4161                 }
4162
4163                 return common;
4164         }
4165
4166         public Set<HeapRegionNode> mayReachSharedObjects(FlatMethod fm,
4167                         Integer paramIndex1, Integer paramIndex2) {
4168
4169                 // get parameter's heap regions
4170                 TempDescriptor paramTemp1 = fm.getParameter(paramIndex1.intValue());
4171                 VariableNode argVar1 = getVariableNodeFromTemp(paramTemp1);
4172                 RefEdge argEdge1 = argVar1.iteratorToReferencees().next();
4173                 HeapRegionNode hrnParam1 = argEdge1.getDst();
4174
4175                 TempDescriptor paramTemp2 = fm.getParameter(paramIndex2.intValue());
4176                 VariableNode argVar2 = getVariableNodeFromTemp(paramTemp2);
4177                 RefEdge argEdge2 = argVar2.iteratorToReferencees().next();
4178                 HeapRegionNode hrnParam2 = argEdge2.getDst();
4179
4180                 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4181                 common.addAll(mayReachSharedObjects(hrnParam1, hrnParam2));
4182
4183                 return common;
4184         }
4185
4186         public Set<HeapRegionNode> mayReachSharedObjects(FlatMethod fm,
4187                         Integer paramIndex, AllocSite as) {
4188
4189                 // get parameter's heap regions
4190                 TempDescriptor paramTemp = fm.getParameter(paramIndex.intValue());
4191                 VariableNode argVar = getVariableNodeFromTemp(paramTemp);
4192                 RefEdge argEdge = argVar.iteratorToReferencees().next();
4193                 HeapRegionNode hrnParam = argEdge.getDst();
4194
4195                 // get summary node
4196                 assert id2hrn.containsKey(as.getSummary());
4197                 HeapRegionNode hrnSummary = id2hrn.get(as.getSummary());
4198                 assert hrnSummary != null;
4199
4200                 Set<HeapRegionNode> common = mayReachSharedObjects(hrnParam, hrnSummary);
4201
4202                 // check for other nodes
4203                 for (int i = 0; i < as.getAllocationDepth(); ++i) {
4204
4205                         assert id2hrn.containsKey(as.getIthOldest(i));
4206                         HeapRegionNode hrnIthOldest = id2hrn.get(as.getIthOldest(i));
4207                         assert hrnIthOldest != null;
4208
4209                         common = mayReachSharedObjects(hrnParam, hrnIthOldest);
4210
4211                 }
4212
4213                 return common;
4214         }
4215
4216         public Set<HeapRegionNode> mayReachSharedObjects(AllocSite as1,
4217                         AllocSite as2) {
4218
4219                 // get summary node 1's alpha
4220                 Integer idSum1 = as1.getSummary();
4221                 assert id2hrn.containsKey(idSum1);
4222                 HeapRegionNode hrnSum1 = id2hrn.get(idSum1);
4223                 assert hrnSum1 != null;
4224
4225                 // get summary node 2's alpha
4226                 Integer idSum2 = as2.getSummary();
4227                 assert id2hrn.containsKey(idSum2);
4228                 HeapRegionNode hrnSum2 = id2hrn.get(idSum2);
4229                 assert hrnSum2 != null;
4230
4231                 Set<HeapRegionNode> common = mayReachSharedObjects(hrnSum1, hrnSum2);
4232
4233                 // check sum2 against alloc1 nodes
4234                 for (int i = 0; i < as1.getAllocationDepth(); ++i) {
4235                         Integer idI1 = as1.getIthOldest(i);
4236                         assert id2hrn.containsKey(idI1);
4237                         HeapRegionNode hrnI1 = id2hrn.get(idI1);
4238                         assert hrnI1 != null;
4239
4240                         common.addAll(mayReachSharedObjects(hrnI1, hrnSum2));
4241                 }
4242
4243                 // check sum1 against alloc2 nodes
4244                 for (int i = 0; i < as2.getAllocationDepth(); ++i) {
4245                         Integer idI2 = as2.getIthOldest(i);
4246                         assert id2hrn.containsKey(idI2);
4247                         HeapRegionNode hrnI2 = id2hrn.get(idI2);
4248                         assert hrnI2 != null;
4249
4250                         common.addAll(mayReachSharedObjects(hrnSum1, hrnI2));
4251
4252                         // while we're at it, do an inner loop for alloc2 vs alloc1 nodes
4253                         for (int j = 0; j < as1.getAllocationDepth(); ++j) {
4254                                 Integer idI1 = as1.getIthOldest(j);
4255
4256                                 // if these are the same site, don't look for the same token, no
4257                                 // alias.
4258                                 // different tokens of the same site could alias together though
4259                                 if (idI1.equals(idI2)) {
4260                                         continue;
4261                                 }
4262
4263                                 HeapRegionNode hrnI1 = id2hrn.get(idI1);
4264
4265                                 common.addAll(mayReachSharedObjects(hrnI1, hrnI2));
4266                         }
4267                 }
4268
4269                 return common;
4270         }
4271   
4272 }