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