op2result.put( op, out );
return out;
}
-
+
+
+ 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 );
+ 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.union( out,
+ Canonical.toCallerContext( state, as )
+ );
+ }
+
+ assert out.isCanonical();
+ op2result.put( op, out );
+ return out;
+ }
+
+ 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 );
+ if( result != null ) {
+ return (ReachSet) result;
+ }
+
+ // otherwise, no cached result...
+ ReachSet out = ReachSet.factory();
+
+ // this method returns a ReachSet instead of a ReachState
+ // because the companion method, toCallee, translates
+ // symbols many-to-one, so state->state
+ // but this method does an ~inverse mapping, one-to-many
+ // so one state can split into a set of branched states
+
+ // 0 -> -0
+ // 1 -> -1
+ // 2S -> -2S
+ // 2S* -> -2S*
+ //
+ // 0? -> 0
+ // 1? -> 1
+ // 2S? -> 2S
+ // -> 0?
+ // -> 1?
+ // -> 2S?
+ // 2S?* -> {2S*, 2S?*}
+
+ boolean found2Sooc = false;
+
+ ReachState baseState = ReachState.factory();
+
+ Iterator<ReachTuple> itr = state.iterator();
+ while( itr.hasNext() ) {
+ ReachTuple rt = itr.next();
+
+ int age = as.getAgeCategory( rt.getHrnID() );
+
+ if( age == AllocSite.AGE_notInThisSite ) {
+ // things not from the site just go back in
+ baseState = Canonical.union( 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.union( baseState,
+ ReachTuple.factory( as.getSummary(),
+ true, // multi
+ ReachTuple.ARITY_ZEROORMORE,
+ false // out-of-context
+ )
+ );
+ baseState = Canonical.union( 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.union( baseState,
+ ReachTuple.factory( as.getSummaryShadow(),
+ true, // multi
+ rt.getArity(),
+ false // out-of-context
+ )
+ );
+ }
+
+ } else {
+ // otherwise the ith symbol becomes shadowed
+ Integer I = as.getAge( rt.getHrnID() );
+ assert I != null;
+
+ assert !rt.isMultiObject();
+
+ baseState = Canonical.union( baseState,
+ ReachTuple.factory( -rt.getHrnID(),
+ false, // multi
+ rt.getArity(),
+ false // out-of-context
+ )
+ );
+ }
+ }
+
+ // now either make branches if we have 2S?, or
+ // the current baseState is the only state we need
+ if( found2Sooc ) {
+ // make a branch with every possibility of the one-to-many
+ // mapping for 2S? appended to the baseState
+ out = Canonical.union( out,
+ Canonical.union( baseState,
+ ReachTuple.factory( as.getSummary(),
+ true, // multi
+ ReachTuple.ARITY_ONE,
+ false // out-of-context
+ )
+ )
+ );
+
+ out = Canonical.union( out,
+ Canonical.union( baseState,
+ ReachTuple.factory( as.getSummary(),
+ true, // multi
+ ReachTuple.ARITY_ONE,
+ true // out-of-context
+ )
+ )
+ );
+
+ for( int i = 0; i < as.getAllocationDepth(); ++i ) {
+ out = Canonical.union( out,
+ Canonical.union( baseState,
+ ReachTuple.factory( as.getIthOldest( i ),
+ false, // multi
+ ReachTuple.ARITY_ONE,
+ true // out-of-context
+ )
+ )
+ );
+ }
+
+ } else {
+ // just use current baseState
+ out = Canonical.union( out,
+ baseState );
+ }
+
+
+ assert out.isCanonical();
+ op2result.put( op, out );
+ return out;
+ }
+
+
+
+
+
+
+
+
+
+ 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 );
+ 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.unshadow( state, as )
+ );
+ }
+
+ assert out.isCanonical();
+ op2result.put( op, out );
+ return out;
+ }
+
+ 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 );
+ if( result != null ) {
+ return (ReachState) result;
+ }
+
+ // 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
+
+ // 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() );
+
+ if( age == AllocSite.SHADOWAGE_notInThisSite ) {
+ // things not from the site just go back in
+ out = Canonical.union( out, rt );
+
+ } else {
+ assert !rt.isOutOfContext();
+
+ // otherwise unshadow it
+ out = Canonical.union( out,
+ ReachTuple.factory( -rt.getHrnID(),
+ rt.isMultiObject(),
+ rt.getArity(),
+ false
+ )
+ );
+ }
+ }
+
+ assert out.isCanonical();
+ op2result.put( op, out );
+ return out;
+ }
}
// first and do a result look-up
public class CanonicalOp {
- public static final int REACHTUPLE_UNIONARITY_REACHTUPLE = 0x1a34;
- public static final int REACHSTATE_UNION_REACHSTATE = 0x5678;
- public static final int REACHSTATE_UNION_REACHTUPLE = 0x32b6;
- public static final int REACHSTATE_UNIONUPARITY_REACHSTATE = 0x9152;
- public static final int REACHSTATE_REMOVE_REACHTUPLE = 0x8173;
- public static final int REACHSTATE_AGETUPLESFROM_ALLOCSITE = 0x4f65;
- public static final int REACHSET_UNION_REACHSET = 0x2131;
- public static final int REACHSET_UNION_REACHSTATE = 0xe3c8;
- public static final int REACHSET_INTERSECTION_REACHSET = 0x3361;
- public static final int REACHSET_REMOVE_REACHSTATE = 0x9391;
- public static final int REACHSET_APPLY_CHANGESET = 0x1d55;
- public static final int REACHSET_UNIONTOCHANGESET_REACHSET = 0x46a9;
- public static final int REACHSET_AGETUPLESFROM_ALLOCSITE = 0x22bb;
- public static final int REACHSET_PRUNEBY_REACHSET = 0xd774;
- public static final int CHANGESET_UNION_CHANGESET = 0x53b3;
- public static final int CHANGESET_UNION_CHANGETUPLE = 0x9ee4;
- public static final int EXISTPREDSET_JOIN_EXISTPREDSET = 0x8a21;
- public static final int EXISTPREDSET_ADD_EXISTPRED = 0xba5f;
- public static final int PRIM_OP_UNUSED = 0xef01;
- public static final int REACHSET_TOCALLEECONTEXT_ALLOCSITE = 0x56f6;
+ public static final int REACHTUPLE_UNIONARITY_REACHTUPLE = 0x1a34;
+ public static final int REACHSTATE_UNION_REACHSTATE = 0x5678;
+ public static final int REACHSTATE_UNION_REACHTUPLE = 0x32b6;
+ public static final int REACHSTATE_UNIONUPARITY_REACHSTATE = 0x9152;
+ public static final int REACHSTATE_REMOVE_REACHTUPLE = 0x8173;
+ public static final int REACHSTATE_AGETUPLESFROM_ALLOCSITE = 0x4f65;
+ public static final int REACHSET_UNION_REACHSET = 0x2131;
+ public static final int REACHSET_UNION_REACHSTATE = 0xe3c8;
+ public static final int REACHSET_INTERSECTION_REACHSET = 0x3361;
+ public static final int REACHSET_REMOVE_REACHSTATE = 0x9391;
+ public static final int REACHSET_APPLY_CHANGESET = 0x1d55;
+ public static final int REACHSET_UNIONTOCHANGESET_REACHSET = 0x46a9;
+ public static final int REACHSET_AGETUPLESFROM_ALLOCSITE = 0x22bb;
+ public static final int REACHSET_PRUNEBY_REACHSET = 0xd774;
+ public static final int CHANGESET_UNION_CHANGESET = 0x53b3;
+ public static final int CHANGESET_UNION_CHANGETUPLE = 0x9ee4;
+ public static final int EXISTPREDSET_JOIN_EXISTPREDSET = 0x8a21;
+ public static final int EXISTPREDSET_ADD_EXISTPRED = 0xba5f;
+ public static final int PRIM_OP_UNUSED = 0xef01;
+ public static final int REACHSET_TOCALLEECONTEXT_ALLOCSITE = 0x56f6;
public static final int REACHSTATE_TOCALLEECONTEXT_ALLOCSITE = 0x7faf;
+ public static final int REACHSET_TOCALLERCONTEXT_ALLOCSITE = 0x2f6a;
+ public static final int REACHSTATE_TOCALLERCONTEXT_ALLOCSITE = 0xb2b1;
+ public static final int REACHSET_UNSHADOW_ALLOCSITE = 0x1049;
+ public static final int REACHSTATE_UNSHADOW_ALLOCSITE = 0x08ef;
protected int opCode;
protected Canonical operand1;
return out;
}
+ // used below to convert a ReachSet to its caller-context
+ // equivalent with respect to allocation sites in this graph
+ protected ReachSet toCallerContext( ReachSet rs ) {
+ ReachSet out = rs;
+ Iterator<AllocSite> asItr = allocSites.iterator();
+ while( asItr.hasNext() ) {
+ AllocSite as = asItr.next();
+ out = Canonical.toCallerContext( out, as );
+ }
+ assert out.isCanonical();
+ return out;
+ }
+
+ // used below to convert a ReachSet to an equivalent
+ // version with shadow IDs merged into unshadowed IDs
+ protected ReachSet unshadow( ReachSet rs ) {
+ ReachSet out = rs;
+ Iterator<AllocSite> asItr = allocSites.iterator();
+ while( asItr.hasNext() ) {
+ AllocSite as = asItr.next();
+ out = Canonical.unshadow( out, as );
+ }
+ assert out.isCanonical();
+ return out;
+ }
+
// use this method to make a new reach graph that is
// what heap the FlatMethod callee from the FlatCall
false, // out-of-context?
hrnCallee.getType(), // type
hrnCallee.getAllocSite(), // allocation site
- hrnCallee.getInherent(), // inherent reach
+ toCallerContext( hrnCallee.getInherent() ), // inherent reach
null, // current reach
predsEmpty, // predicates
hrnCallee.getDescription() // description
}
// TODO: alpha should be some rewritten version of callee in caller context
- hrnCaller.setAlpha( hrnCallee.getAlpha() );
+ hrnCaller.setAlpha( toCallerContext( hrnCallee.getAlpha() ) );
hrnCaller.setPreds( preds );
}
false, // out-of-context?
hrnSrcCallee.getType(), // type
hrnSrcCallee.getAllocSite(), // allocation site
- hrnSrcCallee.getInherent(), // inherent reach
- hrnSrcCallee.getAlpha(), // current reach
+ toCallerContext( hrnSrcCallee.getInherent() ), // inherent reach
+ toCallerContext( hrnSrcCallee.getAlpha() ), // current reach
predsEmpty, // predicates
hrnSrcCallee.getDescription() // description
);
hrnDstCaller,
reCallee.getType(),
reCallee.getField(),
- reCallee.getBeta(),
+ toCallerContext( reCallee.getBeta() ),
preds
);
false, // out-of-context?
hrnDstCallee.getType(), // type
hrnDstCallee.getAllocSite(), // allocation site
- hrnDstCallee.getInherent(), // inherent reach
- hrnDstCallee.getAlpha(), // current reach
+ toCallerContext( hrnDstCallee.getInherent() ), // inherent reach
+ toCallerContext( hrnDstCallee.getAlpha() ), // current reach
predsTrue, // predicates
hrnDstCallee.getDescription() // description
);
hrnDstCaller,
tdNewEdge,
null,
- reCallee.getBeta(),
+ toCallerContext( reCallee.getBeta() ),
predsTrue
);
}
+ if( writeDebugDOTs ) {
+ try {
+ writeGraph( "caller45BeforeUnshadow",
+ true, false, false, false, true, true );
+ } catch( IOException e ) {}
+ }
+
+
+ Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
+ while( itrAllHRNodes.hasNext() ) {
+ Map.Entry me = (Map.Entry) itrAllHRNodes.next();
+ HeapRegionNode hrn = (HeapRegionNode) me.getValue();
+
+ hrn.setAlpha( unshadow( hrn.getAlpha() ) );
+
+ Iterator<RefEdge> itrEdges = hrn.iteratorToReferencers();
+ while( itrEdges.hasNext() ) {
+ RefEdge re = itrEdges.next();
+ re.setBeta( unshadow( re.getBeta() ) );
+ }
+ }
+
+
+
if( writeDebugDOTs ) {
try {
writeGraph( "caller50BeforeGlobalSweep",