Fix tabbing.... Please fix your editors so they do tabbing correctly!!! (Spaces...
[IRC.git] / Robust / src / Analysis / Disjoint / Canonical.java
index bd4b8ee15429a130e738e521375baaf74afb1317..85a5c8a8b3ebba21eb391b50494f8b78eddf4f6f 100644 (file)
@@ -30,23 +30,23 @@ abstract public class Canonical {
 
   // the canon of objects
   private static Hashtable<Canonical, Canonical>
-    canon =  new Hashtable<Canonical, Canonical>();
-  
+  canon =  new Hashtable<Canonical, Canonical>();
 
 
- public static Canonical makeCanonical( Canonical c ) {
 
-    if( canon.containsKey( c ) ) {
-      return canon.get( c );
+  public static Canonical makeCanonical(Canonical c) {
+
+    if( canon.containsKey(c) ) {
+      return canon.get(c);
     }
-    
+
     c.canonicalValue = canonicalCount;
     ++canonicalCount;
-    canon.put( c, c );
+    canon.put(c, c);
     return c;
   }
 
-  
+
   // any Canonical with value still 0 is NOT CANONICAL!
   private int canonicalValue = 0;
 
@@ -59,7 +59,31 @@ abstract public class Canonical {
     return canonicalValue;
   }
 
-  
+
+
+  abstract public boolean equalsSpecific(Object o);
+
+  final public boolean equals(Object o) {
+    if( o == null ) {
+      return false;
+    }
+
+    if( !(o instanceof Canonical) ) {
+      return false;
+    }
+
+    Canonical c = (Canonical) o;
+
+    if( this.canonicalValue == 0 ||
+        c.canonicalValue == 0
+        ) {
+      return equalsSpecific(o);
+    }
+
+    return this.canonicalValue == c.canonicalValue;
+  }
+
+
   // canonical objects should never be modified
   // and therefore have changing hash codes, so
   // use a standard canonical hash code method to
@@ -68,29 +92,34 @@ abstract public class Canonical {
   abstract public int hashCodeSpecific();
 
   private boolean hasHash = false;
-  private int     oldHash;
+  private int oldHash;
   final public int hashCode() {
+
+    // the quick mode
+    if( DisjointAnalysis.releaseMode && hasHash ) {
+      return oldHash;
+    }
+
+    // the safe mode
     int hash = hashCodeSpecific();
 
     if( hasHash ) {
       if( oldHash != hash ) {
-        throw new Error( "A CANONICAL HASH CHANGED" );
+       throw new Error("A CANONICAL HASH CHANGED");
       }
     } else {
       hasHash = true;
       oldHash = hash;
     }
-    
+
     return hash;
   }
 
 
-
-
   // mapping of a non-trivial operation to its result
-  private static    Hashtable<CanonicalOp, Canonical> 
-    op2result = new Hashtable<CanonicalOp, Canonical>();
-  
+  private static Hashtable<CanonicalOp, Canonical>
+  op2result = new Hashtable<CanonicalOp, Canonical>();
+
 
 
   ///////////////////////////////////////////////////////////
@@ -102,10 +131,10 @@ abstract public class Canonical {
   //
   ///////////////////////////////////////////////////////////
 
-  
+
   // not weighty, don't bother with caching
-  public static ReachTuple unionUpArity( ReachTuple rt1,
-                                         ReachTuple rt2 ) {
+  public static ReachTuple unionUpArity(ReachTuple rt1,
+                                        ReachTuple rt2) {
     assert rt1 != null;
     assert rt2 != null;
     assert rt1.isCanonical();
@@ -113,17 +142,17 @@ abstract public class Canonical {
     assert rt1.hrnID          == rt2.hrnID;
     assert rt1.isMultiObject  == rt2.isMultiObject;
     assert rt1.isOutOfContext == rt2.isOutOfContext;
-    
+
     ReachTuple out;
 
     if( rt1.isMultiObject ) {
       // on two non-ZERO arity multi regions, union arity is always
       // ZERO-OR-MORE
-      out = ReachTuple.factory( rt1.hrnID, 
-                                true, 
-                                ReachTuple.ARITY_ZEROORMORE,
-                                rt1.isOutOfContext );
-      
+      out = ReachTuple.factory(rt1.hrnID,
+                               true,
+                               ReachTuple.ARITY_ZEROORMORE,
+                               rt1.isOutOfContext);
+
     } else {
       // a single object region can only be ARITY_ONE (or zero by
       // being absent)
@@ -136,99 +165,99 @@ abstract public class Canonical {
   }
 
   // not weighty, no caching
-  public static ReachTuple changeHrnIDTo( ReachTuple rt,
-                                          Integer    hrnIDToChangeTo ) {
+  public static ReachTuple changeHrnIDTo(ReachTuple rt,
+                                         Integer hrnIDToChangeTo) {
     assert rt              != null;
     assert hrnIDToChangeTo != null;
 
-    ReachTuple out = ReachTuple.factory( hrnIDToChangeTo,
-                                         rt.isMultiObject,
-                                         rt.arity,
-                                         rt.isOutOfContext
-                                         );
+    ReachTuple out = ReachTuple.factory(hrnIDToChangeTo,
+                                        rt.isMultiObject,
+                                        rt.arity,
+                                        rt.isOutOfContext
+                                        );
     assert out.isCanonical();
     return out;
   }
 
 
-  public static ReachState attach( ReachState   rs,
-                                   ExistPredSet preds ) {
+  public static ReachState attach(ReachState rs,
+                                  ExistPredSet preds) {
     assert rs    != null;
     assert preds != null;
     assert rs.isCanonical();
     assert preds.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSTATE_ATTACH_EXISTPREDSET,
-                       rs, 
-                       preds );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSTATE_ATTACH_EXISTPREDSET,
+                      rs,
+                      preds);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachState) result;
     }
-    
+
     // otherwise, no cached result...
     ReachState out = new ReachState();
-    out.reachTuples.addAll( rs.reachTuples );
-    out.preds = Canonical.join( rs.preds,
-                                preds );
-    
-    out = (ReachState) makeCanonical( out );
-    op2result.put( op, out );
+    out.reachTuples.addAll(rs.reachTuples);
+    out.preds = Canonical.join(rs.preds,
+                               preds);
+
+    out = (ReachState) makeCanonical(out);
+    op2result.put(op, out);
     return out;
   }
 
 
-  public static ReachState add( ReachState rs,
-                                ReachTuple rt ) {
+  public static ReachState add(ReachState rs,
+                               ReachTuple rt) {
     assert rs != null;
     assert rt != null;
 
     // this is only safe if we are certain the new tuple's
     // ID doesn't already appear in the reach state
-    assert rs.containsHrnID( rt.getHrnID(),
-                             rt.isOutOfContext() ) == null;
-
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSTATE_ADD_REACHTUPLE,
-                       rs, 
-                       rt );
-    
-    Canonical result = op2result.get( op );
+    assert rs.containsHrnID(rt.getHrnID(),
+                            rt.isOutOfContext() ) == null;
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSTATE_ADD_REACHTUPLE,
+                      rs,
+                      rt);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachState) result;
     }
 
     // otherwise, no cached result...
     ReachState out = new ReachState();
-    out.reachTuples.addAll( rs.reachTuples );
-    out.reachTuples.add( rt );
+    out.reachTuples.addAll(rs.reachTuples);
+    out.reachTuples.add(rt);
     out.preds = rs.preds;
 
-    out = (ReachState) makeCanonical( out );
-    op2result.put( op, out );
+    out = (ReachState) makeCanonical(out);
+    op2result.put(op, out);
     return out;
   }
-  
 
-  public static ReachState unionUpArity( ReachState rs1,
-                                         ReachState rs2 ) {
+
+  public static ReachState unionUpArity(ReachState rs1,
+                                        ReachState rs2) {
     assert rs1 != null;
     assert rs2 != null;
     assert rs1.isCanonical();
     assert rs2.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSTATE_UNIONUPARITY_REACHSTATE,
-                       rs1, 
-                       rs2 );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSTATE_UNIONUPARITY_REACHSTATE,
+                      rs1,
+                      rs2);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachState) result;
     }
-    
+
     // otherwise, no cached result...
     ReachState out = new ReachState();
 
@@ -237,13 +266,13 @@ abstract public class Canonical {
     Iterator<ReachTuple> rtItr = rs1.iterator();
     while( rtItr.hasNext() ) {
       ReachTuple rt1 = rtItr.next();
-      ReachTuple rt2 = rs2.containsHrnID( rt1.getHrnID(),
-                                          rt1.isOutOfContext() 
-                                          );
+      ReachTuple rt2 = rs2.containsHrnID(rt1.getHrnID(),
+                                         rt1.isOutOfContext()
+                                         );
       if( rt2 != null ) {
-       out.reachTuples.add( unionUpArity( rt1, rt2 ) );
+       out.reachTuples.add(unionUpArity(rt1, rt2) );
       } else {
-       out.reachTuples.add( rt1 );
+       out.reachTuples.add(rt1);
       }
     }
 
@@ -251,67 +280,96 @@ abstract public class Canonical {
     rtItr = rs2.iterator();
     while( rtItr.hasNext() ) {
       ReachTuple rt2 = rtItr.next();
-      ReachTuple rt1 = rs1.containsHrnID( rt2.getHrnID(),
-                                          rt2.isOutOfContext()
-                                          );
+      ReachTuple rt1 = rs1.containsHrnID(rt2.getHrnID(),
+                                         rt2.isOutOfContext()
+                                         );
       if( rt1 == null ) {
-       out.reachTuples.add( rt2 );
+       out.reachTuples.add(rt2);
       }
     }
 
-    out.preds = Canonical.join( rs1.getPreds(),
-                                rs2.getPreds()
-                                );
-    
-    out = (ReachState) makeCanonical( out );
-    op2result.put( op, out );
+    out.preds = Canonical.join(rs1.getPreds(),
+                               rs2.getPreds()
+                               );
+
+    out = (ReachState) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
+  }
+
+
+  public static ReachState addUpArity(ReachState rs,
+                                      ReachTuple rt) {
+    assert rs != null;
+    assert rt != null;
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSTATE_ADDUPARITY_REACHTUPLE,
+                      rs,
+                      rt);
+
+    Canonical result = op2result.get(op);
+    if( result != null ) {
+      return (ReachState) result;
+    }
+
+    // otherwise, no cached result...
+    ReachState out;
+
+    // the reason for this add is that we are aware a tuple
+    // with the same hrnID might already be in the state, so
+    // if it is we should combine properly
+    ReachState rtOnly = ReachState.factory(rt);
+    out = Canonical.unionUpArity(rs, rtOnly);
+
+    op2result.put(op, out);
     return out;
   }
 
-  
-  public static ReachState remove( ReachState rs, ReachTuple rt ) {
+
+  public static ReachState remove(ReachState rs, ReachTuple rt) {
     assert rs != null;
     assert rt != null;
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSTATE_REMOVE_REACHTUPLE,
-                       rs, 
-                       rt );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSTATE_REMOVE_REACHTUPLE,
+                      rs,
+                      rt);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachState) result;
     }
 
-    // otherwise, no cached result...    
+    // otherwise, no cached result...
     ReachState out = new ReachState();
-    out.reachTuples.addAll( rs.reachTuples );
-    out.reachTuples.remove( rt );
+    out.reachTuples.addAll(rs.reachTuples);
+    out.reachTuples.remove(rt);
     out.preds = rs.preds;
 
-    out = (ReachState) makeCanonical( out );
-    op2result.put( op, out );
+    out = (ReachState) makeCanonical(out);
+    op2result.put(op, out);
     return out;
   }
-  
-  
-  public static ReachState ageTuplesFrom( ReachState rs, 
-                                          AllocSite  as ) {
+
+
+  public static ReachState ageTuplesFrom(ReachState rs,
+                                         AllocSite as) {
     assert rs != null;
     assert as != null;
     assert rs.isCanonical();
     assert as.isCanonical();
-    
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSTATE_AGETUPLESFROM_ALLOCSITE,
-                       rs, 
-                       as );
-    
-    Canonical result = op2result.get( op );
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSTATE_AGETUPLESFROM_ALLOCSITE,
+                      rs,
+                      as);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachState) result;
     }
-    
+
     // otherwise, no cached result...
     ReachState out = new ReachState();
 
@@ -321,8 +379,8 @@ abstract public class Canonical {
     Iterator<ReachTuple> rtItr = rs.iterator();
     while( rtItr.hasNext() ) {
       ReachTuple rt    = rtItr.next();
-      Integer    hrnID = rt.getHrnID();
-      int        age   = as.getAgeCategory( hrnID );
+      Integer hrnID = rt.getHrnID();
+      int age   = as.getAgeCategory(hrnID);
 
       // hrnIDs not associated with
       // the site should be left alone, and
@@ -330,7 +388,7 @@ abstract public class Canonical {
       if( age == AllocSite.AGE_notInThisSite ||
           rt.isOutOfContext()
           ) {
-       out.reachTuples.add( rt );
+       out.reachTuples.add(rt);
 
       } else if( age == AllocSite.AGE_summary ) {
        // remember the summary tuple, but don't add it
@@ -345,15 +403,15 @@ abstract public class Canonical {
       } else {
        assert age == AllocSite.AGE_in_I;
 
-       Integer I = as.getAge( hrnID );
+       Integer I = as.getAge(hrnID);
        assert I != null;
 
        // otherwise, we change this hrnID to the
        // next older hrnID
-       Integer hrnIDToChangeTo = as.getIthOldest( I + 1 );
+       Integer hrnIDToChangeTo = as.getIthOldest(I + 1);
        ReachTuple rtAged =
-          Canonical.changeHrnIDTo( rt, hrnIDToChangeTo );
-       out.reachTuples.add( rtAged );
+         Canonical.changeHrnIDTo(rt, hrnIDToChangeTo);
+       out.reachTuples.add(rtAged);
       }
     }
 
@@ -366,49 +424,49 @@ abstract public class Canonical {
     //    Merge them by arity
     // 4. (not handled) we found neither, do nothing
     if( rtSummary != null && rtOldest == null ) {
-      out.reachTuples.add( rtSummary );
+      out.reachTuples.add(rtSummary);
 
     } else if( rtSummary == null && rtOldest != null ) {
-      out.reachTuples.add( ReachTuple.factory( as.getSummary(),
-                                               true, // multi
-                                               rtOldest.getArity(),
-                                               false // out-of-context
-                                               )
-                           );
-
-    } else if( rtSummary != null && rtOldest != null ) {     
-      out.reachTuples.add( Canonical.unionUpArity( rtSummary,
-                                                   ReachTuple.factory( as.getSummary(),
-                                                                       true, // muli
-                                                                       rtOldest.getArity(),
-                                                                       false // out-of-context
-                                                                       )
-                                                   )
-                           );
+      out.reachTuples.add(ReachTuple.factory(as.getSummary(),
+                                             true,   // multi
+                                             rtOldest.getArity(),
+                                             false   // out-of-context
+                                             )
+                          );
+
+    } else if( rtSummary != null && rtOldest != null ) {
+      out.reachTuples.add(Canonical.unionUpArity(rtSummary,
+                                                 ReachTuple.factory(as.getSummary(),
+                                                                    true,    // muli
+                                                                    rtOldest.getArity(),
+                                                                    false    // out-of-context
+                                                                    )
+                                                 )
+                          );
     }
 
     out.preds = rs.preds;
 
-    out = (ReachState) makeCanonical( out );
-    op2result.put( op, out );
+    out = (ReachState) makeCanonical(out);
+    op2result.put(op, out);
     return out;
   }
 
 
 
-  public static ReachSet unionORpreds( ReachSet rs1,
-                                       ReachSet rs2 ) {
+  public static ReachSet unionORpreds(ReachSet rs1,
+                                      ReachSet rs2) {
     assert rs1 != null;
     assert rs2 != null;
     assert rs1.isCanonical();
     assert rs2.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_UNIONORPREDS_REACHSET,
-                       rs1, 
-                       rs2 );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSET_UNIONORPREDS_REACHSET,
+                      rs1,
+                      rs2);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachSet) result;
     }
@@ -421,16 +479,16 @@ abstract public class Canonical {
     Iterator<ReachState> stateItr = rs1.iterator();
     while( stateItr.hasNext() ) {
       ReachState state1 = stateItr.next();
-      ReachState state2 = rs2.containsIgnorePreds( state1 );
+      ReachState state2 = rs2.containsIgnorePreds(state1);
 
       if( state2 != null ) {
-       out.reachStates.add( ReachState.factory( state1.reachTuples,
-                                                 Canonical.join( state1.preds,
-                                                                 state2.preds
-                                                                 )
-                                                 ) );
+       out.reachStates.add(ReachState.factory(state1.reachTuples,
+                                              Canonical.join(state1.preds,
+                                                             state2.preds
+                                                             )
+                                              ) );
       } else {
-       out.reachStates.add( state1 );
+       out.reachStates.add(state1);
       }
     }
 
@@ -438,15 +496,15 @@ abstract public class Canonical {
     stateItr = rs2.iterator();
     while( stateItr.hasNext() ) {
       ReachState state2 = stateItr.next();
-      ReachState state1 = rs1.containsIgnorePreds( state2 );
+      ReachState state1 = rs1.containsIgnorePreds(state2);
 
       if( state1 == null ) {
-       out.reachStates.add( state2 );
+       out.reachStates.add(state2);
       }
     }
 
-    out = (ReachSet) makeCanonical( out );
-    op2result.put( op, out );
+    out = (ReachSet) makeCanonical(out);
+    op2result.put(op, out);
     return out;
   }
 
@@ -458,19 +516,19 @@ abstract public class Canonical {
   // avoid eploding states we'll take an overapproximation
   // by preferring the predicates from the state in the FIRST
   // set, so order of arguments matters
-  public static ReachSet intersection( ReachSet rs1,
-                                       ReachSet rs2 ) {
+  public static ReachSet intersection(ReachSet rs1,
+                                      ReachSet rs2) {
     assert rs1 != null;
     assert rs2 != null;
     assert rs1.isCanonical();
     assert rs2.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_INTERSECTION_REACHSET,
-                       rs1, 
-                       rs2 );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSET_INTERSECTION_REACHSET,
+                      rs1,
+                      rs2);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachSet) result;
     }
@@ -480,64 +538,64 @@ abstract public class Canonical {
     Iterator<ReachState> itr = rs1.iterator();
     while( itr.hasNext() ) {
       ReachState state1 = (ReachState) itr.next();
-      ReachState state2 = rs2.containsIgnorePreds( state1 );
+      ReachState state2 = rs2.containsIgnorePreds(state1);
       if( state2 != null ) {
-        // prefer the predicates on state1, an overapproximation
-        // of state1 preds AND state2 preds
-        out.reachStates.add( state1 );
+       // prefer the predicates on state1, an overapproximation
+       // of state1 preds AND state2 preds
+       out.reachStates.add(state1);
       }
     }
 
-    out = (ReachSet) makeCanonical( out );
-    op2result.put( op, out );
+    out = (ReachSet) makeCanonical(out);
+    op2result.put(op, out);
     return out;
   }
 
 
-  public static ReachSet add( ReachSet   rs, 
-                              ReachState state ) {
-    return unionORpreds( rs, 
-                         ReachSet.factory( state )
-                         );
+  public static ReachSet add(ReachSet rs,
+                             ReachState state) {
+    return unionORpreds(rs,
+                        ReachSet.factory(state)
+                        );
   }
 
-  public static ReachSet remove( ReachSet   rs,
-                                 ReachState state ) {
+  public static ReachSet remove(ReachSet rs,
+                                ReachState state) {
     assert rs    != null;
     assert state != null;
     assert rs.isCanonical();
     assert state.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_REMOVE_REACHSTATE,
-                       rs, 
-                       state );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSET_REMOVE_REACHSTATE,
+                      rs,
+                      state);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachSet) result;
     }
 
-    // otherwise, no cached result...    
+    // otherwise, no cached result...
     ReachSet out = new ReachSet();
-    out.reachStates.addAll( rs.reachStates );
-    out.reachStates.remove( state );
+    out.reachStates.addAll(rs.reachStates);
+    out.reachStates.remove(state);
 
-    out = (ReachSet) makeCanonical( out );
-    op2result.put( op, out );
+    out = (ReachSet) makeCanonical(out);
+    op2result.put(op, out);
     return out;
   }
 
 
-  public static ReachSet applyChangeSet( ReachSet  rs, 
-                                         ChangeSet cs,
-                                         boolean   keepSourceState ) {
+  public static ReachSet applyChangeSet(ReachSet rs,
+                                        ChangeSet cs,
+                                        boolean keepSourceState) {
     assert rs != null;
     assert cs != null;
     assert rs.isCanonical();
     assert cs.isCanonical();
 
-    // this primitive operand stuff is just a way to 
+    // this primitive operand stuff is just a way to
     // ensure distinct inputs to a CanonicalOp
     int primOperand;
     if( keepSourceState ) {
@@ -546,18 +604,18 @@ abstract public class Canonical {
       primOperand = 0x2b;
     }
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_APPLY_CHANGESET,
-                       rs, 
-                       cs,
-                       primOperand );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSET_APPLY_CHANGESET,
+                      rs,
+                      cs,
+                      primOperand);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachSet) result;
     }
-    
-    // otherwise, no cached result...    
+
+    // otherwise, no cached result...
     ReachSet out = new ReachSet();
 
     Iterator<ReachState> stateItr = rs.iterator();
@@ -570,46 +628,46 @@ abstract public class Canonical {
       while( ctItr.hasNext() ) {
        ChangeTuple ct = ctItr.next();
 
-       if( stateOrig.equalsIgnorePreds( ct.getStateToMatch() ) ) {
-          // use the new state, but the original predicates
-          ReachState stateNew = 
-            ReachState.factory( ct.getStateToAdd().reachTuples,
-                                stateOrig.preds
-                                );
-          out.reachStates.add( stateNew );
+       if( stateOrig.equalsIgnorePreds(ct.getStateToMatch() ) ) {
+         // use the new state, but the original predicates
+         ReachState stateNew =
+           ReachState.factory(ct.getStateToAdd().reachTuples,
+                              stateOrig.preds
+                              );
+         out.reachStates.add(stateNew);
          changeFound = true;
        }
       }
 
       if( keepSourceState || !changeFound ) {
-       out.reachStates.add( stateOrig );
+       out.reachStates.add(stateOrig);
       }
     }
 
-    out = (ReachSet) makeCanonical( out );
-    op2result.put( op, out );
+    out = (ReachSet) makeCanonical(out);
+    op2result.put(op, out);
     return out;
   }
 
 
-  public static ChangeSet unionUpArityToChangeSet( ReachSet rsO,
-                                                   ReachSet rsR ) {
+  public static ChangeSet unionUpArityToChangeSet(ReachSet rsO,
+                                                  ReachSet rsR) {
     assert rsO != null;
     assert rsR != null;
     assert rsO.isCanonical();
     assert rsR.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_UNIONTOCHANGESET_REACHSET,
-                       rsO, 
-                       rsR );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSET_UNIONTOCHANGESET_REACHSET,
+                      rsO,
+                      rsR);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ChangeSet) result;
     }
-    
-    // otherwise, no cached result...    
+
+    // otherwise, no cached result...
     ChangeSet out = ChangeSet.factory();
 
     Iterator<ReachState> itrO = rsO.iterator();
@@ -625,102 +683,102 @@ abstract public class Canonical {
        Iterator<ReachTuple> itrRelement = r.iterator();
        while( itrRelement.hasNext() ) {
          ReachTuple rtR = itrRelement.next();
-         ReachTuple rtO = o.containsHrnID( rtR.getHrnID(),
-                                            rtR.isOutOfContext()
-                                            );
+         ReachTuple rtO = o.containsHrnID(rtR.getHrnID(),
+                                          rtR.isOutOfContext()
+                                          );
          if( rtO != null ) {
-            theUnion = Canonical.add( theUnion,
-                                      Canonical.unionUpArity( rtR,
-                                                              rtO
-                                                              )
-                                      );
+           theUnion = Canonical.add(theUnion,
+                                    Canonical.unionUpArity(rtR,
+                                                           rtO
+                                                           )
+                                    );
          } else {
-            theUnion = Canonical.add( theUnion,
-                                      rtR
-                                      );
+           theUnion = Canonical.add(theUnion,
+                                    rtR
+                                    );
          }
        }
 
        Iterator<ReachTuple> itrOelement = o.iterator();
        while( itrOelement.hasNext() ) {
          ReachTuple rtO = itrOelement.next();
-         ReachTuple rtR = theUnion.containsHrnID( rtO.getHrnID(),
-                                                   rtO.isOutOfContext()
-                                                   );
+         ReachTuple rtR = theUnion.containsHrnID(rtO.getHrnID(),
+                                                 rtO.isOutOfContext()
+                                                 );
          if( rtR == null ) {
-            theUnion = Canonical.add( theUnion,
-                                      rtO
-                                      );
+           theUnion = Canonical.add(theUnion,
+                                    rtO
+                                    );
          }
        }
-        
+
        if( !theUnion.isEmpty() ) {
-          out = 
-            Canonical.union( out,
-                             ChangeSet.factory( 
-                                               ChangeTuple.factory( o, theUnion ) 
-                                                )
-                             );
+         out =
+           Canonical.union(out,
+                           ChangeSet.factory(
+                             ChangeTuple.factory(o, theUnion)
+                             )
+                           );
        }
       }
     }
 
     assert out.isCanonical();
-    op2result.put( op, out );
+    op2result.put(op, out);
     return out;
   }
 
 
-  public static ReachSet ageTuplesFrom( ReachSet  rs,
-                                        AllocSite as ) {
+  public static ReachSet ageTuplesFrom(ReachSet rs,
+                                       AllocSite as) {
     assert rs != null;
     assert as != null;
     assert rs.isCanonical();
     assert as.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_AGETUPLESFROM_ALLOCSITE,
-                       rs, 
-                       as );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSET_AGETUPLESFROM_ALLOCSITE,
+                      rs,
+                      as);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachSet) result;
     }
-    
+
     // otherwise, no cached result...
     ReachSet out = new ReachSet();
 
     Iterator<ReachState> itrS = rs.iterator();
     while( itrS.hasNext() ) {
       ReachState state = itrS.next();
-      out.reachStates.add( Canonical.ageTuplesFrom( state, as ) );
+      out.reachStates.add(Canonical.ageTuplesFrom(state, as) );
     }
-    
-    out = (ReachSet) makeCanonical( out );
-    op2result.put( op, out );
-    return out;    
+
+    out = (ReachSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
   }
 
 
-  public static ReachSet pruneBy( ReachSet rsO, 
-                                  ReachSet rsP ) {
+  public static ReachSet pruneBy(ReachSet rsO,
+                                 ReachSet rsP) {
     assert rsO != null;
     assert rsP != null;
     assert rsO.isCanonical();
     assert rsP.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_PRUNEBY_REACHSET,
-                       rsO, 
-                       rsP );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSET_PRUNEBY_REACHSET,
+                      rsO,
+                      rsP);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachSet) result;
     }
-    
-    // otherwise, no cached result...    
+
+    // otherwise, no cached result...
     ReachSet out = new ReachSet();
 
     Iterator<ReachState> itrO = rsO.iterator();
@@ -733,108 +791,108 @@ abstract public class Canonical {
       while( itrP.hasNext() && !subsetExists ) {
        ReachState stateP = itrP.next();
 
-       if( stateP.isSubset( stateO ) ) {
+       if( stateP.isSubset(stateO) ) {
          subsetExists = true;
        }
       }
-      
+
       if( subsetExists ) {
-       out.reachStates.add( stateO );
+       out.reachStates.add(stateO);
       }
     }
 
-    out = (ReachSet) makeCanonical( out );
-    op2result.put( op, out );
-    return out;    
+    out = (ReachSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
   }
 
 
-  public static ChangeSet union( ChangeSet cs1, 
-                                 ChangeSet cs2 ) {
+  public static ChangeSet union(ChangeSet cs1,
+                                ChangeSet cs2) {
     assert cs1 != null;
     assert cs2 != null;
     assert cs1.isCanonical();
     assert cs2.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.CHANGESET_UNION_CHANGESET,
-                       cs1, 
-                       cs2 );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.CHANGESET_UNION_CHANGESET,
+                      cs1,
+                      cs2);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ChangeSet) result;
     }
-    
-    // otherwise, no cached result...    
+
+    // otherwise, no cached result...
     ChangeSet out = new ChangeSet();
-    out.changeTuples.addAll( cs1.changeTuples );
-    out.changeTuples.addAll( cs2.changeTuples );
+    out.changeTuples.addAll(cs1.changeTuples);
+    out.changeTuples.addAll(cs2.changeTuples);
 
-    out = (ChangeSet) makeCanonical( out );
-    op2result.put( op, out );
-    return out;    
+    out = (ChangeSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
   }
 
-  public static ChangeSet add( ChangeSet   cs, 
-                               ChangeTuple ct ) {
+  public static ChangeSet add(ChangeSet cs,
+                              ChangeTuple ct) {
     assert cs != null;
     assert ct != null;
     assert cs.isCanonical();
     assert ct.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.CHANGESET_UNION_CHANGETUPLE,
-                       cs, 
-                       ct );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.CHANGESET_UNION_CHANGETUPLE,
+                      cs,
+                      ct);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ChangeSet) result;
     }
-    
-    // otherwise, no cached result...    
+
+    // otherwise, no cached result...
     ChangeSet out = new ChangeSet();
-    out.changeTuples.addAll( cs.changeTuples );
-    out.changeTuples.add( ct );
-    
-    out = (ChangeSet) makeCanonical( out );
-    op2result.put( op, out );
-    return out;    
+    out.changeTuples.addAll(cs.changeTuples);
+    out.changeTuples.add(ct);
+
+    out = (ChangeSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
   }
 
 
 
-  public static ExistPredSet join( ExistPredSet eps1,
-                                   ExistPredSet eps2 ) {
+  public static ExistPredSet join(ExistPredSet eps1,
+                                  ExistPredSet eps2) {
 
     assert eps1 != null;
     assert eps2 != null;
     assert eps1.isCanonical();
     assert eps2.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.EXISTPREDSET_JOIN_EXISTPREDSET,
-                       eps1, 
-                       eps2 );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.EXISTPREDSET_JOIN_EXISTPREDSET,
+                      eps1,
+                      eps2);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ExistPredSet) result;
     }
-    
-    // otherwise, no cached result...    
+
+    // otherwise, no cached result...
     ExistPredSet out = new ExistPredSet();
-    out.preds.addAll( eps1.preds );
-    out.preds.addAll( eps2.preds );
+    out.preds.addAll(eps1.preds);
+    out.preds.addAll(eps2.preds);
 
-    out = (ExistPredSet) makeCanonical( out );
-    op2result.put( op, out );
-    return out;    
+    out = (ExistPredSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
   }
 
-  public static ExistPredSet add( ExistPredSet eps,
-                                  ExistPred    ep ) {
+  public static ExistPredSet add(ExistPredSet eps,
+                                 ExistPred ep) {
 
 
     assert eps != null;
@@ -842,159 +900,40 @@ abstract public class Canonical {
     assert eps.isCanonical();
     assert ep.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.EXISTPREDSET_ADD_EXISTPRED,
-                       eps, 
-                       ep );
-    
-    Canonical result = op2result.get( op );
-    if( result != null ) {
-      return (ExistPredSet) result;
-    }
-    
-    // otherwise, no cached result...    
-    ExistPredSet out = new ExistPredSet();
-    out.preds.addAll( eps.preds );
-    out.preds.add( ep );
-    
-    out = (ExistPredSet) makeCanonical( out );
-    op2result.put( op, out );
-    return out;    
-  }
-
-
-  /*
-  public static ReachSet toCalleeContext( ReachSet  rs,
-                                          AllocSite as ) {
-    assert rs != null;
-    assert as != null;
-    assert rs.isCanonical();
-    assert as.isCanonical();
-
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_TOCALLEECONTEXT_ALLOCSITE,
-                       rs, 
-                       as );
-    
-    Canonical result = op2result.get( op );
-    if( result != null ) {
-      return (ReachSet) result;
-    }
-
-    // otherwise, no cached result...
-    ReachSet out = ReachSet.factory();
-    Iterator<ReachState> itr = rs.iterator();
-    while( itr.hasNext() ) {
-      ReachState state = itr.next();
-      out = Canonical.add( out,
-                           Canonical.toCalleeContext( state, as )
-                           );
-    }
-
-    assert out.isCanonical();
-    op2result.put( op, out );
-    return out;
-  }
-  */
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.EXISTPREDSET_ADD_EXISTPRED,
+                      eps,
+                      ep);
 
-  /*
-  public static ReachState toCalleeContext( ReachState state,
-                                            AllocSite  as ) {
-    assert state != null;
-    assert as    != null;
-    assert state.isCanonical();
-    assert as.isCanonical();
-
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSTATE_TOCALLEECONTEXT_ALLOCSITE,
-                       state, 
-                       as );
-    
-    Canonical result = op2result.get( op );
+    Canonical result = op2result.get(op);
     if( result != null ) {
-      return (ReachState) result;
+      return (ExistPredSet) result;
     }
 
     // otherwise, no cached result...
-    ReachState out = ReachState.factory();
-    Iterator<ReachTuple> itr = state.iterator();
-    while( itr.hasNext() ) {
-      ReachTuple rt = itr.next();
-
-      int age = as.getAgeCategory( rt.getHrnID() );
-
-      // this is the current mapping, where 0, 1, 2S were allocated
-      // in the current context, 0?, 1? and 2S? were allocated in a
-      // previous context, and we're translating to a future context
-      //
-      // 0    -> 0?
-      // 1    -> 1?
-      // 2S   -> 2S?
-      // 2S*  -> 2S?*
-      //
-      // 0?   -> 2S?
-      // 1?   -> 2S?
-      // 2S?  -> 2S?
-      // 2S?* -> 2S?*
-      
-      if( age == AllocSite.AGE_notInThisSite ) {
-        // things not from the site just go back in
-       out = Canonical.union( out, rt );
-
-      } else if( age == AllocSite.AGE_summary ||
-                 rt.isOutOfContext()
-                 ) {
-        // the in-context summary and all existing out-of-context
-        // stuff all become
-        out = Canonical.union( out,
-                               ReachTuple.factory( as.getSummary(),
-                                                   true, // multi
-                                                   rt.getArity(),
-                                                   true  // out-of-context
-                                                   )
-                               );
-      } else {
-        // otherwise everything else just goes to an out-of-context
-        // version, everything else the same
-       Integer I = as.getAge( rt.getHrnID() );
-       assert I != null;
-
-        assert !rt.isMultiObject();
-
-        out = Canonical.union( out,
-                               ReachTuple.factory( rt.getHrnID(),
-                                                   rt.isMultiObject(),
-                                                   rt.getArity(),
-                                                   true  // out-of-context
-                                                   )
-                               );        
-      }
-    }
-
-    out = Canonical.attach( out,
-                            state.getPreds()
-                            );
+    ExistPredSet out = new ExistPredSet();
+    out.preds.addAll(eps.preds);
+    out.preds.add(ep);
 
-    assert out.isCanonical();
-    op2result.put( op, out );
+    out = (ExistPredSet) makeCanonical(out);
+    op2result.put(op, out);
     return out;
   }
-  */
 
 
-  public static ReachSet toCallerContext( ReachSet  rs,
-                                          AllocSite as ) {
+  public static ReachSet toCallerContext(ReachSet rs,
+                                         AllocSite as) {
     assert rs != null;
     assert as != null;
     assert rs.isCanonical();
     assert as.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_TOCALLERCONTEXT_ALLOCSITE,
-                       rs, 
-                       as );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSET_TOCALLERCONTEXT_ALLOCSITE,
+                      rs,
+                      as);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachSet) result;
     }
@@ -1004,30 +943,30 @@ abstract public class Canonical {
     Iterator<ReachState> itr = rs.iterator();
     while( itr.hasNext() ) {
       ReachState state = itr.next();
-      out = Canonical.unionORpreds( out,
-                                    Canonical.toCallerContext( state, as )
-                                    );
+      out = Canonical.unionORpreds(out,
+                                   Canonical.toCallerContext(state, as)
+                                   );
     }
 
     assert out.isCanonical();
-    op2result.put( op, out );
+    op2result.put(op, out);
     return out;
   }
-  
 
-  public static ReachSet toCallerContext( ReachState state,
-                                          AllocSite  as ) {
+
+  public static ReachSet toCallerContext(ReachState state,
+                                         AllocSite as) {
     assert state != null;
     assert as    != null;
     assert state.isCanonical();
     assert as.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSTATE_TOCALLERCONTEXT_ALLOCSITE,
-                       state, 
-                       as );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSTATE_TOCALLERCONTEXT_ALLOCSITE,
+                      state,
+                      as);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachSet) result;
     }
@@ -1052,7 +991,7 @@ abstract public class Canonical {
     //      -> 0?
     //      -> 1?
     //      -> 2S?
-    // 2S?* -> {2S*, 2S?*}    
+    // 2S?* -> {2S*, 2S?*}
 
     boolean found2Sooc = false;
 
@@ -1062,77 +1001,79 @@ abstract public class Canonical {
     while( itr.hasNext() ) {
       ReachTuple rt = itr.next();
 
-      int age = as.getAgeCategory( rt.getHrnID() );
+      int age = as.getAgeCategory(rt.getHrnID() );
 
       if( age == AllocSite.AGE_notInThisSite ) {
-        // things not from the site just go back in
-       baseState = Canonical.add( baseState, rt );
+       // things not from the site just go back in
+       baseState = Canonical.addUpArity(baseState, rt);
 
       } else if( age == AllocSite.AGE_summary ) {
 
-        if( rt.isOutOfContext() ) {
-          // if its out-of-context, we only deal here with the ZERO-OR-MORE
-          // arity, if ARITY-ONE we'll branch the base state after the loop
-          if( rt.getArity() == ReachTuple.ARITY_ZEROORMORE ) {
-            // add two overly conservative symbols to reach state (PUNTING)
-            baseState = Canonical.add( baseState,
-                                       ReachTuple.factory( as.getSummary(),
-                                                           true, // multi
-                                                           ReachTuple.ARITY_ZEROORMORE,
-                                                           false // out-of-context
-                                                           )
-                                         );            
-            baseState = Canonical.add( baseState,
-                                       ReachTuple.factory( as.getSummary(),
-                                                           true, // multi
-                                                           ReachTuple.ARITY_ZEROORMORE,
-                                                           true  // out-of-context
-                                                           )
-                                       );            
-          } else {
-            assert rt.getArity() == ReachTuple.ARITY_ONE;
-            found2Sooc = true;
-          }
-
-        } else {
-          // the in-context just becomes shadow
-          baseState = Canonical.add( baseState,
-                                     ReachTuple.factory( as.getSummaryShadow(),
-                                                         true, // multi
-                                                         rt.getArity(),
-                                                         false  // out-of-context
-                                                         )
-                                     );
-        }
+       if( rt.isOutOfContext() ) {
+         // if its out-of-context, we only deal here with the ZERO-OR-MORE
+         // arity, if ARITY-ONE we'll branch the base state after the loop
+         if( rt.getArity() == ReachTuple.ARITY_ZEROORMORE ) {
+           // add two overly conservative symbols to reach state (PUNTING)
+
+           baseState = Canonical.addUpArity(baseState,
+                                            ReachTuple.factory(as.getSummary(),
+                                                               true,   // multi
+                                                               ReachTuple.ARITY_ZEROORMORE,
+                                                               false   // out-of-context
+                                                               )
+                                            );
+
+           baseState = Canonical.addUpArity(baseState,
+                                            ReachTuple.factory(as.getSummary(),
+                                                               true,   // multi
+                                                               ReachTuple.ARITY_ZEROORMORE,
+                                                               true    // out-of-context
+                                                               )
+                                            );
+         } else {
+           assert rt.getArity() == ReachTuple.ARITY_ONE;
+           found2Sooc = true;
+         }
+
+       } else {
+         // the in-context just becomes shadow
+         baseState = Canonical.addUpArity(baseState,
+                                          ReachTuple.factory(as.getSummaryShadow(),
+                                                             true,   // multi
+                                                             rt.getArity(),
+                                                             false    // out-of-context
+                                                             )
+                                          );
+       }
 
 
       } else {
-        // otherwise age is in range [0, k]
-        Integer I = as.getAge( rt.getHrnID() );
-        assert I != null;        
-        assert !rt.isMultiObject();
-        assert rt.getArity() == ReachTuple.ARITY_ONE;
-
-        if( rt.isOutOfContext() ) {
-          // becomes the in-context version
-          baseState = Canonical.add( baseState,
-                                     ReachTuple.factory( rt.getHrnID(),
-                                                         false, // multi
-                                                         ReachTuple.ARITY_ONE,
-                                                         false  // out-of-context
-                                                         )
-                                     );          
-
-        } else {
-          // otherwise the ith symbol becomes shadowed
-          baseState = Canonical.add( baseState,
-                                     ReachTuple.factory( -rt.getHrnID(),
-                                                         false, // multi
-                                                         ReachTuple.ARITY_ONE,
-                                                         false  // out-of-context
-                                                         )
-                                     );        
-        }
+       // otherwise age is in range [0, k]
+       Integer I = as.getAge(rt.getHrnID() );
+       assert I != null;
+       assert !rt.isMultiObject();
+       assert rt.getArity() == ReachTuple.ARITY_ONE;
+
+       if( rt.isOutOfContext() ) {
+         // becomes the in-context version
+         baseState = Canonical.addUpArity(baseState,
+                                          ReachTuple.factory(rt.getHrnID(),
+                                                             false,   // multi
+                                                             ReachTuple.ARITY_ONE,
+                                                             false    // out-of-context
+                                                             )
+                                          );
+
+       } else {
+         // otherwise the ith symbol becomes shadowed
+         baseState = Canonical.addUpArity(baseState,
+                                          ReachTuple.factory(-rt.getHrnID(),
+                                                             false,   // multi
+                                                             ReachTuple.ARITY_ONE,
+                                                             false    // out-of-context
+                                                             )
+                                          );
+       }
       }
     }
 
@@ -1141,64 +1082,64 @@ abstract public class Canonical {
     if( found2Sooc ) {
       // make a branch with every possibility of the one-to-many
       // mapping for 2S? appended to the baseState
-      out = Canonical.add( out,
-                           Canonical.add( baseState,
-                                          ReachTuple.factory( as.getSummary(),
-                                                              true, // multi
-                                                              ReachTuple.ARITY_ONE,
-                                                              false  // out-of-context
-                                                              )
-                                          )
-                           );
-
-      out = Canonical.add( out,
-                           Canonical.add( baseState,
-                                          ReachTuple.factory( as.getSummary(),
-                                                              true, // multi
-                                                              ReachTuple.ARITY_ONE,
-                                                              true  // out-of-context
-                                                              )
-                                          )
-                           );      
+      out = Canonical.add(out,
+                          Canonical.addUpArity(baseState,
+                                               ReachTuple.factory(as.getSummary(),
+                                                                  true,    // multi
+                                                                  ReachTuple.ARITY_ONE,
+                                                                  false     // out-of-context
+                                                                  )
+                                               )
+                          );
+
+      out = Canonical.add(out,
+                          Canonical.addUpArity(baseState,
+                                               ReachTuple.factory(as.getSummary(),
+                                                                  true,    // multi
+                                                                  ReachTuple.ARITY_ONE,
+                                                                  true     // out-of-context
+                                                                  )
+                                               )
+                          );
 
       for( int i = 0; i < as.getAllocationDepth(); ++i ) {
-        out = Canonical.add( out,
-                             Canonical.add( baseState,
-                                            ReachTuple.factory( as.getIthOldest( i ),
-                                                                false, // multi
-                                                                ReachTuple.ARITY_ONE,
-                                                                true  // out-of-context
-                                                                )
-                                            )
-                             );
+       out = Canonical.add(out,
+                           Canonical.addUpArity(baseState,
+                                                ReachTuple.factory(as.getIthOldest(i),
+                                                                   false,    // multi
+                                                                   ReachTuple.ARITY_ONE,
+                                                                   true     // out-of-context
+                                                                   )
+                                                )
+                           );
       }
 
     } else {
-      // just use current baseState      
-      out = Canonical.add( out,
-                           baseState );
+      // just use current baseState
+      out = Canonical.add(out,
+                          baseState);
     }
 
 
     assert out.isCanonical();
-    op2result.put( op, out );
+    op2result.put(op, out);
     return out;
   }
 
 
-  public static ReachSet unshadow( ReachSet  rs,
-                                   AllocSite as ) {
+  public static ReachSet unshadow(ReachSet rs,
+                                  AllocSite as) {
     assert rs != null;
     assert as != null;
     assert rs.isCanonical();
     assert as.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_UNSHADOW_ALLOCSITE,
-                       rs, 
-                       as );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSET_UNSHADOW_ALLOCSITE,
+                      rs,
+                      as);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachSet) result;
     }
@@ -1208,29 +1149,29 @@ abstract public class Canonical {
     Iterator<ReachState> itr = rs.iterator();
     while( itr.hasNext() ) {
       ReachState state = itr.next();
-      out = Canonical.add( out,
-                           Canonical.unshadow( state, as )
-                           );
+      out = Canonical.add(out,
+                          Canonical.unshadow(state, as)
+                          );
     }
 
     assert out.isCanonical();
-    op2result.put( op, out );
+    op2result.put(op, out);
     return out;
   }
 
-  public static ReachState unshadow( ReachState state,
-                                     AllocSite  as ) {
+  public static ReachState unshadow(ReachState state,
+                                    AllocSite as) {
     assert state != null;
     assert as    != null;
     assert state.isCanonical();
     assert as.isCanonical();
 
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSTATE_UNSHADOW_ALLOCSITE,
-                       state, 
-                       as );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSTATE_UNSHADOW_ALLOCSITE,
+                      state,
+                      as);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachState) result;
     }
@@ -1242,103 +1183,528 @@ abstract public class Canonical {
     // -0   -> 0
     // -1   -> 1
     // -2S  -> 2S
-    
+
     // otherwise, no cached result...
     ReachState out = ReachState.factory();
     Iterator<ReachTuple> itr = state.iterator();
     while( itr.hasNext() ) {
       ReachTuple rt = itr.next();
 
-      int age = as.getShadowAgeCategory( rt.getHrnID() );
-      
+      int age = as.getShadowAgeCategory(rt.getHrnID() );
+
       if( age == AllocSite.SHADOWAGE_notInThisSite ) {
-        // things not from the site just go back in
-       out = Canonical.add( out, rt );
+       // things not from the site just go back in
+       out = Canonical.addUpArity(out, rt);
 
       } else {
-        assert !rt.isOutOfContext();
-
-        // otherwise unshadow it
-        out = Canonical.add( out,
-                             ReachTuple.factory( -rt.getHrnID(),
-                                                 rt.isMultiObject(),
-                                                 rt.getArity(),
-                                                 false
-                                                 )
-                             );
+       assert !rt.isOutOfContext();
+
+       // otherwise unshadow it
+       out = Canonical.addUpArity(out,
+                                  ReachTuple.factory(-rt.getHrnID(),
+                                                     rt.isMultiObject(),
+                                                     rt.getArity(),
+                                                     false
+                                                     )
+                                  );
       }
     }
 
-    out = Canonical.attach( out,
-                            state.getPreds()
-                            );
+    out = Canonical.attach(out,
+                           state.getPreds()
+                           );
 
     assert out.isCanonical();
-    op2result.put( op, out );
+    op2result.put(op, out);
     return out;
   }
 
 
 
-  public static ReachState makePredsTrue( ReachState rs ) {
+  public static ReachState changePredsTo(ReachState rs,
+                                         ExistPredSet preds) {
     assert rs != null;
     assert rs.isCanonical();
 
-    // ops require two canonicals, in this case always supply
-    // the empty reach state as the second, it's never used,
-    // but makes the hashing happy
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSTATE_MAKEPREDSTRUE,
-                       rs, 
-                       ReachState.factory() );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSTATE_CHANGEPREDSTO_EXISTPREDSET,
+                      rs,
+                      preds);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachState) result;
     }
-    
+
     // otherwise, no cached result...
     ReachState out = new ReachState();
 
     // just remake state with the true predicate attached
-    out.reachTuples.addAll( rs.reachTuples );
-    out.preds = ExistPredSet.factory( ExistPred.factory() );
-    
-    out = (ReachState) makeCanonical( out );
-    op2result.put( op, out );
+    out.reachTuples.addAll(rs.reachTuples);
+    out.preds = preds;
+
+    out = (ReachState) makeCanonical(out);
+    op2result.put(op, out);
     return out;
   }
 
 
-  public static ReachSet makePredsTrue( ReachSet rs ) {
+  public static ReachSet changePredsTo(ReachSet rs,
+                                       ExistPredSet preds) {
     assert rs != null;
     assert rs.isCanonical();
 
-    // ops require two canonicals, in this case always supply
-    // the empty reach set as the second, it's never used,
-    // but makes the hashing happy
-    CanonicalOp op = 
-      new CanonicalOp( CanonicalOp.REACHSET_MAKEPREDSTRUE,
-                       rs,
-                       ReachSet.factory() );
-    
-    Canonical result = op2result.get( op );
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.REACHSET_CHANGEPREDSTO_EXISTPREDSET,
+                      rs,
+                      preds);
+
+    Canonical result = op2result.get(op);
     if( result != null ) {
       return (ReachSet) result;
     }
-    
+
     // otherwise, no cached result...
     ReachSet out = ReachSet.factory();
     Iterator<ReachState> itr = rs.iterator();
     while( itr.hasNext() ) {
       ReachState state = itr.next();
-      out = Canonical.add( out,
-                           Canonical.makePredsTrue( state )
-                           );
+      out = Canonical.add(out,
+                          Canonical.changePredsTo(state,
+                                                  preds
+                                                  )
+                          );
+    }
+
+    out = (ReachSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
+  }
+
+
+  public static Taint attach(Taint t,
+                             ExistPredSet preds) {
+    assert t     != null;
+    assert preds != null;
+    assert t.isCanonical();
+    assert preds.isCanonical();
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.TAINT_ATTACH_EXISTPREDSET,
+                      t,
+                      preds);
+
+    Canonical result = op2result.get(op);
+    if( result != null ) {
+      return (Taint) result;
+    }
+
+    // otherwise, no cached result...
+    Taint out = new Taint(t);
+    out.preds = Canonical.join(t.preds,
+                               preds);
+
+    out = (Taint) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
+  }
+
+  public static TaintSet add(TaintSet ts,
+                             Taint t) {
+    assert ts != null;
+    assert t  != null;
+    assert ts.isCanonical();
+    assert t.isCanonical();
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.TAINTSET_ADD_TAINT,
+                      ts,
+                      t);
+
+    Canonical result = op2result.get(op);
+    if( result != null ) {
+      return (TaintSet) result;
+    }
+
+    // otherwise, no cached result...
+    TaintSet out = new TaintSet();
+    out.taints.addAll(ts.taints);
+    out.taints.add(t);
+
+    out = (TaintSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
+  }
+
+
+  public static TaintSet addPTR(TaintSet ts,
+                                Taint t) {
+    return add(ts, t);
+  }
+
+  public static TaintSet union(TaintSet ts1,
+                               TaintSet ts2) {
+    assert ts1 != null;
+    assert ts2 != null;
+    assert ts1.isCanonical();
+    assert ts2.isCanonical();
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.TAINTSET_UNION_TAINTSET,
+                      ts1,
+                      ts2);
+
+    Canonical result = op2result.get(op);
+    if( result != null ) {
+      return (TaintSet) result;
+    }
+
+    // otherwise, no cached result...
+    TaintSet out = new TaintSet();
+
+    // first add everything from 1, and if it was also in 2
+    // take the OR of the predicates
+    Iterator<Taint> tItr = ts1.iterator();
+    while( tItr.hasNext() ) {
+      Taint t1 = tItr.next();
+      Taint t2 = ts2.containsIgnorePreds(t1);
+
+      if( t2 != null ) {
+       Taint tNew = new Taint(t1);
+       tNew.preds = Canonical.join(t1.preds,
+                                   t2.preds
+                                   );
+       tNew = (Taint) makeCanonical(tNew);
+       out.taints.add(tNew);
+      } else {
+       out.taints.add(t1);
+      }
+    }
+
+    // then add everything in 2 that wasn't in 1
+    tItr = ts2.iterator();
+    while( tItr.hasNext() ) {
+      Taint t2 = tItr.next();
+      Taint t1 = ts1.containsIgnorePreds(t2);
+
+      if( t1 == null ) {
+       out.taints.add(t2);
+      }
+    }
+
+    out = (TaintSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
+  }
+
+  public static TaintSet unionPTR(TaintSet ts1,
+                                  TaintSet ts2) {
+    assert ts1 != null;
+    assert ts2 != null;
+    assert ts1.isCanonical();
+    assert ts2.isCanonical();
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.TAINTSET_UNION_TAINTSET,
+                      ts1,
+                      ts2);
+
+    Canonical result = op2result.get(op);
+    if( result != null ) {
+      return (TaintSet) result;
+    }
+
+    // otherwise, no cached result...
+    TaintSet out = new TaintSet();
+
+    out.taints.addAll(ts1.taints);
+    out.taints.addAll(ts2.taints);
+    out= (TaintSet) Canonical.makeCanonical(out);
+    op2result.put(op, out);
+    return out;
+  }
+
+  public static TaintSet unionORpreds(TaintSet ts1,
+                                      TaintSet ts2) {
+    assert ts1 != null;
+    assert ts2 != null;
+    assert ts1.isCanonical();
+    assert ts2.isCanonical();
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.TAINTSET_UNIONORPREDS_TAINTSET,
+                      ts1,
+                      ts2);
+
+    Canonical result = op2result.get(op);
+    if( result != null ) {
+      return (TaintSet) result;
+    }
+
+    // otherwise, no cached result...
+    TaintSet out = new TaintSet();
+
+    // first add everything from 1, and if it was also in 2
+    // take the OR of the predicates
+    Iterator<Taint> tItr = ts1.iterator();
+    while( tItr.hasNext() ) {
+      Taint t1 = tItr.next();
+      Taint t2 = ts2.containsIgnorePreds(t1);
+
+      if( t2 != null ) {
+       Taint tNew = new Taint(t1);
+       tNew.preds = Canonical.join(t1.preds,
+                                   t2.preds
+                                   );
+       tNew = (Taint) makeCanonical(tNew);
+       out.taints.add(tNew);
+      } else {
+       out.taints.add(t1);
+      }
+    }
+
+    // then add everything in 2 that wasn't in 1
+    tItr = ts2.iterator();
+    while( tItr.hasNext() ) {
+      Taint t2 = tItr.next();
+      Taint t1 = ts1.containsIgnorePreds(t2);
+
+      if( t1 == null ) {
+       out.taints.add(t2);
+      }
+    }
+
+    out = (TaintSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
+  }
+
+
+  // BOO, HISS! SESE (rblock) operand does NOT extend
+  // Canonical, so we can't cache this op by its
+  // canonical arguments--THINK ABOUT A BETTER WAY!
+  public static TaintSet removeInContextTaints(TaintSet ts,
+                                               FlatSESEEnterNode sese) {
+    assert ts != null;
+    assert ts.isCanonical();
+    assert sese != null;
+
+    // NEVER a cached result... (cry)
+    TaintSet out = new TaintSet();
+
+    Iterator<Taint> tItr = ts.iterator();
+    while( tItr.hasNext() ) {
+      Taint t = tItr.next();
+
+      // what is allowed through?  stall site taints always
+      // go through, anything where rblock doesn't match is
+      // unaffected, and if the taint has a non-empty predicate
+      // it is out of context so it should go through, too
+      if( t.getSESE() == null ||
+          !t.getSESE().equals(sese) ||
+          !t.getPreds().isEmpty()
+          ) {
+       out.taints.add(t);
+      }
+    }
+
+    out = (TaintSet) makeCanonical(out);
+    //op2result.put( op, out ); CRY CRY
+    return out;
+  }
+
+  // BOO, HISS! SESE (rblock) operand does NOT extend
+  // Canonical, so we can't cache this op by its
+  // canonical arguments--THINK ABOUT A BETTER WAY!
+  public static TaintSet removeSESETaints(TaintSet ts,
+                                          Set<FlatSESEEnterNode> seseSet) {
+    assert ts != null;
+    assert ts.isCanonical();
+
+    // NEVER a cached result... (cry)
+    TaintSet out = new TaintSet();
+
+    Iterator<Taint> tItr = ts.iterator();
+    while( tItr.hasNext() ) {
+      Taint t = tItr.next();
+
+      // what is allowed through?  stall site taints always
+      // go through, anything where rblock doesn't match is
+      // unaffected, and if the taint has a non-empty predicate
+      // it is out of context so it should go through, too
+      if( t.getSESE() == null ||
+          !seseSet.contains(t)) {
+       out.taints.add(t);
+      }
+    }
+
+    out = (TaintSet) makeCanonical(out);
+    //op2result.put( op, out ); CRY CRY
+    return out;
+  }
+
+  public static TaintSet removeInContextTaintsNP(TaintSet ts,
+                                                 FlatSESEEnterNode sese) {
+    assert ts != null;
+    assert ts.isCanonical();
+
+    // NEVER a cached result... (cry)
+    TaintSet out = new TaintSet();
+
+    Iterator<Taint> tItr = ts.iterator();
+    while( tItr.hasNext() ) {
+      Taint t = tItr.next();
+
+      // what is allowed through?  stall site taints always
+      // go through, anything where rblock doesn't match is
+      // unaffected, and if the taint has a non-empty predicate
+      // it is out of context so it should go through, too
+      if( t.getSESE()!=null && t.getSESE()!=sese) {
+       out.taints.add(t);
+      }
+    }
+
+    out = (TaintSet) makeCanonical(out);
+    return out;
+  }
+
+  public static TaintSet removeStallSiteTaints(TaintSet ts) {
+    assert ts != null;
+    assert ts.isCanonical();
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.TAINTSET_REMOVESTALLSITETAINTS,
+                      ts,
+                      ts);
+
+    Canonical result = op2result.get(op);
+    if( result != null ) {
+      return (TaintSet) result;
+    }
+
+    // otherwise, no cached result...
+    TaintSet out = new TaintSet();
+
+    Iterator<Taint> tItr = ts.iterator();
+    while( tItr.hasNext() ) {
+      Taint t = tItr.next();
+
+      // only take non-stall site taints onward
+      if( t.getStallSite() == null ) {
+       out.taints.add(t);
+      }
     }
-    
-    out = (ReachSet) makeCanonical( out );
-    op2result.put( op, out );
+
+    out = (TaintSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
+  }
+
+
+  public static Taint changePredsTo(Taint t,
+                                    ExistPredSet preds) {
+    assert t != null;
+    assert t.isCanonical();
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.TAINT_CHANGEPREDSTO_EXISTPREDSET,
+                      t,
+                      preds);
+
+    Canonical result = op2result.get(op);
+    if( result != null ) {
+      return (Taint) result;
+    }
+
+    // otherwise, no cached result...
+    Taint out = new Taint(t.sese,
+                          t.stallSite,
+                          t.var,
+                          t.allocSite,
+                          t.fnDefined,
+                          preds
+                          );
+
+    out = (Taint) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
+  }
+
+
+  public static TaintSet changePredsTo(TaintSet ts,
+                                       ExistPredSet preds) {
+    assert ts != null;
+    assert ts.isCanonical();
+
+    CanonicalOp op =
+      new CanonicalOp(CanonicalOp.TAINTSET_CHANGEPREDSTO_EXISTPREDSET,
+                      ts,
+                      preds);
+
+    Canonical result = op2result.get(op);
+    if( result != null ) {
+      return (TaintSet) result;
+    }
+
+    // otherwise, no cached result...
+    TaintSet out = TaintSet.factory();
+    Iterator<Taint> itr = ts.iterator();
+    while( itr.hasNext() ) {
+      Taint t = itr.next();
+      out = Canonical.add(out,
+                          Canonical.changePredsTo(t, preds)
+                          );
+    }
+
+    out = (TaintSet) makeCanonical(out);
+    op2result.put(op, out);
+    return out;
+  }
+
+
+
+  // BOO, HISS! FlatNode operand does NOT extend
+  // Canonical, so we can't cache this op by its
+  // canonical arguments--THINK ABOUT A BETTER WAY!
+  public static Taint changeWhereDefined(Taint t,
+                                         FlatNode pp) {
+    assert t != null;
+    assert t.isCanonical();
+
+    // never a cached result...
+    Taint out = new Taint(t.sese,
+                          t.stallSite,
+                          t.var,
+                          t.allocSite,
+                          pp,
+                          t.preds
+                          );
+
+    out = (Taint) makeCanonical(out);
+    //op2result.put( op, out ); CRY CRY
+    return out;
+  }
+
+  // BOO, HISS! FlatNode operand does NOT extend
+  // Canonical, so we can't cache this op by its
+  // canonical arguments--THINK ABOUT A BETTER WAY!
+  public static TaintSet changeWhereDefined(TaintSet ts,
+                                            FlatNode pp) {
+    assert ts != null;
+    assert ts.isCanonical();
+
+    // never a cached result...
+    TaintSet out = TaintSet.factory();
+    Iterator<Taint> itr = ts.iterator();
+    while( itr.hasNext() ) {
+      Taint t = itr.next();
+      out = Canonical.add(out,
+                          Canonical.changeWhereDefined(t, pp)
+                          );
+    }
+
+    out = (TaintSet) makeCanonical(out);
+    //op2result.put( op, out ); CRY CRY
     return out;
   }