have to test predicates of callee states before admitting to caller, and calculate...
[IRC.git] / Robust / src / Analysis / Disjoint / Canonical.java
1 package Analysis.Disjoint;
2
3 import IR.*;
4 import IR.Flat.*;
5 import java.util.*;
6 import java.io.*;
7
8
9 ////////////////////////////////////////////////
10 //
11 //  important note!  The static operations in this class that take
12 //  canonicals and produce a canonical result sometimes need a
13 //  "working copy" that IS NOT CANONICAL.  So, even though it isn't
14 //  perfectly clean, Canonical constructors have been changed from
15 //  private to protected and they may be used in this file--however,
16 //  only use a constructor for an object that will mutate during the
17 //  calculation--use the factory method to obtain everything else!
18 //  This is CRITICAL!
19 //
20 //  What this boils down to is that the only normally constructed
21 //  object in a canonical operation should be the result out.
22 //
23 ////////////////////////////////////////////////
24
25
26 abstract public class Canonical {
27
28   // for generating unique canonical values
29   private static int canonicalCount = 1;
30
31   // the canon of objects
32   private static Hashtable<Canonical, Canonical>
33     canon =  new Hashtable<Canonical, Canonical>();
34   
35
36
37  public static Canonical makeCanonical( Canonical c ) {
38
39     if( canon.containsKey( c ) ) {
40       return canon.get( c );
41     }
42     
43     c.canonicalValue = canonicalCount;
44     ++canonicalCount;
45     canon.put( c, c );
46     return c;
47   }
48
49   
50   // any Canonical with value still 0 is NOT CANONICAL!
51   private int canonicalValue = 0;
52
53   public boolean isCanonical() {
54     return canonicalValue != 0;
55   }
56
57   public int getCanonicalValue() {
58     assert isCanonical();
59     return canonicalValue;
60   }
61
62   
63   // canonical objects should never be modified
64   // and therefore have changing hash codes, so
65   // use a standard canonical hash code method to
66   // enforce this, and define a specific hash code
67   // method each specific subclass should implement
68   abstract public int hashCodeSpecific();
69
70   private boolean hasHash = false;
71   private int     oldHash;
72   final public int hashCode() {
73     int hash = hashCodeSpecific();
74
75     if( hasHash ) {
76       if( oldHash != hash ) {
77         throw new Error( "A CANONICAL HASH CHANGED" );
78       }
79     } else {
80       hasHash = true;
81       oldHash = hash;
82     }
83     
84     return hash;
85   }
86
87
88
89
90   // mapping of a non-trivial operation to its result
91   private static    Hashtable<CanonicalOp, Canonical> 
92     op2result = new Hashtable<CanonicalOp, Canonical>();
93   
94
95
96   ///////////////////////////////////////////////////////////
97   //
98   //  Everything below are static methods that implement
99   //  "mutating" operations on Canonical objects by returning
100   //  the canonical result.  If the op is non-trivial the
101   //  canonical result is hashed by its defining CononicalOp
102   //
103   ///////////////////////////////////////////////////////////
104
105   
106   // not weighty, don't bother with caching
107   public static ReachTuple unionArity( ReachTuple rt1,
108                                        ReachTuple rt2 ) {
109     assert rt1 != null;
110     assert rt2 != null;
111     assert rt1.isCanonical();
112     assert rt2.isCanonical();
113     assert rt1.hrnID          == rt2.hrnID;
114     assert rt1.isMultiObject  == rt2.isMultiObject;
115     assert rt1.isOutOfContext == rt2.isOutOfContext;
116     
117     ReachTuple out;
118
119     if( rt1.isMultiObject ) {
120       // on two non-ZERO arity multi regions, union arity is always
121       // ZERO-OR-MORE
122       out = ReachTuple.factory( rt1.hrnID, 
123                                 true, 
124                                 ReachTuple.ARITY_ZEROORMORE,
125                                 rt1.isOutOfContext );
126       
127     } else {
128       // a single object region can only be ARITY_ONE (or zero by
129       // being absent)
130       assert rt1.arity == ReachTuple.ARITY_ONE;
131       out = rt1;
132     }
133
134     assert out.isCanonical();
135     return out;
136   }
137
138   // not weighty, no caching
139   public static ReachTuple changeHrnIDTo( ReachTuple rt,
140                                           Integer    hrnIDToChangeTo ) {
141     assert rt              != null;
142     assert hrnIDToChangeTo != null;
143
144     ReachTuple out = ReachTuple.factory( hrnIDToChangeTo,
145                                          rt.isMultiObject,
146                                          rt.arity,
147                                          rt.isOutOfContext
148                                          );
149     assert out.isCanonical();
150     return out;
151   }
152
153
154   public static ReachState attach( ReachState   rs,
155                                    ExistPredSet preds ) {
156     assert rs    != null;
157     assert preds != null;
158     assert rs.isCanonical();
159     assert preds.isCanonical();
160
161     CanonicalOp op = 
162       new CanonicalOp( CanonicalOp.REACHSTATE_ATTACH_EXISTPREDSET,
163                        rs, 
164                        preds );
165     
166     Canonical result = op2result.get( op );
167     if( result != null ) {
168       return (ReachState) result;
169     }
170     
171     // otherwise, no cached result...
172     ReachState out = new ReachState();
173     out.reachTuples.addAll( rs.reachTuples );
174     out.preds = Canonical.join( rs.preds,
175                                 preds );
176
177     out = (ReachState) makeCanonical( out );
178     op2result.put( op, out );
179     return out;
180   }
181
182
183   public static ReachState union( ReachState rs1,
184                                   ReachState rs2 ) {
185     assert rs1 != null;
186     assert rs2 != null;
187     assert rs1.isCanonical();
188     assert rs2.isCanonical();
189
190     CanonicalOp op = 
191       new CanonicalOp( CanonicalOp.REACHSTATE_UNION_REACHSTATE,
192                               rs1, 
193                               rs2 );
194     
195     Canonical result = op2result.get( op );
196     if( result != null ) {
197       return (ReachState) result;
198     }
199
200     // otherwise, no cached result...
201     ReachState out = new ReachState();
202     out.reachTuples.addAll( rs1.reachTuples );
203     out.reachTuples.addAll( rs2.reachTuples );
204     out.preds = Canonical.join( rs1.getPreds(),
205                                 rs2.getPreds()
206                                 );
207
208     out = (ReachState) makeCanonical( out );
209     op2result.put( op, out );
210     return out;
211   }
212
213   // this is just a convenience version of above
214   public static ReachState union( ReachState rs,
215                                   ReachTuple rt ) {
216     assert rs != null;
217     assert rt != null;
218
219     CanonicalOp op = 
220       new CanonicalOp( CanonicalOp.REACHSTATE_UNION_REACHTUPLE,
221                        rs, 
222                        rt );
223     
224     Canonical result = op2result.get( op );
225     if( result != null ) {
226       return (ReachState) result;
227     }
228
229     // otherwise, no cached result...
230     ReachState out = new ReachState();
231     out.reachTuples.addAll( rs.reachTuples );
232     out.reachTuples.add( rt );
233     out.preds = rs.preds;
234
235     out = (ReachState) makeCanonical( out );
236     op2result.put( op, out );
237     return out;
238   }
239   
240
241   public static ReachState unionUpArity( ReachState rs1,
242                                          ReachState rs2 ) {
243     assert rs1 != null;
244     assert rs2 != null;
245     assert rs1.isCanonical();
246     assert rs2.isCanonical();
247
248     CanonicalOp op = 
249       new CanonicalOp( CanonicalOp.REACHSTATE_UNIONUPARITY_REACHSTATE,
250                        rs1, 
251                        rs2 );
252     
253     Canonical result = op2result.get( op );
254     if( result != null ) {
255       return (ReachState) result;
256     }
257     
258     // otherwise, no cached result...
259     ReachState out = new ReachState();
260
261     Iterator<ReachTuple> rtItr = rs1.iterator();
262     while( rtItr.hasNext() ) {
263       ReachTuple rt1 = rtItr.next();
264       ReachTuple rt2 = rs2.containsHrnID( rt1.getHrnID(),
265                                           rt1.isOutOfContext() 
266                                           );
267       if( rt2 != null ) {
268         out.reachTuples.add( unionArity( rt1, rt2 ) );
269       } else {
270         out.reachTuples.add( rt1 );
271       }
272     }
273
274     rtItr = rs2.iterator();
275     while( rtItr.hasNext() ) {
276       ReachTuple rt2 = rtItr.next();
277       ReachTuple rto = out.containsHrnID( rt2.getHrnID(),
278                                           rt2.isOutOfContext()
279                                           );
280       if( rto == null ) {
281         out.reachTuples.add( rto );
282       }
283     }
284
285     out.preds = Canonical.join( rs1.getPreds(),
286                                 rs2.getPreds()
287                                 );
288     
289     out = (ReachState) makeCanonical( out );
290     op2result.put( op, out );
291     return out;
292   }
293
294   public static ReachState add( ReachState rs, ReachTuple rt ) {   
295     return union( rs, rt );
296   }
297   
298   public static ReachState remove( ReachState rs, ReachTuple rt ) {
299     assert rs != null;
300     assert rt != null;
301
302     CanonicalOp op = 
303       new CanonicalOp( CanonicalOp.REACHSTATE_REMOVE_REACHTUPLE,
304                        rs, 
305                        rt );
306     
307     Canonical result = op2result.get( op );
308     if( result != null ) {
309       return (ReachState) result;
310     }
311
312     // otherwise, no cached result...    
313     ReachState out = new ReachState();
314     out.reachTuples.addAll( rs.reachTuples );
315     out.reachTuples.remove( rt );
316     out.preds = rs.preds;
317
318     out = (ReachState) makeCanonical( out );
319     op2result.put( op, out );
320     return out;
321   }
322   
323   
324   public static ReachState ageTuplesFrom( ReachState rs, 
325                                           AllocSite  as ) {
326     assert rs != null;
327     assert as != null;
328     assert rs.isCanonical();
329     assert as.isCanonical();
330     
331     CanonicalOp op = 
332       new CanonicalOp( CanonicalOp.REACHSTATE_AGETUPLESFROM_ALLOCSITE,
333                        rs, 
334                        as );
335     
336     Canonical result = op2result.get( op );
337     if( result != null ) {
338       return (ReachState) result;
339     }
340     
341     // otherwise, no cached result...
342     ReachState out = new ReachState();
343
344     ReachTuple rtSummary = null;
345     ReachTuple rtOldest  = null;
346
347     Iterator<ReachTuple> rtItr = rs.iterator();
348     while( rtItr.hasNext() ) {
349       ReachTuple rt    = rtItr.next();
350       Integer    hrnID = rt.getHrnID();
351       int        age   = as.getAgeCategory( hrnID );
352
353       // hrnIDs not associated with
354       // the site should be left alone, and
355       // those from this site but out-of-context
356       if( age == AllocSite.AGE_notInThisSite ||
357           rt.isOutOfContext()
358           ) {
359         out.reachTuples.add( rt );
360
361       } else if( age == AllocSite.AGE_summary ) {
362         // remember the summary tuple, but don't add it
363         // we may combine it with the oldest tuple
364         rtSummary = rt;
365
366       } else if( age == AllocSite.AGE_oldest ) {
367         // found an oldest hrnID, again just remember
368         // for later
369         rtOldest = rt;
370
371       } else {
372         assert age == AllocSite.AGE_in_I;
373
374         Integer I = as.getAge( hrnID );
375         assert I != null;
376
377         // otherwise, we change this hrnID to the
378         // next older hrnID
379         Integer hrnIDToChangeTo = as.getIthOldest( I + 1 );
380         ReachTuple rtAged =
381           Canonical.changeHrnIDTo( rt, hrnIDToChangeTo );
382         out.reachTuples.add( rtAged );
383       }
384     }
385
386     // there are four cases to consider here
387     // 1. we found a summary tuple and no oldest tuple
388     //    Here we just pass the summary unchanged
389     // 2. we found an oldest tuple, no summary
390     //    Make a new, arity-one summary tuple
391     // 3. we found both a summary and an oldest
392     //    Merge them by arity
393     // 4. (not handled) we found neither, do nothing
394     if( rtSummary != null && rtOldest == null ) {
395       out.reachTuples.add( rtSummary );
396
397     } else if( rtSummary == null && rtOldest != null ) {
398       out.reachTuples.add( ReachTuple.factory( as.getSummary(),
399                                                true, // multi
400                                                rtOldest.getArity(),
401                                                false // out-of-context
402                                                )
403                            );
404
405     } else if( rtSummary != null && rtOldest != null ) {     
406       out.reachTuples.add( Canonical.unionArity( rtSummary,
407                                                  ReachTuple.factory( as.getSummary(),
408                                                                      true, // muli
409                                                                      rtOldest.getArity(),
410                                                                      false // out-of-context
411                                                                      )
412                                                  )
413                            );
414     }
415
416     out.preds = rs.preds;
417
418     out = (ReachState) makeCanonical( out );
419     op2result.put( op, out );
420     return out;
421   }
422
423
424
425   public static ReachSet union( ReachSet rs1,
426                                 ReachSet rs2 ) {
427     assert rs1 != null;
428     assert rs2 != null;
429     assert rs1.isCanonical();
430     assert rs2.isCanonical();
431
432     CanonicalOp op = 
433       new CanonicalOp( CanonicalOp.REACHSET_UNION_REACHSET,
434                        rs1, 
435                        rs2 );
436     
437     Canonical result = op2result.get( op );
438     if( result != null ) {
439       return (ReachSet) result;
440     }
441
442     // otherwise, no cached result...
443     ReachSet out = new ReachSet();
444     out.reachStates.addAll( rs1.reachStates );
445     out.reachStates.addAll( rs2.reachStates );
446
447     out = (ReachSet) makeCanonical( out );
448     op2result.put( op, out );
449     return out;
450   }
451
452   public static ReachSet union( ReachSet   rs,
453                                 ReachState state ) {
454
455     assert rs    != null;
456     assert state != null;
457     assert rs.isCanonical();
458     assert state.isCanonical();
459
460     CanonicalOp op = 
461       new CanonicalOp( CanonicalOp.REACHSET_UNION_REACHSTATE,
462                        rs, 
463                        state );
464     
465     Canonical result = op2result.get( op );
466     if( result != null ) {
467       return (ReachSet) result;
468     }
469
470     // otherwise, no cached result...
471     ReachSet out = new ReachSet();
472     out.reachStates.addAll( rs.reachStates );
473     out.reachStates.add( state );
474
475     out = (ReachSet) makeCanonical( out );
476     op2result.put( op, out );
477     return out;
478   }
479
480   public static ReachSet intersection( ReachSet rs1,
481                                        ReachSet rs2 ) {
482     assert rs1 != null;
483     assert rs2 != null;
484     assert rs1.isCanonical();
485     assert rs2.isCanonical();
486
487     CanonicalOp op = 
488       new CanonicalOp( CanonicalOp.REACHSET_INTERSECTION_REACHSET,
489                        rs1, 
490                        rs2 );
491     
492     Canonical result = op2result.get( op );
493     if( result != null ) {
494       return (ReachSet) result;
495     }
496
497     // otherwise, no cached result...
498     ReachSet out = new ReachSet();
499     Iterator<ReachState> itr = rs1.iterator();
500     while( itr.hasNext() ) {
501       ReachState state = (ReachState) itr.next();
502       if( rs2.reachStates.contains( state ) ) {
503         out.reachStates.add( state );
504       }
505     }
506
507     out = (ReachSet) makeCanonical( out );
508     op2result.put( op, out );
509     return out;
510   }
511
512
513   public static ReachSet add( ReachSet   rs, 
514                               ReachState state ) {
515     return union( rs, state );
516   }
517
518   public static ReachSet remove( ReachSet   rs,
519                                  ReachState state ) {
520     assert rs    != null;
521     assert state != null;
522     assert rs.isCanonical();
523     assert state.isCanonical();
524
525     CanonicalOp op = 
526       new CanonicalOp( CanonicalOp.REACHSET_REMOVE_REACHSTATE,
527                        rs, 
528                        state );
529     
530     Canonical result = op2result.get( op );
531     if( result != null ) {
532       return (ReachSet) result;
533     }
534
535     // otherwise, no cached result...    
536     ReachSet out = new ReachSet();
537     out.reachStates.addAll( rs.reachStates );
538     out.reachStates.remove( state );
539
540     out = (ReachSet) makeCanonical( out );
541     op2result.put( op, out );
542     return out;
543   }
544
545
546   public static ReachSet applyChangeSet( ReachSet  rs, 
547                                          ChangeSet cs,
548                                          boolean   keepSourceState ) {
549     assert rs != null;
550     assert cs != null;
551     assert rs.isCanonical();
552     assert cs.isCanonical();
553
554     // this primitive operand stuff is just a way to 
555     // ensure distinct inputs to a CanonicalOp
556     int primOperand;
557     if( keepSourceState ) {
558       primOperand = 0x1f;
559     } else {
560       primOperand = 0x2b;
561     }
562
563     CanonicalOp op = 
564       new CanonicalOp( CanonicalOp.REACHSET_APPLY_CHANGESET,
565                        rs, 
566                        cs,
567                        primOperand );
568     
569     Canonical result = op2result.get( op );
570     if( result != null ) {
571       return (ReachSet) result;
572     }
573     
574     // otherwise, no cached result...    
575     ReachSet out = new ReachSet();
576
577     Iterator<ReachState> stateItr = rs.iterator();
578     while( stateItr.hasNext() ) {
579       ReachState state = stateItr.next();
580
581       boolean changeFound = false;
582
583       Iterator<ChangeTuple> ctItr = cs.iterator();
584       while( ctItr.hasNext() ) {
585         ChangeTuple ct = ctItr.next();
586
587         if( state.equals( ct.getSetToMatch() ) ) {
588           out.reachStates.add( ct.getSetToAdd() );
589           changeFound = true;
590         }
591       }
592
593       if( keepSourceState || !changeFound ) {
594         out.reachStates.add( state );
595       }
596     }
597
598     out = (ReachSet) makeCanonical( out );
599     op2result.put( op, out );
600     return out;
601   }
602
603
604   public static ChangeSet unionUpArityToChangeSet( ReachSet rsO,
605                                                    ReachSet rsR ) {
606     assert rsO != null;
607     assert rsR != null;
608     assert rsO.isCanonical();
609     assert rsR.isCanonical();
610
611     CanonicalOp op = 
612       new CanonicalOp( CanonicalOp.REACHSET_UNIONTOCHANGESET_REACHSET,
613                        rsO, 
614                        rsR );
615     
616     Canonical result = op2result.get( op );
617     if( result != null ) {
618       return (ChangeSet) result;
619     }
620     
621     // otherwise, no cached result...    
622     ChangeSet out = ChangeSet.factory();
623
624     Iterator<ReachState> itrO = rsO.iterator();
625     while( itrO.hasNext() ) {
626       ReachState o = itrO.next();
627
628       Iterator<ReachState> itrR = rsR.iterator();
629       while( itrR.hasNext() ) {
630         ReachState r = itrR.next();
631
632         ReachState theUnion = ReachState.factory();
633
634         Iterator<ReachTuple> itrRelement = r.iterator();
635         while( itrRelement.hasNext() ) {
636           ReachTuple rtR = itrRelement.next();
637           ReachTuple rtO = o.containsHrnID( rtR.getHrnID(),
638                                             rtR.isOutOfContext()
639                                             );
640           if( rtO != null ) {
641             theUnion = Canonical.union( theUnion,
642                                         Canonical.unionArity( rtR,
643                                                               rtO
644                                                               )
645                                         );
646           } else {
647             theUnion = Canonical.union( theUnion,
648                                         rtR
649                                         );
650           }
651         }
652
653         Iterator<ReachTuple> itrOelement = o.iterator();
654         while( itrOelement.hasNext() ) {
655           ReachTuple rtO = itrOelement.next();
656           ReachTuple rtR = theUnion.containsHrnID( rtO.getHrnID(),
657                                                    rtO.isOutOfContext()
658                                                    );
659           if( rtR == null ) {
660             theUnion = Canonical.union( theUnion,
661                                         rtO
662                                         );
663           }
664         }
665         
666         if( !theUnion.isEmpty() ) {
667           out = 
668             Canonical.union( out,
669                              ChangeSet.factory( 
670                                                ChangeTuple.factory( o, theUnion ) 
671                                                 )
672                              );
673         }
674       }
675     }
676
677     assert out.isCanonical();
678     op2result.put( op, out );
679     return out;
680   }
681
682
683   public static ReachSet ageTuplesFrom( ReachSet  rs,
684                                         AllocSite as ) {
685     assert rs != null;
686     assert as != null;
687     assert rs.isCanonical();
688     assert as.isCanonical();
689
690     CanonicalOp op = 
691       new CanonicalOp( CanonicalOp.REACHSET_AGETUPLESFROM_ALLOCSITE,
692                        rs, 
693                        as );
694     
695     Canonical result = op2result.get( op );
696     if( result != null ) {
697       return (ReachSet) result;
698     }
699     
700     // otherwise, no cached result...
701     ReachSet out = new ReachSet();
702
703     Iterator<ReachState> itrS = rs.iterator();
704     while( itrS.hasNext() ) {
705       ReachState state = itrS.next();
706       out.reachStates.add( Canonical.ageTuplesFrom( state, as ) );
707     }
708     
709     out = (ReachSet) makeCanonical( out );
710     op2result.put( op, out );
711     return out;    
712   }
713
714
715   public static ReachSet pruneBy( ReachSet rsO, 
716                                   ReachSet rsP ) {
717     assert rsO != null;
718     assert rsP != null;
719     assert rsO.isCanonical();
720     assert rsP.isCanonical();
721
722     CanonicalOp op = 
723       new CanonicalOp( CanonicalOp.REACHSET_PRUNEBY_REACHSET,
724                        rsO, 
725                        rsP );
726     
727     Canonical result = op2result.get( op );
728     if( result != null ) {
729       return (ReachSet) result;
730     }
731     
732     // otherwise, no cached result...    
733     ReachSet out = new ReachSet();
734
735     Iterator<ReachState> itrO = rsO.iterator();
736     while( itrO.hasNext() ) {
737       ReachState stateO = itrO.next();
738
739       boolean subsetExists = false;
740
741       Iterator<ReachState> itrP = rsP.iterator();
742       while( itrP.hasNext() && !subsetExists ) {
743         ReachState stateP = itrP.next();
744
745         if( stateP.isSubset( stateO ) ) {
746           subsetExists = true;
747         }
748       }
749       
750       if( subsetExists ) {
751         out.reachStates.add( stateO );
752       }
753     }
754
755     out = (ReachSet) makeCanonical( out );
756     op2result.put( op, out );
757     return out;    
758   }
759
760
761   public static ChangeSet union( ChangeSet cs1, 
762                                  ChangeSet cs2 ) {
763     assert cs1 != null;
764     assert cs2 != null;
765     assert cs1.isCanonical();
766     assert cs2.isCanonical();
767
768     CanonicalOp op = 
769       new CanonicalOp( CanonicalOp.CHANGESET_UNION_CHANGESET,
770                        cs1, 
771                        cs2 );
772     
773     Canonical result = op2result.get( op );
774     if( result != null ) {
775       return (ChangeSet) result;
776     }
777     
778     // otherwise, no cached result...    
779     ChangeSet out = new ChangeSet();
780     out.changeTuples.addAll( cs1.changeTuples );
781     out.changeTuples.addAll( cs2.changeTuples );
782
783     out = (ChangeSet) makeCanonical( out );
784     op2result.put( op, out );
785     return out;    
786   }
787
788   public static ChangeSet union( ChangeSet   cs, 
789                                  ChangeTuple ct ) {
790     assert cs != null;
791     assert ct != null;
792     assert cs.isCanonical();
793     assert ct.isCanonical();
794
795     CanonicalOp op = 
796       new CanonicalOp( CanonicalOp.CHANGESET_UNION_CHANGETUPLE,
797                        cs, 
798                        ct );
799     
800     Canonical result = op2result.get( op );
801     if( result != null ) {
802       return (ChangeSet) result;
803     }
804     
805     // otherwise, no cached result...    
806     ChangeSet out = new ChangeSet();
807     out.changeTuples.addAll( cs.changeTuples );
808     out.changeTuples.add( ct );
809     
810     out = (ChangeSet) makeCanonical( out );
811     op2result.put( op, out );
812     return out;    
813   }
814
815
816
817   public static ExistPredSet join( ExistPredSet eps1,
818                                    ExistPredSet eps2 ) {
819
820     assert eps1 != null;
821     assert eps2 != null;
822     assert eps1.isCanonical();
823     assert eps2.isCanonical();
824
825     CanonicalOp op = 
826       new CanonicalOp( CanonicalOp.EXISTPREDSET_JOIN_EXISTPREDSET,
827                        eps1, 
828                        eps2 );
829     
830     Canonical result = op2result.get( op );
831     if( result != null ) {
832       return (ExistPredSet) result;
833     }
834     
835     // otherwise, no cached result...    
836     ExistPredSet out = new ExistPredSet();
837     out.preds.addAll( eps1.preds );
838     out.preds.addAll( eps2.preds );
839
840     out = (ExistPredSet) makeCanonical( out );
841     op2result.put( op, out );
842     return out;    
843   }
844
845   public static ExistPredSet add( ExistPredSet eps,
846                                   ExistPred    ep ) {
847
848
849     assert eps != null;
850     assert ep  != null;
851     assert eps.isCanonical();
852     assert ep.isCanonical();
853
854     CanonicalOp op = 
855       new CanonicalOp( CanonicalOp.EXISTPREDSET_ADD_EXISTPRED,
856                        eps, 
857                        ep );
858     
859     Canonical result = op2result.get( op );
860     if( result != null ) {
861       return (ExistPredSet) result;
862     }
863     
864     // otherwise, no cached result...    
865     ExistPredSet out = new ExistPredSet();
866     out.preds.addAll( eps.preds );
867     out.preds.add( ep );
868     
869     out = (ExistPredSet) makeCanonical( out );
870     op2result.put( op, out );
871     return out;    
872   }
873
874
875   /*
876   public static ReachSet toCalleeContext( ReachSet  rs,
877                                           AllocSite as ) {
878     assert rs != null;
879     assert as != null;
880     assert rs.isCanonical();
881     assert as.isCanonical();
882
883     CanonicalOp op = 
884       new CanonicalOp( CanonicalOp.REACHSET_TOCALLEECONTEXT_ALLOCSITE,
885                        rs, 
886                        as );
887     
888     Canonical result = op2result.get( op );
889     if( result != null ) {
890       return (ReachSet) result;
891     }
892
893     // otherwise, no cached result...
894     ReachSet out = ReachSet.factory();
895     Iterator<ReachState> itr = rs.iterator();
896     while( itr.hasNext() ) {
897       ReachState state = itr.next();
898       out = Canonical.add( out,
899                            Canonical.toCalleeContext( state, as )
900                            );
901     }
902
903     assert out.isCanonical();
904     op2result.put( op, out );
905     return out;
906   }
907   */
908
909   public static ReachState toCalleeContext( ReachState state,
910                                             AllocSite  as ) {
911     assert state != null;
912     assert as    != null;
913     assert state.isCanonical();
914     assert as.isCanonical();
915
916     CanonicalOp op = 
917       new CanonicalOp( CanonicalOp.REACHSTATE_TOCALLEECONTEXT_ALLOCSITE,
918                        state, 
919                        as );
920     
921     Canonical result = op2result.get( op );
922     if( result != null ) {
923       return (ReachState) result;
924     }
925
926     // otherwise, no cached result...
927     ReachState out = ReachState.factory();
928     Iterator<ReachTuple> itr = state.iterator();
929     while( itr.hasNext() ) {
930       ReachTuple rt = itr.next();
931
932       int age = as.getAgeCategory( rt.getHrnID() );
933
934       // this is the current mapping, where 0, 1, 2S were allocated
935       // in the current context, 0?, 1? and 2S? were allocated in a
936       // previous context, and we're translating to a future context
937       //
938       // 0    -> 0?
939       // 1    -> 1?
940       // 2S   -> 2S?
941       // 2S*  -> 2S?*
942       //
943       // 0?   -> 2S?
944       // 1?   -> 2S?
945       // 2S?  -> 2S?
946       // 2S?* -> 2S?*
947       
948       if( age == AllocSite.AGE_notInThisSite ) {
949         // things not from the site just go back in
950         out = Canonical.union( out, rt );
951
952       } else if( age == AllocSite.AGE_summary ||
953                  rt.isOutOfContext()
954                  ) {
955         // the in-context summary and all existing out-of-context
956         // stuff all become
957         out = Canonical.union( out,
958                                ReachTuple.factory( as.getSummary(),
959                                                    true, // multi
960                                                    rt.getArity(),
961                                                    true  // out-of-context
962                                                    )
963                                );
964       } else {
965         // otherwise everything else just goes to an out-of-context
966         // version, everything else the same
967         Integer I = as.getAge( rt.getHrnID() );
968         assert I != null;
969
970         assert !rt.isMultiObject();
971
972         out = Canonical.union( out,
973                                ReachTuple.factory( rt.getHrnID(),
974                                                    rt.isMultiObject(),
975                                                    rt.getArity(),
976                                                    true  // out-of-context
977                                                    )
978                                );        
979       }
980     }
981
982     out = Canonical.attach( out,
983                             state.getPreds()
984                             );
985
986     assert out.isCanonical();
987     op2result.put( op, out );
988     return out;
989   }
990
991
992
993   public static ReachSet toCallerContext( ReachSet  rs,
994                                           AllocSite as ) {
995     assert rs != null;
996     assert as != null;
997     assert rs.isCanonical();
998     assert as.isCanonical();
999
1000     CanonicalOp op = 
1001       new CanonicalOp( CanonicalOp.REACHSET_TOCALLERCONTEXT_ALLOCSITE,
1002                        rs, 
1003                        as );
1004     
1005     Canonical result = op2result.get( op );
1006     if( result != null ) {
1007       return (ReachSet) result;
1008     }
1009
1010     // otherwise, no cached result...
1011     ReachSet out = ReachSet.factory();
1012     Iterator<ReachState> itr = rs.iterator();
1013     while( itr.hasNext() ) {
1014       ReachState state = itr.next();
1015       out = Canonical.union( out,
1016                              Canonical.toCallerContext( state, as )
1017                              );
1018     }
1019
1020     assert out.isCanonical();
1021     op2result.put( op, out );
1022     return out;
1023   }
1024   
1025
1026   public static ReachSet toCallerContext( ReachState state,
1027                                           AllocSite  as ) {
1028     assert state != null;
1029     assert as    != null;
1030     assert state.isCanonical();
1031     assert as.isCanonical();
1032
1033     CanonicalOp op = 
1034       new CanonicalOp( CanonicalOp.REACHSTATE_TOCALLERCONTEXT_ALLOCSITE,
1035                        state, 
1036                        as );
1037     
1038     Canonical result = op2result.get( op );
1039     if( result != null ) {
1040       return (ReachSet) result;
1041     }
1042
1043     // otherwise, no cached result...
1044     ReachSet out = ReachSet.factory();
1045
1046     // this method returns a ReachSet instead of a ReachState
1047     // because the companion method, toCallee, translates
1048     // symbols many-to-one, so state->state
1049     // but this method does an ~inverse mapping, one-to-many
1050     // so one state can split into a set of branched states
1051
1052     // 0    -> -0
1053     // 1    -> -1
1054     // 2S   -> -2S
1055     // 2S*  -> -2S*
1056     //
1057     // 0?   -> 0
1058     // 1?   -> 1
1059     // 2S?  -> 2S
1060     //      -> 0?
1061     //      -> 1?
1062     //      -> 2S?
1063     // 2S?* -> {2S*, 2S?*}    
1064
1065     boolean found2Sooc = false;
1066
1067     ReachState baseState = ReachState.factory();
1068
1069     Iterator<ReachTuple> itr = state.iterator();
1070     while( itr.hasNext() ) {
1071       ReachTuple rt = itr.next();
1072
1073       int age = as.getAgeCategory( rt.getHrnID() );
1074
1075       if( age == AllocSite.AGE_notInThisSite ) {
1076         // things not from the site just go back in
1077         baseState = Canonical.union( baseState, rt );
1078
1079       } else if( age == AllocSite.AGE_summary ) {
1080
1081         if( rt.isOutOfContext() ) {
1082           // if its out-of-context, we only deal here with the ZERO-OR-MORE
1083           // arity, if ARITY-ONE we'll branch the base state after the loop
1084           if( rt.getArity() == ReachTuple.ARITY_ZEROORMORE ) {
1085             // add two overly conservative symbols to reach state (PUNTING)
1086             baseState = Canonical.union( baseState,
1087                                          ReachTuple.factory( as.getSummary(),
1088                                                              true, // multi
1089                                                              ReachTuple.ARITY_ZEROORMORE,
1090                                                              false // out-of-context
1091                                                              )
1092                                          );            
1093             baseState = Canonical.union( baseState,
1094                                          ReachTuple.factory( as.getSummary(),
1095                                                              true, // multi
1096                                                              ReachTuple.ARITY_ZEROORMORE,
1097                                                              true  // out-of-context
1098                                                              )
1099                                          );            
1100           } else {
1101             assert rt.getArity() == ReachTuple.ARITY_ONE;
1102             found2Sooc = true;
1103           }
1104
1105         } else {
1106           // the in-context just becomes shadow
1107           baseState = Canonical.union( baseState,
1108                                        ReachTuple.factory( as.getSummaryShadow(),
1109                                                            true, // multi
1110                                                            rt.getArity(),
1111                                                            false  // out-of-context
1112                                                            )
1113                                        );
1114         }
1115
1116       } else {
1117         // otherwise the ith symbol becomes shadowed
1118         Integer I = as.getAge( rt.getHrnID() );
1119         assert I != null;
1120         
1121         assert !rt.isMultiObject();
1122
1123         baseState = Canonical.union( baseState,
1124                                      ReachTuple.factory( -rt.getHrnID(),
1125                                                          false, // multi
1126                                                          rt.getArity(),
1127                                                          false  // out-of-context
1128                                                          )
1129                                      );        
1130       }
1131     }
1132
1133     // now either make branches if we have 2S?, or
1134     // the current baseState is the only state we need
1135     if( found2Sooc ) {
1136       // make a branch with every possibility of the one-to-many
1137       // mapping for 2S? appended to the baseState
1138       out = Canonical.union( out,
1139                              Canonical.union( baseState,
1140                                               ReachTuple.factory( as.getSummary(),
1141                                                                   true, // multi
1142                                                                   ReachTuple.ARITY_ONE,
1143                                                                   false  // out-of-context
1144                                                                   )
1145                                               )
1146                              );
1147
1148       out = Canonical.union( out,
1149                              Canonical.union( baseState,
1150                                               ReachTuple.factory( as.getSummary(),
1151                                                                   true, // multi
1152                                                                   ReachTuple.ARITY_ONE,
1153                                                                   true  // out-of-context
1154                                                                   )
1155                                               )
1156                              );      
1157
1158       for( int i = 0; i < as.getAllocationDepth(); ++i ) {
1159         out = Canonical.union( out,
1160                                Canonical.union( baseState,
1161                                                 ReachTuple.factory( as.getIthOldest( i ),
1162                                                                     false, // multi
1163                                                                     ReachTuple.ARITY_ONE,
1164                                                                     true  // out-of-context
1165                                                                     )
1166                                                 )
1167                                );
1168       }
1169
1170     } else {
1171       // just use current baseState      
1172       out = Canonical.union( out,
1173                              baseState );
1174     }
1175
1176
1177     assert out.isCanonical();
1178     op2result.put( op, out );
1179     return out;
1180   }
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190   public static ReachSet unshadow( ReachSet  rs,
1191                                    AllocSite as ) {
1192     assert rs != null;
1193     assert as != null;
1194     assert rs.isCanonical();
1195     assert as.isCanonical();
1196
1197     CanonicalOp op = 
1198       new CanonicalOp( CanonicalOp.REACHSET_UNSHADOW_ALLOCSITE,
1199                        rs, 
1200                        as );
1201     
1202     Canonical result = op2result.get( op );
1203     if( result != null ) {
1204       return (ReachSet) result;
1205     }
1206
1207     // otherwise, no cached result...
1208     ReachSet out = ReachSet.factory();
1209     Iterator<ReachState> itr = rs.iterator();
1210     while( itr.hasNext() ) {
1211       ReachState state = itr.next();
1212       out = Canonical.add( out,
1213                            Canonical.unshadow( state, as )
1214                            );
1215     }
1216
1217     assert out.isCanonical();
1218     op2result.put( op, out );
1219     return out;
1220   }
1221
1222   public static ReachState unshadow( ReachState state,
1223                                      AllocSite  as ) {
1224     assert state != null;
1225     assert as    != null;
1226     assert state.isCanonical();
1227     assert as.isCanonical();
1228
1229     CanonicalOp op = 
1230       new CanonicalOp( CanonicalOp.REACHSTATE_UNSHADOW_ALLOCSITE,
1231                        state, 
1232                        as );
1233     
1234     Canonical result = op2result.get( op );
1235     if( result != null ) {
1236       return (ReachState) result;
1237     }
1238
1239     // this is the current mapping, where 0, 1, 2S were allocated
1240     // in the current context, 0?, 1? and 2S? were allocated in a
1241     // previous context, and we're translating to a future context
1242     //
1243     // -0   -> 0
1244     // -1   -> 1
1245     // -2S  -> 2S
1246     
1247     // otherwise, no cached result...
1248     ReachState out = ReachState.factory();
1249     Iterator<ReachTuple> itr = state.iterator();
1250     while( itr.hasNext() ) {
1251       ReachTuple rt = itr.next();
1252
1253       int age = as.getShadowAgeCategory( rt.getHrnID() );
1254       
1255       if( age == AllocSite.SHADOWAGE_notInThisSite ) {
1256         // things not from the site just go back in
1257         out = Canonical.union( out, rt );
1258
1259       } else {
1260         assert !rt.isOutOfContext();
1261
1262         // otherwise unshadow it
1263         out = Canonical.union( out,
1264                                ReachTuple.factory( -rt.getHrnID(),
1265                                                    rt.isMultiObject(),
1266                                                    rt.getArity(),
1267                                                    false
1268                                                    )
1269                                );
1270       }
1271     }
1272
1273     out = Canonical.attach( out,
1274                             state.getPreds()
1275                             );
1276
1277     assert out.isCanonical();
1278     op2result.put( op, out );
1279     return out;
1280   }
1281
1282 }