Bug fix, subtle errors in exist pred hashcode, and a cycle pred.hashCode to state...
[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 temps
16   protected static TempDescriptor tdReturn;
17   protected static TempDescriptor tdStrLiteralBytes;
18   
19   public static void initOutOfScopeTemps() {
20     tdReturn = new TempDescriptor("_Return___");
21
22     tdStrLiteralBytes = 
23       new TempDescriptor("_strLiteralBytes___",
24                          new TypeDescriptor(TypeDescriptor.CHAR).makeArray( state )
25                          );
26   }
27
28   // predicate constants
29   public static final ExistPred predTrue   = ExistPred.factory();    // if no args, true
30   public static final ExistPredSet predsEmpty = ExistPredSet.factory();
31   public static final ExistPredSet predsTrue  = ExistPredSet.factory(predTrue);
32
33   // some frequently used reachability constants
34   protected static final ReachState rstateEmpty        = ReachState.factory();
35   protected static final ReachSet rsetEmpty          = ReachSet.factory();
36   protected static final ReachSet rsetWithEmptyState = Canonical.changePredsTo(ReachSet.factory(rstateEmpty),
37                                                                                predsTrue);
38
39   // from DisjointAnalysis for convenience
40   protected static int allocationDepth   = -1;
41   protected static TypeUtil typeUtil          = null;
42   protected static State state             = null;
43
44
45   // variable and heap region nodes indexed by unique ID
46   public Hashtable<Integer,        HeapRegionNode> id2hrn;
47   public Hashtable<TempDescriptor, VariableNode  > td2vn;
48
49   // convenient set of alloc sites for all heap regions
50   // present in the graph without having to search
51   public Set<AllocSite> allocSites;
52
53   // set of inaccessible variables for current program statement
54   // with respect to stall-site analysis
55   public Set<TempDescriptor> inaccessibleVars;
56
57
58   public ReachGraph() {
59     id2hrn           = new Hashtable<Integer,        HeapRegionNode>();
60     td2vn            = new Hashtable<TempDescriptor, VariableNode  >();
61     allocSites       = new HashSet<AllocSite>();
62     inaccessibleVars = new HashSet<TempDescriptor>();
63   }
64
65
66   // temp descriptors are globally unique and map to
67   // exactly one variable node, easy
68   protected VariableNode getVariableNodeFromTemp(TempDescriptor td) {
69     assert td != null;
70
71     if( !td2vn.containsKey(td) ) {
72       td2vn.put(td, new VariableNode(td) );
73     }
74
75     return td2vn.get(td);
76   }
77
78   //This method is created for client modules to access the Reachgraph
79   //after the analysis is done and no modifications are to be made.
80   public VariableNode getVariableNodeNoMutation(TempDescriptor td) {
81     assert td != null;
82
83     if( !td2vn.containsKey(td) ) {
84       return null;
85     }
86
87     return td2vn.get(td);
88   }
89
90   public boolean hasVariable(TempDescriptor td) {
91     return td2vn.containsKey(td);
92   }
93
94
95   // this suite of methods can be used to assert a
96   // very important property of ReachGraph objects:
97   // some element, HeapRegionNode, RefEdge etc.
98   // should be referenced by at most ONE ReachGraph!!
99   // If a heap region or edge or variable should be
100   // in another graph, make a new object with
101   // equivalent properties for a new graph
102   public boolean belongsToThis(RefSrcNode rsn) {
103     if( rsn instanceof VariableNode ) {
104       VariableNode vn = (VariableNode) rsn;
105       return this.td2vn.get(vn.getTempDescriptor() ) == vn;
106     }
107     HeapRegionNode hrn = (HeapRegionNode) rsn;
108     return this.id2hrn.get(hrn.getID() ) == hrn;
109   }
110
111
112
113
114
115   // the reason for this method is to have the option
116   // of creating new heap regions with specific IDs, or
117   // duplicating heap regions with specific IDs (especially
118   // in the merge() operation) or to create new heap
119   // regions with a new unique ID
120   protected HeapRegionNode
121   createNewHeapRegionNode(Integer id,
122                           boolean isSingleObject,
123                           boolean isNewSummary,
124                           boolean isOutOfContext,
125                           TypeDescriptor type,
126                           AllocSite allocSite,
127                           ReachSet inherent,
128                           ReachSet alpha,
129                           ExistPredSet preds,
130                           String description
131                           ) {
132
133     TypeDescriptor typeToUse = null;
134     if( allocSite != null ) {
135       typeToUse = allocSite.getType();
136       allocSites.add(allocSite);
137     } else {
138       typeToUse = type;
139     }
140
141     boolean markForAnalysis = false;
142     if( allocSite != null && allocSite.isFlagged() ) {
143       markForAnalysis = true;
144     }
145
146     if( allocSite == null ) {
147       assert !markForAnalysis;
148
149     } else if( markForAnalysis != allocSite.isFlagged() ) {
150       assert false;
151     }
152
153
154     if( id == null ) {
155       id = DisjointAnalysis.generateUniqueHeapRegionNodeID();
156     }
157
158     if( inherent == null ) {
159       if( markForAnalysis ) {
160         inherent =
161           Canonical.changePredsTo(
162             ReachSet.factory(
163               ReachState.factory(
164                 ReachTuple.factory(id,
165                                    !isSingleObject,
166                                    ReachTuple.ARITY_ONE,
167                                    false                                                        // out-of-context
168                                    )
169                 )
170               ),
171             predsTrue
172             );
173       } else {
174         inherent = rsetWithEmptyState;
175       }
176     }
177
178     if( alpha == null ) {
179       alpha = inherent;
180     }
181
182     assert preds != null;
183
184     HeapRegionNode hrn = new HeapRegionNode(id,
185                                             isSingleObject,
186                                             markForAnalysis,
187                                             isNewSummary,
188                                             isOutOfContext,
189                                             typeToUse,
190                                             allocSite,
191                                             inherent,
192                                             alpha,
193                                             preds,
194                                             description);
195     id2hrn.put(id, hrn);
196     return hrn;
197   }
198
199
200
201   ////////////////////////////////////////////////
202   //
203   //  Low-level referencee and referencer methods
204   //
205   //  These methods provide the lowest level for
206   //  creating references between reachability nodes
207   //  and handling the details of maintaining both
208   //  list of referencers and referencees.
209   //
210   ////////////////////////////////////////////////
211   protected void addRefEdge(RefSrcNode referencer,
212                             HeapRegionNode referencee,
213                             RefEdge edge) {
214     assert referencer != null;
215     assert referencee != null;
216     assert edge       != null;
217     assert edge.getSrc() == referencer;
218     assert edge.getDst() == referencee;
219     assert belongsToThis(referencer);
220     assert belongsToThis(referencee);
221
222     // edges are getting added twice to graphs now, the
223     // kind that should have abstract facts merged--use
224     // this check to prevent that
225     assert referencer.getReferenceTo(referencee,
226                                      edge.getType(),
227                                      edge.getField()
228                                      ) == null;
229
230     referencer.addReferencee(edge);
231     referencee.addReferencer(edge);
232   }
233
234   protected void removeRefEdge(RefEdge e) {
235     removeRefEdge(e.getSrc(),
236                   e.getDst(),
237                   e.getType(),
238                   e.getField() );
239   }
240
241   protected void removeRefEdge(RefSrcNode referencer,
242                                HeapRegionNode referencee,
243                                TypeDescriptor type,
244                                String field) {
245     assert referencer != null;
246     assert referencee != null;
247
248     RefEdge edge = referencer.getReferenceTo(referencee,
249                                              type,
250                                              field);
251     assert edge != null;
252     assert edge == referencee.getReferenceFrom(referencer,
253                                                type,
254                                                field);
255
256     referencer.removeReferencee(edge);
257     referencee.removeReferencer(edge);
258   }
259
260   // return whether at least one edge was removed
261   protected boolean clearRefEdgesFrom(RefSrcNode referencer,
262                                       TypeDescriptor type,
263                                       String field,
264                                       boolean removeAll) {
265     assert referencer != null;
266
267     boolean atLeastOneEdgeRemoved = false;
268
269     // get a copy of the set to iterate over, otherwise
270     // we will be trying to take apart the set as we
271     // are iterating over it, which won't work
272     Iterator<RefEdge> i = referencer.iteratorToReferenceesClone();
273     while( i.hasNext() ) {
274       RefEdge edge = i.next();
275
276       if( removeAll                                          ||
277           (edge.typeEquals(type) && edge.fieldEquals(field))
278           ) {
279
280         HeapRegionNode referencee = edge.getDst();
281
282         removeRefEdge(referencer,
283                       referencee,
284                       edge.getType(),
285                       edge.getField() );
286
287         atLeastOneEdgeRemoved = true;
288       }
289     }
290
291     return atLeastOneEdgeRemoved;
292   }
293
294   protected void clearRefEdgesTo(HeapRegionNode referencee,
295                                  TypeDescriptor type,
296                                  String field,
297                                  boolean removeAll) {
298     assert referencee != null;
299
300     // get a copy of the set to iterate over, otherwise
301     // we will be trying to take apart the set as we
302     // are iterating over it, which won't work
303     Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
304     while( i.hasNext() ) {
305       RefEdge edge = i.next();
306
307       if( removeAll                                          ||
308           (edge.typeEquals(type) && edge.fieldEquals(field))
309           ) {
310
311         RefSrcNode referencer = edge.getSrc();
312
313         removeRefEdge(referencer,
314                       referencee,
315                       edge.getType(),
316                       edge.getField() );
317       }
318     }
319   }
320
321   protected void clearNonVarRefEdgesTo(HeapRegionNode referencee) {
322     assert referencee != null;
323
324     // get a copy of the set to iterate over, otherwise
325     // we will be trying to take apart the set as we
326     // are iterating over it, which won't work
327     Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
328     while( i.hasNext() ) {
329       RefEdge edge = i.next();
330       RefSrcNode referencer = edge.getSrc();
331       if( !(referencer instanceof VariableNode) ) {
332         removeRefEdge(referencer,
333                       referencee,
334                       edge.getType(),
335                       edge.getField() );
336       }
337     }
338   }
339
340   // this is a common operation in many transfer functions: we want
341   // to add an edge, but if there is already such an edge we should
342   // merge the properties of the existing and the new edges
343   protected void addEdgeOrMergeWithExisting(RefEdge edgeNew) {
344
345     RefSrcNode src = edgeNew.getSrc();
346     assert belongsToThis(src);
347
348     HeapRegionNode dst = edgeNew.getDst();
349     assert belongsToThis(dst);
350
351     // look to see if an edge with same field exists
352     // and merge with it, otherwise just add the edge
353     RefEdge edgeExisting = src.getReferenceTo(dst,
354                                               edgeNew.getType(),
355                                               edgeNew.getField()
356                                               );
357
358     if( edgeExisting != null ) {
359       edgeExisting.setBeta(
360         Canonical.unionORpreds(edgeExisting.getBeta(),
361                                edgeNew.getBeta()
362                                )
363         );
364       edgeExisting.setPreds(
365         Canonical.join(edgeExisting.getPreds(),
366                        edgeNew.getPreds()
367                        )
368         );
369       edgeExisting.setTaints(
370         Canonical.unionORpreds(edgeExisting.getTaints(),
371                                edgeNew.getTaints()
372                                )
373         );
374
375     } else {
376       addRefEdge(src, dst, edgeNew);
377     }
378   }
379
380
381
382   ////////////////////////////////////////////////////
383   //
384   //  Assignment Operation Methods
385   //
386   //  These methods are high-level operations for
387   //  modeling program assignment statements using
388   //  the low-level reference create/remove methods
389   //  above.
390   //
391   ////////////////////////////////////////////////////
392
393   public void assignTempEqualToStringLiteral(TempDescriptor  x,
394                                              AllocSite       asStringLiteral,
395                                              AllocSite       asStringLiteralBytes,
396                                              FieldDescriptor fdStringBytesField) {
397     // model this to get points-to information right for
398     // pointers to string literals, even though it doesn't affect
399     // reachability paths in the heap
400     assignTempEqualToNewAlloc( x, 
401                                asStringLiteral );
402
403     assignTempEqualToNewAlloc( tdStrLiteralBytes, 
404                                asStringLiteralBytes );
405
406     assignTempXFieldFEqualToTempY( x,
407                                    fdStringBytesField,
408                                    tdStrLiteralBytes,
409                                    null );
410   }
411
412
413   public void assignTempXEqualToTempY(TempDescriptor x,
414                                       TempDescriptor y) {
415     assignTempXEqualToCastedTempY(x, y, null);
416
417   }
418
419   public void assignTempXEqualToCastedTempY(TempDescriptor x,
420                                             TempDescriptor y,
421                                             TypeDescriptor tdCast) {
422
423     VariableNode lnX = getVariableNodeFromTemp(x);
424     VariableNode lnY = getVariableNodeFromTemp(y);
425
426     clearRefEdgesFrom(lnX, null, null, true);
427
428     // note it is possible that the types of temps in the
429     // flat node to analyze will reveal that some typed
430     // edges in the reachability graph are impossible
431     Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
432
433     Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
434     while( itrYhrn.hasNext() ) {
435       RefEdge edgeY      = itrYhrn.next();
436       HeapRegionNode referencee = edgeY.getDst();
437       RefEdge edgeNew    = edgeY.copy();
438
439       if( !isSuperiorType(x.getType(), edgeY.getType() ) ) {
440         impossibleEdges.add(edgeY);
441         continue;
442       }
443
444       edgeNew.setSrc(lnX);
445
446       if( tdCast == null ) {
447         edgeNew.setType(mostSpecificType(y.getType(),
448                                          edgeY.getType(),
449                                          referencee.getType()
450                                          )
451                         );
452       } else {
453         edgeNew.setType(mostSpecificType(y.getType(),
454                                          edgeY.getType(),
455                                          referencee.getType(),
456                                          tdCast
457                                          )
458                         );
459       }
460
461       edgeNew.setField(null);
462
463       addRefEdge(lnX, referencee, edgeNew);
464     }
465
466     Iterator<RefEdge> itrImp = impossibleEdges.iterator();
467     while( itrImp.hasNext() ) {
468       RefEdge edgeImp = itrImp.next();
469       removeRefEdge(edgeImp);
470     }
471   }
472
473
474   public void assignTempXEqualToTempYFieldF(TempDescriptor x,
475                                             TempDescriptor y,
476                                             FieldDescriptor f,
477                                             FlatNode currentProgramPoint
478                                             ) {
479
480     VariableNode lnX = getVariableNodeFromTemp(x);
481     VariableNode lnY = getVariableNodeFromTemp(y);
482
483     clearRefEdgesFrom(lnX, null, null, true);
484
485     // note it is possible that the types of temps in the
486     // flat node to analyze will reveal that some typed
487     // edges in the reachability graph are impossible
488     Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
489
490     Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
491     while( itrYhrn.hasNext() ) {
492       RefEdge edgeY = itrYhrn.next();
493       HeapRegionNode hrnY  = edgeY.getDst();
494       ReachSet betaY = edgeY.getBeta();
495
496       Iterator<RefEdge> itrHrnFhrn = hrnY.iteratorToReferencees();
497
498       while( itrHrnFhrn.hasNext() ) {
499         RefEdge edgeHrn = itrHrnFhrn.next();
500         HeapRegionNode hrnHrn  = edgeHrn.getDst();
501         ReachSet betaHrn = edgeHrn.getBeta();
502
503         // prune edges that are not a matching field
504         if( edgeHrn.getType() != null &&
505             !edgeHrn.getField().equals(f.getSymbol() )
506             ) {
507           continue;
508         }
509
510         // check for impossible edges
511         if( !isSuperiorType(x.getType(), edgeHrn.getType() ) ) {
512           impossibleEdges.add(edgeHrn);
513           continue;
514         }
515
516         TypeDescriptor tdNewEdge =
517           mostSpecificType(edgeHrn.getType(),
518                            hrnHrn.getType()
519                            );
520
521         TaintSet taints = Canonical.unionORpreds(edgeHrn.getTaints(),
522                                                  edgeY.getTaints()
523                                                  );
524         if( state.RCR ) {
525           // the DFJ way to generate taints changes for field statements
526           taints = Canonical.changeWhereDefined(taints,
527                                                 currentProgramPoint);
528         }
529
530         RefEdge edgeNew = new RefEdge(lnX,
531                                       hrnHrn,
532                                       tdNewEdge,
533                                       null,
534                                       Canonical.intersection(betaY, betaHrn),
535                                       predsTrue,
536                                       taints
537                                       );
538
539         addEdgeOrMergeWithExisting(edgeNew);
540       }
541     }
542
543     Iterator<RefEdge> itrImp = impossibleEdges.iterator();
544     while( itrImp.hasNext() ) {
545       RefEdge edgeImp = itrImp.next();
546       removeRefEdge(edgeImp);
547     }
548
549     // anytime you might remove edges between heap regions
550     // you must global sweep to clean up broken reachability
551     if( !impossibleEdges.isEmpty() ) {
552       if( !DISABLE_GLOBAL_SWEEP ) {
553         globalSweep();
554       }
555     }
556
557   }
558
559
560   // return whether a strong update was actually effected
561   public boolean assignTempXFieldFEqualToTempY(TempDescriptor x,
562                                                FieldDescriptor f,
563                                                TempDescriptor y,
564                                                FlatNode currentProgramPoint
565                                                ) {
566
567     VariableNode lnX = getVariableNodeFromTemp(x);
568     VariableNode lnY = getVariableNodeFromTemp(y);
569
570     HashSet<HeapRegionNode> nodesWithNewAlpha = new HashSet<HeapRegionNode>();
571     HashSet<RefEdge>        edgesWithNewBeta  = new HashSet<RefEdge>();
572
573     // note it is possible that the types of temps in the
574     // flat node to analyze will reveal that some typed
575     // edges in the reachability graph are impossible
576     Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
577
578     // first look for possible strong updates and remove those edges
579     boolean strongUpdateCond          = false;
580     boolean edgeRemovedByStrongUpdate = false;
581
582     Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
583     while( itrXhrn.hasNext() ) {
584       RefEdge edgeX = itrXhrn.next();
585       HeapRegionNode hrnX  = edgeX.getDst();
586
587       // we can do a strong update here if one of two cases holds
588       if( f != null &&
589           f != DisjointAnalysis.getArrayField(f.getType() ) &&
590           (   (hrnX.getNumReferencers()                         == 1) || // case 1
591               (hrnX.isSingleObject() && lnX.getNumReferencees() == 1)    // case 2
592           )
593           ) {
594         if( !DISABLE_STRONG_UPDATES ) {
595           strongUpdateCond = true;
596
597           boolean atLeastOne =
598             clearRefEdgesFrom(hrnX,
599                               f.getType(),
600                               f.getSymbol(),
601                               false);
602           if( atLeastOne ) {
603             edgeRemovedByStrongUpdate = true;
604           }
605         }
606       }
607     }
608
609     // then do all token propagation
610     itrXhrn = lnX.iteratorToReferencees();
611     while( itrXhrn.hasNext() ) {
612       RefEdge edgeX = itrXhrn.next();
613       HeapRegionNode hrnX  = edgeX.getDst();
614       ReachSet betaX = edgeX.getBeta();
615       ReachSet R     = Canonical.intersection(hrnX.getAlpha(),
616                                               edgeX.getBeta()
617                                               );
618
619       Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
620       while( itrYhrn.hasNext() ) {
621         RefEdge edgeY = itrYhrn.next();
622         HeapRegionNode hrnY  = edgeY.getDst();
623         ReachSet O     = edgeY.getBeta();
624
625         // check for impossible edges
626         if( !isSuperiorType(f.getType(), edgeY.getType() ) ) {
627           impossibleEdges.add(edgeY);
628           continue;
629         }
630
631         // propagate tokens over nodes starting from hrnSrc, and it will
632         // take care of propagating back up edges from any touched nodes
633         ChangeSet Cy = Canonical.unionUpArityToChangeSet(O, R);
634         propagateTokensOverNodes(hrnY, Cy, nodesWithNewAlpha, edgesWithNewBeta);
635
636         // then propagate back just up the edges from hrn
637         ChangeSet Cx = Canonical.unionUpArityToChangeSet(R, O);
638         HashSet<RefEdge> todoEdges = new HashSet<RefEdge>();
639
640         Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
641           new Hashtable<RefEdge, ChangeSet>();
642
643         Iterator<RefEdge> referItr = hrnX.iteratorToReferencers();
644         while( referItr.hasNext() ) {
645           RefEdge edgeUpstream = referItr.next();
646           todoEdges.add(edgeUpstream);
647           edgePlannedChanges.put(edgeUpstream, Cx);
648         }
649
650         propagateTokensOverEdges(todoEdges,
651                                  edgePlannedChanges,
652                                  edgesWithNewBeta);
653       }
654     }
655
656
657     // apply the updates to reachability
658     Iterator<HeapRegionNode> nodeItr = nodesWithNewAlpha.iterator();
659     while( nodeItr.hasNext() ) {
660       nodeItr.next().applyAlphaNew();
661     }
662
663     Iterator<RefEdge> edgeItr = edgesWithNewBeta.iterator();
664     while( edgeItr.hasNext() ) {
665       edgeItr.next().applyBetaNew();
666     }
667
668
669     // then go back through and add the new edges
670     itrXhrn = lnX.iteratorToReferencees();
671     while( itrXhrn.hasNext() ) {
672       RefEdge edgeX = itrXhrn.next();
673       HeapRegionNode hrnX  = edgeX.getDst();
674
675       Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
676       while( itrYhrn.hasNext() ) {
677         RefEdge edgeY = itrYhrn.next();
678         HeapRegionNode hrnY  = edgeY.getDst();
679
680         // skip impossible edges here, we already marked them
681         // when computing reachability propagations above
682         if( !isSuperiorType(f.getType(), edgeY.getType() ) ) {
683           continue;
684         }
685
686         // prepare the new reference edge hrnX.f -> hrnY
687         TypeDescriptor tdNewEdge =
688           mostSpecificType(y.getType(),
689                            edgeY.getType(),
690                            hrnY.getType()
691                            );
692
693         TaintSet taints = edgeY.getTaints();
694
695         if( state.RCR ) {
696           // the DFJ way to generate taints changes for field statements
697           taints = Canonical.changeWhereDefined(taints,
698                                                 currentProgramPoint);
699         }
700
701         RefEdge edgeNew =
702           new RefEdge(hrnX,
703                       hrnY,
704                       tdNewEdge,
705                       f.getSymbol(),
706                       Canonical.changePredsTo(
707                         Canonical.pruneBy(edgeY.getBeta(),
708                                           hrnX.getAlpha()
709                                           ),
710                         predsTrue
711                         ),
712                       predsTrue,
713                       taints
714                       );
715
716         addEdgeOrMergeWithExisting(edgeNew);
717       }
718     }
719
720     Iterator<RefEdge> itrImp = impossibleEdges.iterator();
721     while( itrImp.hasNext() ) {
722       RefEdge edgeImp = itrImp.next();
723       removeRefEdge(edgeImp);
724     }
725
726     // if there was a strong update, make sure to improve
727     // reachability with a global sweep
728     if( edgeRemovedByStrongUpdate || !impossibleEdges.isEmpty() ) {
729       if( !DISABLE_GLOBAL_SWEEP ) {
730         globalSweep();
731       }
732     }
733
734     return edgeRemovedByStrongUpdate;
735   }
736
737
738   public void assignReturnEqualToTemp(TempDescriptor x) {
739
740     VariableNode lnR = getVariableNodeFromTemp(tdReturn);
741     VariableNode lnX = getVariableNodeFromTemp(x);
742
743     clearRefEdgesFrom(lnR, null, null, true);
744
745     Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
746     while( itrXhrn.hasNext() ) {
747       RefEdge edgeX      = itrXhrn.next();
748       HeapRegionNode referencee = edgeX.getDst();
749       RefEdge edgeNew    = edgeX.copy();
750       edgeNew.setSrc(lnR);
751       edgeNew.setTaints(Canonical.changePredsTo(edgeNew.getTaints(),
752                                                 predsTrue
753                                                 )
754                         );
755
756       addRefEdge(lnR, referencee, edgeNew);
757     }
758   }
759
760
761   public void assignTempEqualToNewAlloc(TempDescriptor x,
762                                         AllocSite as) {
763     assert x  != null;
764     assert as != null;
765
766     age(as);
767
768     // after the age operation the newest (or zero-ith oldest)
769     // node associated with the allocation site should have
770     // no references to it as if it were a newly allocated
771     // heap region
772     Integer idNewest   = as.getIthOldest(0);
773     HeapRegionNode hrnNewest  = id2hrn.get(idNewest);
774     assert hrnNewest != null;
775
776     VariableNode lnX = getVariableNodeFromTemp(x);
777     clearRefEdgesFrom(lnX, null, null, true);
778
779     // make a new reference to allocated node
780     TypeDescriptor type = as.getType();
781
782     RefEdge edgeNew =
783       new RefEdge(lnX,                   // source
784                   hrnNewest,             // dest
785                   type,                  // type
786                   null,                  // field name
787                   hrnNewest.getAlpha(),  // beta
788                   predsTrue,             // predicates
789                   TaintSet.factory()     // taints
790                   );
791
792     addRefEdge(lnX, hrnNewest, edgeNew);
793   }
794
795
796   // use the allocation site (unique to entire analysis) to
797   // locate the heap region nodes in this reachability graph
798   // that should be aged.  The process models the allocation
799   // of new objects and collects all the oldest allocations
800   // in a summary node to allow for a finite analysis
801   //
802   // There is an additional property of this method.  After
803   // running it on a particular reachability graph (many graphs
804   // may have heap regions related to the same allocation site)
805   // the heap region node objects in this reachability graph will be
806   // allocated.  Therefore, after aging a graph for an allocation
807   // site, attempts to retrieve the heap region nodes using the
808   // integer id's contained in the allocation site should always
809   // return non-null heap regions.
810   public void age(AllocSite as) {
811
812     // keep track of allocation sites that are represented
813     // in this graph for efficiency with other operations
814     allocSites.add(as);
815
816     // if there is a k-th oldest node, it merges into
817     // the summary node
818     Integer idK = as.getOldest();
819     if( id2hrn.containsKey(idK) ) {
820       HeapRegionNode hrnK = id2hrn.get(idK);
821
822       // retrieve the summary node, or make it
823       // from scratch
824       HeapRegionNode hrnSummary = getSummaryNode(as, false);
825
826       mergeIntoSummary(hrnK, hrnSummary);
827     }
828
829     // move down the line of heap region nodes
830     // clobbering the ith and transferring all references
831     // to and from i-1 to node i.
832     for( int i = allocationDepth - 1; i > 0; --i ) {
833
834       // only do the transfer if the i-1 node exists
835       Integer idImin1th = as.getIthOldest(i - 1);
836       if( id2hrn.containsKey(idImin1th) ) {
837         HeapRegionNode hrnImin1 = id2hrn.get(idImin1th);
838         if( hrnImin1.isWiped() ) {
839           // there is no info on this node, just skip
840           continue;
841         }
842
843         // either retrieve or make target of transfer
844         HeapRegionNode hrnI = getIthNode(as, i, false);
845
846         transferOnto(hrnImin1, hrnI);
847       }
848
849     }
850
851     // as stated above, the newest node should have had its
852     // references moved over to the second oldest, so we wipe newest
853     // in preparation for being the new object to assign something to
854     HeapRegionNode hrn0 = getIthNode(as, 0, false);
855     wipeOut(hrn0, true);
856
857     // now tokens in reachability sets need to "age" also
858     Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
859     while( itrAllHRNodes.hasNext() ) {
860       Map.Entry me       = (Map.Entry)itrAllHRNodes.next();
861       HeapRegionNode hrnToAge = (HeapRegionNode) me.getValue();
862
863       ageTuplesFrom(as, hrnToAge);
864
865       Iterator<RefEdge> itrEdges = hrnToAge.iteratorToReferencers();
866       while( itrEdges.hasNext() ) {
867         ageTuplesFrom(as, itrEdges.next() );
868       }
869     }
870
871
872     // after tokens have been aged, reset newest node's reachability
873     // and a brand new node has a "true" predicate
874     hrn0.setAlpha( Canonical.changePredsTo( hrn0.getInherent(), predsTrue ) );
875     hrn0.setPreds( predsTrue);
876   }
877
878
879   // either retrieve or create the needed heap region node
880   protected HeapRegionNode getSummaryNode(AllocSite as,
881                                           boolean shadow) {
882
883     Integer idSummary;
884     if( shadow ) {
885       idSummary = as.getSummaryShadow();
886     } else {
887       idSummary = as.getSummary();
888     }
889
890     HeapRegionNode hrnSummary = id2hrn.get(idSummary);
891
892     if( hrnSummary == null ) {
893
894       String strDesc = as.toStringForDOT()+"\\nsummary";
895
896       hrnSummary =
897         createNewHeapRegionNode(idSummary,     // id or null to generate a new one
898                                 false,         // single object?
899                                 true,          // summary?
900                                 false,         // out-of-context?
901                                 as.getType(),  // type
902                                 as,            // allocation site
903                                 null,          // inherent reach
904                                 null,          // current reach
905                                 predsEmpty,    // predicates
906                                 strDesc        // description
907                                 );
908     }
909
910     return hrnSummary;
911   }
912
913   // either retrieve or create the needed heap region node
914   protected HeapRegionNode getIthNode(AllocSite as,
915                                       Integer i,
916                                       boolean shadow) {
917
918     Integer idIth;
919     if( shadow ) {
920       idIth = as.getIthOldestShadow(i);
921     } else {
922       idIth = as.getIthOldest(i);
923     }
924
925     HeapRegionNode hrnIth = id2hrn.get(idIth);
926
927     if( hrnIth == null ) {
928
929       String strDesc = as.toStringForDOT()+"\\n"+i+" oldest";
930
931       hrnIth = createNewHeapRegionNode(idIth,         // id or null to generate a new one
932                                        true,          // single object?
933                                        false,         // summary?
934                                        false,         // out-of-context?
935                                        as.getType(),  // type
936                                        as,            // allocation site
937                                        null,          // inherent reach
938                                        null,          // current reach
939                                        predsEmpty,    // predicates
940                                        strDesc        // description
941                                        );
942     }
943
944     return hrnIth;
945   }
946
947
948   protected void mergeIntoSummary(HeapRegionNode hrn,
949                                   HeapRegionNode hrnSummary) {
950     assert hrnSummary.isNewSummary();
951
952     // assert that these nodes belong to THIS graph
953     assert belongsToThis(hrn);
954     assert belongsToThis(hrnSummary);
955
956     assert hrn != hrnSummary;
957
958     // transfer references _from_ hrn over to hrnSummary
959     Iterator<RefEdge> itrReferencee = hrn.iteratorToReferencees();
960     while( itrReferencee.hasNext() ) {
961       RefEdge edge       = itrReferencee.next();
962       RefEdge edgeMerged = edge.copy();
963       edgeMerged.setSrc(hrnSummary);
964
965       HeapRegionNode hrnReferencee = edge.getDst();
966       RefEdge edgeSummary   =
967         hrnSummary.getReferenceTo(hrnReferencee,
968                                   edge.getType(),
969                                   edge.getField()
970                                   );
971
972       if( edgeSummary == null ) {
973         // the merge is trivial, nothing to be done
974         addRefEdge(hrnSummary, hrnReferencee, edgeMerged);
975
976       } else {
977         // otherwise an edge from the referencer to hrnSummary exists already
978         // and the edge referencer->hrn should be merged with it
979         edgeSummary.setBeta(
980           Canonical.unionORpreds(edgeMerged.getBeta(),
981                                  edgeSummary.getBeta()
982                                  )
983           );
984         edgeSummary.setPreds(
985           Canonical.join(edgeMerged.getPreds(),
986                          edgeSummary.getPreds()
987                          )
988           );
989       }
990     }
991
992     // next transfer references _to_ hrn over to hrnSummary
993     Iterator<RefEdge> itrReferencer = hrn.iteratorToReferencers();
994     while( itrReferencer.hasNext() ) {
995       RefEdge edge         = itrReferencer.next();
996       RefEdge edgeMerged   = edge.copy();
997       edgeMerged.setDst(hrnSummary);
998
999       RefSrcNode onReferencer = edge.getSrc();
1000       RefEdge edgeSummary  =
1001         onReferencer.getReferenceTo(hrnSummary,
1002                                     edge.getType(),
1003                                     edge.getField()
1004                                     );
1005
1006       if( edgeSummary == null ) {
1007         // the merge is trivial, nothing to be done
1008         addRefEdge(onReferencer, hrnSummary, edgeMerged);
1009
1010       } else {
1011         // otherwise an edge from the referencer to alpha_S exists already
1012         // and the edge referencer->alpha_K should be merged with it
1013         edgeSummary.setBeta(
1014           Canonical.unionORpreds(edgeMerged.getBeta(),
1015                                  edgeSummary.getBeta()
1016                                  )
1017           );
1018         edgeSummary.setPreds(
1019           Canonical.join(edgeMerged.getPreds(),
1020                          edgeSummary.getPreds()
1021                          )
1022           );
1023       }
1024     }
1025
1026     // then merge hrn reachability into hrnSummary
1027     hrnSummary.setAlpha(
1028       Canonical.unionORpreds(hrnSummary.getAlpha(),
1029                              hrn.getAlpha()
1030                              )
1031       );
1032
1033     hrnSummary.setPreds(
1034       Canonical.join(hrnSummary.getPreds(),
1035                      hrn.getPreds()
1036                      )
1037       );
1038
1039     // and afterward, this node is gone
1040     wipeOut(hrn, true);
1041   }
1042
1043
1044   protected void transferOnto(HeapRegionNode hrnA,
1045                               HeapRegionNode hrnB) {
1046
1047     assert belongsToThis(hrnA);
1048     assert belongsToThis(hrnB);
1049     assert hrnA != hrnB;
1050
1051     // clear references in and out of node b?
1052     assert hrnB.isWiped();
1053
1054     // copy each: (edge in and out of A) to B
1055     Iterator<RefEdge> itrReferencee = hrnA.iteratorToReferencees();
1056     while( itrReferencee.hasNext() ) {
1057       RefEdge edge          = itrReferencee.next();
1058       HeapRegionNode hrnReferencee = edge.getDst();
1059       RefEdge edgeNew       = edge.copy();
1060       edgeNew.setSrc(hrnB);
1061       edgeNew.setDst(hrnReferencee);
1062
1063       addRefEdge(hrnB, hrnReferencee, edgeNew);
1064     }
1065
1066     Iterator<RefEdge> itrReferencer = hrnA.iteratorToReferencers();
1067     while( itrReferencer.hasNext() ) {
1068       RefEdge edge          = itrReferencer.next();
1069       RefSrcNode rsnReferencer = edge.getSrc();
1070       RefEdge edgeNew       = edge.copy();
1071       edgeNew.setSrc(rsnReferencer);
1072       edgeNew.setDst(hrnB);
1073
1074       addRefEdge(rsnReferencer, hrnB, edgeNew);
1075     }
1076
1077     // replace hrnB reachability and preds with hrnA's
1078     hrnB.setAlpha(hrnA.getAlpha() );
1079     hrnB.setPreds(hrnA.getPreds() );
1080
1081     // after transfer, wipe out source
1082     wipeOut(hrnA, true);
1083   }
1084
1085
1086   // the purpose of this method is to conceptually "wipe out"
1087   // a heap region from the graph--purposefully not called REMOVE
1088   // because the node is still hanging around in the graph, just
1089   // not mechanically connected or have any reach or predicate
1090   // information on it anymore--lots of ops can use this
1091   protected void wipeOut(HeapRegionNode hrn,
1092                          boolean wipeVariableReferences) {
1093
1094     assert belongsToThis(hrn);
1095
1096     clearRefEdgesFrom(hrn, null, null, true);
1097
1098     if( wipeVariableReferences ) {
1099       clearRefEdgesTo(hrn, null, null, true);
1100     } else {
1101       clearNonVarRefEdgesTo(hrn);
1102     }
1103
1104     hrn.setAlpha(rsetEmpty);
1105     hrn.setPreds(predsEmpty);
1106   }
1107
1108
1109   protected void ageTuplesFrom(AllocSite as, RefEdge edge) {
1110     edge.setBeta(
1111       Canonical.ageTuplesFrom(edge.getBeta(),
1112                               as
1113                               )
1114       );
1115   }
1116
1117   protected void ageTuplesFrom(AllocSite as, HeapRegionNode hrn) {
1118     hrn.setAlpha(
1119       Canonical.ageTuplesFrom(hrn.getAlpha(),
1120                               as
1121                               )
1122       );
1123   }
1124
1125
1126
1127   protected void propagateTokensOverNodes(HeapRegionNode nPrime,
1128                                           ChangeSet c0,
1129                                           HashSet<HeapRegionNode> nodesWithNewAlpha,
1130                                           HashSet<RefEdge>        edgesWithNewBeta) {
1131
1132     HashSet<HeapRegionNode> todoNodes
1133       = new HashSet<HeapRegionNode>();
1134     todoNodes.add(nPrime);
1135
1136     HashSet<RefEdge> todoEdges
1137       = new HashSet<RefEdge>();
1138
1139     Hashtable<HeapRegionNode, ChangeSet> nodePlannedChanges
1140       = new Hashtable<HeapRegionNode, ChangeSet>();
1141     nodePlannedChanges.put(nPrime, c0);
1142
1143     Hashtable<RefEdge, ChangeSet> edgePlannedChanges
1144       = new Hashtable<RefEdge, ChangeSet>();
1145
1146     // first propagate change sets everywhere they can go
1147     while( !todoNodes.isEmpty() ) {
1148       HeapRegionNode n = todoNodes.iterator().next();
1149       ChangeSet C = nodePlannedChanges.get(n);
1150
1151       Iterator<RefEdge> referItr = n.iteratorToReferencers();
1152       while( referItr.hasNext() ) {
1153         RefEdge edge = referItr.next();
1154         todoEdges.add(edge);
1155
1156         if( !edgePlannedChanges.containsKey(edge) ) {
1157           edgePlannedChanges.put(edge,
1158                                  ChangeSet.factory()
1159                                  );
1160         }
1161
1162         edgePlannedChanges.put(edge,
1163                                Canonical.union(edgePlannedChanges.get(edge),
1164                                                C
1165                                                )
1166                                );
1167       }
1168
1169       Iterator<RefEdge> refeeItr = n.iteratorToReferencees();
1170       while( refeeItr.hasNext() ) {
1171         RefEdge edgeF = refeeItr.next();
1172         HeapRegionNode m     = edgeF.getDst();
1173
1174         ChangeSet changesToPass = ChangeSet.factory();
1175
1176         Iterator<ChangeTuple> itrCprime = C.iterator();
1177         while( itrCprime.hasNext() ) {
1178           ChangeTuple c = itrCprime.next();
1179           if( edgeF.getBeta().containsIgnorePreds(c.getStateToMatch() )
1180               != null
1181               ) {
1182             changesToPass = Canonical.add(changesToPass, c);
1183           }
1184         }
1185
1186         if( !changesToPass.isEmpty() ) {
1187           if( !nodePlannedChanges.containsKey(m) ) {
1188             nodePlannedChanges.put(m, ChangeSet.factory() );
1189           }
1190
1191           ChangeSet currentChanges = nodePlannedChanges.get(m);
1192
1193           if( !changesToPass.isSubset(currentChanges) ) {
1194
1195             nodePlannedChanges.put(m,
1196                                    Canonical.union(currentChanges,
1197                                                    changesToPass
1198                                                    )
1199                                    );
1200             todoNodes.add(m);
1201           }
1202         }
1203       }
1204
1205       todoNodes.remove(n);
1206     }
1207
1208     // then apply all of the changes for each node at once
1209     Iterator itrMap = nodePlannedChanges.entrySet().iterator();
1210     while( itrMap.hasNext() ) {
1211       Map.Entry me = (Map.Entry)itrMap.next();
1212       HeapRegionNode n  = (HeapRegionNode) me.getKey();
1213       ChangeSet C  = (ChangeSet)      me.getValue();
1214
1215       // this propagation step is with respect to one change,
1216       // so we capture the full change from the old alpha:
1217       ReachSet localDelta = Canonical.applyChangeSet(n.getAlpha(),
1218                                                      C,
1219                                                      true
1220                                                      );
1221       // but this propagation may be only one of many concurrent
1222       // possible changes, so keep a running union with the node's
1223       // partially updated new alpha set
1224       n.setAlphaNew(Canonical.unionORpreds(n.getAlphaNew(),
1225                                            localDelta
1226                                            )
1227                     );
1228
1229       nodesWithNewAlpha.add(n);
1230     }
1231
1232     propagateTokensOverEdges(todoEdges,
1233                              edgePlannedChanges,
1234                              edgesWithNewBeta
1235                              );
1236   }
1237
1238
1239   protected void propagateTokensOverEdges(HashSet  <RefEdge>            todoEdges,
1240                                           Hashtable<RefEdge, ChangeSet> edgePlannedChanges,
1241                                           HashSet  <RefEdge>            edgesWithNewBeta) {
1242
1243     // first propagate all change tuples everywhere they can go
1244     while( !todoEdges.isEmpty() ) {
1245       RefEdge edgeE = todoEdges.iterator().next();
1246       todoEdges.remove(edgeE);
1247
1248       if( !edgePlannedChanges.containsKey(edgeE) ) {
1249         edgePlannedChanges.put(edgeE,
1250                                ChangeSet.factory()
1251                                );
1252       }
1253
1254       ChangeSet C = edgePlannedChanges.get(edgeE);
1255
1256       ChangeSet changesToPass = ChangeSet.factory();
1257
1258       Iterator<ChangeTuple> itrC = C.iterator();
1259       while( itrC.hasNext() ) {
1260         ChangeTuple c = itrC.next();
1261         if( edgeE.getBeta().containsIgnorePreds(c.getStateToMatch() )
1262             != null
1263             ) {
1264           changesToPass = Canonical.add(changesToPass, c);
1265         }
1266       }
1267
1268       RefSrcNode rsn = edgeE.getSrc();
1269
1270       if( !changesToPass.isEmpty() && rsn instanceof HeapRegionNode ) {
1271         HeapRegionNode n = (HeapRegionNode) rsn;
1272
1273         Iterator<RefEdge> referItr = n.iteratorToReferencers();
1274         while( referItr.hasNext() ) {
1275           RefEdge edgeF = referItr.next();
1276
1277           if( !edgePlannedChanges.containsKey(edgeF) ) {
1278             edgePlannedChanges.put(edgeF,
1279                                    ChangeSet.factory()
1280                                    );
1281           }
1282
1283           ChangeSet currentChanges = edgePlannedChanges.get(edgeF);
1284
1285           if( !changesToPass.isSubset(currentChanges) ) {
1286             todoEdges.add(edgeF);
1287             edgePlannedChanges.put(edgeF,
1288                                    Canonical.union(currentChanges,
1289                                                    changesToPass
1290                                                    )
1291                                    );
1292           }
1293         }
1294       }
1295     }
1296
1297     // then apply all of the changes for each edge at once
1298     Iterator itrMap = edgePlannedChanges.entrySet().iterator();
1299     while( itrMap.hasNext() ) {
1300       Map.Entry me = (Map.Entry)itrMap.next();
1301       RefEdge e  = (RefEdge)   me.getKey();
1302       ChangeSet C  = (ChangeSet) me.getValue();
1303
1304       // this propagation step is with respect to one change,
1305       // so we capture the full change from the old beta:
1306       ReachSet localDelta =
1307         Canonical.applyChangeSet(e.getBeta(),
1308                                  C,
1309                                  true
1310                                  );
1311
1312       // but this propagation may be only one of many concurrent
1313       // possible changes, so keep a running union with the edge's
1314       // partially updated new beta set
1315       e.setBetaNew(Canonical.unionORpreds(e.getBetaNew(),
1316                                           localDelta
1317                                           )
1318                    );
1319
1320       edgesWithNewBeta.add(e);
1321     }
1322   }
1323
1324
1325   public void taintInSetVars(FlatSESEEnterNode sese) {
1326
1327     Iterator<TempDescriptor> isvItr = sese.getInVarSet().iterator();
1328     while( isvItr.hasNext() ) {
1329       TempDescriptor isv = isvItr.next();
1330
1331       // use this where defined flatnode to support RCR/DFJ
1332       FlatNode whereDefined = null;
1333
1334       // in-set var taints should NOT propagate back into callers
1335       // so give it FALSE(EMPTY) predicates
1336       taintTemp(sese,
1337                 null,
1338                 isv,
1339                 whereDefined,
1340                 predsEmpty
1341                 );
1342     }
1343   }
1344
1345   public void taintStallSite(FlatNode stallSite,
1346                              TempDescriptor var) {
1347
1348     // use this where defined flatnode to support RCR/DFJ
1349     FlatNode whereDefined = null;
1350
1351     // stall site taint should propagate back into callers
1352     // so give it TRUE predicates
1353     taintTemp(null,
1354               stallSite,
1355               var,
1356               whereDefined,
1357               predsTrue
1358               );
1359   }
1360
1361   protected void taintTemp(FlatSESEEnterNode sese,
1362                            FlatNode stallSite,
1363                            TempDescriptor var,
1364                            FlatNode whereDefined,
1365                            ExistPredSet preds
1366                            ) {
1367
1368     VariableNode vn = getVariableNodeFromTemp(var);
1369
1370     Iterator<RefEdge> reItr = vn.iteratorToReferencees();
1371     while( reItr.hasNext() ) {
1372       RefEdge re = reItr.next();
1373
1374       Taint taint = Taint.factory(sese,
1375                                   stallSite,
1376                                   var,
1377                                   re.getDst().getAllocSite(),
1378                                   whereDefined,
1379                                   preds
1380                                   );
1381
1382       re.setTaints(Canonical.add(re.getTaints(),
1383                                  taint
1384                                  )
1385                    );
1386     }
1387   }
1388
1389   public void removeInContextTaints(FlatSESEEnterNode sese) {
1390
1391     Iterator meItr = id2hrn.entrySet().iterator();
1392     while( meItr.hasNext() ) {
1393       Map.Entry me  = (Map.Entry)meItr.next();
1394       Integer id  = (Integer)        me.getKey();
1395       HeapRegionNode hrn = (HeapRegionNode) me.getValue();
1396
1397       Iterator<RefEdge> reItr = hrn.iteratorToReferencers();
1398       while( reItr.hasNext() ) {
1399         RefEdge re = reItr.next();
1400
1401         re.setTaints(Canonical.removeInContextTaints(re.getTaints(),
1402                                                      sese
1403                                                      )
1404                      );
1405       }
1406     }
1407   }
1408
1409   public void removeAllStallSiteTaints() {
1410
1411     Iterator meItr = id2hrn.entrySet().iterator();
1412     while( meItr.hasNext() ) {
1413       Map.Entry me  = (Map.Entry)meItr.next();
1414       Integer id  = (Integer)        me.getKey();
1415       HeapRegionNode hrn = (HeapRegionNode) me.getValue();
1416
1417       Iterator<RefEdge> reItr = hrn.iteratorToReferencers();
1418       while( reItr.hasNext() ) {
1419         RefEdge re = reItr.next();
1420
1421         re.setTaints(Canonical.removeStallSiteTaints(re.getTaints()
1422                                                      )
1423                      );
1424       }
1425     }
1426   }
1427
1428
1429   // used in makeCalleeView below to decide if there is
1430   // already an appropriate out-of-context edge in a callee
1431   // view graph for merging, or null if a new one will be added
1432   protected RefEdge
1433   getOutOfContextReferenceTo(HeapRegionNode hrn,
1434                              TypeDescriptor srcType,
1435                              TypeDescriptor refType,
1436                              String refField) {
1437     assert belongsToThis(hrn);
1438
1439     HeapRegionNode hrnInContext = id2hrn.get(hrn.getID() );
1440     if( hrnInContext == null ) {
1441       return null;
1442     }
1443
1444     Iterator<RefEdge> refItr = hrnInContext.iteratorToReferencers();
1445     while( refItr.hasNext() ) {
1446       RefEdge re = refItr.next();
1447
1448       assert belongsToThis(re.getSrc() );
1449       assert belongsToThis(re.getDst() );
1450
1451       if( !(re.getSrc() instanceof HeapRegionNode) ) {
1452         continue;
1453       }
1454
1455       HeapRegionNode hrnSrc = (HeapRegionNode) re.getSrc();
1456       if( !hrnSrc.isOutOfContext() ) {
1457         continue;
1458       }
1459
1460       if( srcType == null ) {
1461         if( hrnSrc.getType() != null ) {
1462           continue;
1463         }
1464       } else {
1465         if( !srcType.equals(hrnSrc.getType() ) ) {
1466           continue;
1467         }
1468       }
1469
1470       if( !re.typeEquals(refType) ) {
1471         continue;
1472       }
1473
1474       if( !re.fieldEquals(refField) ) {
1475         continue;
1476       }
1477
1478       // tada!  We found it!
1479       return re;
1480     }
1481
1482     return null;
1483   }
1484
1485   // used below to convert a ReachSet to its callee-context
1486   // equivalent with respect to allocation sites in this graph
1487   protected ReachSet toCalleeContext(ReachSet rs,
1488                                      ExistPredSet predsNodeOrEdge,
1489                                      Set<HrnIdOoc> oocHrnIdOoc2callee
1490                                      ) {
1491     ReachSet out = ReachSet.factory();
1492
1493     Iterator<ReachState> itr = rs.iterator();
1494     while( itr.hasNext() ) {
1495       ReachState stateCaller = itr.next();
1496
1497       ReachState stateCallee = stateCaller;
1498
1499       Iterator<AllocSite> asItr = allocSites.iterator();
1500       while( asItr.hasNext() ) {
1501         AllocSite as = asItr.next();
1502
1503         ReachState stateNew = ReachState.factory();
1504         Iterator<ReachTuple> rtItr = stateCallee.iterator();
1505         while( rtItr.hasNext() ) {
1506           ReachTuple rt = rtItr.next();
1507
1508           // only translate this tuple if it is
1509           // in the out-callee-context bag
1510           HrnIdOoc hio = new HrnIdOoc(rt.getHrnID(),
1511                                       rt.isOutOfContext()
1512                                       );
1513           if( !oocHrnIdOoc2callee.contains(hio) ) {
1514             stateNew = Canonical.addUpArity(stateNew, rt);
1515             continue;
1516           }
1517
1518           int age = as.getAgeCategory(rt.getHrnID() );
1519
1520           // this is the current mapping, where 0, 1, 2S were allocated
1521           // in the current context, 0?, 1? and 2S? were allocated in a
1522           // previous context, and we're translating to a future context
1523           //
1524           // 0    -> 0?
1525           // 1    -> 1?
1526           // 2S   -> 2S?
1527           // 2S*  -> 2S?*
1528           //
1529           // 0?   -> 2S?
1530           // 1?   -> 2S?
1531           // 2S?  -> 2S?
1532           // 2S?* -> 2S?*
1533
1534           if( age == AllocSite.AGE_notInThisSite ) {
1535             // things not from the site just go back in
1536             stateNew = Canonical.addUpArity(stateNew, rt);
1537
1538           } else if( age == AllocSite.AGE_summary ||
1539                      rt.isOutOfContext()
1540                      ) {
1541
1542             stateNew = Canonical.addUpArity(stateNew,
1543                                             ReachTuple.factory(as.getSummary(),
1544                                                                true,   // multi
1545                                                                rt.getArity(),
1546                                                                true    // out-of-context
1547                                                                )
1548                                             );
1549
1550           } else {
1551             // otherwise everything else just goes to an out-of-context
1552             // version, everything else the same
1553             Integer I = as.getAge(rt.getHrnID() );
1554             assert I != null;
1555
1556             assert !rt.isMultiObject();
1557
1558             stateNew = Canonical.addUpArity(stateNew,
1559                                             ReachTuple.factory(rt.getHrnID(),
1560                                                                rt.isMultiObject(),   // multi
1561                                                                rt.getArity(),
1562                                                                true    // out-of-context
1563                                                                )
1564                                             );
1565           }
1566         }
1567
1568         stateCallee = stateNew;
1569       }
1570
1571       // make a predicate of the caller graph element
1572       // and the caller state we just converted
1573       ExistPredSet predsWithState = ExistPredSet.factory();
1574
1575       Iterator<ExistPred> predItr = predsNodeOrEdge.iterator();
1576       while( predItr.hasNext() ) {
1577         ExistPred predNodeOrEdge = predItr.next();
1578
1579         predsWithState =
1580           Canonical.add(predsWithState,
1581                         ExistPred.factory(predNodeOrEdge.n_hrnID,
1582                                           predNodeOrEdge.e_tdSrc,
1583                                           predNodeOrEdge.e_hrnSrcID,
1584                                           predNodeOrEdge.e_hrnDstID,
1585                                           predNodeOrEdge.e_type,
1586                                           predNodeOrEdge.e_field,
1587                                           stateCaller,
1588                                           null,
1589                                           predNodeOrEdge.e_srcOutCalleeContext,
1590                                           predNodeOrEdge.e_srcOutCallerContext
1591                                           )
1592                         );
1593       }
1594
1595       stateCallee = Canonical.changePredsTo(stateCallee,
1596                                             predsWithState);
1597
1598       out = Canonical.add(out,
1599                           stateCallee
1600                           );
1601     }
1602     assert out.isCanonical();
1603     return out;
1604   }
1605
1606   // used below to convert a ReachSet to its caller-context
1607   // equivalent with respect to allocation sites in this graph
1608   protected ReachSet
1609   toCallerContext(ReachSet rs,
1610                   Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied
1611                   ) {
1612     ReachSet out = ReachSet.factory();
1613
1614     // when the mapping is null it means there were no
1615     // predicates satisfied
1616     if( calleeStatesSatisfied == null ) {
1617       return out;
1618     }
1619
1620     Iterator<ReachState> itr = rs.iterator();
1621     while( itr.hasNext() ) {
1622       ReachState stateCallee = itr.next();
1623
1624       if( calleeStatesSatisfied.containsKey(stateCallee) ) {
1625
1626         // starting from one callee state...
1627         ReachSet rsCaller = ReachSet.factory(stateCallee);
1628
1629         // possibly branch it into many states, which any
1630         // allocation site might do, so lots of derived states
1631         Iterator<AllocSite> asItr = allocSites.iterator();
1632         while( asItr.hasNext() ) {
1633           AllocSite as = asItr.next();
1634           rsCaller = Canonical.toCallerContext(rsCaller, as);
1635         }
1636
1637         // then before adding each derived, now caller-context
1638         // states to the output, attach the appropriate pred
1639         // based on the source callee state
1640         Iterator<ReachState> stateItr = rsCaller.iterator();
1641         while( stateItr.hasNext() ) {
1642           ReachState stateCaller = stateItr.next();
1643           stateCaller = Canonical.attach(stateCaller,
1644                                          calleeStatesSatisfied.get(stateCallee)
1645                                          );
1646           out = Canonical.add(out,
1647                               stateCaller
1648                               );
1649         }
1650       }
1651     }
1652
1653     assert out.isCanonical();
1654     return out;
1655   }
1656
1657
1658   // used below to convert a ReachSet to an equivalent
1659   // version with shadow IDs merged into unshadowed IDs
1660   protected ReachSet unshadow(ReachSet rs) {
1661     ReachSet out = rs;
1662     Iterator<AllocSite> asItr = allocSites.iterator();
1663     while( asItr.hasNext() ) {
1664       AllocSite as = asItr.next();
1665       out = Canonical.unshadow(out, as);
1666     }
1667     assert out.isCanonical();
1668     return out;
1669   }
1670
1671
1672   // convert a caller taint set into a callee taint set
1673   protected TaintSet
1674   toCalleeContext(TaintSet ts,
1675                   ExistPredSet predsEdge) {
1676
1677     TaintSet out = TaintSet.factory();
1678
1679     // the idea is easy, the taint identifier itself doesn't
1680     // change at all, but the predicates should be tautology:
1681     // start with the preds passed in from the caller edge
1682     // that host the taints, and alter them to have the taint
1683     // added, just becoming more specific than edge predicate alone
1684
1685     Iterator<Taint> itr = ts.iterator();
1686     while( itr.hasNext() ) {
1687       Taint tCaller = itr.next();
1688
1689       ExistPredSet predsWithTaint = ExistPredSet.factory();
1690
1691       Iterator<ExistPred> predItr = predsEdge.iterator();
1692       while( predItr.hasNext() ) {
1693         ExistPred predEdge = predItr.next();
1694
1695         predsWithTaint =
1696           Canonical.add(predsWithTaint,
1697                         ExistPred.factory(predEdge.e_tdSrc,
1698                                           predEdge.e_hrnSrcID,
1699                                           predEdge.e_hrnDstID,
1700                                           predEdge.e_type,
1701                                           predEdge.e_field,
1702                                           null,
1703                                           tCaller,
1704                                           predEdge.e_srcOutCalleeContext,
1705                                           predEdge.e_srcOutCallerContext
1706                                           )
1707                         );
1708       }
1709
1710       Taint tCallee = Canonical.changePredsTo(tCaller,
1711                                               predsWithTaint);
1712
1713       out = Canonical.add(out,
1714                           tCallee
1715                           );
1716     }
1717
1718     assert out.isCanonical();
1719     return out;
1720   }
1721
1722
1723   // used below to convert a TaintSet to its caller-context
1724   // equivalent, just eliminate Taints with bad preds
1725   protected TaintSet
1726   toCallerContext(TaintSet ts,
1727                   Hashtable<Taint, ExistPredSet> calleeTaintsSatisfied
1728                   ) {
1729
1730     TaintSet out = TaintSet.factory();
1731
1732     // when the mapping is null it means there were no
1733     // predicates satisfied
1734     if( calleeTaintsSatisfied == null ) {
1735       return out;
1736     }
1737
1738     Iterator<Taint> itr = ts.iterator();
1739     while( itr.hasNext() ) {
1740       Taint tCallee = itr.next();
1741
1742       if( calleeTaintsSatisfied.containsKey(tCallee) ) {
1743
1744         Taint tCaller =
1745           Canonical.attach(Taint.factory(tCallee.sese,
1746                                          tCallee.stallSite,
1747                                          tCallee.var,
1748                                          tCallee.allocSite,
1749                                          tCallee.fnDefined,
1750                                          ExistPredSet.factory() ),
1751                            calleeTaintsSatisfied.get(tCallee)
1752                            );
1753         out = Canonical.add(out,
1754                             tCaller
1755                             );
1756       }
1757     }
1758
1759     assert out.isCanonical();
1760     return out;
1761   }
1762
1763
1764
1765
1766   // use this method to make a new reach graph that is
1767   // what heap the FlatMethod callee from the FlatCall
1768   // would start with reaching from its arguments in
1769   // this reach graph
1770   public ReachGraph
1771   makeCalleeView(FlatCall fc,
1772                  FlatMethod fmCallee,
1773                  Set<Integer> callerNodeIDsCopiedToCallee,
1774                  boolean writeDebugDOTs
1775                  ) {
1776
1777
1778     // first traverse this context to find nodes and edges
1779     //  that will be callee-reachable
1780     Set<HeapRegionNode> reachableCallerNodes =
1781       new HashSet<HeapRegionNode>();
1782
1783     // caller edges between callee-reachable nodes
1784     Set<RefEdge> reachableCallerEdges =
1785       new HashSet<RefEdge>();
1786
1787     // caller edges from arg vars, and the matching param index
1788     // because these become a special edge in callee
1789     // NOTE! One argument may be passed in as more than one parameter,
1790     // so map to a set of parameter indices!
1791     Hashtable< RefEdge, Set<Integer> > reachableCallerArgEdges2paramIndices =
1792       new Hashtable< RefEdge, Set<Integer> >();
1793
1794     // caller edges from local vars or callee-unreachable nodes
1795     // (out-of-context sources) to callee-reachable nodes
1796     Set<RefEdge> oocCallerEdges =
1797       new HashSet<RefEdge>();
1798
1799
1800     for( int i = 0; i < fmCallee.numParameters(); ++i ) {
1801
1802       TempDescriptor tdArg = fc.getArgMatchingParamIndex(fmCallee, i);
1803       VariableNode vnArgCaller = this.getVariableNodeFromTemp(tdArg);
1804
1805       Set<RefSrcNode> toVisitInCaller = new HashSet<RefSrcNode>();
1806       Set<RefSrcNode> visitedInCaller = new HashSet<RefSrcNode>();
1807
1808       toVisitInCaller.add(vnArgCaller);
1809
1810       while( !toVisitInCaller.isEmpty() ) {
1811         RefSrcNode rsnCaller = toVisitInCaller.iterator().next();
1812         toVisitInCaller.remove(rsnCaller);
1813         visitedInCaller.add(rsnCaller);
1814
1815         Iterator<RefEdge> itrRefEdges = rsnCaller.iteratorToReferencees();
1816         while( itrRefEdges.hasNext() ) {
1817           RefEdge reCaller  = itrRefEdges.next();
1818           HeapRegionNode hrnCaller = reCaller.getDst();
1819
1820           callerNodeIDsCopiedToCallee.add(hrnCaller.getID() );
1821           reachableCallerNodes.add(hrnCaller);
1822
1823           if( reCaller.getSrc() instanceof HeapRegionNode ) {
1824             reachableCallerEdges.add(reCaller);
1825           } else {
1826
1827             if( rsnCaller.equals(vnArgCaller) ) {
1828               Set<Integer> pIndices = 
1829                 reachableCallerArgEdges2paramIndices.get( reCaller );
1830
1831               if( pIndices == null ) {
1832                 pIndices = new HashSet<Integer>();
1833                 reachableCallerArgEdges2paramIndices.put( reCaller, pIndices );
1834               }
1835               pIndices.add( i );
1836
1837             } else {
1838               oocCallerEdges.add(reCaller);
1839             }
1840           }
1841
1842           if( !visitedInCaller.contains(hrnCaller) ) {
1843             toVisitInCaller.add(hrnCaller);
1844           }
1845
1846         } // end edge iteration
1847       } // end visiting heap nodes in caller
1848     } // end iterating over parameters as starting points
1849
1850
1851
1852     // now collect out-of-callee-context IDs and
1853     // map them to whether the ID is out of the caller
1854     // context as well
1855     Set<HrnIdOoc> oocHrnIdOoc2callee = new HashSet<HrnIdOoc>();
1856
1857     Iterator<Integer> itrInContext =
1858       callerNodeIDsCopiedToCallee.iterator();
1859     while( itrInContext.hasNext() ) {
1860       Integer hrnID                 = itrInContext.next();
1861       HeapRegionNode hrnCallerAndInContext = id2hrn.get(hrnID);
1862
1863       Iterator<RefEdge> itrMightCross =
1864         hrnCallerAndInContext.iteratorToReferencers();
1865       while( itrMightCross.hasNext() ) {
1866         RefEdge edgeMightCross = itrMightCross.next();
1867
1868         RefSrcNode rsnCallerAndOutContext =
1869           edgeMightCross.getSrc();
1870
1871         if( rsnCallerAndOutContext instanceof VariableNode ) {
1872           // variables do not have out-of-context reach states,
1873           // so jump out now
1874           oocCallerEdges.add(edgeMightCross);
1875           continue;
1876         }
1877
1878         HeapRegionNode hrnCallerAndOutContext =
1879           (HeapRegionNode) rsnCallerAndOutContext;
1880
1881         // is this source node out-of-context?
1882         if( callerNodeIDsCopiedToCallee.contains(hrnCallerAndOutContext.getID() ) ) {
1883           // no, skip this edge
1884           continue;
1885         }
1886
1887         // okay, we got one
1888         oocCallerEdges.add(edgeMightCross);
1889
1890         // add all reach tuples on the node to list
1891         // of things that are out-of-context: insight
1892         // if this node is reachable from someting that WAS
1893         // in-context, then this node should already be in-context
1894         Iterator<ReachState> stateItr = hrnCallerAndOutContext.getAlpha().iterator();
1895         while( stateItr.hasNext() ) {
1896           ReachState state = stateItr.next();
1897
1898           Iterator<ReachTuple> rtItr = state.iterator();
1899           while( rtItr.hasNext() ) {
1900             ReachTuple rt = rtItr.next();
1901
1902             oocHrnIdOoc2callee.add(new HrnIdOoc(rt.getHrnID(),
1903                                                 rt.isOutOfContext()
1904                                                 )
1905                                    );
1906           }
1907         }
1908       }
1909     }
1910
1911     // the callee view is a new graph: DON'T MODIFY *THIS* graph
1912     ReachGraph rg = new ReachGraph();
1913
1914     // add nodes to callee graph
1915     Iterator<HeapRegionNode> hrnItr = reachableCallerNodes.iterator();
1916     while( hrnItr.hasNext() ) {
1917       HeapRegionNode hrnCaller = hrnItr.next();
1918
1919       assert callerNodeIDsCopiedToCallee.contains(hrnCaller.getID() );
1920       assert !rg.id2hrn.containsKey(hrnCaller.getID() );
1921
1922       ExistPred pred  = ExistPred.factory(hrnCaller.getID(), null);
1923       ExistPredSet preds = ExistPredSet.factory(pred);
1924
1925       rg.createNewHeapRegionNode(hrnCaller.getID(),
1926                                  hrnCaller.isSingleObject(),
1927                                  hrnCaller.isNewSummary(),
1928                                  false,  // out-of-context?
1929                                  hrnCaller.getType(),
1930                                  hrnCaller.getAllocSite(),
1931                                  toCalleeContext(hrnCaller.getInherent(),
1932                                                  preds,
1933                                                  oocHrnIdOoc2callee
1934                                                  ),
1935                                  toCalleeContext(hrnCaller.getAlpha(),
1936                                                  preds,
1937                                                  oocHrnIdOoc2callee
1938                                                  ),
1939                                  preds,
1940                                  hrnCaller.getDescription()
1941                                  );
1942     }
1943
1944     // add param edges to callee graph
1945     Iterator argEdges =
1946       reachableCallerArgEdges2paramIndices.entrySet().iterator();
1947     while( argEdges.hasNext() ) {
1948       Map.Entry    me    = (Map.Entry)    argEdges.next();
1949       RefEdge      reArg = (RefEdge)      me.getKey();
1950       Set<Integer> pInxs = (Set<Integer>) me.getValue();
1951
1952       VariableNode   vnCaller  = (VariableNode) reArg.getSrc();
1953       TempDescriptor argCaller = vnCaller.getTempDescriptor();
1954
1955       HeapRegionNode hrnDstCaller = reArg.getDst();
1956       HeapRegionNode hrnDstCallee = rg.id2hrn.get(hrnDstCaller.getID() );
1957       assert hrnDstCallee != null;
1958
1959       ExistPred pred =
1960         ExistPred.factory(argCaller,
1961                           null,
1962                           hrnDstCallee.getID(),
1963                           reArg.getType(),
1964                           reArg.getField(),
1965                           null,   // state
1966                           null,   // taint
1967                           true,   // out-of-callee-context
1968                           false   // out-of-caller-context
1969                           );
1970
1971       ExistPredSet preds =
1972         ExistPredSet.factory(pred);
1973
1974       for( Integer index: pInxs ) {
1975
1976         TempDescriptor paramCallee = fmCallee.getParameter(index);
1977         VariableNode vnCallee    = rg.getVariableNodeFromTemp(paramCallee);
1978
1979         RefEdge reCallee =
1980           new RefEdge(vnCallee,
1981                       hrnDstCallee,
1982                       reArg.getType(),
1983                       reArg.getField(),
1984                       toCalleeContext(reArg.getBeta(),
1985                                       preds,
1986                                       oocHrnIdOoc2callee
1987                                       ),
1988                       preds,
1989                       toCalleeContext(reArg.getTaints(),
1990                                       preds)
1991                       );
1992         
1993         rg.addRefEdge(vnCallee,
1994                       hrnDstCallee,
1995                       reCallee
1996                       );
1997       }
1998     }
1999
2000     // add in-context edges to callee graph
2001     Iterator<RefEdge> reItr = reachableCallerEdges.iterator();
2002     while( reItr.hasNext() ) {
2003       RefEdge reCaller  = reItr.next();
2004       RefSrcNode rsnCaller = reCaller.getSrc();
2005       assert rsnCaller instanceof HeapRegionNode;
2006       HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
2007       HeapRegionNode hrnDstCaller = reCaller.getDst();
2008
2009       HeapRegionNode hrnSrcCallee = rg.id2hrn.get(hrnSrcCaller.getID() );
2010       HeapRegionNode hrnDstCallee = rg.id2hrn.get(hrnDstCaller.getID() );
2011       assert hrnSrcCallee != null;
2012       assert hrnDstCallee != null;
2013
2014       ExistPred pred =
2015         ExistPred.factory(null,
2016                           hrnSrcCallee.getID(),
2017                           hrnDstCallee.getID(),
2018                           reCaller.getType(),
2019                           reCaller.getField(),
2020                           null,   // state
2021                           null,   // taint
2022                           false,  // out-of-callee-context
2023                           false   // out-of-caller-context
2024                           );
2025
2026       ExistPredSet preds =
2027         ExistPredSet.factory(pred);
2028
2029       RefEdge reCallee =
2030         new RefEdge(hrnSrcCallee,
2031                     hrnDstCallee,
2032                     reCaller.getType(),
2033                     reCaller.getField(),
2034                     toCalleeContext(reCaller.getBeta(),
2035                                     preds,
2036                                     oocHrnIdOoc2callee
2037                                     ),
2038                     preds,
2039                     toCalleeContext(reCaller.getTaints(),
2040                                     preds)
2041                     );
2042
2043       rg.addRefEdge(hrnSrcCallee,
2044                     hrnDstCallee,
2045                     reCallee
2046                     );
2047     }
2048
2049     // add out-of-context edges to callee graph
2050     reItr = oocCallerEdges.iterator();
2051     while( reItr.hasNext() ) {
2052       RefEdge reCaller     = reItr.next();
2053       RefSrcNode rsnCaller    = reCaller.getSrc();
2054       HeapRegionNode hrnDstCaller = reCaller.getDst();
2055       HeapRegionNode hrnDstCallee = rg.id2hrn.get(hrnDstCaller.getID() );
2056       assert hrnDstCallee != null;
2057
2058       TypeDescriptor oocNodeType;
2059       ReachSet oocReach;
2060       TempDescriptor oocPredSrcTemp = null;
2061       Integer oocPredSrcID   = null;
2062       boolean outOfCalleeContext;
2063       boolean outOfCallerContext;
2064
2065       if( rsnCaller instanceof VariableNode ) {
2066         VariableNode vnCaller = (VariableNode) rsnCaller;
2067         oocNodeType    = null;
2068         oocReach       = rsetEmpty;
2069         oocPredSrcTemp = vnCaller.getTempDescriptor();
2070         outOfCalleeContext = true;
2071         outOfCallerContext = false;
2072
2073       } else {
2074         HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
2075         assert !callerNodeIDsCopiedToCallee.contains(hrnSrcCaller.getID() );
2076         oocNodeType  = hrnSrcCaller.getType();
2077         oocReach     = hrnSrcCaller.getAlpha();
2078         oocPredSrcID = hrnSrcCaller.getID();
2079         if( hrnSrcCaller.isOutOfContext() ) {
2080           outOfCalleeContext = false;
2081           outOfCallerContext = true;
2082         } else {
2083           outOfCalleeContext = true;
2084           outOfCallerContext = false;
2085         }
2086       }
2087
2088       ExistPred pred =
2089         ExistPred.factory(oocPredSrcTemp,
2090                           oocPredSrcID,
2091                           hrnDstCallee.getID(),
2092                           reCaller.getType(),
2093                           reCaller.getField(),
2094                           null,
2095                           null,
2096                           outOfCalleeContext,
2097                           outOfCallerContext
2098                           );
2099
2100       ExistPredSet preds =
2101         ExistPredSet.factory(pred);
2102
2103       RefEdge oocEdgeExisting =
2104         rg.getOutOfContextReferenceTo(hrnDstCallee,
2105                                       oocNodeType,
2106                                       reCaller.getType(),
2107                                       reCaller.getField()
2108                                       );
2109
2110       if( oocEdgeExisting == null ) {
2111         // for consistency, map one out-of-context "identifier"
2112         // to one heap region node id, otherwise no convergence
2113         String oocid = "oocid"+
2114                        fmCallee+
2115                        hrnDstCallee.getIDString()+
2116                        oocNodeType+
2117                        reCaller.getType()+
2118                        reCaller.getField();
2119
2120         Integer oocHrnID = oocid2hrnid.get(oocid);
2121
2122         HeapRegionNode hrnCalleeAndOutContext;
2123
2124         if( oocHrnID == null ) {
2125
2126           hrnCalleeAndOutContext =
2127             rg.createNewHeapRegionNode(null,   // ID
2128                                        false,  // single object?
2129                                        false,  // new summary?
2130                                        true,   // out-of-context?
2131                                        oocNodeType,
2132                                        null,   // alloc site, shouldn't be used
2133                                        toCalleeContext(oocReach,
2134                                                        preds,
2135                                                        oocHrnIdOoc2callee
2136                                                        ),
2137                                        toCalleeContext(oocReach,
2138                                                        preds,
2139                                                        oocHrnIdOoc2callee
2140                                                        ),
2141                                        preds,
2142                                        "out-of-context"
2143                                        );
2144
2145           oocid2hrnid.put(oocid, hrnCalleeAndOutContext.getID() );
2146
2147         } else {
2148
2149           // the mapping already exists, so see if node is there
2150           hrnCalleeAndOutContext = rg.id2hrn.get(oocHrnID);
2151
2152           if( hrnCalleeAndOutContext == null ) {
2153             // nope, make it
2154             hrnCalleeAndOutContext =
2155               rg.createNewHeapRegionNode(oocHrnID,   // ID
2156                                          false,  // single object?
2157                                          false,  // new summary?
2158                                          true,   // out-of-context?
2159                                          oocNodeType,
2160                                          null,   // alloc site, shouldn't be used
2161                                          toCalleeContext(oocReach,
2162                                                          preds,
2163                                                          oocHrnIdOoc2callee
2164                                                          ),
2165                                          toCalleeContext(oocReach,
2166                                                          preds,
2167                                                          oocHrnIdOoc2callee
2168                                                          ),
2169                                          preds,
2170                                          "out-of-context"
2171                                          );
2172
2173           } else {
2174             // otherwise it is there, so merge reachability
2175             hrnCalleeAndOutContext.setAlpha(Canonical.unionORpreds(hrnCalleeAndOutContext.getAlpha(),
2176                                                                    toCalleeContext(oocReach,
2177                                                                                    preds,
2178                                                                                    oocHrnIdOoc2callee
2179                                                                                    )
2180                                                                    )
2181                                             );
2182           }
2183         }
2184
2185         assert hrnCalleeAndOutContext.reachHasOnlyOOC();
2186
2187         rg.addRefEdge(hrnCalleeAndOutContext,
2188                       hrnDstCallee,
2189                       new RefEdge(hrnCalleeAndOutContext,
2190                                   hrnDstCallee,
2191                                   reCaller.getType(),
2192                                   reCaller.getField(),
2193                                   toCalleeContext(reCaller.getBeta(),
2194                                                   preds,
2195                                                   oocHrnIdOoc2callee
2196                                                   ),
2197                                   preds,
2198                                   toCalleeContext(reCaller.getTaints(),
2199                                                   preds)
2200                                   )
2201                       );
2202
2203       } else {
2204         // the out-of-context edge already exists
2205         oocEdgeExisting.setBeta(Canonical.unionORpreds(oocEdgeExisting.getBeta(),
2206                                                        toCalleeContext(reCaller.getBeta(),
2207                                                                        preds,
2208                                                                        oocHrnIdOoc2callee
2209                                                                        )
2210                                                        )
2211                                 );
2212
2213         oocEdgeExisting.setPreds(Canonical.join(oocEdgeExisting.getPreds(),
2214                                                 preds
2215                                                 )
2216                                  );
2217
2218         oocEdgeExisting.setTaints(Canonical.unionORpreds(oocEdgeExisting.getTaints(),
2219                                                          toCalleeContext(reCaller.getTaints(),
2220                                                                          preds
2221                                                                          )
2222                                                          )
2223                                   );
2224
2225         HeapRegionNode hrnCalleeAndOutContext =
2226           (HeapRegionNode) oocEdgeExisting.getSrc();
2227         hrnCalleeAndOutContext.setAlpha(Canonical.unionORpreds(hrnCalleeAndOutContext.getAlpha(),
2228                                                                toCalleeContext(oocReach,
2229                                                                                preds,
2230                                                                                oocHrnIdOoc2callee
2231                                                                                )
2232                                                                )
2233                                         );
2234
2235         assert hrnCalleeAndOutContext.reachHasOnlyOOC();
2236       }
2237     }
2238
2239
2240     if( writeDebugDOTs ) {
2241       debugGraphPrefix = String.format("call%03d", debugCallSiteVisitCounter);
2242       rg.writeGraph(debugGraphPrefix+"calleeview",
2243                     resolveMethodDebugDOTwriteLabels,
2244                     resolveMethodDebugDOTselectTemps,
2245                     resolveMethodDebugDOTpruneGarbage,
2246                     resolveMethodDebugDOThideReach,
2247                     resolveMethodDebugDOThideSubsetReach,
2248                     resolveMethodDebugDOThidePreds,
2249                     resolveMethodDebugDOThideEdgeTaints);
2250     }
2251
2252     return rg;
2253   }
2254
2255   private static Hashtable<String, Integer> oocid2hrnid =
2256     new Hashtable<String, Integer>();
2257
2258
2259   private static boolean resolveMethodDebugDOTwriteLabels     = true;
2260   private static boolean resolveMethodDebugDOTselectTemps     = true;
2261   private static boolean resolveMethodDebugDOTpruneGarbage    = true;
2262   private static boolean resolveMethodDebugDOThideReach       = false;
2263   private static boolean resolveMethodDebugDOThideSubsetReach = true;
2264   private static boolean resolveMethodDebugDOThidePreds       = false;
2265   private static boolean resolveMethodDebugDOThideEdgeTaints  = true;
2266
2267   static String debugGraphPrefix;
2268   static int debugCallSiteVisitCounter;
2269   static int debugCallSiteVisitStartCapture;
2270   static int debugCallSiteNumVisitsToCapture;
2271   static boolean debugCallSiteStopAfter;
2272
2273
2274   public void
2275   resolveMethodCall(FlatCall fc,
2276                     FlatMethod fmCallee,
2277                     ReachGraph rgCallee,
2278                     Set<Integer> callerNodeIDsCopiedToCallee,
2279                     boolean writeDebugDOTs
2280                     ) {
2281
2282     if( writeDebugDOTs ) {
2283
2284       System.out.println("  Writing out visit "+
2285                          debugCallSiteVisitCounter+
2286                          " to debug call site");
2287
2288       debugGraphPrefix = String.format("call%03d",
2289                                        debugCallSiteVisitCounter);
2290
2291       rgCallee.writeGraph(debugGraphPrefix+"callee",
2292                           resolveMethodDebugDOTwriteLabels,
2293                           resolveMethodDebugDOTselectTemps,
2294                           resolveMethodDebugDOTpruneGarbage,
2295                           resolveMethodDebugDOThideReach,
2296                           resolveMethodDebugDOThideSubsetReach,
2297                           resolveMethodDebugDOThidePreds,
2298                           resolveMethodDebugDOThideEdgeTaints);
2299
2300       writeGraph(debugGraphPrefix+"caller00In",
2301                  resolveMethodDebugDOTwriteLabels,
2302                  resolveMethodDebugDOTselectTemps,
2303                  resolveMethodDebugDOTpruneGarbage,
2304                  resolveMethodDebugDOThideReach,
2305                  resolveMethodDebugDOThideSubsetReach,
2306                  resolveMethodDebugDOThidePreds,
2307                  resolveMethodDebugDOThideEdgeTaints,
2308                  callerNodeIDsCopiedToCallee);
2309     }
2310
2311
2312
2313     // method call transfer function steps:
2314     // 1. Use current callee-reachable heap (CRH) to test callee
2315     //    predicates and mark what will be coming in.
2316     // 2. Wipe CRH out of caller.
2317     // 3. Transplant marked callee parts in:
2318     //    a) bring in nodes
2319     //    b) bring in callee -> callee edges
2320     //    c) resolve out-of-context -> callee edges
2321     //    d) assign return value
2322     // 4. Collapse shadow nodes down
2323     // 5. Global sweep it.
2324
2325
2326     // 1. mark what callee elements have satisfied predicates
2327     Hashtable<HeapRegionNode, ExistPredSet> calleeNodesSatisfied =
2328       new Hashtable<HeapRegionNode, ExistPredSet>();
2329
2330     Hashtable<RefEdge, ExistPredSet> calleeEdgesSatisfied =
2331       new Hashtable<RefEdge, ExistPredSet>();
2332
2333     Hashtable< HeapRegionNode, Hashtable<ReachState, ExistPredSet> >
2334     calleeNode2calleeStatesSatisfied =
2335       new Hashtable< HeapRegionNode, Hashtable<ReachState, ExistPredSet> >();
2336
2337     Hashtable< RefEdge, Hashtable<ReachState, ExistPredSet> >
2338     calleeEdge2calleeStatesSatisfied =
2339       new Hashtable< RefEdge, Hashtable<ReachState, ExistPredSet> >();
2340
2341     Hashtable< RefEdge, Hashtable<Taint, ExistPredSet> >
2342     calleeEdge2calleeTaintsSatisfied =
2343       new Hashtable< RefEdge, Hashtable<Taint, ExistPredSet> >();
2344
2345     Hashtable< RefEdge, Set<RefSrcNode> > calleeEdges2oocCallerSrcMatches =
2346       new Hashtable< RefEdge, Set<RefSrcNode> >();
2347
2348
2349
2350     Iterator meItr = rgCallee.id2hrn.entrySet().iterator();
2351     while( meItr.hasNext() ) {
2352       Map.Entry me        = (Map.Entry)meItr.next();
2353       Integer id        = (Integer)        me.getKey();
2354       HeapRegionNode hrnCallee = (HeapRegionNode) me.getValue();
2355
2356       // if a callee element's predicates are satisfied then a set
2357       // of CALLER predicates is returned: they are the predicates
2358       // that the callee element moved into the caller context
2359       // should have, and it is inefficient to find this again later
2360       ExistPredSet predsIfSatis =
2361         hrnCallee.getPreds().isSatisfiedBy(this,
2362                                            callerNodeIDsCopiedToCallee,
2363                                            null);
2364
2365       if( predsIfSatis != null ) {
2366         calleeNodesSatisfied.put(hrnCallee, predsIfSatis);
2367       } else {
2368         // otherwise don't bother looking at edges to this node
2369         continue;
2370       }
2371
2372
2373
2374       // since the node is coming over, find out which reach
2375       // states on it should come over, too
2376       assert calleeNode2calleeStatesSatisfied.get(hrnCallee) == null;
2377
2378       Iterator<ReachState> stateItr = hrnCallee.getAlpha().iterator();
2379       while( stateItr.hasNext() ) {
2380         ReachState stateCallee = stateItr.next();
2381
2382         predsIfSatis =
2383           stateCallee.getPreds().isSatisfiedBy(this,
2384                                                callerNodeIDsCopiedToCallee,
2385                                                null);
2386         if( predsIfSatis != null ) {
2387
2388           Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied =
2389             calleeNode2calleeStatesSatisfied.get(hrnCallee);
2390
2391           if( calleeStatesSatisfied == null ) {
2392             calleeStatesSatisfied =
2393               new Hashtable<ReachState, ExistPredSet>();
2394
2395             calleeNode2calleeStatesSatisfied.put(hrnCallee, calleeStatesSatisfied);
2396           }
2397
2398           calleeStatesSatisfied.put(stateCallee, predsIfSatis);
2399         }
2400       }
2401
2402       // then look at edges to the node
2403       Iterator<RefEdge> reItr = hrnCallee.iteratorToReferencers();
2404       while( reItr.hasNext() ) {
2405         RefEdge reCallee  = reItr.next();
2406         RefSrcNode rsnCallee = reCallee.getSrc();
2407
2408         // (caller local variables to in-context heap regions)
2409         // have an (out-of-context heap region -> in-context heap region)
2410         // abstraction in the callEE, so its true we never need to
2411         // look at a (var node -> heap region) edge in callee to bring
2412         // those over for the call site transfer, except for the special
2413         // case of *RETURN var* -> heap region edges.
2414         // What about (param var->heap region)
2415         // edges in callee? They are dealt with below this loop.
2416
2417         if( rsnCallee instanceof VariableNode ) {
2418
2419           // looking for the return-value variable only
2420           VariableNode vnCallee = (VariableNode) rsnCallee;
2421           if( vnCallee.getTempDescriptor() != tdReturn ) {
2422             continue;
2423           }
2424
2425           TempDescriptor returnTemp = fc.getReturnTemp();
2426           if( returnTemp == null ||
2427               !DisjointAnalysis.shouldAnalysisTrack(returnTemp.getType() )
2428               ) {
2429             continue;
2430           }
2431
2432           // note that the assignment of the return value is to a
2433           // variable in the caller which is out-of-context with
2434           // respect to the callee
2435           VariableNode vnLhsCaller = getVariableNodeFromTemp(returnTemp);
2436           Set<RefSrcNode> rsnCallers = new HashSet<RefSrcNode>();
2437           rsnCallers.add(vnLhsCaller);
2438           calleeEdges2oocCallerSrcMatches.put(reCallee, rsnCallers);
2439
2440
2441         } else {
2442           // for HeapRegionNode callee sources...
2443
2444           // first see if the source is out-of-context, and only
2445           // proceed with this edge if we find some caller-context
2446           // matches
2447           HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2448           boolean matchedOutOfContext = false;
2449
2450           if( !hrnSrcCallee.isOutOfContext() ) {
2451
2452             predsIfSatis =
2453               hrnSrcCallee.getPreds().isSatisfiedBy(this,
2454                                                     callerNodeIDsCopiedToCallee,
2455                                                     null);
2456             if( predsIfSatis != null ) {
2457               calleeNodesSatisfied.put(hrnSrcCallee, predsIfSatis);
2458             } else {
2459               // otherwise forget this edge
2460               continue;
2461             }
2462
2463           } else {
2464             // hrnSrcCallee is out-of-context
2465             assert !calleeEdges2oocCallerSrcMatches.containsKey(reCallee);
2466
2467             Set<RefSrcNode> rsnCallers = new HashSet<RefSrcNode>();
2468
2469             // use the isSatisfiedBy with a non-null callers set to capture
2470             // nodes in the caller that match the predicates
2471             reCallee.getPreds().isSatisfiedBy( this,
2472                                                callerNodeIDsCopiedToCallee,
2473                                                rsnCallers );
2474
2475             if( !rsnCallers.isEmpty() ) {
2476               matchedOutOfContext = true;
2477               calleeEdges2oocCallerSrcMatches.put(reCallee, rsnCallers);
2478             }
2479           }
2480
2481           if( hrnSrcCallee.isOutOfContext() &&
2482               !matchedOutOfContext ) {
2483             continue;
2484           }
2485         }
2486
2487
2488         predsIfSatis =
2489           reCallee.getPreds().isSatisfiedBy(this,
2490                                             callerNodeIDsCopiedToCallee,
2491                                             null);
2492
2493
2494         if( predsIfSatis != null ) {
2495           calleeEdgesSatisfied.put(reCallee, predsIfSatis);
2496
2497           // since the edge is coming over, find out which reach
2498           // states on it should come over, too
2499           assert calleeEdge2calleeStatesSatisfied.get(reCallee) == null;
2500
2501           stateItr = reCallee.getBeta().iterator();
2502           while( stateItr.hasNext() ) {
2503             ReachState stateCallee = stateItr.next();
2504
2505             predsIfSatis =
2506               stateCallee.getPreds().isSatisfiedBy(this,
2507                                                    callerNodeIDsCopiedToCallee,
2508                                                    null);
2509             if( predsIfSatis != null ) {
2510
2511               Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied =
2512                 calleeEdge2calleeStatesSatisfied.get(reCallee);
2513
2514               if( calleeStatesSatisfied == null ) {
2515                 calleeStatesSatisfied =
2516                   new Hashtable<ReachState, ExistPredSet>();
2517
2518                 calleeEdge2calleeStatesSatisfied.put(reCallee, calleeStatesSatisfied);
2519               }
2520
2521               calleeStatesSatisfied.put(stateCallee, predsIfSatis);
2522             }
2523           }
2524
2525           // since the edge is coming over, find out which taints
2526           // on it should come over, too
2527           assert calleeEdge2calleeTaintsSatisfied.get(reCallee) == null;
2528
2529           Iterator<Taint> tItr = reCallee.getTaints().iterator();
2530           while( tItr.hasNext() ) {
2531             Taint tCallee = tItr.next();
2532
2533             predsIfSatis =
2534               tCallee.getPreds().isSatisfiedBy(this,
2535                                                callerNodeIDsCopiedToCallee,
2536                                                null);
2537             if( predsIfSatis != null ) {
2538
2539               Hashtable<Taint, ExistPredSet> calleeTaintsSatisfied =
2540                 calleeEdge2calleeTaintsSatisfied.get(reCallee);
2541
2542               if( calleeTaintsSatisfied == null ) {
2543                 calleeTaintsSatisfied =
2544                   new Hashtable<Taint, ExistPredSet>();
2545
2546                 calleeEdge2calleeTaintsSatisfied.put(reCallee, calleeTaintsSatisfied);
2547               }
2548
2549               calleeTaintsSatisfied.put(tCallee, predsIfSatis);
2550             }
2551           }
2552         }
2553       }
2554     }
2555
2556     if( writeDebugDOTs ) {
2557       writeGraph(debugGraphPrefix+"caller20BeforeWipe",
2558                  resolveMethodDebugDOTwriteLabels,
2559                  resolveMethodDebugDOTselectTemps,
2560                  resolveMethodDebugDOTpruneGarbage,
2561                  resolveMethodDebugDOThideReach,
2562                  resolveMethodDebugDOThideSubsetReach,
2563                  resolveMethodDebugDOThidePreds,
2564                  resolveMethodDebugDOThideEdgeTaints);
2565     }
2566
2567
2568     // 2. predicates tested, ok to wipe out caller part
2569     Iterator<Integer> hrnItr = callerNodeIDsCopiedToCallee.iterator();
2570     while( hrnItr.hasNext() ) {
2571       Integer hrnID     = hrnItr.next();
2572       HeapRegionNode hrnCaller = id2hrn.get(hrnID);
2573       assert hrnCaller != null;
2574
2575       // when clearing off nodes, also eliminate variable
2576       // references
2577       wipeOut(hrnCaller, true);
2578     }
2579
2580     // if we are assigning the return value to something, clobber now
2581     // as part of the wipe
2582     TempDescriptor returnTemp = fc.getReturnTemp();
2583     if( returnTemp != null &&
2584         DisjointAnalysis.shouldAnalysisTrack(returnTemp.getType() )
2585         ) {
2586
2587       VariableNode vnLhsCaller = getVariableNodeFromTemp(returnTemp);
2588       clearRefEdgesFrom(vnLhsCaller, null, null, true);
2589     }
2590
2591
2592
2593
2594     if( writeDebugDOTs ) {
2595       writeGraph(debugGraphPrefix+"caller30BeforeAddingNodes",
2596                  resolveMethodDebugDOTwriteLabels,
2597                  resolveMethodDebugDOTselectTemps,
2598                  resolveMethodDebugDOTpruneGarbage,
2599                  resolveMethodDebugDOThideReach,
2600                  resolveMethodDebugDOThideSubsetReach,
2601                  resolveMethodDebugDOThidePreds,
2602                  resolveMethodDebugDOThideEdgeTaints);
2603     }
2604
2605
2606
2607
2608     // 3. callee elements with satisfied preds come in, note that
2609     //    the mapping of elements satisfied to preds is like this:
2610     //    A callee element EE has preds EEp that are satisfied by
2611     //    some caller element ER.  We bring EE into the caller
2612     //    context as ERee with the preds of ER, namely ERp, which
2613     //    in the following algorithm is the value in the mapping
2614
2615     // 3.a) nodes
2616     Iterator satisItr = calleeNodesSatisfied.entrySet().iterator();
2617     while( satisItr.hasNext() ) {
2618       Map.Entry me        = (Map.Entry)satisItr.next();
2619       HeapRegionNode hrnCallee = (HeapRegionNode) me.getKey();
2620       ExistPredSet preds     = (ExistPredSet)   me.getValue();
2621
2622       // TODO: I think its true that the current implementation uses
2623       // the type of the OOC region and the predicates OF THE EDGE from
2624       // it to link everything up in caller context, so that's why we're
2625       // skipping this... maybe that's a sillier way to do it?
2626       if( hrnCallee.isOutOfContext() ) {
2627         continue;
2628       }
2629
2630       AllocSite as = hrnCallee.getAllocSite();
2631       allocSites.add(as);
2632
2633       Integer hrnIDshadow = as.getShadowIDfromID(hrnCallee.getID() );
2634
2635       HeapRegionNode hrnCaller = id2hrn.get(hrnIDshadow);
2636       if( hrnCaller == null ) {
2637         hrnCaller =
2638           createNewHeapRegionNode(hrnIDshadow,                 // id or null to generate a new one
2639                                   hrnCallee.isSingleObject(),  // single object?
2640                                   hrnCallee.isNewSummary(),    // summary?
2641                                   false,                       // out-of-context?
2642                                   hrnCallee.getType(),         // type
2643                                   hrnCallee.getAllocSite(),    // allocation site
2644                                   toCallerContext(hrnCallee.getInherent(),
2645                                                   calleeNode2calleeStatesSatisfied.get(hrnCallee) ),     // inherent reach
2646                                   null,                        // current reach
2647                                   predsEmpty,                  // predicates
2648                                   hrnCallee.getDescription()   // description
2649                                   );
2650       } else {
2651         assert hrnCaller.isWiped();
2652       }
2653
2654       hrnCaller.setAlpha(toCallerContext(hrnCallee.getAlpha(),
2655                                          calleeNode2calleeStatesSatisfied.get(hrnCallee)
2656                                          )
2657                          );
2658
2659       hrnCaller.setPreds(preds);
2660     }
2661
2662
2663
2664
2665
2666     if( writeDebugDOTs ) {
2667       writeGraph(debugGraphPrefix+"caller31BeforeAddingEdges",
2668                  resolveMethodDebugDOTwriteLabels,
2669                  resolveMethodDebugDOTselectTemps,
2670                  resolveMethodDebugDOTpruneGarbage,
2671                  resolveMethodDebugDOThideReach,
2672                  resolveMethodDebugDOThideSubsetReach,
2673                  resolveMethodDebugDOThidePreds,
2674                  resolveMethodDebugDOThideEdgeTaints);
2675     }
2676
2677
2678     // set these up during the next procedure so after
2679     // the caller has all of its nodes and edges put
2680     // back together we can propagate the callee's
2681     // reach changes backwards into the caller graph
2682     HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
2683
2684     Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
2685       new Hashtable<RefEdge, ChangeSet>();
2686
2687
2688     // 3.b) callee -> callee edges AND out-of-context -> callee
2689     //      which includes return temp -> callee edges now, too
2690     satisItr = calleeEdgesSatisfied.entrySet().iterator();
2691     while( satisItr.hasNext() ) {
2692       Map.Entry me       = (Map.Entry)satisItr.next();
2693       RefEdge reCallee = (RefEdge)      me.getKey();
2694       ExistPredSet preds    = (ExistPredSet) me.getValue();
2695
2696       HeapRegionNode hrnDstCallee = reCallee.getDst();
2697       AllocSite asDst        = hrnDstCallee.getAllocSite();
2698       allocSites.add(asDst);
2699
2700       Integer hrnIDDstShadow =
2701         asDst.getShadowIDfromID(hrnDstCallee.getID() );
2702
2703       HeapRegionNode hrnDstCaller = id2hrn.get(hrnIDDstShadow);
2704       assert hrnDstCaller != null;
2705
2706
2707       RefSrcNode rsnCallee = reCallee.getSrc();
2708
2709       Set<RefSrcNode> rsnCallers =
2710         new HashSet<RefSrcNode>();
2711
2712       Set<RefSrcNode> oocCallers =
2713         calleeEdges2oocCallerSrcMatches.get(reCallee);
2714
2715       if( rsnCallee instanceof HeapRegionNode ) {
2716         HeapRegionNode hrnCalleeSrc = (HeapRegionNode) rsnCallee;
2717         if( hrnCalleeSrc.isOutOfContext() ) {
2718           assert oocCallers != null;
2719         }
2720       }
2721
2722
2723       if( oocCallers == null ) {
2724         // there are no out-of-context matches, so it's
2725         // either a param/arg var or one in-context heap region
2726         if( rsnCallee instanceof VariableNode ) {
2727           // variable -> node in the callee should only
2728           // come into the caller if its from a param var
2729           VariableNode vnCallee = (VariableNode) rsnCallee;
2730           TempDescriptor tdParam  = vnCallee.getTempDescriptor();
2731           TempDescriptor tdArg    = fc.getArgMatchingParam(fmCallee,
2732                                                            tdParam);
2733           if( tdArg == null ) {
2734             // this means the variable isn't a parameter, its local
2735             // to the callee so we ignore it in call site transfer
2736             // shouldn't this NEVER HAPPEN?
2737             assert false;
2738           }
2739
2740           rsnCallers.add(this.getVariableNodeFromTemp(tdArg) );
2741
2742         } else {
2743           // otherwise source is in context, one region
2744
2745           HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2746
2747           // translate an in-context node to shadow
2748           AllocSite asSrc = hrnSrcCallee.getAllocSite();
2749           allocSites.add(asSrc);
2750
2751           Integer hrnIDSrcShadow =
2752             asSrc.getShadowIDfromID(hrnSrcCallee.getID() );
2753
2754           HeapRegionNode hrnSrcCallerShadow =
2755             this.id2hrn.get(hrnIDSrcShadow);
2756
2757           assert hrnSrcCallerShadow != null;
2758
2759           rsnCallers.add(hrnSrcCallerShadow);
2760         }
2761
2762       } else {
2763         // otherwise we have a set of out-of-context srcs
2764         // that should NOT be translated to shadow nodes
2765         assert !oocCallers.isEmpty();
2766         rsnCallers.addAll(oocCallers);
2767       }
2768
2769       // now make all caller edges we've identified from
2770       // this callee edge with a satisfied predicate
2771       assert !rsnCallers.isEmpty();
2772       Iterator<RefSrcNode> rsnItr = rsnCallers.iterator();
2773       while( rsnItr.hasNext() ) {
2774         RefSrcNode rsnCaller = rsnItr.next();
2775
2776         RefEdge reCaller = new RefEdge(rsnCaller,
2777                                        hrnDstCaller,
2778                                        reCallee.getType(),
2779                                        reCallee.getField(),
2780                                        toCallerContext(reCallee.getBeta(),
2781                                                        calleeEdge2calleeStatesSatisfied.get(reCallee) ),
2782                                        preds,
2783                                        toCallerContext(reCallee.getTaints(),
2784                                                        calleeEdge2calleeTaintsSatisfied.get(reCallee) )
2785                                        );
2786
2787         ChangeSet cs = ChangeSet.factory();
2788         Iterator<ReachState> rsItr = reCaller.getBeta().iterator();
2789         while( rsItr.hasNext() ) {
2790           ReachState state          = rsItr.next();
2791           ExistPredSet predsPreCallee = state.getPreds();
2792
2793           if( state.isEmpty() ) {
2794             continue;
2795           }
2796
2797           Iterator<ExistPred> predItr = predsPreCallee.iterator();
2798           while( predItr.hasNext() ) {
2799             ExistPred pred = predItr.next();
2800             ReachState old = pred.ne_state;
2801
2802             if( old == null ) {
2803               old = rstateEmpty;
2804             }
2805
2806             cs = Canonical.add(cs,
2807                                ChangeTuple.factory(old,
2808                                                    state
2809                                                    )
2810                                );
2811           }
2812         }
2813
2814         // we're just going to use the convenient "merge-if-exists"
2815         // edge call below, but still take a separate look if there
2816         // is an existing caller edge to build change sets properly
2817         if( !cs.isEmpty() ) {
2818           RefEdge edgeExisting = rsnCaller.getReferenceTo(hrnDstCaller,
2819                                                           reCallee.getType(),
2820                                                           reCallee.getField()
2821                                                           );
2822           if( edgeExisting != null ) {
2823             ChangeSet csExisting = edgePlannedChanges.get(edgeExisting);
2824             if( csExisting == null ) {
2825               csExisting = ChangeSet.factory();
2826             }
2827             edgePlannedChanges.put(edgeExisting,
2828                                    Canonical.union(csExisting,
2829                                                    cs
2830                                                    )
2831                                    );
2832           } else {
2833             edgesForPropagation.add(reCaller);
2834             assert !edgePlannedChanges.containsKey(reCaller);
2835             edgePlannedChanges.put(reCaller, cs);
2836           }
2837         }
2838
2839         // then add new caller edge or merge
2840         addEdgeOrMergeWithExisting(reCaller);
2841       }
2842     }
2843
2844
2845
2846
2847
2848     if( writeDebugDOTs ) {
2849       writeGraph(debugGraphPrefix+"caller38propagateReach",
2850                  resolveMethodDebugDOTwriteLabels,
2851                  resolveMethodDebugDOTselectTemps,
2852                  resolveMethodDebugDOTpruneGarbage,
2853                  resolveMethodDebugDOThideReach,
2854                  resolveMethodDebugDOThideSubsetReach,
2855                  resolveMethodDebugDOThidePreds,
2856                  resolveMethodDebugDOThideEdgeTaints);
2857     }
2858
2859     // propagate callee reachability changes to the rest
2860     // of the caller graph edges
2861     HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
2862
2863     propagateTokensOverEdges(edgesForPropagation,  // source edges
2864                              edgePlannedChanges,   // map src edge to change set
2865                              edgesUpdated);        // list of updated edges
2866
2867     // commit beta' (beta<-betaNew)
2868     Iterator<RefEdge> edgeItr = edgesUpdated.iterator();
2869     while( edgeItr.hasNext() ) {
2870       edgeItr.next().applyBetaNew();
2871     }
2872
2873
2874
2875
2876
2877
2878
2879     if( writeDebugDOTs ) {
2880       writeGraph(debugGraphPrefix+"caller40BeforeShadowMerge",
2881                  resolveMethodDebugDOTwriteLabels,
2882                  resolveMethodDebugDOTselectTemps,
2883                  resolveMethodDebugDOTpruneGarbage,
2884                  resolveMethodDebugDOThideReach,
2885                  resolveMethodDebugDOThideSubsetReach,
2886                  resolveMethodDebugDOThidePreds,
2887                  resolveMethodDebugDOThideEdgeTaints);
2888     }
2889
2890
2891     // 4) merge shadow nodes so alloc sites are back to k
2892     Iterator<AllocSite> asItr = rgCallee.allocSites.iterator();
2893     while( asItr.hasNext() ) {
2894       // for each allocation site do the following to merge
2895       // shadow nodes (newest from callee) with any existing
2896       // look for the newest normal and newest shadow "slot"
2897       // not being used, transfer normal to shadow.  Keep
2898       // doing this until there are no more normal nodes, or
2899       // no empty shadow slots: then merge all remaining normal
2900       // nodes into the shadow summary.  Finally, convert all
2901       // shadow to their normal versions.
2902       AllocSite as = asItr.next();
2903       int ageNorm = 0;
2904       int ageShad = 0;
2905
2906       while( ageNorm < allocationDepth &&
2907              ageShad < allocationDepth ) {
2908
2909         // first, are there any normal nodes left?
2910         Integer idNorm  = as.getIthOldest(ageNorm);
2911         HeapRegionNode hrnNorm = id2hrn.get(idNorm);
2912         if( hrnNorm == null ) {
2913           // no, this age of normal node not in the caller graph
2914           ageNorm++;
2915           continue;
2916         }
2917
2918         // yes, a normal node exists, is there an empty shadow
2919         // "slot" to transfer it onto?
2920         HeapRegionNode hrnShad = getIthNode(as, ageShad, true);
2921         if( !hrnShad.isWiped() ) {
2922           // no, this age of shadow node is not empty
2923           ageShad++;
2924           continue;
2925         }
2926
2927         // yes, this shadow node is empty
2928         transferOnto(hrnNorm, hrnShad);
2929         ageNorm++;
2930         ageShad++;
2931       }
2932
2933       // now, while there are still normal nodes but no shadow
2934       // slots, merge normal nodes into the shadow summary
2935       while( ageNorm < allocationDepth ) {
2936
2937         // first, are there any normal nodes left?
2938         Integer idNorm  = as.getIthOldest(ageNorm);
2939         HeapRegionNode hrnNorm = id2hrn.get(idNorm);
2940         if( hrnNorm == null ) {
2941           // no, this age of normal node not in the caller graph
2942           ageNorm++;
2943           continue;
2944         }
2945
2946         // yes, a normal node exists, so get the shadow summary
2947         HeapRegionNode summShad = getSummaryNode(as, true);
2948         mergeIntoSummary(hrnNorm, summShad);
2949
2950         // now tokens in reachability sets need to age also
2951         Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
2952         while( itrAllHRNodes.hasNext() ) {
2953           Map.Entry me       = (Map.Entry)itrAllHRNodes.next();
2954           HeapRegionNode hrnToAge = (HeapRegionNode) me.getValue();
2955
2956           ageTuplesFrom(as, hrnToAge);
2957
2958           Iterator<RefEdge> itrEdges = hrnToAge.iteratorToReferencers();
2959           while( itrEdges.hasNext() ) {
2960             ageTuplesFrom(as, itrEdges.next() );
2961           }
2962         }
2963
2964         ageNorm++;
2965       }
2966
2967       // if there is a normal summary, merge it into shadow summary
2968       Integer idNorm   = as.getSummary();
2969       HeapRegionNode summNorm = id2hrn.get(idNorm);
2970       if( summNorm != null ) {
2971         HeapRegionNode summShad = getSummaryNode(as, true);
2972         mergeIntoSummary(summNorm, summShad);
2973       }
2974
2975       // finally, flip all existing shadow nodes onto the normal
2976       for( int i = 0; i < allocationDepth; ++i ) {
2977         Integer idShad  = as.getIthOldestShadow(i);
2978         HeapRegionNode hrnShad = id2hrn.get(idShad);
2979         if( hrnShad != null ) {
2980           // flip it
2981           HeapRegionNode hrnNorm = getIthNode(as, i, false);
2982           assert hrnNorm.isWiped();
2983           transferOnto(hrnShad, hrnNorm);
2984         }
2985       }
2986
2987       Integer idShad   = as.getSummaryShadow();
2988       HeapRegionNode summShad = id2hrn.get(idShad);
2989       if( summShad != null ) {
2990         summNorm = getSummaryNode(as, false);
2991         transferOnto(summShad, summNorm);
2992       }
2993     }
2994
2995
2996
2997
2998
2999
3000     if( writeDebugDOTs ) {
3001       writeGraph(debugGraphPrefix+"caller45BeforeUnshadow",
3002                  resolveMethodDebugDOTwriteLabels,
3003                  resolveMethodDebugDOTselectTemps,
3004                  resolveMethodDebugDOTpruneGarbage,
3005                  resolveMethodDebugDOThideReach,
3006                  resolveMethodDebugDOThideSubsetReach,
3007                  resolveMethodDebugDOThidePreds,
3008                  resolveMethodDebugDOThideEdgeTaints);
3009     }
3010
3011
3012     Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
3013     while( itrAllHRNodes.hasNext() ) {
3014       Map.Entry me  = (Map.Entry)itrAllHRNodes.next();
3015       HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3016
3017       hrn.setAlpha(unshadow(hrn.getAlpha() ) );
3018
3019       Iterator<RefEdge> itrEdges = hrn.iteratorToReferencers();
3020       while( itrEdges.hasNext() ) {
3021         RefEdge re = itrEdges.next();
3022         re.setBeta(unshadow(re.getBeta() ) );
3023       }
3024     }
3025
3026
3027
3028
3029     if( writeDebugDOTs ) {
3030       writeGraph(debugGraphPrefix+"caller50BeforeGlobalSweep",
3031                  resolveMethodDebugDOTwriteLabels,
3032                  resolveMethodDebugDOTselectTemps,
3033                  resolveMethodDebugDOTpruneGarbage,
3034                  resolveMethodDebugDOThideReach,
3035                  resolveMethodDebugDOThideSubsetReach,
3036                  resolveMethodDebugDOThidePreds,
3037                  resolveMethodDebugDOThideEdgeTaints);
3038     }
3039
3040
3041     // 5.
3042     if( !DISABLE_GLOBAL_SWEEP ) {
3043       globalSweep();
3044     }
3045
3046
3047     if( writeDebugDOTs ) {
3048       writeGraph(debugGraphPrefix+"caller90AfterTransfer",
3049                  resolveMethodDebugDOTwriteLabels,
3050                  resolveMethodDebugDOTselectTemps,
3051                  resolveMethodDebugDOTpruneGarbage,
3052                  resolveMethodDebugDOThideReach,
3053                  resolveMethodDebugDOThideSubsetReach,
3054                  resolveMethodDebugDOThidePreds,
3055                  resolveMethodDebugDOThideEdgeTaints);
3056     }
3057   }
3058
3059
3060
3061   ////////////////////////////////////////////////////
3062   //
3063   //  Abstract garbage collection simply removes
3064   //  heap region nodes that are not mechanically
3065   //  reachable from a root set.  This step is
3066   //  essential for testing node and edge existence
3067   //  predicates efficiently
3068   //
3069   ////////////////////////////////////////////////////
3070   public void abstractGarbageCollect(Set<TempDescriptor> liveSet) {
3071
3072     // calculate a root set, will be different for Java
3073     // version of analysis versus Bamboo version
3074     Set<RefSrcNode> toVisit = new HashSet<RefSrcNode>();
3075
3076     // visit every variable in graph while building root
3077     // set, and do iterating on a copy, so we can remove
3078     // dead variables while we're at this
3079     Iterator makeCopyItr = td2vn.entrySet().iterator();
3080     Set entrysCopy  = new HashSet();
3081     while( makeCopyItr.hasNext() ) {
3082       entrysCopy.add(makeCopyItr.next() );
3083     }
3084
3085     Iterator eItr = entrysCopy.iterator();
3086     while( eItr.hasNext() ) {
3087       Map.Entry me = (Map.Entry)eItr.next();
3088       TempDescriptor td = (TempDescriptor) me.getKey();
3089       VariableNode vn = (VariableNode)   me.getValue();
3090
3091       if( liveSet.contains(td) ) {
3092         toVisit.add(vn);
3093
3094       } else {
3095         // dead var, remove completely from graph
3096         td2vn.remove(td);
3097         clearRefEdgesFrom(vn, null, null, true);
3098       }
3099     }
3100
3101     // everything visited in a traversal is
3102     // considered abstractly live
3103     Set<RefSrcNode> visited = new HashSet<RefSrcNode>();
3104
3105     while( !toVisit.isEmpty() ) {
3106       RefSrcNode rsn = toVisit.iterator().next();
3107       toVisit.remove(rsn);
3108       visited.add(rsn);
3109
3110       Iterator<RefEdge> hrnItr = rsn.iteratorToReferencees();
3111       while( hrnItr.hasNext() ) {
3112         RefEdge edge = hrnItr.next();
3113         HeapRegionNode hrn  = edge.getDst();
3114
3115         if( !visited.contains(hrn) ) {
3116           toVisit.add(hrn);
3117         }
3118       }
3119     }
3120
3121     // get a copy of the set to iterate over because
3122     // we're going to monkey with the graph when we
3123     // identify a garbage node
3124     Set<HeapRegionNode> hrnAllPrior = new HashSet<HeapRegionNode>();
3125     Iterator<HeapRegionNode> hrnItr = id2hrn.values().iterator();
3126     while( hrnItr.hasNext() ) {
3127       hrnAllPrior.add(hrnItr.next() );
3128     }
3129
3130     Iterator<HeapRegionNode> hrnAllItr = hrnAllPrior.iterator();
3131     while( hrnAllItr.hasNext() ) {
3132       HeapRegionNode hrn = hrnAllItr.next();
3133
3134       if( !visited.contains(hrn) ) {
3135
3136         // heap region nodes are compared across ReachGraph
3137         // objects by their integer ID, so when discarding
3138         // garbage nodes we must also discard entries in
3139         // the ID -> heap region hashtable.
3140         id2hrn.remove(hrn.getID() );
3141
3142         // RefEdge objects are two-way linked between
3143         // nodes, so when a node is identified as garbage,
3144         // actively clear references to and from it so
3145         // live nodes won't have dangling RefEdge's
3146         wipeOut(hrn, true);
3147
3148         // if we just removed the last node from an allocation
3149         // site, it should be taken out of the ReachGraph's list
3150         AllocSite as = hrn.getAllocSite();
3151         if( !hasNodesOf(as) ) {
3152           allocSites.remove(as);
3153         }
3154       }
3155     }
3156   }
3157
3158   protected boolean hasNodesOf(AllocSite as) {
3159     if( id2hrn.containsKey(as.getSummary() ) ) {
3160       return true;
3161     }
3162
3163     for( int i = 0; i < allocationDepth; ++i ) {
3164       if( id2hrn.containsKey(as.getIthOldest(i) ) ) {
3165         return true;
3166       }
3167     }
3168     return false;
3169   }
3170
3171
3172   ////////////////////////////////////////////////////
3173   //
3174   //  This global sweep is an optional step to prune
3175   //  reachability sets that are not internally
3176   //  consistent with the global graph.  It should be
3177   //  invoked after strong updates or method calls.
3178   //
3179   ////////////////////////////////////////////////////
3180   public void globalSweep() {
3181
3182     // boldB is part of the phase 1 sweep
3183     // it has an in-context table and an out-of-context table
3184     Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBic =
3185       new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();
3186
3187     Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBooc =
3188       new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();
3189
3190     // visit every heap region to initialize alphaNew and betaNew,
3191     // and make a map of every hrnID to the source nodes it should
3192     // propagate forward from.  In-context flagged hrnID's propagate
3193     // from only the in-context node they name, but out-of-context
3194     // ID's may propagate from several out-of-context nodes
3195     Hashtable< Integer, Set<HeapRegionNode> > icID2srcs =
3196       new Hashtable< Integer, Set<HeapRegionNode> >();
3197
3198     Hashtable< Integer, Set<HeapRegionNode> > oocID2srcs =
3199       new Hashtable< Integer, Set<HeapRegionNode> >();
3200
3201
3202     Iterator itrHrns = id2hrn.entrySet().iterator();
3203     while( itrHrns.hasNext() ) {
3204       Map.Entry me    = (Map.Entry)itrHrns.next();
3205       Integer hrnID = (Integer)        me.getKey();
3206       HeapRegionNode hrn   = (HeapRegionNode) me.getValue();
3207
3208       // assert that this node and incoming edges have clean alphaNew
3209       // and betaNew sets, respectively
3210       assert rsetEmpty.equals(hrn.getAlphaNew() );
3211
3212       Iterator<RefEdge> itrRers = hrn.iteratorToReferencers();
3213       while( itrRers.hasNext() ) {
3214         RefEdge edge = itrRers.next();
3215         assert rsetEmpty.equals(edge.getBetaNew() );
3216       }
3217
3218       // make a mapping of IDs to heap regions they propagate from
3219       if( hrn.isFlagged() ) {
3220         assert !hrn.isOutOfContext();
3221         assert !icID2srcs.containsKey(hrn.getID() );
3222
3223         // in-context flagged node IDs simply propagate from the
3224         // node they name
3225         Set<HeapRegionNode> srcs = new HashSet<HeapRegionNode>();
3226         srcs.add(hrn);
3227         icID2srcs.put(hrn.getID(), srcs);
3228       }
3229
3230       if( hrn.isOutOfContext() ) {
3231         assert !hrn.isFlagged();
3232
3233         // the reachability states on an out-of-context
3234         // node are not really important (combinations of
3235         // IDs or arity)--what matters is that the states
3236         // specify which nodes this out-of-context node
3237         // stands in for.  For example, if the state [17?, 19*]
3238         // appears on the ooc node, it may serve as a source
3239         // for node 17? and a source for node 19.
3240         Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3241         while( stateItr.hasNext() ) {
3242           ReachState state = stateItr.next();
3243
3244           Iterator<ReachTuple> rtItr = state.iterator();
3245           while( rtItr.hasNext() ) {
3246             ReachTuple rt = rtItr.next();
3247             assert rt.isOutOfContext();
3248
3249             Set<HeapRegionNode> srcs = oocID2srcs.get(rt.getHrnID() );
3250             if( srcs == null ) {
3251               srcs = new HashSet<HeapRegionNode>();
3252             }
3253             srcs.add(hrn);
3254             oocID2srcs.put(rt.getHrnID(), srcs);
3255           }
3256         }
3257       }
3258     }
3259
3260     // calculate boldB for all hrnIDs identified by the above
3261     // node traversal, propagating from every source
3262     while( !icID2srcs.isEmpty() || !oocID2srcs.isEmpty() ) {
3263
3264       Integer hrnID;
3265       Set<HeapRegionNode> srcs;
3266       boolean inContext;
3267
3268       if( !icID2srcs.isEmpty() ) {
3269         Map.Entry me = (Map.Entry)icID2srcs.entrySet().iterator().next();
3270         hrnID = (Integer)             me.getKey();
3271         srcs  = (Set<HeapRegionNode>)me.getValue();
3272         inContext = true;
3273         icID2srcs.remove(hrnID);
3274
3275       } else {
3276         assert !oocID2srcs.isEmpty();
3277
3278         Map.Entry me = (Map.Entry)oocID2srcs.entrySet().iterator().next();
3279         hrnID = (Integer)             me.getKey();
3280         srcs  = (Set<HeapRegionNode>)me.getValue();
3281         inContext = false;
3282         oocID2srcs.remove(hrnID);
3283       }
3284
3285
3286       Hashtable<RefEdge, ReachSet> boldB_f =
3287         new Hashtable<RefEdge, ReachSet>();
3288
3289       Set<RefEdge> workSetEdges = new HashSet<RefEdge>();
3290
3291       Iterator<HeapRegionNode> hrnItr = srcs.iterator();
3292       while( hrnItr.hasNext() ) {
3293         HeapRegionNode hrn = hrnItr.next();
3294
3295         assert workSetEdges.isEmpty();
3296
3297         // initial boldB_f constraints
3298         Iterator<RefEdge> itrRees = hrn.iteratorToReferencees();
3299         while( itrRees.hasNext() ) {
3300           RefEdge edge = itrRees.next();
3301
3302           assert !boldB_f.containsKey(edge);
3303           boldB_f.put(edge, edge.getBeta() );
3304
3305           assert !workSetEdges.contains(edge);
3306           workSetEdges.add(edge);
3307         }
3308
3309         // enforce the boldB_f constraint at edges until we reach a fixed point
3310         while( !workSetEdges.isEmpty() ) {
3311           RefEdge edge = workSetEdges.iterator().next();
3312           workSetEdges.remove(edge);
3313
3314           Iterator<RefEdge> itrPrime = edge.getDst().iteratorToReferencees();
3315           while( itrPrime.hasNext() ) {
3316             RefEdge edgePrime = itrPrime.next();
3317
3318             ReachSet prevResult   = boldB_f.get(edgePrime);
3319             ReachSet intersection = Canonical.intersection(boldB_f.get(edge),
3320                                                            edgePrime.getBeta()
3321                                                            );
3322
3323             if( prevResult == null ||
3324                 Canonical.unionORpreds(prevResult,
3325                                        intersection).size()
3326                 > prevResult.size()
3327                 ) {
3328
3329               if( prevResult == null ) {
3330                 boldB_f.put(edgePrime,
3331                             Canonical.unionORpreds(edgePrime.getBeta(),
3332                                                    intersection
3333                                                    )
3334                             );
3335               } else {
3336                 boldB_f.put(edgePrime,
3337                             Canonical.unionORpreds(prevResult,
3338                                                    intersection
3339                                                    )
3340                             );
3341               }
3342               workSetEdges.add(edgePrime);
3343             }
3344           }
3345         }
3346       }
3347
3348       if( inContext ) {
3349         boldBic.put(hrnID, boldB_f);
3350       } else {
3351         boldBooc.put(hrnID, boldB_f);
3352       }
3353     }
3354
3355
3356     // use boldB to prune hrnIDs from alpha states that are impossible
3357     // and propagate the differences backwards across edges
3358     HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
3359
3360     Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
3361       new Hashtable<RefEdge, ChangeSet>();
3362
3363
3364     itrHrns = id2hrn.entrySet().iterator();
3365     while( itrHrns.hasNext() ) {
3366       Map.Entry me    = (Map.Entry)itrHrns.next();
3367       Integer hrnID = (Integer)        me.getKey();
3368       HeapRegionNode hrn   = (HeapRegionNode) me.getValue();
3369
3370       // out-of-context nodes don't participate in the
3371       // global sweep, they serve as sources for the pass
3372       // performed above
3373       if( hrn.isOutOfContext() ) {
3374         continue;
3375       }
3376
3377       // the inherent states of a region are the exception
3378       // to removal as the global sweep prunes
3379       ReachTuple rtException = ReachTuple.factory(hrnID,
3380                                                   !hrn.isSingleObject(),
3381                                                   ReachTuple.ARITY_ONE,
3382                                                   false  // out-of-context
3383                                                   );
3384
3385       ChangeSet cts = ChangeSet.factory();
3386
3387       // mark hrnIDs for removal
3388       Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3389       while( stateItr.hasNext() ) {
3390         ReachState stateOld = stateItr.next();
3391
3392         ReachState markedHrnIDs = ReachState.factory();
3393
3394         Iterator<ReachTuple> rtItr = stateOld.iterator();
3395         while( rtItr.hasNext() ) {
3396           ReachTuple rtOld = rtItr.next();
3397
3398           // never remove the inherent hrnID from a flagged region
3399           // because it is trivially satisfied
3400           if( hrn.isFlagged() ) {
3401             if( rtOld == rtException ) {
3402               continue;
3403             }
3404           }
3405
3406           // does boldB allow this hrnID?
3407           boolean foundState = false;
3408           Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3409           while( incidentEdgeItr.hasNext() ) {
3410             RefEdge incidentEdge = incidentEdgeItr.next();
3411
3412             Hashtable<RefEdge, ReachSet> B;
3413             if( rtOld.isOutOfContext() ) {
3414               B = boldBooc.get(rtOld.getHrnID() );
3415             } else {
3416
3417               if( !id2hrn.containsKey(rtOld.getHrnID() ) ) {
3418                 // let symbols not in the graph get pruned
3419                 break;
3420               }
3421
3422               B = boldBic.get(rtOld.getHrnID() );
3423             }
3424
3425             if( B != null ) {
3426               ReachSet boldB_rtOld_incident = B.get(incidentEdge);
3427               if( boldB_rtOld_incident != null &&
3428                   boldB_rtOld_incident.containsIgnorePreds(stateOld) != null
3429                   ) {
3430                 foundState = true;
3431               }
3432             }
3433           }
3434
3435           if( !foundState ) {
3436             markedHrnIDs = Canonical.addUpArity(markedHrnIDs, rtOld);
3437           }
3438         }
3439
3440         // if there is nothing marked, just move on
3441         if( markedHrnIDs.isEmpty() ) {
3442           hrn.setAlphaNew(Canonical.add(hrn.getAlphaNew(),
3443                                         stateOld
3444                                         )
3445                           );
3446           continue;
3447         }
3448
3449         // remove all marked hrnIDs and establish a change set that should
3450         // propagate backwards over edges from this node
3451         ReachState statePruned = ReachState.factory();
3452         rtItr = stateOld.iterator();
3453         while( rtItr.hasNext() ) {
3454           ReachTuple rtOld = rtItr.next();
3455
3456           if( !markedHrnIDs.containsTuple(rtOld) ) {
3457             statePruned = Canonical.addUpArity(statePruned, rtOld);
3458           }
3459         }
3460         assert !stateOld.equals(statePruned);
3461
3462         hrn.setAlphaNew(Canonical.add(hrn.getAlphaNew(),
3463                                       statePruned
3464                                       )
3465                         );
3466         ChangeTuple ct = ChangeTuple.factory(stateOld,
3467                                              statePruned
3468                                              );
3469         cts = Canonical.add(cts, ct);
3470       }
3471
3472       // throw change tuple set on all incident edges
3473       if( !cts.isEmpty() ) {
3474         Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3475         while( incidentEdgeItr.hasNext() ) {
3476           RefEdge incidentEdge = incidentEdgeItr.next();
3477
3478           edgesForPropagation.add(incidentEdge);
3479
3480           if( edgePlannedChanges.get(incidentEdge) == null ) {
3481             edgePlannedChanges.put(incidentEdge, cts);
3482           } else {
3483             edgePlannedChanges.put(
3484               incidentEdge,
3485               Canonical.union(edgePlannedChanges.get(incidentEdge),
3486                               cts
3487                               )
3488               );
3489           }
3490         }
3491       }
3492     }
3493
3494     HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
3495
3496     propagateTokensOverEdges(edgesForPropagation,
3497                              edgePlannedChanges,
3498                              edgesUpdated);
3499
3500     // at the end of the 1st phase reference edges have
3501     // beta, betaNew that correspond to beta and betaR
3502     //
3503     // commit beta<-betaNew, so beta=betaR and betaNew
3504     // will represent the beta' calculation in 2nd phase
3505     //
3506     // commit alpha<-alphaNew because it won't change
3507     HashSet<RefEdge> res = new HashSet<RefEdge>();
3508
3509     Iterator<HeapRegionNode> nodeItr = id2hrn.values().iterator();
3510     while( nodeItr.hasNext() ) {
3511       HeapRegionNode hrn = nodeItr.next();
3512
3513       // as mentioned above, out-of-context nodes only serve
3514       // as sources of reach states for the sweep, not part
3515       // of the changes
3516       if( hrn.isOutOfContext() ) {
3517         assert hrn.getAlphaNew().equals(rsetEmpty);
3518       } else {
3519         hrn.applyAlphaNew();
3520       }
3521
3522       Iterator<RefEdge> itrRes = hrn.iteratorToReferencers();
3523       while( itrRes.hasNext() ) {
3524         res.add(itrRes.next() );
3525       }
3526     }
3527
3528
3529     // 2nd phase
3530     Iterator<RefEdge> edgeItr = res.iterator();
3531     while( edgeItr.hasNext() ) {
3532       RefEdge edge = edgeItr.next();
3533       HeapRegionNode hrn  = edge.getDst();
3534
3535       // commit results of last phase
3536       if( edgesUpdated.contains(edge) ) {
3537         edge.applyBetaNew();
3538       }
3539
3540       // compute intial condition of 2nd phase
3541       edge.setBetaNew(Canonical.intersection(edge.getBeta(),
3542                                              hrn.getAlpha()
3543                                              )
3544                       );
3545     }
3546
3547     // every edge in the graph is the initial workset
3548     Set<RefEdge> edgeWorkSet = (Set) res.clone();
3549     while( !edgeWorkSet.isEmpty() ) {
3550       RefEdge edgePrime = edgeWorkSet.iterator().next();
3551       edgeWorkSet.remove(edgePrime);
3552
3553       RefSrcNode rsn = edgePrime.getSrc();
3554       if( !(rsn instanceof HeapRegionNode) ) {
3555         continue;
3556       }
3557       HeapRegionNode hrn = (HeapRegionNode) rsn;
3558
3559       Iterator<RefEdge> itrEdge = hrn.iteratorToReferencers();
3560       while( itrEdge.hasNext() ) {
3561         RefEdge edge = itrEdge.next();
3562
3563         ReachSet prevResult = edge.getBetaNew();
3564         assert prevResult != null;
3565
3566         ReachSet intersection =
3567           Canonical.intersection(edge.getBeta(),
3568                                  edgePrime.getBetaNew()
3569                                  );
3570
3571         if( Canonical.unionORpreds(prevResult,
3572                                    intersection
3573                                    ).size()
3574             > prevResult.size()
3575             ) {
3576
3577           edge.setBetaNew(
3578             Canonical.unionORpreds(prevResult,
3579                                    intersection
3580                                    )
3581             );
3582           edgeWorkSet.add(edge);
3583         }
3584       }
3585     }
3586
3587     // commit beta' (beta<-betaNew)
3588     edgeItr = res.iterator();
3589     while( edgeItr.hasNext() ) {
3590       edgeItr.next().applyBetaNew();
3591     }
3592   }
3593
3594
3595   // a useful assertion for debugging:
3596   // every in-context tuple on any edge or
3597   // any node should name a node that is
3598   // part of the graph
3599   public boolean inContextTuplesInGraph() {
3600
3601     Iterator hrnItr = id2hrn.entrySet().iterator();
3602     while( hrnItr.hasNext() ) {
3603       Map.Entry me  = (Map.Entry)hrnItr.next();
3604       HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3605
3606       {
3607         Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3608         while( stateItr.hasNext() ) {
3609           ReachState state = stateItr.next();
3610
3611           Iterator<ReachTuple> rtItr = state.iterator();
3612           while( rtItr.hasNext() ) {
3613             ReachTuple rt = rtItr.next();
3614
3615             if( !rt.isOutOfContext() ) {
3616               if( !id2hrn.containsKey(rt.getHrnID() ) ) {
3617                 System.out.println(rt.getHrnID()+" is missing");
3618                 return false;
3619               }
3620             }
3621           }
3622         }
3623       }
3624
3625       Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3626       while( edgeItr.hasNext() ) {
3627         RefEdge edge = edgeItr.next();
3628
3629         Iterator<ReachState> stateItr = edge.getBeta().iterator();
3630         while( stateItr.hasNext() ) {
3631           ReachState state = stateItr.next();
3632
3633           Iterator<ReachTuple> rtItr = state.iterator();
3634           while( rtItr.hasNext() ) {
3635             ReachTuple rt = rtItr.next();
3636
3637             if( !rt.isOutOfContext() ) {
3638               if( !id2hrn.containsKey(rt.getHrnID() ) ) {
3639                 System.out.println(rt.getHrnID()+" is missing");
3640                 return false;
3641               }
3642             }
3643           }
3644         }
3645       }
3646     }
3647
3648     return true;
3649   }
3650
3651
3652   // another useful assertion for debugging
3653   public boolean noEmptyReachSetsInGraph() {
3654
3655     Iterator hrnItr = id2hrn.entrySet().iterator();
3656     while( hrnItr.hasNext() ) {
3657       Map.Entry me  = (Map.Entry)hrnItr.next();
3658       HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3659
3660       if( !hrn.isOutOfContext() &&
3661           !hrn.isWiped()        &&
3662           hrn.getAlpha().isEmpty()
3663           ) {
3664         System.out.println("!!! "+hrn+" has an empty ReachSet !!!");
3665         return false;
3666       }
3667
3668       Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3669       while( edgeItr.hasNext() ) {
3670         RefEdge edge = edgeItr.next();
3671
3672         if( edge.getBeta().isEmpty() ) {
3673           System.out.println("!!! "+edge+" has an empty ReachSet !!!");
3674           return false;
3675         }
3676       }
3677     }
3678
3679     return true;
3680   }
3681
3682
3683   public boolean everyReachStateWTrue() {
3684
3685     Iterator hrnItr = id2hrn.entrySet().iterator();
3686     while( hrnItr.hasNext() ) {
3687       Map.Entry me  = (Map.Entry)hrnItr.next();
3688       HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3689
3690       {
3691         Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3692         while( stateItr.hasNext() ) {
3693           ReachState state = stateItr.next();
3694
3695           if( !state.getPreds().equals(predsTrue) ) {
3696             return false;
3697           }
3698         }
3699       }
3700
3701       Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3702       while( edgeItr.hasNext() ) {
3703         RefEdge edge = edgeItr.next();
3704
3705         Iterator<ReachState> stateItr = edge.getBeta().iterator();
3706         while( stateItr.hasNext() ) {
3707           ReachState state = stateItr.next();
3708
3709           if( !state.getPreds().equals(predsTrue) ) {
3710             return false;
3711           }
3712         }
3713       }
3714     }
3715
3716     return true;
3717   }
3718
3719
3720
3721
3722   ////////////////////////////////////////////////////
3723   // in merge() and equals() methods the suffix A
3724   // represents the passed in graph and the suffix
3725   // B refers to the graph in this object
3726   // Merging means to take the incoming graph A and
3727   // merge it into B, so after the operation graph B
3728   // is the final result.
3729   ////////////////////////////////////////////////////
3730   protected void merge(ReachGraph rg) {
3731
3732     if( rg == null ) {
3733       return;
3734     }
3735
3736     mergeNodes(rg);
3737     mergeRefEdges(rg);
3738     mergeAllocSites(rg);
3739     mergeInaccessibleVars(rg);
3740   }
3741
3742   protected void mergeNodes(ReachGraph rg) {
3743
3744     // start with heap region nodes
3745     Set sA = rg.id2hrn.entrySet();
3746     Iterator iA = sA.iterator();
3747     while( iA.hasNext() ) {
3748       Map.Entry meA  = (Map.Entry)iA.next();
3749       Integer idA  = (Integer)        meA.getKey();
3750       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3751
3752       // if this graph doesn't have a node the
3753       // incoming graph has, allocate it
3754       if( !id2hrn.containsKey(idA) ) {
3755         HeapRegionNode hrnB = hrnA.copy();
3756         id2hrn.put(idA, hrnB);
3757
3758       } else {
3759         // otherwise this is a node present in both graphs
3760         // so make the new reachability set a union of the
3761         // nodes' reachability sets
3762         HeapRegionNode hrnB = id2hrn.get(idA);
3763         hrnB.setAlpha(Canonical.unionORpreds(hrnB.getAlpha(),
3764                                              hrnA.getAlpha()
3765                                              )
3766                       );
3767
3768         hrnB.setPreds(Canonical.join(hrnB.getPreds(),
3769                                      hrnA.getPreds()
3770                                      )
3771                       );
3772
3773
3774
3775         if( !hrnA.equals(hrnB) ) {
3776           rg.writeGraph("graphA");
3777           this.writeGraph("graphB");
3778           throw new Error("flagged not matching");
3779         }
3780
3781
3782
3783       }
3784     }
3785
3786     // now add any variable nodes that are in graph B but
3787     // not in A
3788     sA = rg.td2vn.entrySet();
3789     iA = sA.iterator();
3790     while( iA.hasNext() ) {
3791       Map.Entry meA = (Map.Entry)iA.next();
3792       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3793       VariableNode lnA = (VariableNode)   meA.getValue();
3794
3795       // if the variable doesn't exist in B, allocate and add it
3796       VariableNode lnB = getVariableNodeFromTemp(tdA);
3797     }
3798   }
3799
3800   protected void mergeRefEdges(ReachGraph rg) {
3801
3802     // between heap regions
3803     Set sA = rg.id2hrn.entrySet();
3804     Iterator iA = sA.iterator();
3805     while( iA.hasNext() ) {
3806       Map.Entry meA  = (Map.Entry)iA.next();
3807       Integer idA  = (Integer)        meA.getKey();
3808       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3809
3810       Iterator<RefEdge> heapRegionsItrA = hrnA.iteratorToReferencees();
3811       while( heapRegionsItrA.hasNext() ) {
3812         RefEdge edgeA     = heapRegionsItrA.next();
3813         HeapRegionNode hrnChildA = edgeA.getDst();
3814         Integer idChildA  = hrnChildA.getID();
3815
3816         // at this point we know an edge in graph A exists
3817         // idA -> idChildA, does this exist in B?
3818         assert id2hrn.containsKey(idA);
3819         HeapRegionNode hrnB        = id2hrn.get(idA);
3820         RefEdge edgeToMerge = null;
3821
3822         Iterator<RefEdge> heapRegionsItrB = hrnB.iteratorToReferencees();
3823         while( heapRegionsItrB.hasNext() &&
3824                edgeToMerge == null          ) {
3825
3826           RefEdge edgeB     = heapRegionsItrB.next();
3827           HeapRegionNode hrnChildB = edgeB.getDst();
3828           Integer idChildB  = hrnChildB.getID();
3829
3830           // don't use the RefEdge.equals() here because
3831           // we're talking about existence between graphs,
3832           // not intragraph equal
3833           if( idChildB.equals(idChildA) &&
3834               edgeB.typeAndFieldEquals(edgeA) ) {
3835
3836             edgeToMerge = edgeB;
3837           }
3838         }
3839
3840         // if the edge from A was not found in B,
3841         // add it to B.
3842         if( edgeToMerge == null ) {
3843           assert id2hrn.containsKey(idChildA);
3844           HeapRegionNode hrnChildB = id2hrn.get(idChildA);
3845           edgeToMerge = edgeA.copy();
3846           edgeToMerge.setSrc(hrnB);
3847           edgeToMerge.setDst(hrnChildB);
3848           addRefEdge(hrnB, hrnChildB, edgeToMerge);
3849         }
3850         // otherwise, the edge already existed in both graphs
3851         // so merge their reachability sets
3852         else {
3853           // just replace this beta set with the union
3854           assert edgeToMerge != null;
3855           edgeToMerge.setBeta(
3856             Canonical.unionORpreds(edgeToMerge.getBeta(),
3857                                    edgeA.getBeta()
3858                                    )
3859             );
3860           edgeToMerge.setPreds(
3861             Canonical.join(edgeToMerge.getPreds(),
3862                            edgeA.getPreds()
3863                            )
3864             );
3865           edgeToMerge.setTaints(
3866             Canonical.union(edgeToMerge.getTaints(),
3867                             edgeA.getTaints()
3868                             )
3869             );
3870         }
3871       }
3872     }
3873
3874     // and then again from variable nodes
3875     sA = rg.td2vn.entrySet();
3876     iA = sA.iterator();
3877     while( iA.hasNext() ) {
3878       Map.Entry meA = (Map.Entry)iA.next();
3879       TempDescriptor tdA = (TempDescriptor) meA.getKey();
3880       VariableNode vnA = (VariableNode)   meA.getValue();
3881
3882       Iterator<RefEdge> heapRegionsItrA = vnA.iteratorToReferencees();
3883       while( heapRegionsItrA.hasNext() ) {
3884         RefEdge edgeA     = heapRegionsItrA.next();
3885         HeapRegionNode hrnChildA = edgeA.getDst();
3886         Integer idChildA  = hrnChildA.getID();
3887
3888         // at this point we know an edge in graph A exists
3889         // tdA -> idChildA, does this exist in B?
3890         assert td2vn.containsKey(tdA);
3891         VariableNode vnB         = td2vn.get(tdA);
3892         RefEdge edgeToMerge = null;
3893
3894         Iterator<RefEdge> heapRegionsItrB = vnB.iteratorToReferencees();
3895         while( heapRegionsItrB.hasNext() &&
3896                edgeToMerge == null          ) {
3897
3898           RefEdge edgeB     = heapRegionsItrB.next();
3899           HeapRegionNode hrnChildB = edgeB.getDst();
3900           Integer idChildB  = hrnChildB.getID();
3901
3902           // don't use the RefEdge.equals() here because
3903           // we're talking about existence between graphs
3904           if( idChildB.equals(idChildA) &&
3905               edgeB.typeAndFieldEquals(edgeA) ) {
3906
3907             edgeToMerge = edgeB;
3908           }
3909         }
3910
3911         // if the edge from A was not found in B,
3912         // add it to B.
3913         if( edgeToMerge == null ) {
3914           assert id2hrn.containsKey(idChildA);
3915           HeapRegionNode hrnChildB = id2hrn.get(idChildA);
3916           edgeToMerge = edgeA.copy();
3917           edgeToMerge.setSrc(vnB);
3918           edgeToMerge.setDst(hrnChildB);
3919           addRefEdge(vnB, hrnChildB, edgeToMerge);
3920         }
3921         // otherwise, the edge already existed in both graphs
3922         // so merge their reachability sets
3923         else {
3924           // just replace this beta set with the union
3925           edgeToMerge.setBeta(Canonical.unionORpreds(edgeToMerge.getBeta(),
3926                                                      edgeA.getBeta()
3927                                                      )
3928                               );
3929           edgeToMerge.setPreds(Canonical.join(edgeToMerge.getPreds(),
3930                                               edgeA.getPreds()
3931                                               )
3932                                );
3933           edgeToMerge.setTaints(
3934             Canonical.union(edgeToMerge.getTaints(),
3935                             edgeA.getTaints()
3936                             )
3937             );
3938         }
3939       }
3940     }
3941   }
3942
3943   protected void mergeAllocSites(ReachGraph rg) {
3944     allocSites.addAll(rg.allocSites);
3945   }
3946
3947   protected void mergeInaccessibleVars(ReachGraph rg) {
3948     inaccessibleVars.addAll(rg.inaccessibleVars);
3949   }
3950
3951
3952
3953   static public boolean dbgEquals = false;
3954
3955
3956   // it is necessary in the equals() member functions
3957   // to "check both ways" when comparing the data
3958   // structures of two graphs.  For instance, if all
3959   // edges between heap region nodes in graph A are
3960   // present and equal in graph B it is not sufficient
3961   // to say the graphs are equal.  Consider that there
3962   // may be edges in graph B that are not in graph A.
3963   // the only way to know that all edges in both graphs
3964   // are equally present is to iterate over both data
3965   // structures and compare against the other graph.
3966   public boolean equals(ReachGraph rg) {
3967
3968     if( rg == null ) {
3969       if( dbgEquals ) {
3970         System.out.println("rg is null");
3971       }
3972       return false;
3973     }
3974
3975     if( !areHeapRegionNodesEqual(rg) ) {
3976       if( dbgEquals ) {
3977         System.out.println("hrn not equal");
3978       }
3979       return false;
3980     }
3981
3982     if( !areVariableNodesEqual(rg) ) {
3983       if( dbgEquals ) {
3984         System.out.println("vars not equal");
3985       }
3986       return false;
3987     }
3988
3989     if( !areRefEdgesEqual(rg) ) {
3990       if( dbgEquals ) {
3991         System.out.println("edges not equal");
3992       }
3993       return false;
3994     }
3995
3996     if( !inaccessibleVars.equals(rg.inaccessibleVars) ) {
3997       return false;
3998     }
3999
4000     // if everything is equal up to this point,
4001     // assert that allocSites is also equal--
4002     // this data is redundant but kept for efficiency
4003     assert allocSites.equals(rg.allocSites);
4004
4005     return true;
4006   }
4007
4008
4009   protected boolean areHeapRegionNodesEqual(ReachGraph rg) {
4010
4011     if( !areallHRNinAalsoinBandequal(this, rg) ) {
4012       return false;
4013     }
4014
4015     if( !areallHRNinAalsoinBandequal(rg, this) ) {
4016       return false;
4017     }
4018
4019     return true;
4020   }
4021
4022   static protected boolean areallHRNinAalsoinBandequal(ReachGraph rgA,
4023                                                        ReachGraph rgB) {
4024     Set sA = rgA.id2hrn.entrySet();
4025     Iterator iA = sA.iterator();
4026     while( iA.hasNext() ) {
4027       Map.Entry meA  = (Map.Entry)iA.next();
4028       Integer idA  = (Integer)        meA.getKey();
4029       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
4030
4031       if( !rgB.id2hrn.containsKey(idA) ) {
4032         return false;
4033       }
4034
4035       HeapRegionNode hrnB = rgB.id2hrn.get(idA);
4036       if( !hrnA.equalsIncludingAlphaAndPreds(hrnB) ) {
4037         return false;
4038       }
4039     }
4040
4041     return true;
4042   }
4043
4044   protected boolean areVariableNodesEqual(ReachGraph rg) {
4045
4046     if( !areallVNinAalsoinBandequal(this, rg) ) {
4047       return false;
4048     }
4049
4050     if( !areallVNinAalsoinBandequal(rg, this) ) {
4051       return false;
4052     }
4053
4054     return true;
4055   }
4056
4057   static protected boolean areallVNinAalsoinBandequal(ReachGraph rgA,
4058                                                       ReachGraph rgB) {
4059     Set sA = rgA.td2vn.entrySet();
4060     Iterator iA = sA.iterator();
4061     while( iA.hasNext() ) {
4062       Map.Entry meA = (Map.Entry)iA.next();
4063       TempDescriptor tdA = (TempDescriptor) meA.getKey();
4064
4065       if( !rgB.td2vn.containsKey(tdA) ) {
4066         return false;
4067       }
4068     }
4069
4070     return true;
4071   }
4072
4073
4074   protected boolean areRefEdgesEqual(ReachGraph rg) {
4075     if( !areallREinAandBequal(this, rg) ) {
4076       return false;
4077     }
4078
4079     if( !areallREinAandBequal(rg, this) ) {
4080       return false;
4081     }
4082
4083     return true;
4084   }
4085
4086   static protected boolean areallREinAandBequal(ReachGraph rgA,
4087                                                 ReachGraph rgB) {
4088
4089     // check all the heap region->heap region edges
4090     Set sA = rgA.id2hrn.entrySet();
4091     Iterator iA = sA.iterator();
4092     while( iA.hasNext() ) {
4093       Map.Entry meA  = (Map.Entry)iA.next();
4094       Integer idA  = (Integer)        meA.getKey();
4095       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
4096
4097       // we should have already checked that the same
4098       // heap regions exist in both graphs
4099       assert rgB.id2hrn.containsKey(idA);
4100
4101       if( !areallREfromAequaltoB(rgA, hrnA, rgB) ) {
4102         return false;
4103       }
4104
4105       // then check every edge in B for presence in A, starting
4106       // from the same parent HeapRegionNode
4107       HeapRegionNode hrnB = rgB.id2hrn.get(idA);
4108
4109       if( !areallREfromAequaltoB(rgB, hrnB, rgA) ) {
4110         return false;
4111       }
4112     }
4113
4114     // then check all the variable->heap region edges
4115     sA = rgA.td2vn.entrySet();
4116     iA = sA.iterator();
4117     while( iA.hasNext() ) {
4118       Map.Entry meA = (Map.Entry)iA.next();
4119       TempDescriptor tdA = (TempDescriptor) meA.getKey();
4120       VariableNode vnA = (VariableNode)   meA.getValue();
4121
4122       // we should have already checked that the same
4123       // label nodes exist in both graphs
4124       assert rgB.td2vn.containsKey(tdA);
4125
4126       if( !areallREfromAequaltoB(rgA, vnA, rgB) ) {
4127         return false;
4128       }
4129
4130       // then check every edge in B for presence in A, starting
4131       // from the same parent VariableNode
4132       VariableNode vnB = rgB.td2vn.get(tdA);
4133
4134       if( !areallREfromAequaltoB(rgB, vnB, rgA) ) {
4135         return false;
4136       }
4137     }
4138
4139     return true;
4140   }
4141
4142
4143   static protected boolean areallREfromAequaltoB(ReachGraph rgA,
4144                                                  RefSrcNode rnA,
4145                                                  ReachGraph rgB) {
4146
4147     Iterator<RefEdge> itrA = rnA.iteratorToReferencees();
4148     while( itrA.hasNext() ) {
4149       RefEdge edgeA     = itrA.next();
4150       HeapRegionNode hrnChildA = edgeA.getDst();
4151       Integer idChildA  = hrnChildA.getID();
4152
4153       assert rgB.id2hrn.containsKey(idChildA);
4154
4155       // at this point we know an edge in graph A exists
4156       // rnA -> idChildA, does this exact edge exist in B?
4157       boolean edgeFound = false;
4158
4159       RefSrcNode rnB = null;
4160       if( rnA instanceof HeapRegionNode ) {
4161         HeapRegionNode hrnA = (HeapRegionNode) rnA;
4162         rnB = rgB.id2hrn.get(hrnA.getID() );
4163       } else {
4164         VariableNode vnA = (VariableNode) rnA;
4165         rnB = rgB.td2vn.get(vnA.getTempDescriptor() );
4166       }
4167
4168       Iterator<RefEdge> itrB = rnB.iteratorToReferencees();
4169       while( itrB.hasNext() ) {
4170         RefEdge edgeB     = itrB.next();
4171         HeapRegionNode hrnChildB = edgeB.getDst();
4172         Integer idChildB  = hrnChildB.getID();
4173
4174         if( idChildA.equals(idChildB) &&
4175             edgeA.typeAndFieldEquals(edgeB) ) {
4176
4177           // there is an edge in the right place with the right field,
4178           // but do they have the same attributes?
4179           if( edgeA.equalsIncludingBetaPredsTaints( edgeB ) ) {
4180             edgeFound = true;
4181           }
4182         }
4183       }
4184
4185       if( !edgeFound ) {
4186         return false;
4187       }
4188     }
4189
4190     return true;
4191   }
4192
4193
4194   // can be used to assert monotonicity
4195   static public boolean isNoSmallerThan(ReachGraph rgA,
4196                                         ReachGraph rgB) {
4197
4198     //System.out.println( "*** Asking if A is no smaller than B ***" );
4199
4200     Iterator iA = rgA.id2hrn.entrySet().iterator();
4201     while( iA.hasNext() ) {
4202       Map.Entry meA  = (Map.Entry)iA.next();
4203       Integer idA  = (Integer)        meA.getKey();
4204       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
4205
4206       if( !rgB.id2hrn.containsKey(idA) ) {
4207         System.out.println("  regions smaller");
4208         return false;
4209       }
4210     }
4211
4212     // this works just fine, no smaller than
4213     if( !areallVNinAalsoinBandequal(rgA, rgB) ) {
4214       System.out.println("  vars smaller:");
4215       System.out.println("    A:"+rgA.td2vn.keySet() );
4216       System.out.println("    B:"+rgB.td2vn.keySet() );
4217       return false;
4218     }
4219
4220
4221     iA = rgA.id2hrn.entrySet().iterator();
4222     while( iA.hasNext() ) {
4223       Map.Entry meA  = (Map.Entry)iA.next();
4224       Integer idA  = (Integer)        meA.getKey();
4225       HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
4226
4227       Iterator<RefEdge> reItr = hrnA.iteratorToReferencers();
4228       while( reItr.hasNext() ) {
4229         RefEdge edgeA = reItr.next();
4230         RefSrcNode rsnA  = edgeA.getSrc();
4231
4232         // we already checked that nodes were present
4233         HeapRegionNode hrnB = rgB.id2hrn.get(hrnA.getID() );
4234         assert hrnB != null;
4235
4236         RefSrcNode rsnB;
4237         if( rsnA instanceof VariableNode ) {
4238           VariableNode vnA = (VariableNode) rsnA;
4239           rsnB = rgB.td2vn.get(vnA.getTempDescriptor() );
4240
4241         } else {
4242           HeapRegionNode hrnSrcA = (HeapRegionNode) rsnA;
4243           rsnB = rgB.id2hrn.get(hrnSrcA.getID() );
4244         }
4245         assert rsnB != null;
4246
4247         RefEdge edgeB = rsnB.getReferenceTo(hrnB,
4248                                             edgeA.getType(),
4249                                             edgeA.getField()
4250                                             );
4251         if( edgeB == null ) {
4252           System.out.println("  edges smaller:");
4253           return false;
4254         }
4255       }
4256     }
4257
4258
4259     return true;
4260   }
4261
4262
4263
4264
4265
4266   // this analysis no longer has the "match anything"
4267   // type which was represented by null
4268   protected TypeDescriptor mostSpecificType(TypeDescriptor td1,
4269                                             TypeDescriptor td2) {
4270     assert td1 != null;
4271     assert td2 != null;
4272
4273     if( td1.isNull() ) {
4274       return td2;
4275     }
4276     if( td2.isNull() ) {
4277       return td1;
4278     }
4279     return typeUtil.mostSpecific(td1, td2);
4280   }
4281
4282   protected TypeDescriptor mostSpecificType(TypeDescriptor td1,
4283                                             TypeDescriptor td2,
4284                                             TypeDescriptor td3) {
4285
4286     return mostSpecificType(td1,
4287                             mostSpecificType(td2, td3)
4288                             );
4289   }
4290
4291   protected TypeDescriptor mostSpecificType(TypeDescriptor td1,
4292                                             TypeDescriptor td2,
4293                                             TypeDescriptor td3,
4294                                             TypeDescriptor td4) {
4295
4296     return mostSpecificType(mostSpecificType(td1, td2),
4297                             mostSpecificType(td3, td4)
4298                             );
4299   }
4300
4301   protected boolean isSuperiorType(TypeDescriptor possibleSuper,
4302                                    TypeDescriptor possibleChild) {
4303     assert possibleSuper != null;
4304     assert possibleChild != null;
4305
4306     if( possibleSuper.isNull() ||
4307         possibleChild.isNull() ) {
4308       return true;
4309     }
4310
4311     return typeUtil.isSuperorType(possibleSuper, possibleChild);
4312   }
4313
4314
4315   protected boolean hasMatchingField(HeapRegionNode src,
4316                                      RefEdge edge) {
4317
4318     TypeDescriptor tdSrc = src.getType();
4319     assert tdSrc != null;
4320
4321     if( tdSrc.isArray() ) {
4322       TypeDescriptor td = edge.getType();
4323       assert td != null;
4324
4325       TypeDescriptor tdSrcDeref = tdSrc.dereference();
4326       assert tdSrcDeref != null;
4327
4328       if( !typeUtil.isSuperorType(tdSrcDeref, td) ) {
4329         return false;
4330       }
4331
4332       return edge.getField().equals(DisjointAnalysis.arrayElementFieldName);
4333     }
4334
4335     // if it's not a class, it doesn't have any fields to match
4336     if( !tdSrc.isClass() ) {
4337       return false;
4338     }
4339
4340     ClassDescriptor cd = tdSrc.getClassDesc();
4341     while( cd != null ) {
4342       Iterator fieldItr = cd.getFields();
4343
4344       while( fieldItr.hasNext() ) {
4345         FieldDescriptor fd = (FieldDescriptor) fieldItr.next();
4346
4347         if( fd.getType().equals(edge.getType() ) &&
4348             fd.getSymbol().equals(edge.getField() ) ) {
4349           return true;
4350         }
4351       }
4352
4353       cd = cd.getSuperDesc();
4354     }
4355
4356     // otherwise it is a class with fields
4357     // but we didn't find a match
4358     return false;
4359   }
4360
4361   protected boolean hasMatchingType(RefEdge edge,
4362                                     HeapRegionNode dst) {
4363
4364     // if the region has no type, matches everything
4365     TypeDescriptor tdDst = dst.getType();
4366     assert tdDst != null;
4367
4368     // if the type is not a class or an array, don't
4369     // match because primitives are copied, no aliases
4370     ClassDescriptor cdDst = tdDst.getClassDesc();
4371     if( cdDst == null && !tdDst.isArray() ) {
4372       return false;
4373     }
4374
4375     // if the edge type is null, it matches everything
4376     TypeDescriptor tdEdge = edge.getType();
4377     assert tdEdge != null;
4378
4379     return typeUtil.isSuperorType(tdEdge, tdDst);
4380   }
4381
4382
4383
4384   // the default signature for quick-and-dirty debugging
4385   public void writeGraph(String graphName) {
4386     writeGraph(graphName,
4387                true,   // write labels
4388                true,   // label select
4389                true,   // prune garbage
4390                false,  // hide reachability
4391                true,   // hide subset reachability
4392                true,   // hide predicates
4393                false,   // hide edge taints
4394                null    // in-context boundary
4395                );
4396   }
4397
4398   public void writeGraph(String graphName,
4399                          boolean writeLabels,
4400                          boolean labelSelect,
4401                          boolean pruneGarbage,
4402                          boolean hideReachability,
4403                          boolean hideSubsetReachability,
4404                          boolean hidePredicates,
4405                          boolean hideEdgeTaints
4406                          ) {
4407     writeGraph(graphName,
4408                writeLabels,
4409                labelSelect,
4410                pruneGarbage,
4411                hideReachability,
4412                hideSubsetReachability,
4413                hidePredicates,
4414                hideEdgeTaints,
4415                null);
4416   }
4417
4418   public void writeGraph(String graphName,
4419                          boolean writeLabels,
4420                          boolean labelSelect,
4421                          boolean pruneGarbage,
4422                          boolean hideReachability,
4423                          boolean hideSubsetReachability,
4424                          boolean hidePredicates,
4425                          boolean hideEdgeTaints,
4426                          Set<Integer> callerNodeIDsCopiedToCallee
4427                          ) {
4428     try {
4429       // remove all non-word characters from the graph name so
4430       // the filename and identifier in dot don't cause errors
4431       // jjenista - also replace underscore '_' to prevent some
4432       // really, really long names from IHMS debugging
4433       graphName = graphName.replaceAll("[\\W_]", "");
4434
4435       BufferedWriter bw =
4436         new BufferedWriter(new FileWriter(graphName+".dot") );
4437
4438       bw.write("digraph "+graphName+" {\n");
4439
4440
4441       // this is an optional step to form the callee-reachable
4442       // "cut-out" into a DOT cluster for visualization
4443       if( callerNodeIDsCopiedToCallee != null ) {
4444
4445         bw.write("  subgraph cluster0 {\n");
4446         bw.write("    color=blue;\n");
4447
4448         Iterator i = id2hrn.entrySet().iterator();
4449         while( i.hasNext() ) {
4450           Map.Entry me  = (Map.Entry)i.next();
4451           HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4452
4453           if( callerNodeIDsCopiedToCallee.contains(hrn.getID() ) ) {
4454             bw.write("    "+
4455                      hrn.toString()+
4456                      hrn.toStringDOT(hideReachability,
4457                                      hideSubsetReachability,
4458                                      hidePredicates)+
4459                      ";\n");
4460           }
4461         }
4462
4463         bw.write("  }\n");
4464       }
4465
4466
4467       Set<HeapRegionNode> visited = new HashSet<HeapRegionNode>();
4468
4469       // then visit every heap region node
4470       Iterator i = id2hrn.entrySet().iterator();
4471       while( i.hasNext() ) {
4472         Map.Entry me  = (Map.Entry)i.next();
4473         HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4474
4475         // only visit nodes worth writing out--for instance
4476         // not every node at an allocation is referenced
4477         // (think of it as garbage-collected), etc.
4478         if( !pruneGarbage        ||
4479             hrn.isOutOfContext() ||
4480             (hrn.isFlagged() && hrn.getID() > 0 && !hrn.isWiped()) // a non-shadow flagged node
4481             ) {
4482
4483           if( !visited.contains(hrn) ) {
4484             traverseHeapRegionNodes(hrn,
4485                                     bw,
4486                                     null,
4487                                     visited,
4488                                     hideReachability,
4489                                     hideSubsetReachability,
4490                                     hidePredicates,
4491                                     hideEdgeTaints,
4492                                     callerNodeIDsCopiedToCallee);
4493           }
4494         }
4495       }
4496
4497       bw.write("  graphTitle[label=\""+graphName+"\",shape=box];\n");
4498
4499
4500       // then visit every label node, useful for debugging
4501       if( writeLabels ) {
4502         i = td2vn.entrySet().iterator();
4503         while( i.hasNext() ) {
4504           Map.Entry me = (Map.Entry)i.next();
4505           VariableNode vn = (VariableNode) me.getValue();
4506
4507           if( labelSelect ) {
4508             String labelStr = vn.getTempDescriptorString();
4509             if( labelStr.startsWith("___temp")     ||
4510                 labelStr.startsWith("___dst")      ||
4511                 labelStr.startsWith("___srctmp")   ||
4512                 labelStr.startsWith("___neverused")
4513                 ) {
4514               continue;
4515             }
4516           }
4517
4518           Iterator<RefEdge> heapRegionsItr = vn.iteratorToReferencees();
4519           while( heapRegionsItr.hasNext() ) {
4520             RefEdge edge = heapRegionsItr.next();
4521             HeapRegionNode hrn  = edge.getDst();
4522
4523             if( !visited.contains(hrn) ) {
4524               traverseHeapRegionNodes(hrn,
4525                                       bw,
4526                                       null,
4527                                       visited,
4528                                       hideReachability,
4529                                       hideSubsetReachability,
4530                                       hidePredicates,
4531                                       hideEdgeTaints,
4532                                       callerNodeIDsCopiedToCallee);
4533             }
4534
4535             bw.write("  "+vn.toString()+
4536                      " -> "+hrn.toString()+
4537                      edge.toStringDOT(hideReachability,
4538                                       hideSubsetReachability,
4539                                       hidePredicates,
4540                                       hideEdgeTaints,
4541                                       "")+
4542                      ";\n");
4543           }
4544         }
4545       }
4546
4547       bw.write("}\n");
4548       bw.close();
4549
4550     } catch( IOException e ) {
4551       throw new Error("Error writing out DOT graph "+graphName);
4552     }
4553   }
4554
4555   protected void
4556   traverseHeapRegionNodes(HeapRegionNode hrn,
4557                           BufferedWriter bw,
4558                           TempDescriptor td,
4559                           Set<HeapRegionNode> visited,
4560                           boolean hideReachability,
4561                           boolean hideSubsetReachability,
4562                           boolean hidePredicates,
4563                           boolean hideEdgeTaints,
4564                           Set<Integer>        callerNodeIDsCopiedToCallee
4565                           ) throws java.io.IOException {
4566
4567     if( visited.contains(hrn) ) {
4568       return;
4569     }
4570     visited.add(hrn);
4571
4572     // if we're drawing the callee-view subgraph, only
4573     // write out the node info if it hasn't already been
4574     // written
4575     if( callerNodeIDsCopiedToCallee == null ||
4576         !callerNodeIDsCopiedToCallee.contains(hrn.getID() )
4577         ) {
4578       bw.write("  "+
4579                hrn.toString()+
4580                hrn.toStringDOT(hideReachability,
4581                                hideSubsetReachability,
4582                                hidePredicates)+
4583                ";\n");
4584     }
4585
4586     Iterator<RefEdge> childRegionsItr = hrn.iteratorToReferencees();
4587     while( childRegionsItr.hasNext() ) {
4588       RefEdge edge     = childRegionsItr.next();
4589       HeapRegionNode hrnChild = edge.getDst();
4590
4591       if( callerNodeIDsCopiedToCallee != null &&
4592           (edge.getSrc() instanceof HeapRegionNode) ) {
4593         HeapRegionNode hrnSrc = (HeapRegionNode) edge.getSrc();
4594         if( callerNodeIDsCopiedToCallee.contains(hrnSrc.getID()        ) &&
4595             callerNodeIDsCopiedToCallee.contains(edge.getDst().getID() )
4596             ) {
4597           bw.write("  "+hrn.toString()+
4598                    " -> "+hrnChild.toString()+
4599                    edge.toStringDOT(hideReachability,
4600                                     hideSubsetReachability,
4601                                     hidePredicates,
4602                                     hideEdgeTaints,
4603                                     ",color=blue")+
4604                    ";\n");
4605         } else if( !callerNodeIDsCopiedToCallee.contains(hrnSrc.getID()       ) &&
4606                    callerNodeIDsCopiedToCallee.contains(edge.getDst().getID() )
4607                    ) {
4608           bw.write("  "+hrn.toString()+
4609                    " -> "+hrnChild.toString()+
4610                    edge.toStringDOT(hideReachability,
4611                                     hideSubsetReachability,
4612                                     hidePredicates,
4613                                     hideEdgeTaints,
4614                                     ",color=blue,style=dashed")+
4615                    ";\n");
4616         } else {
4617           bw.write("  "+hrn.toString()+
4618                    " -> "+hrnChild.toString()+
4619                    edge.toStringDOT(hideReachability,
4620                                     hideSubsetReachability,
4621                                     hidePredicates,
4622                                     hideEdgeTaints,
4623                                     "")+
4624                    ";\n");
4625         }
4626       } else {
4627         bw.write("  "+hrn.toString()+
4628                  " -> "+hrnChild.toString()+
4629                  edge.toStringDOT(hideReachability,
4630                                   hideSubsetReachability,
4631                                   hidePredicates,
4632                                   hideEdgeTaints,
4633                                   "")+
4634                  ";\n");
4635       }
4636
4637       traverseHeapRegionNodes(hrnChild,
4638                               bw,
4639                               td,
4640                               visited,
4641                               hideReachability,
4642                               hideSubsetReachability,
4643                               hidePredicates,
4644                               hideEdgeTaints,
4645                               callerNodeIDsCopiedToCallee);
4646     }
4647   }
4648
4649
4650
4651
4652
4653
4654   // return the set of heap regions from the given allocation
4655   // site, if any, that exist in this graph
4656   protected Set<HeapRegionNode> getAnyExisting(AllocSite as) {
4657
4658     Set<HeapRegionNode> out = new HashSet<HeapRegionNode>();
4659
4660     Integer idSum = as.getSummary();
4661     if( id2hrn.containsKey(idSum) ) {
4662       out.add(id2hrn.get(idSum) );
4663     }
4664
4665     for( int i = 0; i < as.getAllocationDepth(); ++i ) {
4666       Integer idI = as.getIthOldest(i);
4667       if( id2hrn.containsKey(idI) ) {
4668         out.add(id2hrn.get(idI) );
4669       }
4670     }
4671
4672     return out;
4673   }
4674
4675   // return the set of reach tuples (NOT A REACH STATE! JUST A SET!)
4676   // from the given allocation site, if any, from regions for that
4677   // site that exist in this graph
4678   protected Set<ReachTuple> getAnyExisting(AllocSite as,
4679                                            boolean includeARITY_ZEROORMORE,
4680                                            boolean includeARITY_ONE) {
4681
4682     Set<ReachTuple> out = new HashSet<ReachTuple>();
4683
4684     Integer idSum = as.getSummary();
4685     if( id2hrn.containsKey(idSum) ) {
4686
4687       HeapRegionNode hrn = id2hrn.get(idSum);
4688       assert !hrn.isOutOfContext();
4689
4690       if( !includeARITY_ZEROORMORE ) {
4691         out.add(ReachTuple.factory(hrn.getID(),
4692                                    true,     // multi-obj region
4693                                    ReachTuple.ARITY_ZEROORMORE,
4694                                    false)    // ooc?
4695                 );
4696       }
4697
4698       if( includeARITY_ONE ) {
4699         out.add(ReachTuple.factory(hrn.getID(),
4700                                    true,     // multi-object region
4701                                    ReachTuple.ARITY_ONE,
4702                                    false)    // ooc?
4703                 );
4704       }
4705     }
4706
4707     if( !includeARITY_ONE ) {
4708       // no need to do the single-object regions that
4709       // only have an ARITY ONE possible
4710       return out;
4711     }
4712
4713     for( int i = 0; i < as.getAllocationDepth(); ++i ) {
4714
4715       Integer idI = as.getIthOldest(i);
4716       if( id2hrn.containsKey(idI) ) {
4717
4718         HeapRegionNode hrn = id2hrn.get(idI);
4719         assert !hrn.isOutOfContext();
4720
4721         out.add(ReachTuple.factory(hrn.getID(),
4722                                    false,    // multi-object region
4723                                    ReachTuple.ARITY_ONE,
4724                                    false)    // ooc?
4725                 );
4726       }
4727     }
4728
4729     return out;
4730   }
4731
4732
4733   // if an object allocated at the target site may be
4734   // reachable from both an object from root1 and an
4735   // object allocated at root2, return TRUE
4736   public boolean mayBothReachTarget(AllocSite asRoot1,
4737                                     AllocSite asRoot2,
4738                                     AllocSite asTarget) {
4739
4740     // consider all heap regions of the target and look
4741     // for a reach state that indicates regions of root1
4742     // and root2 might be able to reach same object
4743     Set<HeapRegionNode> hrnSetTarget = getAnyExisting(asTarget);
4744
4745     // get relevant reach tuples, include ARITY_ZEROORMORE and ARITY_ONE
4746     Set<ReachTuple> rtSet1 = getAnyExisting(asRoot1, true, true);
4747     Set<ReachTuple> rtSet2 = getAnyExisting(asRoot2, true, true);
4748
4749     Iterator<HeapRegionNode> hrnItr = hrnSetTarget.iterator();
4750     while( hrnItr.hasNext() ) {
4751       HeapRegionNode hrn = hrnItr.next();
4752
4753       Iterator<ReachTuple> rtItr1 = rtSet1.iterator();
4754       while( rtItr1.hasNext() ) {
4755         ReachTuple rt1 = rtItr1.next();
4756
4757         Iterator<ReachTuple> rtItr2 = rtSet2.iterator();
4758         while( rtItr2.hasNext() ) {
4759           ReachTuple rt2 = rtItr2.next();
4760
4761           if( !hrn.getAlpha().getStatesWithBoth(rt1, rt2).isEmpty() ) {
4762             return true;
4763           }
4764         }
4765       }
4766     }
4767
4768     return false;
4769   }
4770
4771   // similar to the method above, return TRUE if ever
4772   // more than one object from the root allocation site
4773   // may reach an object from the target site
4774   public boolean mayManyReachTarget(AllocSite asRoot,
4775                                     AllocSite asTarget) {
4776
4777     // consider all heap regions of the target and look
4778     // for a reach state that multiple objects of root
4779     // might be able to reach the same object
4780     Set<HeapRegionNode> hrnSetTarget = getAnyExisting(asTarget);
4781
4782     // get relevant reach tuples
4783     Set<ReachTuple> rtSetZOM = getAnyExisting(asRoot, true,  false);
4784     Set<ReachTuple> rtSetONE = getAnyExisting(asRoot, false, true);
4785
4786     Iterator<HeapRegionNode> hrnItr = hrnSetTarget.iterator();
4787     while( hrnItr.hasNext() ) {
4788       HeapRegionNode hrn = hrnItr.next();
4789
4790       // if any ZERORMORE tuples are here, TRUE
4791       Iterator<ReachTuple> rtItr = rtSetZOM.iterator();
4792       while( rtItr.hasNext() ) {
4793         ReachTuple rtZOM = rtItr.next();
4794
4795         if( hrn.getAlpha().containsTuple(rtZOM) ) {
4796           return true;
4797         }
4798       }
4799
4800       // otherwise, look for any pair of ONE tuples
4801       Iterator<ReachTuple> rtItr1 = rtSetONE.iterator();
4802       while( rtItr1.hasNext() ) {
4803         ReachTuple rt1 = rtItr1.next();
4804
4805         Iterator<ReachTuple> rtItr2 = rtSetONE.iterator();
4806         while( rtItr2.hasNext() ) {
4807           ReachTuple rt2 = rtItr2.next();
4808
4809           if( rt1 == rt2 ) {
4810             continue;
4811           }
4812
4813           if( !hrn.getAlpha().getStatesWithBoth(rt1, rt2).isEmpty() ) {
4814             return true;
4815           }
4816         }
4817       }
4818     }
4819
4820     return false;
4821   }
4822
4823
4824
4825
4826
4827   public Set<HeapRegionNode> findCommonReachableNodes(ReachSet proofOfSharing) {
4828
4829     Set<HeapRegionNode> exhibitProofState =
4830       new HashSet<HeapRegionNode>();
4831
4832     Iterator hrnItr = id2hrn.entrySet().iterator();
4833     while( hrnItr.hasNext() ) {
4834       Map.Entry me  = (Map.Entry)hrnItr.next();
4835       HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4836
4837       ReachSet intersection =
4838         Canonical.intersection(proofOfSharing,
4839                                hrn.getAlpha()
4840                                );
4841       if( !intersection.isEmpty() ) {
4842         assert !hrn.isOutOfContext();
4843         exhibitProofState.add(hrn);
4844       }
4845     }
4846
4847     return exhibitProofState;
4848   }
4849
4850
4851   public Set<HeapRegionNode> mayReachSharedObjects(HeapRegionNode hrn1,
4852                                                    HeapRegionNode hrn2) {
4853     assert hrn1 != null;
4854     assert hrn2 != null;
4855
4856     assert !hrn1.isOutOfContext();
4857     assert !hrn2.isOutOfContext();
4858
4859     assert belongsToThis(hrn1);
4860     assert belongsToThis(hrn2);
4861
4862     assert !hrn1.getID().equals(hrn2.getID() );
4863
4864
4865     // then get the various tokens for these heap regions
4866     ReachTuple h1 =
4867       ReachTuple.factory(hrn1.getID(),
4868                          !hrn1.isSingleObject(),  // multi?
4869                          ReachTuple.ARITY_ONE,
4870                          false);                  // ooc?
4871
4872     ReachTuple h1star = null;
4873     if( !hrn1.isSingleObject() ) {
4874       h1star =
4875         ReachTuple.factory(hrn1.getID(),
4876                            !hrn1.isSingleObject(),
4877                            ReachTuple.ARITY_ZEROORMORE,
4878                            false);
4879     }
4880
4881     ReachTuple h2 =
4882       ReachTuple.factory(hrn2.getID(),
4883                          !hrn2.isSingleObject(),
4884                          ReachTuple.ARITY_ONE,
4885                          false);
4886
4887     ReachTuple h2star = null;
4888     if( !hrn2.isSingleObject() ) {
4889       h2star =
4890         ReachTuple.factory(hrn2.getID(),
4891                            !hrn2.isSingleObject(),
4892                            ReachTuple.ARITY_ZEROORMORE,
4893                            false);
4894     }
4895
4896     // then get the merged beta of all out-going edges from these heap
4897     // regions
4898
4899     ReachSet beta1 = ReachSet.factory();
4900     Iterator<RefEdge> itrEdge = hrn1.iteratorToReferencees();
4901     while (itrEdge.hasNext()) {
4902       RefEdge edge = itrEdge.next();
4903       beta1 = Canonical.unionORpreds(beta1, edge.getBeta());
4904     }
4905
4906     ReachSet beta2 = ReachSet.factory();
4907     itrEdge = hrn2.iteratorToReferencees();
4908     while (itrEdge.hasNext()) {
4909       RefEdge edge = itrEdge.next();
4910       beta2 = Canonical.unionORpreds(beta2, edge.getBeta());
4911     }
4912
4913     ReachSet proofOfSharing = ReachSet.factory();
4914
4915     proofOfSharing =
4916       Canonical.unionORpreds(proofOfSharing,
4917                              beta1.getStatesWithBoth(h1, h2)
4918                              );
4919     proofOfSharing =
4920       Canonical.unionORpreds(proofOfSharing,
4921                              beta2.getStatesWithBoth(h1, h2)
4922                              );
4923
4924     if( !hrn1.isSingleObject() ) {
4925       proofOfSharing =
4926         Canonical.unionORpreds(proofOfSharing,
4927                                beta1.getStatesWithBoth(h1star, h2)
4928                                );
4929       proofOfSharing =
4930         Canonical.unionORpreds(proofOfSharing,
4931                                beta2.getStatesWithBoth(h1star, h2)
4932                                );
4933     }
4934
4935     if( !hrn2.isSingleObject() ) {
4936       proofOfSharing =
4937         Canonical.unionORpreds(proofOfSharing,
4938                                beta1.getStatesWithBoth(h1, h2star)
4939                                );
4940       proofOfSharing =
4941         Canonical.unionORpreds(proofOfSharing,
4942                                beta2.getStatesWithBoth(h1, h2star)
4943                                );
4944     }
4945
4946     if( !hrn1.isSingleObject() &&
4947         !hrn2.isSingleObject()
4948         ) {
4949       proofOfSharing =
4950         Canonical.unionORpreds(proofOfSharing,
4951                                beta1.getStatesWithBoth(h1star, h2star)
4952                                );
4953       proofOfSharing =
4954         Canonical.unionORpreds(proofOfSharing,
4955                                beta2.getStatesWithBoth(h1star, h2star)
4956                                );
4957     }
4958
4959     Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4960     if( !proofOfSharing.isEmpty() ) {
4961       common = findCommonReachableNodes(proofOfSharing);
4962       if( !DISABLE_STRONG_UPDATES &&
4963           !DISABLE_GLOBAL_SWEEP
4964           ) {
4965         assert !common.isEmpty();
4966       }
4967     }
4968
4969     return common;
4970   }
4971
4972   // this version of the above method checks whether there is sharing
4973   // among the objects in a summary node
4974   public Set<HeapRegionNode> mayReachSharedObjects(HeapRegionNode hrn) {
4975     assert hrn != null;
4976     assert hrn.isNewSummary();
4977     assert !hrn.isOutOfContext();
4978     assert belongsToThis(hrn);
4979
4980     ReachTuple hstar =
4981       ReachTuple.factory(hrn.getID(),
4982                          true,     // multi
4983                          ReachTuple.ARITY_ZEROORMORE,
4984                          false);   // ooc
4985
4986     // then get the merged beta of all out-going edges from
4987     // this heap region
4988
4989     ReachSet beta = ReachSet.factory();
4990     Iterator<RefEdge> itrEdge = hrn.iteratorToReferencees();
4991     while (itrEdge.hasNext()) {
4992       RefEdge edge = itrEdge.next();
4993       beta = Canonical.unionORpreds(beta, edge.getBeta());
4994     }
4995
4996     ReachSet proofOfSharing = ReachSet.factory();
4997
4998     proofOfSharing =
4999       Canonical.unionORpreds(proofOfSharing,
5000                              beta.getStatesWithBoth(hstar, hstar)
5001                              );
5002
5003     Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
5004     if( !proofOfSharing.isEmpty() ) {
5005       common = findCommonReachableNodes(proofOfSharing);
5006       if( !DISABLE_STRONG_UPDATES &&
5007           !DISABLE_GLOBAL_SWEEP
5008           ) {
5009         assert !common.isEmpty();
5010       }
5011     }
5012
5013     return common;
5014   }
5015
5016
5017   public Set<HeapRegionNode> mayReachSharedObjects(FlatMethod fm,
5018                                                    Integer paramIndex1,
5019                                                    Integer paramIndex2) {
5020
5021     // get parameter's heap regions
5022     TempDescriptor paramTemp1 = fm.getParameter(paramIndex1.intValue());
5023     assert this.hasVariable(paramTemp1);
5024     VariableNode paramVar1 = getVariableNodeFromTemp(paramTemp1);
5025
5026
5027     if( !(paramVar1.getNumReferencees() == 1) ) {
5028       System.out.println("\n  fm="+fm+"\n  param="+paramTemp1);
5029       writeGraph("whatup");
5030     }
5031
5032
5033     assert paramVar1.getNumReferencees() == 1;
5034     RefEdge paramEdge1 = paramVar1.iteratorToReferencees().next();
5035     HeapRegionNode hrnParam1 = paramEdge1.getDst();
5036
5037     TempDescriptor paramTemp2 = fm.getParameter(paramIndex2.intValue());
5038     assert this.hasVariable(paramTemp2);
5039     VariableNode paramVar2 = getVariableNodeFromTemp(paramTemp2);
5040
5041     if( !(paramVar2.getNumReferencees() == 1) ) {
5042       System.out.println("\n  fm="+fm+"\n  param="+paramTemp2);
5043       writeGraph("whatup");
5044     }
5045
5046     assert paramVar2.getNumReferencees() == 1;
5047     RefEdge paramEdge2 = paramVar2.iteratorToReferencees().next();
5048     HeapRegionNode hrnParam2 = paramEdge2.getDst();
5049
5050     Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
5051     common.addAll(mayReachSharedObjects(hrnParam1, hrnParam2));
5052
5053     return common;
5054   }
5055
5056   public Set<HeapRegionNode> mayReachSharedObjects(FlatMethod fm,
5057                                                    Integer paramIndex,
5058                                                    AllocSite as) {
5059
5060     // get parameter's heap regions
5061     TempDescriptor paramTemp = fm.getParameter(paramIndex.intValue());
5062     assert this.hasVariable(paramTemp);
5063     VariableNode paramVar = getVariableNodeFromTemp(paramTemp);
5064     assert paramVar.getNumReferencees() == 1;
5065     RefEdge paramEdge = paramVar.iteratorToReferencees().next();
5066     HeapRegionNode hrnParam = paramEdge.getDst();
5067
5068     // get summary node
5069     HeapRegionNode hrnSummary=null;
5070     if(id2hrn.containsKey(as.getSummary())) {
5071       // if summary node doesn't exist, ignore this case
5072       hrnSummary = id2hrn.get(as.getSummary());
5073       assert hrnSummary != null;
5074     }
5075
5076     Set<HeapRegionNode> common  = new HashSet<HeapRegionNode>();
5077     if(hrnSummary!=null) {
5078       common.addAll(mayReachSharedObjects(hrnParam, hrnSummary) );
5079     }
5080
5081     // check for other nodes
5082     for (int i = 0; i < as.getAllocationDepth(); ++i) {
5083
5084       assert id2hrn.containsKey(as.getIthOldest(i));
5085       HeapRegionNode hrnIthOldest = id2hrn.get(as.getIthOldest(i));
5086       assert hrnIthOldest != null;
5087
5088       common.addAll(mayReachSharedObjects(hrnParam, hrnIthOldest));
5089
5090     }
5091
5092     return common;
5093   }
5094
5095   public Set<HeapRegionNode> mayReachSharedObjects(AllocSite as1,
5096                                                    AllocSite as2) {
5097
5098     // get summary node 1's alpha
5099     Integer idSum1 = as1.getSummary();
5100     HeapRegionNode hrnSum1=null;
5101     if(id2hrn.containsKey(idSum1)) {
5102       hrnSum1 = id2hrn.get(idSum1);
5103     }
5104
5105     // get summary node 2's alpha
5106     Integer idSum2 = as2.getSummary();
5107     HeapRegionNode hrnSum2=null;
5108     if(id2hrn.containsKey(idSum2)) {
5109       hrnSum2 = id2hrn.get(idSum2);
5110     }
5111
5112     Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
5113     if(hrnSum1!=null && hrnSum2!=null && hrnSum1!=hrnSum2) {
5114       common.addAll(mayReachSharedObjects(hrnSum1, hrnSum2));
5115     }
5116
5117     if(hrnSum1!=null) {
5118       // ask if objects from this summary share among each other
5119       common.addAll(mayReachSharedObjects(hrnSum1));
5120     }
5121
5122     // check sum2 against alloc1 nodes
5123     if(hrnSum2!=null) {
5124       for (int i = 0; i < as1.getAllocationDepth(); ++i) {
5125         Integer idI1 = as1.getIthOldest(i);
5126         assert id2hrn.containsKey(idI1);
5127         HeapRegionNode hrnI1 = id2hrn.get(idI1);
5128         assert hrnI1 != null;
5129         common.addAll(mayReachSharedObjects(hrnI1, hrnSum2));
5130       }
5131
5132       // also ask if objects from this summary share among each other
5133       common.addAll(mayReachSharedObjects(hrnSum2));
5134     }
5135
5136     // check sum1 against alloc2 nodes
5137     for (int i = 0; i < as2.getAllocationDepth(); ++i) {
5138       Integer idI2 = as2.getIthOldest(i);
5139       assert id2hrn.containsKey(idI2);
5140       HeapRegionNode hrnI2 = id2hrn.get(idI2);
5141       assert hrnI2 != null;
5142
5143       if(hrnSum1!=null) {
5144         common.addAll(mayReachSharedObjects(hrnSum1, hrnI2));
5145       }
5146
5147       // while we're at it, do an inner loop for alloc2 vs alloc1 nodes
5148       for (int j = 0; j < as1.getAllocationDepth(); ++j) {
5149         Integer idI1 = as1.getIthOldest(j);
5150
5151         // if these are the same site, don't look for the same token, no
5152         // alias.
5153         // different tokens of the same site could alias together though
5154         if (idI1.equals(idI2)) {
5155           continue;
5156         }
5157
5158         HeapRegionNode hrnI1 = id2hrn.get(idI1);
5159
5160         common.addAll(mayReachSharedObjects(hrnI1, hrnI2));
5161       }
5162     }
5163
5164     return common;
5165   }
5166
5167   public void makeInaccessible(Set<TempDescriptor> vars) {
5168     inaccessibleVars.addAll(vars);
5169   }
5170
5171   public void makeInaccessible(TempDescriptor td) {
5172     inaccessibleVars.add(td);
5173   }
5174
5175   public void makeAccessible(TempDescriptor td) {
5176     inaccessibleVars.remove(td);
5177   }
5178
5179   public boolean isAccessible(TempDescriptor td) {
5180     return !inaccessibleVars.contains(td);
5181   }
5182
5183   public Set<TempDescriptor> getInaccessibleVars() {
5184     return inaccessibleVars;
5185   }
5186
5187
5188
5189
5190   public Set<Alloc> canPointTo( TempDescriptor x ) {
5191
5192     if( !DisjointAnalysis.shouldAnalysisTrack( x.getType() ) ) {
5193       // if we don't care to track it, return null which means
5194       // "a client of this result shouldn't care either"
5195       return HeapAnalysis.DONTCARE_PTR;
5196     }
5197
5198     Set<Alloc> out = new HashSet<Alloc>();
5199
5200     VariableNode vn = getVariableNodeNoMutation( x );
5201     if( vn == null ) {
5202       // the empty set means "can't point to anything"
5203       return out;
5204     }
5205
5206     Iterator<RefEdge> edgeItr = vn.iteratorToReferencees();
5207     while( edgeItr.hasNext() ) {
5208       HeapRegionNode hrn = edgeItr.next().getDst();
5209       out.add( hrn.getAllocSite() );
5210     }
5211
5212     return out;
5213   }
5214
5215
5216
5217   public Hashtable< Alloc, Set<Alloc> > canPointTo( TempDescriptor x,
5218                                                     String         field,
5219                                                     TypeDescriptor fieldType ) {
5220
5221     if( !DisjointAnalysis.shouldAnalysisTrack( x.getType() ) ) {
5222       // if we don't care to track it, return null which means
5223       // "a client of this result shouldn't care either"
5224       return HeapAnalysis.DONTCARE_DREF;
5225     }
5226
5227     Hashtable< Alloc, Set<Alloc> > out = new Hashtable< Alloc, Set<Alloc> >();
5228     
5229     VariableNode vn = getVariableNodeNoMutation( x );
5230     if( vn == null ) {
5231       // the empty table means "x can't point to anything"
5232       return out;
5233     }
5234
5235     Iterator<RefEdge> edgeItr = vn.iteratorToReferencees();
5236     while( edgeItr.hasNext() ) {
5237       HeapRegionNode hrn = edgeItr.next().getDst();
5238       Alloc          key = hrn.getAllocSite();
5239
5240       if( !DisjointAnalysis.shouldAnalysisTrack( fieldType ) ) {
5241         // if we don't care to track it, put no entry which means
5242         // "a client of this result shouldn't care either"
5243         out.put( key, HeapAnalysis.DONTCARE_PTR );
5244         continue;
5245       }
5246
5247       Set<Alloc> moreValues = new HashSet<Alloc>();
5248       Iterator<RefEdge> edgeItr2 = hrn.iteratorToReferencees();
5249       while( edgeItr2.hasNext() ) {
5250         RefEdge edge = edgeItr2.next();
5251         
5252         if( field.equals( edge.getField() ) ) {
5253           moreValues.add( edge.getDst().getAllocSite() );
5254         }
5255       }
5256
5257       if( out.containsKey( key ) ) {
5258         out.get( key ).addAll( moreValues );
5259       } else {
5260         out.put( key, moreValues );
5261       }
5262     }
5263     
5264     return out;
5265   }
5266
5267
5268
5269   // for debugging
5270   public TempDescriptor findVariableByName( String name ) {
5271
5272     for( TempDescriptor td: td2vn.keySet() ) {
5273       if( td.getSymbol().contains( name ) ) {
5274         return td;
5275       }
5276     }
5277     
5278     return null;
5279   }
5280 }