1 package Analysis.Disjoint;
5 import Util.UtilAlgorithms;
9 public class ReachGraph {
11 // use to disable improvements for comparison
12 protected static final boolean DISABLE_STRONG_UPDATES = true;
13 protected static final boolean DISABLE_GLOBAL_SWEEP = false;
15 // a special out-of-scope temp
16 protected static final TempDescriptor tdReturn = new TempDescriptor( "_Return___" );
18 // some frequently used reachability constants
19 protected static final ReachState rstateEmpty = ReachState.factory();
20 protected static final ReachSet rsetEmpty = ReachSet.factory();
21 protected static final ReachSet rsetWithEmptyState = Canonical.makePredsTrue(ReachSet.factory( rstateEmpty ));
23 // predicate constants
24 protected static final ExistPred predTrue = ExistPred.factory(); // if no args, true
25 protected static final ExistPredSet predsEmpty = ExistPredSet.factory();
26 protected static final ExistPredSet predsTrue = ExistPredSet.factory( predTrue );
29 // from DisjointAnalysis for convenience
30 protected static int allocationDepth = -1;
31 protected static TypeUtil typeUtil = null;
34 // variable and heap region nodes indexed by unique ID
35 public Hashtable<Integer, HeapRegionNode> id2hrn;
36 public Hashtable<TempDescriptor, VariableNode > td2vn;
38 // convenient set of alloc sites for all heap regions
39 // present in the graph without having to search
40 public HashSet<AllocSite> allocSites;
43 id2hrn = new Hashtable<Integer, HeapRegionNode>();
44 td2vn = new Hashtable<TempDescriptor, VariableNode >();
45 allocSites = new HashSet<AllocSite>();
49 // temp descriptors are globally unique and map to
50 // exactly one variable node, easy
51 protected VariableNode getVariableNodeFromTemp( TempDescriptor td ) {
54 if( !td2vn.containsKey( td ) ) {
55 td2vn.put( td, new VariableNode( td ) );
58 return td2vn.get( td );
61 public boolean hasVariable( TempDescriptor td ) {
62 return td2vn.containsKey( td );
66 // this suite of methods can be used to assert a
67 // very important property of ReachGraph objects:
68 // some element, HeapRegionNode, RefEdge etc.
69 // should be referenced by at most ONE ReachGraph!!
70 // If a heap region or edge or variable should be
71 // in another graph, make a new object with
72 // equivalent properties for a new graph
73 public boolean belongsToThis( RefSrcNode rsn ) {
74 if( rsn instanceof VariableNode ) {
75 VariableNode vn = (VariableNode) rsn;
76 return this.td2vn.get( vn.getTempDescriptor() ) == vn;
78 HeapRegionNode hrn = (HeapRegionNode) rsn;
79 return this.id2hrn.get( hrn.getID() ) == hrn;
86 // the reason for this method is to have the option
87 // of creating new heap regions with specific IDs, or
88 // duplicating heap regions with specific IDs (especially
89 // in the merge() operation) or to create new heap
90 // regions with a new unique ID
91 protected HeapRegionNode
92 createNewHeapRegionNode( Integer id,
93 boolean isSingleObject,
96 boolean isOutOfContext,
105 boolean markForAnalysis = isFlagged;
107 TypeDescriptor typeToUse = null;
108 if( allocSite != null ) {
109 typeToUse = allocSite.getType();
110 allocSites.add( allocSite );
115 if( allocSite != null && allocSite.getDisjointAnalysisId() != null ) {
116 markForAnalysis = true;
120 id = DisjointAnalysis.generateUniqueHeapRegionNodeID();
123 if( inherent == null ) {
124 if( markForAnalysis ) {
126 Canonical.makePredsTrue(
129 ReachTuple.factory( id,
131 ReachTuple.ARITY_ONE,
132 false // out-of-context
138 inherent = rsetWithEmptyState;
142 if( alpha == null ) {
146 assert preds != null;
148 HeapRegionNode hrn = new HeapRegionNode( id,
159 id2hrn.put( id, hrn );
165 ////////////////////////////////////////////////
167 // Low-level referencee and referencer methods
169 // These methods provide the lowest level for
170 // creating references between reachability nodes
171 // and handling the details of maintaining both
172 // list of referencers and referencees.
174 ////////////////////////////////////////////////
175 protected void addRefEdge( RefSrcNode referencer,
176 HeapRegionNode referencee,
178 assert referencer != null;
179 assert referencee != null;
181 assert edge.getSrc() == referencer;
182 assert edge.getDst() == referencee;
183 assert belongsToThis( referencer );
184 assert belongsToThis( referencee );
186 // edges are getting added twice to graphs now, the
187 // kind that should have abstract facts merged--use
188 // this check to prevent that
189 assert referencer.getReferenceTo( referencee,
194 referencer.addReferencee( edge );
195 referencee.addReferencer( edge );
198 protected void removeRefEdge( RefEdge e ) {
199 removeRefEdge( e.getSrc(),
205 protected void removeRefEdge( RefSrcNode referencer,
206 HeapRegionNode referencee,
209 assert referencer != null;
210 assert referencee != null;
212 RefEdge edge = referencer.getReferenceTo( referencee,
216 assert edge == referencee.getReferenceFrom( referencer,
220 referencer.removeReferencee( edge );
221 referencee.removeReferencer( edge );
224 protected void clearRefEdgesFrom( RefSrcNode referencer,
227 boolean removeAll ) {
228 assert referencer != null;
230 // get a copy of the set to iterate over, otherwise
231 // we will be trying to take apart the set as we
232 // are iterating over it, which won't work
233 Iterator<RefEdge> i = referencer.iteratorToReferenceesClone();
234 while( i.hasNext() ) {
235 RefEdge edge = i.next();
238 (edge.typeEquals( type ) && edge.fieldEquals( field ))
241 HeapRegionNode referencee = edge.getDst();
243 removeRefEdge( referencer,
251 protected void clearRefEdgesTo( HeapRegionNode referencee,
254 boolean removeAll ) {
255 assert referencee != null;
257 // get a copy of the set to iterate over, otherwise
258 // we will be trying to take apart the set as we
259 // are iterating over it, which won't work
260 Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
261 while( i.hasNext() ) {
262 RefEdge edge = i.next();
265 (edge.typeEquals( type ) && edge.fieldEquals( field ))
268 RefSrcNode referencer = edge.getSrc();
270 removeRefEdge( referencer,
278 protected void clearNonVarRefEdgesTo( HeapRegionNode referencee ) {
279 assert referencee != null;
281 // get a copy of the set to iterate over, otherwise
282 // we will be trying to take apart the set as we
283 // are iterating over it, which won't work
284 Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
285 while( i.hasNext() ) {
286 RefEdge edge = i.next();
287 RefSrcNode referencer = edge.getSrc();
288 if( !(referencer instanceof VariableNode) ) {
289 removeRefEdge( referencer,
297 // this is a common operation in many transfer functions: we want
298 // to add an edge, but if there is already such an edge we should
299 // merge the properties of the existing and the new edges
300 protected void addEdgeOrMergeWithExisting( RefEdge edgeNew ) {
302 RefSrcNode src = edgeNew.getSrc();
303 assert belongsToThis( src );
305 HeapRegionNode dst = edgeNew.getDst();
306 assert belongsToThis( dst );
308 // look to see if an edge with same field exists
309 // and merge with it, otherwise just add the edge
310 RefEdge edgeExisting = src.getReferenceTo( dst,
315 if( edgeExisting != null ) {
316 edgeExisting.setBeta(
317 Canonical.unionORpreds( edgeExisting.getBeta(),
321 edgeExisting.setPreds(
322 Canonical.join( edgeExisting.getPreds(),
328 addRefEdge( src, dst, edgeNew );
334 ////////////////////////////////////////////////////
336 // Assignment Operation Methods
338 // These methods are high-level operations for
339 // modeling program assignment statements using
340 // the low-level reference create/remove methods
343 ////////////////////////////////////////////////////
345 public void assignTempXEqualToTempY( TempDescriptor x,
347 assignTempXEqualToCastedTempY( x, y, null );
350 public void assignTempXEqualToCastedTempY( TempDescriptor x,
352 TypeDescriptor tdCast ) {
354 VariableNode lnX = getVariableNodeFromTemp( x );
355 VariableNode lnY = getVariableNodeFromTemp( y );
357 clearRefEdgesFrom( lnX, null, null, true );
359 // note it is possible that the types of temps in the
360 // flat node to analyze will reveal that some typed
361 // edges in the reachability graph are impossible
362 Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
364 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
365 while( itrYhrn.hasNext() ) {
366 RefEdge edgeY = itrYhrn.next();
367 HeapRegionNode referencee = edgeY.getDst();
368 RefEdge edgeNew = edgeY.copy();
370 if( !isSuperiorType( x.getType(), edgeY.getType() ) ) {
371 impossibleEdges.add( edgeY );
375 edgeNew.setSrc( lnX );
377 if( tdCast == null ) {
378 edgeNew.setType( mostSpecificType( y.getType(),
384 edgeNew.setType( mostSpecificType( y.getType(),
386 referencee.getType(),
392 edgeNew.setField( null );
394 addRefEdge( lnX, referencee, edgeNew );
397 Iterator<RefEdge> itrImp = impossibleEdges.iterator();
398 while( itrImp.hasNext() ) {
399 RefEdge edgeImp = itrImp.next();
400 removeRefEdge( edgeImp );
405 public void assignTempXEqualToTempYFieldF( TempDescriptor x,
407 FieldDescriptor f ) {
408 VariableNode lnX = getVariableNodeFromTemp( x );
409 VariableNode lnY = getVariableNodeFromTemp( y );
411 clearRefEdgesFrom( lnX, null, null, true );
413 // note it is possible that the types of temps in the
414 // flat node to analyze will reveal that some typed
415 // edges in the reachability graph are impossible
416 Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
418 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
419 while( itrYhrn.hasNext() ) {
420 RefEdge edgeY = itrYhrn.next();
421 HeapRegionNode hrnY = edgeY.getDst();
422 ReachSet betaY = edgeY.getBeta();
424 Iterator<RefEdge> itrHrnFhrn = hrnY.iteratorToReferencees();
425 while( itrHrnFhrn.hasNext() ) {
426 RefEdge edgeHrn = itrHrnFhrn.next();
427 HeapRegionNode hrnHrn = edgeHrn.getDst();
428 ReachSet betaHrn = edgeHrn.getBeta();
430 // prune edges that are not a matching field
431 if( edgeHrn.getType() != null &&
432 !edgeHrn.getField().equals( f.getSymbol() )
437 // check for impossible edges
438 if( !isSuperiorType( x.getType(), edgeHrn.getType() ) ) {
439 impossibleEdges.add( edgeHrn );
443 TypeDescriptor tdNewEdge =
444 mostSpecificType( edgeHrn.getType(),
448 RefEdge edgeNew = new RefEdge( lnX,
452 Canonical.intersection( betaY, betaHrn ),
456 addEdgeOrMergeWithExisting( edgeNew );
460 Iterator<RefEdge> itrImp = impossibleEdges.iterator();
461 while( itrImp.hasNext() ) {
462 RefEdge edgeImp = itrImp.next();
463 removeRefEdge( edgeImp );
466 // anytime you might remove edges between heap regions
467 // you must global sweep to clean up broken reachability
468 if( !impossibleEdges.isEmpty() ) {
469 if( !DISABLE_GLOBAL_SWEEP ) {
476 public void assignTempXFieldFEqualToTempY( TempDescriptor x,
480 VariableNode lnX = getVariableNodeFromTemp( x );
481 VariableNode lnY = getVariableNodeFromTemp( y );
483 HashSet<HeapRegionNode> nodesWithNewAlpha = new HashSet<HeapRegionNode>();
484 HashSet<RefEdge> edgesWithNewBeta = new HashSet<RefEdge>();
486 // note it is possible that the types of temps in the
487 // flat node to analyze will reveal that some typed
488 // edges in the reachability graph are impossible
489 Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
491 // first look for possible strong updates and remove those edges
492 boolean strongUpdate = false;
494 Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
495 while( itrXhrn.hasNext() ) {
496 RefEdge edgeX = itrXhrn.next();
497 HeapRegionNode hrnX = edgeX.getDst();
499 // we can do a strong update here if one of two cases holds
501 f != DisjointAnalysis.getArrayField( f.getType() ) &&
502 ( (hrnX.getNumReferencers() == 1) || // case 1
503 (hrnX.isSingleObject() && lnX.getNumReferencees() == 1) // case 2
506 if( !DISABLE_STRONG_UPDATES ) {
508 clearRefEdgesFrom( hrnX, f.getType(), f.getSymbol(), false );
513 // then do all token propagation
514 itrXhrn = lnX.iteratorToReferencees();
515 while( itrXhrn.hasNext() ) {
516 RefEdge edgeX = itrXhrn.next();
517 HeapRegionNode hrnX = edgeX.getDst();
518 ReachSet betaX = edgeX.getBeta();
519 ReachSet R = Canonical.intersection( hrnX.getAlpha(),
523 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
524 while( itrYhrn.hasNext() ) {
525 RefEdge edgeY = itrYhrn.next();
526 HeapRegionNode hrnY = edgeY.getDst();
527 ReachSet O = edgeY.getBeta();
529 // check for impossible edges
530 if( !isSuperiorType( f.getType(), edgeY.getType() ) ) {
531 impossibleEdges.add( edgeY );
535 // propagate tokens over nodes starting from hrnSrc, and it will
536 // take care of propagating back up edges from any touched nodes
537 ChangeSet Cy = Canonical.unionUpArityToChangeSet( O, R );
538 propagateTokensOverNodes( hrnY, Cy, nodesWithNewAlpha, edgesWithNewBeta );
540 // then propagate back just up the edges from hrn
541 ChangeSet Cx = Canonical.unionUpArityToChangeSet( R, O );
542 HashSet<RefEdge> todoEdges = new HashSet<RefEdge>();
544 Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
545 new Hashtable<RefEdge, ChangeSet>();
547 Iterator<RefEdge> referItr = hrnX.iteratorToReferencers();
548 while( referItr.hasNext() ) {
549 RefEdge edgeUpstream = referItr.next();
550 todoEdges.add( edgeUpstream );
551 edgePlannedChanges.put( edgeUpstream, Cx );
554 propagateTokensOverEdges( todoEdges,
561 // apply the updates to reachability
562 Iterator<HeapRegionNode> nodeItr = nodesWithNewAlpha.iterator();
563 while( nodeItr.hasNext() ) {
564 nodeItr.next().applyAlphaNew();
567 Iterator<RefEdge> edgeItr = edgesWithNewBeta.iterator();
568 while( edgeItr.hasNext() ) {
569 edgeItr.next().applyBetaNew();
573 // then go back through and add the new edges
574 itrXhrn = lnX.iteratorToReferencees();
575 while( itrXhrn.hasNext() ) {
576 RefEdge edgeX = itrXhrn.next();
577 HeapRegionNode hrnX = edgeX.getDst();
579 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
580 while( itrYhrn.hasNext() ) {
581 RefEdge edgeY = itrYhrn.next();
582 HeapRegionNode hrnY = edgeY.getDst();
584 // skip impossible edges here, we already marked them
585 // when computing reachability propagations above
586 if( !isSuperiorType( f.getType(), edgeY.getType() ) ) {
590 // prepare the new reference edge hrnX.f -> hrnY
591 TypeDescriptor tdNewEdge =
592 mostSpecificType( y.getType(),
602 Canonical.makePredsTrue(
603 Canonical.pruneBy( edgeY.getBeta(),
610 addEdgeOrMergeWithExisting( edgeNew );
614 Iterator<RefEdge> itrImp = impossibleEdges.iterator();
615 while( itrImp.hasNext() ) {
616 RefEdge edgeImp = itrImp.next();
617 removeRefEdge( edgeImp );
620 // if there was a strong update, make sure to improve
621 // reachability with a global sweep
622 if( strongUpdate || !impossibleEdges.isEmpty() ) {
623 if( !DISABLE_GLOBAL_SWEEP ) {
630 public void assignReturnEqualToTemp( TempDescriptor x ) {
632 VariableNode lnR = getVariableNodeFromTemp( tdReturn );
633 VariableNode lnX = getVariableNodeFromTemp( x );
635 clearRefEdgesFrom( lnR, null, null, true );
637 Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
638 while( itrXhrn.hasNext() ) {
639 RefEdge edgeX = itrXhrn.next();
640 HeapRegionNode referencee = edgeX.getDst();
641 RefEdge edgeNew = edgeX.copy();
642 edgeNew.setSrc( lnR );
644 addRefEdge( lnR, referencee, edgeNew );
649 public void assignTempEqualToNewAlloc( TempDescriptor x,
656 // after the age operation the newest (or zero-ith oldest)
657 // node associated with the allocation site should have
658 // no references to it as if it were a newly allocated
660 Integer idNewest = as.getIthOldest( 0 );
661 HeapRegionNode hrnNewest = id2hrn.get( idNewest );
662 assert hrnNewest != null;
664 VariableNode lnX = getVariableNodeFromTemp( x );
665 clearRefEdgesFrom( lnX, null, null, true );
667 // make a new reference to allocated node
668 TypeDescriptor type = as.getType();
671 new RefEdge( lnX, // source
675 hrnNewest.getAlpha(), // beta
676 predsTrue // predicates
679 addRefEdge( lnX, hrnNewest, edgeNew );
683 // use the allocation site (unique to entire analysis) to
684 // locate the heap region nodes in this reachability graph
685 // that should be aged. The process models the allocation
686 // of new objects and collects all the oldest allocations
687 // in a summary node to allow for a finite analysis
689 // There is an additional property of this method. After
690 // running it on a particular reachability graph (many graphs
691 // may have heap regions related to the same allocation site)
692 // the heap region node objects in this reachability graph will be
693 // allocated. Therefore, after aging a graph for an allocation
694 // site, attempts to retrieve the heap region nodes using the
695 // integer id's contained in the allocation site should always
696 // return non-null heap regions.
697 public void age( AllocSite as ) {
699 // keep track of allocation sites that are represented
700 // in this graph for efficiency with other operations
701 allocSites.add( as );
703 // if there is a k-th oldest node, it merges into
705 Integer idK = as.getOldest();
706 if( id2hrn.containsKey( idK ) ) {
707 HeapRegionNode hrnK = id2hrn.get( idK );
709 // retrieve the summary node, or make it
711 HeapRegionNode hrnSummary = getSummaryNode( as, false );
713 mergeIntoSummary( hrnK, hrnSummary );
716 // move down the line of heap region nodes
717 // clobbering the ith and transferring all references
718 // to and from i-1 to node i.
719 for( int i = allocationDepth - 1; i > 0; --i ) {
721 // only do the transfer if the i-1 node exists
722 Integer idImin1th = as.getIthOldest( i - 1 );
723 if( id2hrn.containsKey( idImin1th ) ) {
724 HeapRegionNode hrnImin1 = id2hrn.get( idImin1th );
725 if( hrnImin1.isWiped() ) {
726 // there is no info on this node, just skip
730 // either retrieve or make target of transfer
731 HeapRegionNode hrnI = getIthNode( as, i, false );
733 transferOnto( hrnImin1, hrnI );
738 // as stated above, the newest node should have had its
739 // references moved over to the second oldest, so we wipe newest
740 // in preparation for being the new object to assign something to
741 HeapRegionNode hrn0 = getIthNode( as, 0, false );
742 wipeOut( hrn0, true );
744 // now tokens in reachability sets need to "age" also
745 Iterator itrAllVariableNodes = td2vn.entrySet().iterator();
746 while( itrAllVariableNodes.hasNext() ) {
747 Map.Entry me = (Map.Entry) itrAllVariableNodes.next();
748 VariableNode ln = (VariableNode) me.getValue();
750 Iterator<RefEdge> itrEdges = ln.iteratorToReferencees();
751 while( itrEdges.hasNext() ) {
752 ageTuplesFrom( as, itrEdges.next() );
756 Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
757 while( itrAllHRNodes.hasNext() ) {
758 Map.Entry me = (Map.Entry) itrAllHRNodes.next();
759 HeapRegionNode hrnToAge = (HeapRegionNode) me.getValue();
761 ageTuplesFrom( as, hrnToAge );
763 Iterator<RefEdge> itrEdges = hrnToAge.iteratorToReferencees();
764 while( itrEdges.hasNext() ) {
765 ageTuplesFrom( as, itrEdges.next() );
770 // after tokens have been aged, reset newest node's reachability
771 // and a brand new node has a "true" predicate
772 hrn0.setAlpha( hrn0.getInherent() );
773 hrn0.setPreds( predsTrue );
777 // either retrieve or create the needed heap region node
778 protected HeapRegionNode getSummaryNode( AllocSite as,
783 idSummary = as.getSummaryShadow();
785 idSummary = as.getSummary();
788 HeapRegionNode hrnSummary = id2hrn.get( idSummary );
790 if( hrnSummary == null ) {
792 boolean hasFlags = false;
793 if( as.getType().isClass() ) {
794 hasFlags = as.getType().getClassDesc().hasFlags();
798 hasFlags = as.getFlag();
801 String strDesc = as.toStringForDOT()+"\\nsummary";
804 createNewHeapRegionNode( idSummary, // id or null to generate a new one
805 false, // single object?
807 hasFlags, // flagged?
808 false, // out-of-context?
809 as.getType(), // type
810 as, // allocation site
811 null, // inherent reach
812 null, // current reach
813 predsEmpty, // predicates
814 strDesc // description
821 // either retrieve or create the needed heap region node
822 protected HeapRegionNode getIthNode( AllocSite as,
828 idIth = as.getIthOldestShadow( i );
830 idIth = as.getIthOldest( i );
833 HeapRegionNode hrnIth = id2hrn.get( idIth );
835 if( hrnIth == null ) {
837 boolean hasFlags = false;
838 if( as.getType().isClass() ) {
839 hasFlags = as.getType().getClassDesc().hasFlags();
843 hasFlags = as.getFlag();
846 String strDesc = as.toStringForDOT()+"\\n"+i+" oldest";
848 hrnIth = createNewHeapRegionNode( idIth, // id or null to generate a new one
849 true, // single object?
851 hasFlags, // flagged?
852 false, // out-of-context?
853 as.getType(), // type
854 as, // allocation site
855 null, // inherent reach
856 null, // current reach
857 predsEmpty, // predicates
858 strDesc // description
866 protected void mergeIntoSummary( HeapRegionNode hrn,
867 HeapRegionNode hrnSummary ) {
868 assert hrnSummary.isNewSummary();
870 // assert that these nodes belong to THIS graph
871 assert belongsToThis( hrn );
872 assert belongsToThis( hrnSummary );
874 assert hrn != hrnSummary;
876 // transfer references _from_ hrn over to hrnSummary
877 Iterator<RefEdge> itrReferencee = hrn.iteratorToReferencees();
878 while( itrReferencee.hasNext() ) {
879 RefEdge edge = itrReferencee.next();
880 RefEdge edgeMerged = edge.copy();
881 edgeMerged.setSrc( hrnSummary );
883 HeapRegionNode hrnReferencee = edge.getDst();
884 RefEdge edgeSummary =
885 hrnSummary.getReferenceTo( hrnReferencee,
890 if( edgeSummary == null ) {
891 // the merge is trivial, nothing to be done
892 addRefEdge( hrnSummary, hrnReferencee, edgeMerged );
895 // otherwise an edge from the referencer to hrnSummary exists already
896 // and the edge referencer->hrn should be merged with it
898 Canonical.unionORpreds( edgeMerged.getBeta(),
899 edgeSummary.getBeta()
902 edgeSummary.setPreds(
903 Canonical.join( edgeMerged.getPreds(),
904 edgeSummary.getPreds()
910 // next transfer references _to_ hrn over to hrnSummary
911 Iterator<RefEdge> itrReferencer = hrn.iteratorToReferencers();
912 while( itrReferencer.hasNext() ) {
913 RefEdge edge = itrReferencer.next();
914 RefEdge edgeMerged = edge.copy();
915 edgeMerged.setDst( hrnSummary );
917 RefSrcNode onReferencer = edge.getSrc();
918 RefEdge edgeSummary =
919 onReferencer.getReferenceTo( hrnSummary,
924 if( edgeSummary == null ) {
925 // the merge is trivial, nothing to be done
926 addRefEdge( onReferencer, hrnSummary, edgeMerged );
929 // otherwise an edge from the referencer to alpha_S exists already
930 // and the edge referencer->alpha_K should be merged with it
932 Canonical.unionORpreds( edgeMerged.getBeta(),
933 edgeSummary.getBeta()
936 edgeSummary.setPreds(
937 Canonical.join( edgeMerged.getPreds(),
938 edgeSummary.getPreds()
944 // then merge hrn reachability into hrnSummary
946 Canonical.unionORpreds( hrnSummary.getAlpha(),
952 Canonical.join( hrnSummary.getPreds(),
957 // and afterward, this node is gone
958 wipeOut( hrn, true );
962 protected void transferOnto( HeapRegionNode hrnA,
963 HeapRegionNode hrnB ) {
965 assert belongsToThis( hrnA );
966 assert belongsToThis( hrnB );
969 // clear references in and out of node b?
970 assert hrnB.isWiped();
972 // copy each: (edge in and out of A) to B
973 Iterator<RefEdge> itrReferencee = hrnA.iteratorToReferencees();
974 while( itrReferencee.hasNext() ) {
975 RefEdge edge = itrReferencee.next();
976 HeapRegionNode hrnReferencee = edge.getDst();
977 RefEdge edgeNew = edge.copy();
978 edgeNew.setSrc( hrnB );
979 edgeNew.setDst( hrnReferencee );
981 addRefEdge( hrnB, hrnReferencee, edgeNew );
984 Iterator<RefEdge> itrReferencer = hrnA.iteratorToReferencers();
985 while( itrReferencer.hasNext() ) {
986 RefEdge edge = itrReferencer.next();
987 RefSrcNode rsnReferencer = edge.getSrc();
988 RefEdge edgeNew = edge.copy();
989 edgeNew.setSrc( rsnReferencer );
990 edgeNew.setDst( hrnB );
992 addRefEdge( rsnReferencer, hrnB, edgeNew );
995 // replace hrnB reachability and preds with hrnA's
996 hrnB.setAlpha( hrnA.getAlpha() );
997 hrnB.setPreds( hrnA.getPreds() );
999 // after transfer, wipe out source
1000 wipeOut( hrnA, true );
1004 // the purpose of this method is to conceptually "wipe out"
1005 // a heap region from the graph--purposefully not called REMOVE
1006 // because the node is still hanging around in the graph, just
1007 // not mechanically connected or have any reach or predicate
1008 // information on it anymore--lots of ops can use this
1009 protected void wipeOut( HeapRegionNode hrn,
1010 boolean wipeVariableReferences ) {
1012 assert belongsToThis( hrn );
1014 clearRefEdgesFrom( hrn, null, null, true );
1016 if( wipeVariableReferences ) {
1017 clearRefEdgesTo( hrn, null, null, true );
1019 clearNonVarRefEdgesTo( hrn );
1022 hrn.setAlpha( rsetEmpty );
1023 hrn.setPreds( predsEmpty );
1027 protected void ageTuplesFrom( AllocSite as, RefEdge edge ) {
1029 Canonical.ageTuplesFrom( edge.getBeta(),
1035 protected void ageTuplesFrom( AllocSite as, HeapRegionNode hrn ) {
1037 Canonical.ageTuplesFrom( hrn.getAlpha(),
1045 protected void propagateTokensOverNodes( HeapRegionNode nPrime,
1047 HashSet<HeapRegionNode> nodesWithNewAlpha,
1048 HashSet<RefEdge> edgesWithNewBeta ) {
1050 HashSet<HeapRegionNode> todoNodes
1051 = new HashSet<HeapRegionNode>();
1052 todoNodes.add( nPrime );
1054 HashSet<RefEdge> todoEdges
1055 = new HashSet<RefEdge>();
1057 Hashtable<HeapRegionNode, ChangeSet> nodePlannedChanges
1058 = new Hashtable<HeapRegionNode, ChangeSet>();
1059 nodePlannedChanges.put( nPrime, c0 );
1061 Hashtable<RefEdge, ChangeSet> edgePlannedChanges
1062 = new Hashtable<RefEdge, ChangeSet>();
1064 // first propagate change sets everywhere they can go
1065 while( !todoNodes.isEmpty() ) {
1066 HeapRegionNode n = todoNodes.iterator().next();
1067 ChangeSet C = nodePlannedChanges.get( n );
1069 Iterator<RefEdge> referItr = n.iteratorToReferencers();
1070 while( referItr.hasNext() ) {
1071 RefEdge edge = referItr.next();
1072 todoEdges.add( edge );
1074 if( !edgePlannedChanges.containsKey( edge ) ) {
1075 edgePlannedChanges.put( edge,
1080 edgePlannedChanges.put( edge,
1081 Canonical.union( edgePlannedChanges.get( edge ),
1087 Iterator<RefEdge> refeeItr = n.iteratorToReferencees();
1088 while( refeeItr.hasNext() ) {
1089 RefEdge edgeF = refeeItr.next();
1090 HeapRegionNode m = edgeF.getDst();
1092 ChangeSet changesToPass = ChangeSet.factory();
1094 Iterator<ChangeTuple> itrCprime = C.iterator();
1095 while( itrCprime.hasNext() ) {
1096 ChangeTuple c = itrCprime.next();
1097 if( edgeF.getBeta().containsIgnorePreds( c.getStateToMatch() )
1100 changesToPass = Canonical.add( changesToPass, c );
1104 if( !changesToPass.isEmpty() ) {
1105 if( !nodePlannedChanges.containsKey( m ) ) {
1106 nodePlannedChanges.put( m, ChangeSet.factory() );
1109 ChangeSet currentChanges = nodePlannedChanges.get( m );
1111 if( !changesToPass.isSubset( currentChanges ) ) {
1113 nodePlannedChanges.put( m,
1114 Canonical.union( currentChanges,
1123 todoNodes.remove( n );
1126 // then apply all of the changes for each node at once
1127 Iterator itrMap = nodePlannedChanges.entrySet().iterator();
1128 while( itrMap.hasNext() ) {
1129 Map.Entry me = (Map.Entry) itrMap.next();
1130 HeapRegionNode n = (HeapRegionNode) me.getKey();
1131 ChangeSet C = (ChangeSet) me.getValue();
1133 // this propagation step is with respect to one change,
1134 // so we capture the full change from the old alpha:
1135 ReachSet localDelta = Canonical.applyChangeSet( n.getAlpha(),
1139 // but this propagation may be only one of many concurrent
1140 // possible changes, so keep a running union with the node's
1141 // partially updated new alpha set
1142 n.setAlphaNew( Canonical.unionORpreds( n.getAlphaNew(),
1147 nodesWithNewAlpha.add( n );
1150 propagateTokensOverEdges( todoEdges,
1157 protected void propagateTokensOverEdges( HashSet <RefEdge> todoEdges,
1158 Hashtable<RefEdge, ChangeSet> edgePlannedChanges,
1159 HashSet <RefEdge> edgesWithNewBeta ) {
1161 // first propagate all change tuples everywhere they can go
1162 while( !todoEdges.isEmpty() ) {
1163 RefEdge edgeE = todoEdges.iterator().next();
1164 todoEdges.remove( edgeE );
1166 if( !edgePlannedChanges.containsKey( edgeE ) ) {
1167 edgePlannedChanges.put( edgeE,
1172 ChangeSet C = edgePlannedChanges.get( edgeE );
1174 ChangeSet changesToPass = ChangeSet.factory();
1176 Iterator<ChangeTuple> itrC = C.iterator();
1177 while( itrC.hasNext() ) {
1178 ChangeTuple c = itrC.next();
1179 if( edgeE.getBeta().containsIgnorePreds( c.getStateToMatch() )
1182 changesToPass = Canonical.add( changesToPass, c );
1186 RefSrcNode rsn = edgeE.getSrc();
1188 if( !changesToPass.isEmpty() && rsn instanceof HeapRegionNode ) {
1189 HeapRegionNode n = (HeapRegionNode) rsn;
1191 Iterator<RefEdge> referItr = n.iteratorToReferencers();
1192 while( referItr.hasNext() ) {
1193 RefEdge edgeF = referItr.next();
1195 if( !edgePlannedChanges.containsKey( edgeF ) ) {
1196 edgePlannedChanges.put( edgeF,
1201 ChangeSet currentChanges = edgePlannedChanges.get( edgeF );
1203 if( !changesToPass.isSubset( currentChanges ) ) {
1204 todoEdges.add( edgeF );
1205 edgePlannedChanges.put( edgeF,
1206 Canonical.union( currentChanges,
1215 // then apply all of the changes for each edge at once
1216 Iterator itrMap = edgePlannedChanges.entrySet().iterator();
1217 while( itrMap.hasNext() ) {
1218 Map.Entry me = (Map.Entry) itrMap.next();
1219 RefEdge e = (RefEdge) me.getKey();
1220 ChangeSet C = (ChangeSet) me.getValue();
1222 // this propagation step is with respect to one change,
1223 // so we capture the full change from the old beta:
1224 ReachSet localDelta =
1225 Canonical.applyChangeSet( e.getBeta(),
1230 // but this propagation may be only one of many concurrent
1231 // possible changes, so keep a running union with the edge's
1232 // partially updated new beta set
1233 e.setBetaNew( Canonical.unionORpreds( e.getBetaNew(),
1238 edgesWithNewBeta.add( e );
1243 // used in makeCalleeView below to decide if there is
1244 // already an appropriate out-of-context edge in a callee
1245 // view graph for merging, or null if a new one will be added
1247 getOutOfContextReferenceTo( HeapRegionNode hrn,
1248 TypeDescriptor srcType,
1249 TypeDescriptor refType,
1251 assert belongsToThis( hrn );
1253 HeapRegionNode hrnInContext = id2hrn.get( hrn.getID() );
1254 if( hrnInContext == null ) {
1258 Iterator<RefEdge> refItr = hrnInContext.iteratorToReferencers();
1259 while( refItr.hasNext() ) {
1260 RefEdge re = refItr.next();
1262 assert belongsToThis( re.getSrc() );
1263 assert belongsToThis( re.getDst() );
1265 if( !(re.getSrc() instanceof HeapRegionNode) ) {
1269 HeapRegionNode hrnSrc = (HeapRegionNode) re.getSrc();
1270 if( !hrnSrc.isOutOfContext() ) {
1274 if( srcType == null ) {
1275 if( hrnSrc.getType() != null ) {
1279 if( !srcType.equals( hrnSrc.getType() ) ) {
1284 if( !re.typeEquals( refType ) ) {
1288 if( !re.fieldEquals( refField ) ) {
1292 // tada! We found it!
1299 // used below to convert a ReachSet to its callee-context
1300 // equivalent with respect to allocation sites in this graph
1301 protected ReachSet toCalleeContext( ReachSet rs,
1303 Set<HrnIdOoc> oocHrnIdOoc2callee
1305 ReachSet out = ReachSet.factory();
1307 Iterator<ReachState> itr = rs.iterator();
1308 while( itr.hasNext() ) {
1309 ReachState stateCaller = itr.next();
1311 ReachState stateCallee = stateCaller;
1313 Iterator<AllocSite> asItr = allocSites.iterator();
1314 while( asItr.hasNext() ) {
1315 AllocSite as = asItr.next();
1317 ReachState stateNew = ReachState.factory();
1318 Iterator<ReachTuple> rtItr = stateCallee.iterator();
1319 while( rtItr.hasNext() ) {
1320 ReachTuple rt = rtItr.next();
1322 // only translate this tuple if it is
1323 // in the out-callee-context bag
1324 HrnIdOoc hio = new HrnIdOoc( rt.getHrnID(),
1327 if( !oocHrnIdOoc2callee.contains( hio ) ) {
1328 stateNew = Canonical.add( stateNew, rt );
1332 int age = as.getAgeCategory( rt.getHrnID() );
1334 // this is the current mapping, where 0, 1, 2S were allocated
1335 // in the current context, 0?, 1? and 2S? were allocated in a
1336 // previous context, and we're translating to a future context
1348 if( age == AllocSite.AGE_notInThisSite ) {
1349 // things not from the site just go back in
1350 stateNew = Canonical.add( stateNew, rt );
1352 } else if( age == AllocSite.AGE_summary ||
1355 // the in-context summary and all existing out-of-context
1357 stateNew = Canonical.add( stateNew,
1358 ReachTuple.factory( as.getSummary(),
1361 true // out-of-context
1365 // otherwise everything else just goes to an out-of-context
1366 // version, everything else the same
1367 Integer I = as.getAge( rt.getHrnID() );
1370 assert !rt.isMultiObject();
1372 stateNew = Canonical.add( stateNew,
1373 ReachTuple.factory( rt.getHrnID(),
1376 true // out-of-context
1382 stateCallee = stateNew;
1385 // attach the passed in preds
1386 stateCallee = Canonical.attach( stateCallee,
1389 out = Canonical.add( out,
1394 assert out.isCanonical();
1398 // used below to convert a ReachSet to its caller-context
1399 // equivalent with respect to allocation sites in this graph
1401 toCallerContext( ReachSet rs,
1402 Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied
1404 ReachSet out = ReachSet.factory();
1406 Iterator<ReachState> itr = rs.iterator();
1407 while( itr.hasNext() ) {
1408 ReachState stateCallee = itr.next();
1410 if( calleeStatesSatisfied.containsKey( stateCallee ) ) {
1412 // starting from one callee state...
1413 ReachSet rsCaller = ReachSet.factory( stateCallee );
1415 // possibly branch it into many states, which any
1416 // allocation site might do, so lots of derived states
1417 Iterator<AllocSite> asItr = allocSites.iterator();
1418 while( asItr.hasNext() ) {
1419 AllocSite as = asItr.next();
1420 rsCaller = Canonical.toCallerContext( rsCaller, as );
1423 // then before adding each derived, now caller-context
1424 // states to the output, attach the appropriate pred
1425 // based on the source callee state
1426 Iterator<ReachState> stateItr = rsCaller.iterator();
1427 while( stateItr.hasNext() ) {
1428 ReachState stateCaller = stateItr.next();
1429 stateCaller = Canonical.attach( stateCaller,
1430 calleeStatesSatisfied.get( stateCallee )
1432 out = Canonical.add( out,
1439 assert out.isCanonical();
1443 // used below to convert a ReachSet to an equivalent
1444 // version with shadow IDs merged into unshadowed IDs
1445 protected ReachSet unshadow( ReachSet rs ) {
1447 Iterator<AllocSite> asItr = allocSites.iterator();
1448 while( asItr.hasNext() ) {
1449 AllocSite as = asItr.next();
1450 out = Canonical.unshadow( out, as );
1452 assert out.isCanonical();
1457 // use this method to make a new reach graph that is
1458 // what heap the FlatMethod callee from the FlatCall
1459 // would start with reaching from its arguments in
1462 makeCalleeView( FlatCall fc,
1463 FlatMethod fmCallee,
1464 Set<Integer> callerNodeIDsCopiedToCallee,
1465 boolean writeDebugDOTs
1469 // first traverse this context to find nodes and edges
1470 // that will be callee-reachable
1471 Set<HeapRegionNode> reachableCallerNodes =
1472 new HashSet<HeapRegionNode>();
1474 // caller edges between callee-reachable nodes
1475 Set<RefEdge> reachableCallerEdges =
1476 new HashSet<RefEdge>();
1478 // caller edges from arg vars, and the matching param index
1479 // because these become a special edge in callee
1480 Hashtable<RefEdge, Integer> reachableCallerArgEdges2paramIndex =
1481 new Hashtable<RefEdge, Integer>();
1483 // caller edges from local vars or callee-unreachable nodes
1484 // (out-of-context sources) to callee-reachable nodes
1485 Set<RefEdge> oocCallerEdges =
1486 new HashSet<RefEdge>();
1489 for( int i = 0; i < fmCallee.numParameters(); ++i ) {
1491 TempDescriptor tdArg = fc.getArgMatchingParamIndex( fmCallee, i );
1492 VariableNode vnArgCaller = this.getVariableNodeFromTemp( tdArg );
1494 Set<RefSrcNode> toVisitInCaller = new HashSet<RefSrcNode>();
1495 Set<RefSrcNode> visitedInCaller = new HashSet<RefSrcNode>();
1497 toVisitInCaller.add( vnArgCaller );
1499 while( !toVisitInCaller.isEmpty() ) {
1500 RefSrcNode rsnCaller = toVisitInCaller.iterator().next();
1501 toVisitInCaller.remove( rsnCaller );
1502 visitedInCaller.add( rsnCaller );
1504 Iterator<RefEdge> itrRefEdges = rsnCaller.iteratorToReferencees();
1505 while( itrRefEdges.hasNext() ) {
1506 RefEdge reCaller = itrRefEdges.next();
1507 HeapRegionNode hrnCaller = reCaller.getDst();
1509 callerNodeIDsCopiedToCallee.add( hrnCaller.getID() );
1510 reachableCallerNodes.add( hrnCaller );
1512 if( reCaller.getSrc() instanceof HeapRegionNode ) {
1513 reachableCallerEdges.add( reCaller );
1515 if( rsnCaller.equals( vnArgCaller ) ) {
1516 reachableCallerArgEdges2paramIndex.put( reCaller, i );
1518 oocCallerEdges.add( reCaller );
1522 if( !visitedInCaller.contains( hrnCaller ) ) {
1523 toVisitInCaller.add( hrnCaller );
1526 } // end edge iteration
1527 } // end visiting heap nodes in caller
1528 } // end iterating over parameters as starting points
1531 // now collect out-of-callee-context IDs and
1532 // map them to whether the ID is out of the caller
1534 Set<HrnIdOoc> oocHrnIdOoc2callee = new HashSet<HrnIdOoc>();
1536 Iterator<Integer> itrInContext =
1537 callerNodeIDsCopiedToCallee.iterator();
1538 while( itrInContext.hasNext() ) {
1539 Integer hrnID = itrInContext.next();
1540 HeapRegionNode hrnCallerAndInContext = id2hrn.get( hrnID );
1542 Iterator<RefEdge> itrMightCross =
1543 hrnCallerAndInContext.iteratorToReferencers();
1544 while( itrMightCross.hasNext() ) {
1545 RefEdge edgeMightCross = itrMightCross.next();
1547 RefSrcNode rsnCallerAndOutContext =
1548 edgeMightCross.getSrc();
1550 if( rsnCallerAndOutContext instanceof VariableNode ) {
1551 // variables do not have out-of-context reach states,
1553 oocCallerEdges.add( edgeMightCross );
1557 HeapRegionNode hrnCallerAndOutContext =
1558 (HeapRegionNode) rsnCallerAndOutContext;
1560 // is this source node out-of-context?
1561 if( callerNodeIDsCopiedToCallee.contains( hrnCallerAndOutContext.getID() ) ) {
1562 // no, skip this edge
1567 oocCallerEdges.add( edgeMightCross );
1569 // add all reach tuples on the node to list
1570 // of things that are out-of-context: insight
1571 // if this node is reachable from someting that WAS
1572 // in-context, then this node should already be in-context
1573 Iterator<ReachState> stateItr = hrnCallerAndOutContext.getAlpha().iterator();
1574 while( stateItr.hasNext() ) {
1575 ReachState state = stateItr.next();
1577 Iterator<ReachTuple> rtItr = state.iterator();
1578 while( rtItr.hasNext() ) {
1579 ReachTuple rt = rtItr.next();
1581 oocHrnIdOoc2callee.add( new HrnIdOoc( rt.getHrnID(),
1590 // the callee view is a new graph: DON'T MODIFY *THIS* graph
1591 ReachGraph rg = new ReachGraph();
1593 // add nodes to callee graph
1594 Iterator<HeapRegionNode> hrnItr = reachableCallerNodes.iterator();
1595 while( hrnItr.hasNext() ) {
1596 HeapRegionNode hrnCaller = hrnItr.next();
1598 assert callerNodeIDsCopiedToCallee.contains( hrnCaller.getID() );
1599 assert !rg.id2hrn.containsKey( hrnCaller.getID() );
1601 ExistPred pred = ExistPred.factory( hrnCaller.getID(), null );
1602 ExistPredSet preds = ExistPredSet.factory( pred );
1604 rg.createNewHeapRegionNode( hrnCaller.getID(),
1605 hrnCaller.isSingleObject(),
1606 hrnCaller.isNewSummary(),
1607 hrnCaller.isFlagged(),
1608 false, // out-of-context?
1609 hrnCaller.getType(),
1610 hrnCaller.getAllocSite(),
1611 toCalleeContext( hrnCaller.getInherent(),
1615 toCalleeContext( hrnCaller.getAlpha(),
1620 hrnCaller.getDescription()
1624 // add param edges to callee graph
1626 reachableCallerArgEdges2paramIndex.entrySet().iterator();
1627 while( argEdges.hasNext() ) {
1628 Map.Entry me = (Map.Entry) argEdges.next();
1629 RefEdge reArg = (RefEdge) me.getKey();
1630 Integer index = (Integer) me.getValue();
1632 TempDescriptor arg = fmCallee.getParameter( index );
1634 VariableNode vnCallee =
1635 rg.getVariableNodeFromTemp( arg );
1637 HeapRegionNode hrnDstCaller = reArg.getDst();
1638 HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1639 assert hrnDstCallee != null;
1642 ExistPred.factory( arg,
1644 hrnDstCallee.getID(),
1648 true, // out-of-callee-context
1649 false // out-of-caller-context
1652 ExistPredSet preds =
1653 ExistPredSet.factory( pred );
1656 new RefEdge( vnCallee,
1660 toCalleeContext( reArg.getBeta(),
1667 rg.addRefEdge( vnCallee,
1673 // add in-context edges to callee graph
1674 Iterator<RefEdge> reItr = reachableCallerEdges.iterator();
1675 while( reItr.hasNext() ) {
1676 RefEdge reCaller = reItr.next();
1677 RefSrcNode rsnCaller = reCaller.getSrc();
1678 assert rsnCaller instanceof HeapRegionNode;
1679 HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1680 HeapRegionNode hrnDstCaller = reCaller.getDst();
1682 HeapRegionNode hrnSrcCallee = rg.id2hrn.get( hrnSrcCaller.getID() );
1683 HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1684 assert hrnSrcCallee != null;
1685 assert hrnDstCallee != null;
1688 ExistPred.factory( null,
1689 hrnSrcCallee.getID(),
1690 hrnDstCallee.getID(),
1692 reCaller.getField(),
1694 false, // out-of-callee-context
1695 false // out-of-caller-context
1698 ExistPredSet preds =
1699 ExistPredSet.factory( pred );
1702 new RefEdge( hrnSrcCallee,
1705 reCaller.getField(),
1706 toCalleeContext( reCaller.getBeta(),
1713 rg.addRefEdge( hrnSrcCallee,
1719 // add out-of-context edges to callee graph
1720 reItr = oocCallerEdges.iterator();
1721 while( reItr.hasNext() ) {
1722 RefEdge reCaller = reItr.next();
1723 RefSrcNode rsnCaller = reCaller.getSrc();
1724 HeapRegionNode hrnDstCaller = reCaller.getDst();
1725 HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1726 assert hrnDstCallee != null;
1728 TypeDescriptor oocNodeType;
1730 TempDescriptor oocPredSrcTemp = null;
1731 Integer oocPredSrcID = null;
1732 boolean outOfCalleeContext;
1733 boolean outOfCallerContext;
1735 if( rsnCaller instanceof VariableNode ) {
1736 VariableNode vnCaller = (VariableNode) rsnCaller;
1738 oocReach = rsetEmpty;
1739 oocPredSrcTemp = vnCaller.getTempDescriptor();
1740 outOfCalleeContext = true;
1741 outOfCallerContext = false;
1744 HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1745 assert !callerNodeIDsCopiedToCallee.contains( hrnSrcCaller.getID() );
1746 oocNodeType = hrnSrcCaller.getType();
1747 oocReach = hrnSrcCaller.getAlpha();
1748 oocPredSrcID = hrnSrcCaller.getID();
1749 if( hrnSrcCaller.isOutOfContext() ) {
1750 outOfCalleeContext = false;
1751 outOfCallerContext = true;
1753 outOfCalleeContext = true;
1754 outOfCallerContext = false;
1759 ExistPred.factory( oocPredSrcTemp,
1761 hrnDstCallee.getID(),
1763 reCaller.getField(),
1769 ExistPredSet preds =
1770 ExistPredSet.factory( pred );
1772 RefEdge oocEdgeExisting =
1773 rg.getOutOfContextReferenceTo( hrnDstCallee,
1779 if( oocEdgeExisting == null ) {
1780 // for consistency, map one out-of-context "identifier"
1781 // to one heap region node id, otherwise no convergence
1782 String oocid = "oocid"+
1784 hrnDstCallee.getIDString()+
1787 reCaller.getField();
1789 Integer oocHrnID = oocid2hrnid.get( oocid );
1791 HeapRegionNode hrnCalleeAndOutContext;
1793 if( oocHrnID == null ) {
1795 hrnCalleeAndOutContext =
1796 rg.createNewHeapRegionNode( null, // ID
1797 false, // single object?
1798 false, // new summary?
1800 true, // out-of-context?
1802 null, // alloc site, shouldn't be used
1803 toCalleeContext( oocReach,
1807 toCalleeContext( oocReach,
1815 oocid2hrnid.put( oocid, hrnCalleeAndOutContext.getID() );
1819 // the mapping already exists, so see if node is there
1820 hrnCalleeAndOutContext = rg.id2hrn.get( oocHrnID );
1822 if( hrnCalleeAndOutContext == null ) {
1824 hrnCalleeAndOutContext =
1825 rg.createNewHeapRegionNode( oocHrnID, // ID
1826 false, // single object?
1827 false, // new summary?
1829 true, // out-of-context?
1831 null, // alloc site, shouldn't be used
1832 toCalleeContext( oocReach,
1836 toCalleeContext( oocReach,
1845 // otherwise it is there, so merge reachability
1846 hrnCalleeAndOutContext.setAlpha( Canonical.unionORpreds( hrnCalleeAndOutContext.getAlpha(),
1847 toCalleeContext( oocReach,
1856 assert hrnCalleeAndOutContext.reachHasOnlyOOC();
1858 rg.addRefEdge( hrnCalleeAndOutContext,
1860 new RefEdge( hrnCalleeAndOutContext,
1863 reCaller.getField(),
1864 toCalleeContext( reCaller.getBeta(),
1873 // the out-of-context edge already exists
1874 oocEdgeExisting.setBeta( Canonical.unionORpreds( oocEdgeExisting.getBeta(),
1875 toCalleeContext( reCaller.getBeta(),
1882 oocEdgeExisting.setPreds( Canonical.join( oocEdgeExisting.getPreds(),
1887 HeapRegionNode hrnCalleeAndOutContext =
1888 (HeapRegionNode) oocEdgeExisting.getSrc();
1889 hrnCalleeAndOutContext.setAlpha( Canonical.unionORpreds( hrnCalleeAndOutContext.getAlpha(),
1890 toCalleeContext( oocReach,
1897 assert hrnCalleeAndOutContext.reachHasOnlyOOC();
1902 if( writeDebugDOTs ) {
1903 debugGraphPrefix = String.format( "call%02d", debugCallSiteVisits );
1904 rg.writeGraph( debugGraphPrefix+"calleeview",
1905 resolveMethodDebugDOTwriteLabels,
1906 resolveMethodDebugDOTselectTemps,
1907 resolveMethodDebugDOTpruneGarbage,
1908 resolveMethodDebugDOThideSubsetReach,
1909 resolveMethodDebugDOThideEdgeTaints );
1915 private static Hashtable<String, Integer> oocid2hrnid =
1916 new Hashtable<String, Integer>();
1919 // useful since many graphs writes in the method call debug code
1920 private static boolean resolveMethodDebugDOTwriteLabels = true;
1921 private static boolean resolveMethodDebugDOTselectTemps = true;
1922 private static boolean resolveMethodDebugDOTpruneGarbage = true;
1923 private static boolean resolveMethodDebugDOThideSubsetReach = false;
1924 private static boolean resolveMethodDebugDOThideEdgeTaints = true;
1926 static String debugGraphPrefix;
1927 static int debugCallSiteVisits = 0;
1928 static int debugCallSiteVisitsUntilExit = 0;
1932 resolveMethodCall( FlatCall fc,
1933 FlatMethod fmCallee,
1934 ReachGraph rgCallee,
1935 Set<Integer> callerNodeIDsCopiedToCallee,
1936 boolean writeDebugDOTs
1939 if( writeDebugDOTs ) {
1940 System.out.println( " Writing out visit "+
1941 debugCallSiteVisits+
1942 " to debug call site" );
1944 debugGraphPrefix = String.format( "call%02d",
1945 debugCallSiteVisits );
1947 rgCallee.writeGraph( debugGraphPrefix+"callee",
1948 resolveMethodDebugDOTwriteLabels,
1949 resolveMethodDebugDOTselectTemps,
1950 resolveMethodDebugDOTpruneGarbage,
1951 resolveMethodDebugDOThideSubsetReach,
1952 resolveMethodDebugDOThideEdgeTaints );
1954 writeGraph( debugGraphPrefix+"caller00In",
1955 resolveMethodDebugDOTwriteLabels,
1956 resolveMethodDebugDOTselectTemps,
1957 resolveMethodDebugDOTpruneGarbage,
1958 resolveMethodDebugDOThideSubsetReach,
1959 resolveMethodDebugDOThideEdgeTaints,
1960 callerNodeIDsCopiedToCallee );
1965 // method call transfer function steps:
1966 // 1. Use current callee-reachable heap (CRH) to test callee
1967 // predicates and mark what will be coming in.
1968 // 2. Wipe CRH out of caller.
1969 // 3. Transplant marked callee parts in:
1970 // a) bring in nodes
1971 // b) bring in callee -> callee edges
1972 // c) resolve out-of-context -> callee edges
1973 // d) assign return value
1974 // 4. Collapse shadow nodes down
1975 // 5. Global sweep it.
1978 // 1. mark what callee elements have satisfied predicates
1979 Hashtable<HeapRegionNode, ExistPredSet> calleeNodesSatisfied =
1980 new Hashtable<HeapRegionNode, ExistPredSet>();
1982 Hashtable<RefEdge, ExistPredSet> calleeEdgesSatisfied =
1983 new Hashtable<RefEdge, ExistPredSet>();
1985 Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied =
1986 new Hashtable<ReachState, ExistPredSet>();
1988 Hashtable< RefEdge, Set<RefSrcNode> > calleeEdges2oocCallerSrcMatches =
1989 new Hashtable< RefEdge, Set<RefSrcNode> >();
1991 Iterator meItr = rgCallee.id2hrn.entrySet().iterator();
1992 while( meItr.hasNext() ) {
1993 Map.Entry me = (Map.Entry) meItr.next();
1994 Integer id = (Integer) me.getKey();
1995 HeapRegionNode hrnCallee = (HeapRegionNode) me.getValue();
1997 // if a callee element's predicates are satisfied then a set
1998 // of CALLER predicates is returned: they are the predicates
1999 // that the callee element moved into the caller context
2000 // should have, and it is inefficient to find this again later
2001 ExistPredSet predsIfSatis =
2002 hrnCallee.getPreds().isSatisfiedBy( this,
2003 callerNodeIDsCopiedToCallee
2006 if( predsIfSatis != null ) {
2007 calleeNodesSatisfied.put( hrnCallee, predsIfSatis );
2009 // otherwise don't bother looking at edges to this node
2013 // since the node is coming over, find out which reach
2014 // states on it should come over, too
2015 Iterator<ReachState> stateItr = hrnCallee.getAlpha().iterator();
2016 while( stateItr.hasNext() ) {
2017 ReachState stateCallee = stateItr.next();
2020 stateCallee.getPreds().isSatisfiedBy( this,
2021 callerNodeIDsCopiedToCallee
2023 if( predsIfSatis != null ) {
2024 calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2028 // then look at edges to the node
2029 Iterator<RefEdge> reItr = hrnCallee.iteratorToReferencers();
2030 while( reItr.hasNext() ) {
2031 RefEdge reCallee = reItr.next();
2032 RefSrcNode rsnCallee = reCallee.getSrc();
2034 // (caller local variables to in-context heap regions)
2035 // have an (out-of-context heap region -> in-context heap region)
2036 // abstraction in the callEE, so its true we never need to
2037 // look at a (var node -> heap region) edge in callee to bring
2038 // those over for the call site transfer. What about (param var->heap region)
2039 // edges in callee? They are dealt with below this loop.
2040 // So, yes, at this point skip (var->region) edges in callee
2041 if( rsnCallee instanceof VariableNode ) {
2045 // first see if the source is out-of-context, and only
2046 // proceed with this edge if we find some caller-context
2048 HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2049 boolean matchedOutOfContext = false;
2051 if( !hrnSrcCallee.isOutOfContext() ) {
2054 hrnSrcCallee.getPreds().isSatisfiedBy( this,
2055 callerNodeIDsCopiedToCallee
2057 if( predsIfSatis != null ) {
2058 calleeNodesSatisfied.put( hrnSrcCallee, predsIfSatis );
2060 // otherwise forget this edge
2065 // hrnSrcCallee is out-of-context
2067 assert !calleeEdges2oocCallerSrcMatches.containsKey( reCallee );
2069 Set<RefSrcNode> rsnCallers = new HashSet<RefSrcNode>();
2071 // is the target node in the caller?
2072 HeapRegionNode hrnDstCaller = this.id2hrn.get( hrnCallee.getID() );
2073 if( hrnDstCaller == null ) {
2077 Iterator<RefEdge> reDstItr = hrnDstCaller.iteratorToReferencers();
2078 while( reDstItr.hasNext() ) {
2079 // the edge and field (either possibly null) must match
2080 RefEdge reCaller = reDstItr.next();
2082 if( !reCaller.typeEquals ( reCallee.getType() ) ||
2083 !reCaller.fieldEquals( reCallee.getField() )
2088 RefSrcNode rsnCaller = reCaller.getSrc();
2089 if( rsnCaller instanceof VariableNode ) {
2091 // a variable node matches an OOC region with null type
2092 if( hrnSrcCallee.getType() != null ) {
2097 // otherwise types should match
2098 HeapRegionNode hrnCallerSrc = (HeapRegionNode) rsnCaller;
2099 if( hrnSrcCallee.getType() == null ) {
2100 if( hrnCallerSrc.getType() != null ) {
2104 if( !hrnSrcCallee.getType().equals( hrnCallerSrc.getType() ) ) {
2110 rsnCallers.add( rsnCaller );
2111 matchedOutOfContext = true;
2114 if( !rsnCallers.isEmpty() ) {
2115 calleeEdges2oocCallerSrcMatches.put( reCallee, rsnCallers );
2119 if( hrnSrcCallee.isOutOfContext() &&
2120 !matchedOutOfContext ) {
2125 reCallee.getPreds().isSatisfiedBy( this,
2126 callerNodeIDsCopiedToCallee
2129 if( predsIfSatis != null ) {
2130 calleeEdgesSatisfied.put( reCallee, predsIfSatis );
2132 // since the edge is coming over, find out which reach
2133 // states on it should come over, too
2134 stateItr = reCallee.getBeta().iterator();
2135 while( stateItr.hasNext() ) {
2136 ReachState stateCallee = stateItr.next();
2139 stateCallee.getPreds().isSatisfiedBy( this,
2140 callerNodeIDsCopiedToCallee
2142 if( predsIfSatis != null ) {
2143 calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2150 if( writeDebugDOTs ) {
2151 writeGraph( debugGraphPrefix+"caller20BeforeWipe",
2152 resolveMethodDebugDOTwriteLabels,
2153 resolveMethodDebugDOTselectTemps,
2154 resolveMethodDebugDOTpruneGarbage,
2155 resolveMethodDebugDOThideSubsetReach,
2156 resolveMethodDebugDOThideEdgeTaints );
2160 // 2. predicates tested, ok to wipe out caller part
2161 Iterator<Integer> hrnItr = callerNodeIDsCopiedToCallee.iterator();
2162 while( hrnItr.hasNext() ) {
2163 Integer hrnID = hrnItr.next();
2164 HeapRegionNode hrnCaller = id2hrn.get( hrnID );
2165 assert hrnCaller != null;
2167 // when clearing off nodes, also eliminate variable
2169 wipeOut( hrnCaller, true );
2174 if( writeDebugDOTs ) {
2175 writeGraph( debugGraphPrefix+"caller30BeforeAddingNodes",
2176 resolveMethodDebugDOTwriteLabels,
2177 resolveMethodDebugDOTselectTemps,
2178 resolveMethodDebugDOTpruneGarbage,
2179 resolveMethodDebugDOThideSubsetReach,
2180 resolveMethodDebugDOThideEdgeTaints );
2186 // 3. callee elements with satisfied preds come in, note that
2187 // the mapping of elements satisfied to preds is like this:
2188 // A callee element EE has preds EEp that are satisfied by
2189 // some caller element ER. We bring EE into the caller
2190 // context as ERee with the preds of ER, namely ERp, which
2191 // in the following algorithm is the value in the mapping
2194 Iterator satisItr = calleeNodesSatisfied.entrySet().iterator();
2195 while( satisItr.hasNext() ) {
2196 Map.Entry me = (Map.Entry) satisItr.next();
2197 HeapRegionNode hrnCallee = (HeapRegionNode) me.getKey();
2198 ExistPredSet preds = (ExistPredSet) me.getValue();
2200 // TODO: I think its true that the current implementation uses
2201 // the type of the OOC region and the predicates OF THE EDGE from
2202 // it to link everything up in caller context, so that's why we're
2203 // skipping this... maybe that's a sillier way to do it?
2204 if( hrnCallee.isOutOfContext() ) {
2208 AllocSite as = hrnCallee.getAllocSite();
2209 allocSites.add( as );
2211 Integer hrnIDshadow = as.getShadowIDfromID( hrnCallee.getID() );
2213 HeapRegionNode hrnCaller = id2hrn.get( hrnIDshadow );
2214 if( hrnCaller == null ) {
2216 createNewHeapRegionNode( hrnIDshadow, // id or null to generate a new one
2217 hrnCallee.isSingleObject(), // single object?
2218 hrnCallee.isNewSummary(), // summary?
2219 hrnCallee.isFlagged(), // flagged?
2220 false, // out-of-context?
2221 hrnCallee.getType(), // type
2222 hrnCallee.getAllocSite(), // allocation site
2223 toCallerContext( hrnCallee.getInherent(),
2224 calleeStatesSatisfied ), // inherent reach
2225 null, // current reach
2226 predsEmpty, // predicates
2227 hrnCallee.getDescription() // description
2230 assert hrnCaller.isWiped();
2233 hrnCaller.setAlpha( toCallerContext( hrnCallee.getAlpha(),
2234 calleeStatesSatisfied
2238 hrnCaller.setPreds( preds );
2245 if( writeDebugDOTs ) {
2246 writeGraph( debugGraphPrefix+"caller31BeforeAddingEdges",
2247 resolveMethodDebugDOTwriteLabels,
2248 resolveMethodDebugDOTselectTemps,
2249 resolveMethodDebugDOTpruneGarbage,
2250 resolveMethodDebugDOThideSubsetReach,
2251 resolveMethodDebugDOThideEdgeTaints );
2255 // set these up during the next procedure so after
2256 // the caller has all of its nodes and edges put
2257 // back together we can propagate the callee's
2258 // reach changes backwards into the caller graph
2259 HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
2261 Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
2262 new Hashtable<RefEdge, ChangeSet>();
2265 // 3.b) callee -> callee edges AND out-of-context -> callee
2266 satisItr = calleeEdgesSatisfied.entrySet().iterator();
2267 while( satisItr.hasNext() ) {
2268 Map.Entry me = (Map.Entry) satisItr.next();
2269 RefEdge reCallee = (RefEdge) me.getKey();
2270 ExistPredSet preds = (ExistPredSet) me.getValue();
2272 HeapRegionNode hrnDstCallee = reCallee.getDst();
2273 AllocSite asDst = hrnDstCallee.getAllocSite();
2274 allocSites.add( asDst );
2276 Integer hrnIDDstShadow =
2277 asDst.getShadowIDfromID( hrnDstCallee.getID() );
2279 HeapRegionNode hrnDstCaller = id2hrn.get( hrnIDDstShadow );
2280 assert hrnDstCaller != null;
2283 RefSrcNode rsnCallee = reCallee.getSrc();
2285 Set<RefSrcNode> rsnCallers =
2286 new HashSet<RefSrcNode>();
2288 Set<RefSrcNode> oocCallers =
2289 calleeEdges2oocCallerSrcMatches.get( reCallee );
2291 if( rsnCallee instanceof HeapRegionNode ) {
2292 HeapRegionNode hrnCalleeSrc = (HeapRegionNode) rsnCallee;
2293 if( hrnCalleeSrc.isOutOfContext() ) {
2294 assert oocCallers != null;
2299 if( oocCallers == null ) {
2300 // there are no out-of-context matches, so it's
2301 // either a param/arg var or one in-context heap region
2302 if( rsnCallee instanceof VariableNode ) {
2303 // variable -> node in the callee should only
2304 // come into the caller if its from a param var
2305 VariableNode vnCallee = (VariableNode) rsnCallee;
2306 TempDescriptor tdParam = vnCallee.getTempDescriptor();
2307 TempDescriptor tdArg = fc.getArgMatchingParam( fmCallee,
2309 if( tdArg == null ) {
2310 // this means the variable isn't a parameter, its local
2311 // to the callee so we ignore it in call site transfer
2312 // shouldn't this NEVER HAPPEN?
2316 rsnCallers.add( this.getVariableNodeFromTemp( tdArg ) );
2319 // otherwise source is in context, one region
2321 HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2323 // translate an in-context node to shadow
2324 AllocSite asSrc = hrnSrcCallee.getAllocSite();
2325 allocSites.add( asSrc );
2327 Integer hrnIDSrcShadow =
2328 asSrc.getShadowIDfromID( hrnSrcCallee.getID() );
2330 HeapRegionNode hrnSrcCallerShadow =
2331 this.id2hrn.get( hrnIDSrcShadow );
2333 assert hrnSrcCallerShadow != null;
2335 rsnCallers.add( hrnSrcCallerShadow );
2339 // otherwise we have a set of out-of-context srcs
2340 // that should NOT be translated to shadow nodes
2341 assert !oocCallers.isEmpty();
2342 rsnCallers.addAll( oocCallers );
2345 // now make all caller edges we've identified from
2346 // this callee edge with a satisfied predicate
2347 assert !rsnCallers.isEmpty();
2348 Iterator<RefSrcNode> rsnItr = rsnCallers.iterator();
2349 while( rsnItr.hasNext() ) {
2350 RefSrcNode rsnCaller = rsnItr.next();
2352 RefEdge reCaller = new RefEdge( rsnCaller,
2355 reCallee.getField(),
2356 toCallerContext( reCallee.getBeta(),
2357 calleeStatesSatisfied ),
2361 ChangeSet cs = ChangeSet.factory();
2362 Iterator<ReachState> rsItr = reCaller.getBeta().iterator();
2363 while( rsItr.hasNext() ) {
2364 ReachState state = rsItr.next();
2365 ExistPredSet predsPreCallee = state.getPreds();
2367 if( state.isEmpty() ) {
2371 Iterator<ExistPred> predItr = predsPreCallee.iterator();
2372 while( predItr.hasNext() ) {
2373 ExistPred pred = predItr.next();
2374 ReachState old = pred.ne_state;
2380 cs = Canonical.add( cs,
2381 ChangeTuple.factory( old,
2389 // look to see if an edge with same field exists
2390 // and merge with it, otherwise just add the edge
2391 RefEdge edgeExisting = rsnCaller.getReferenceTo( hrnDstCaller,
2395 if( edgeExisting != null ) {
2396 edgeExisting.setBeta(
2397 Canonical.unionORpreds( edgeExisting.getBeta(),
2401 edgeExisting.setPreds(
2402 Canonical.join( edgeExisting.getPreds(),
2407 // for reach propagation
2408 if( !cs.isEmpty() ) {
2409 ChangeSet csExisting = edgePlannedChanges.get( edgeExisting );
2410 if( csExisting == null ) {
2411 csExisting = ChangeSet.factory();
2413 edgePlannedChanges.put( edgeExisting,
2414 Canonical.union( csExisting,
2421 addRefEdge( rsnCaller, hrnDstCaller, reCaller );
2423 // for reach propagation
2424 if( !cs.isEmpty() ) {
2425 edgesForPropagation.add( reCaller );
2426 assert !edgePlannedChanges.containsKey( reCaller );
2427 edgePlannedChanges.put( reCaller, cs );
2436 if( writeDebugDOTs ) {
2437 writeGraph( debugGraphPrefix+"caller35BeforeAssignReturnValue",
2438 resolveMethodDebugDOTwriteLabels,
2439 resolveMethodDebugDOTselectTemps,
2440 resolveMethodDebugDOTpruneGarbage,
2441 resolveMethodDebugDOThideSubsetReach,
2442 resolveMethodDebugDOThideEdgeTaints );
2447 // TODO: WAIT! THIS SHOULD BE MERGED INTO OTHER PARTS, BECAUSE
2448 // AS IT IS WE'RE NOT VERIFYING PREDICATES OF RETURN VALUE
2449 // EDGES, JUST BRINGING THEM ALL! It'll work for now, over approximation
2451 // 3.d) handle return value assignment if needed
2452 TempDescriptor returnTemp = fc.getReturnTemp();
2453 if( returnTemp != null &&
2454 DisjointAnalysis.shouldAnalysisTrack( returnTemp.getType() )
2457 VariableNode vnLhsCaller = getVariableNodeFromTemp( returnTemp );
2458 clearRefEdgesFrom( vnLhsCaller, null, null, true );
2460 VariableNode vnReturnCallee = rgCallee.getVariableNodeFromTemp( tdReturn );
2461 Iterator<RefEdge> reCalleeItr = vnReturnCallee.iteratorToReferencees();
2462 while( reCalleeItr.hasNext() ) {
2463 RefEdge reCallee = reCalleeItr.next();
2464 HeapRegionNode hrnDstCallee = reCallee.getDst();
2466 // some edge types are not possible return values when we can
2467 // see what type variable we are assigning it to
2468 if( !isSuperiorType( returnTemp.getType(), reCallee.getType() ) ) {
2469 System.out.println( "*** NOT EXPECTING TO SEE THIS: Throwing out "+
2470 reCallee+" for return temp "+returnTemp );
2475 AllocSite asDst = hrnDstCallee.getAllocSite();
2476 allocSites.add( asDst );
2478 Integer hrnIDDstShadow = asDst.getShadowIDfromID( hrnDstCallee.getID() );
2480 HeapRegionNode hrnDstCaller = id2hrn.get( hrnIDDstShadow );
2481 if( hrnDstCaller == null ) {
2483 createNewHeapRegionNode( hrnIDDstShadow, // id or null to generate a new one
2484 hrnDstCallee.isSingleObject(), // single object?
2485 hrnDstCallee.isNewSummary(), // summary?
2486 hrnDstCallee.isFlagged(), // flagged?
2487 false, // out-of-context?
2488 hrnDstCallee.getType(), // type
2489 hrnDstCallee.getAllocSite(), // allocation site
2490 toCallerContext( hrnDstCallee.getInherent(),
2491 calleeStatesSatisfied ), // inherent reach
2492 toCallerContext( hrnDstCallee.getAlpha(),
2493 calleeStatesSatisfied ), // current reach
2494 predsTrue, // predicates
2495 hrnDstCallee.getDescription() // description
2499 TypeDescriptor tdNewEdge =
2500 mostSpecificType( reCallee.getType(),
2501 hrnDstCallee.getType(),
2502 hrnDstCaller.getType()
2505 RefEdge reCaller = new RefEdge( vnLhsCaller,
2509 toCallerContext( reCallee.getBeta(),
2510 calleeStatesSatisfied ),
2514 addRefEdge( vnLhsCaller, hrnDstCaller, reCaller );
2520 if( writeDebugDOTs ) {
2521 writeGraph( debugGraphPrefix+"caller38propagateReach",
2522 resolveMethodDebugDOTwriteLabels,
2523 resolveMethodDebugDOTselectTemps,
2524 resolveMethodDebugDOTpruneGarbage,
2525 resolveMethodDebugDOThideSubsetReach,
2526 resolveMethodDebugDOThideEdgeTaints );
2529 // propagate callee reachability changes to the rest
2530 // of the caller graph edges
2531 HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
2533 propagateTokensOverEdges( edgesForPropagation, // source edges
2534 edgePlannedChanges, // map src edge to change set
2535 edgesUpdated ); // list of updated edges
2537 // commit beta' (beta<-betaNew)
2538 Iterator<RefEdge> edgeItr = edgesUpdated.iterator();
2539 while( edgeItr.hasNext() ) {
2540 edgeItr.next().applyBetaNew();
2549 if( writeDebugDOTs ) {
2550 writeGraph( debugGraphPrefix+"caller40BeforeShadowMerge",
2551 resolveMethodDebugDOTwriteLabels,
2552 resolveMethodDebugDOTselectTemps,
2553 resolveMethodDebugDOTpruneGarbage,
2554 resolveMethodDebugDOThideSubsetReach,
2555 resolveMethodDebugDOThideEdgeTaints );
2559 // 4) merge shadow nodes so alloc sites are back to k
2560 Iterator<AllocSite> asItr = rgCallee.allocSites.iterator();
2561 while( asItr.hasNext() ) {
2562 // for each allocation site do the following to merge
2563 // shadow nodes (newest from callee) with any existing
2564 // look for the newest normal and newest shadow "slot"
2565 // not being used, transfer normal to shadow. Keep
2566 // doing this until there are no more normal nodes, or
2567 // no empty shadow slots: then merge all remaining normal
2568 // nodes into the shadow summary. Finally, convert all
2569 // shadow to their normal versions.
2570 AllocSite as = asItr.next();
2573 while( ageNorm < allocationDepth &&
2574 ageShad < allocationDepth ) {
2576 // first, are there any normal nodes left?
2577 Integer idNorm = as.getIthOldest( ageNorm );
2578 HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2579 if( hrnNorm == null ) {
2580 // no, this age of normal node not in the caller graph
2585 // yes, a normal node exists, is there an empty shadow
2586 // "slot" to transfer it onto?
2587 HeapRegionNode hrnShad = getIthNode( as, ageShad, true );
2588 if( !hrnShad.isWiped() ) {
2589 // no, this age of shadow node is not empty
2594 // yes, this shadow node is empty
2595 transferOnto( hrnNorm, hrnShad );
2600 // now, while there are still normal nodes but no shadow
2601 // slots, merge normal nodes into the shadow summary
2602 while( ageNorm < allocationDepth ) {
2604 // first, are there any normal nodes left?
2605 Integer idNorm = as.getIthOldest( ageNorm );
2606 HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2607 if( hrnNorm == null ) {
2608 // no, this age of normal node not in the caller graph
2613 // yes, a normal node exists, so get the shadow summary
2614 HeapRegionNode summShad = getSummaryNode( as, true );
2615 mergeIntoSummary( hrnNorm, summShad );
2619 // if there is a normal summary, merge it into shadow summary
2620 Integer idNorm = as.getSummary();
2621 HeapRegionNode summNorm = id2hrn.get( idNorm );
2622 if( summNorm != null ) {
2623 HeapRegionNode summShad = getSummaryNode( as, true );
2624 mergeIntoSummary( summNorm, summShad );
2627 // finally, flip all existing shadow nodes onto the normal
2628 for( int i = 0; i < allocationDepth; ++i ) {
2629 Integer idShad = as.getIthOldestShadow( i );
2630 HeapRegionNode hrnShad = id2hrn.get( idShad );
2631 if( hrnShad != null ) {
2633 HeapRegionNode hrnNorm = getIthNode( as, i, false );
2634 assert hrnNorm.isWiped();
2635 transferOnto( hrnShad, hrnNorm );
2639 Integer idShad = as.getSummaryShadow();
2640 HeapRegionNode summShad = id2hrn.get( idShad );
2641 if( summShad != null ) {
2642 summNorm = getSummaryNode( as, false );
2643 transferOnto( summShad, summNorm );
2652 if( writeDebugDOTs ) {
2653 writeGraph( debugGraphPrefix+"caller45BeforeUnshadow",
2654 resolveMethodDebugDOTwriteLabels,
2655 resolveMethodDebugDOTselectTemps,
2656 resolveMethodDebugDOTpruneGarbage,
2657 resolveMethodDebugDOThideSubsetReach,
2658 resolveMethodDebugDOThideEdgeTaints );
2662 Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
2663 while( itrAllHRNodes.hasNext() ) {
2664 Map.Entry me = (Map.Entry) itrAllHRNodes.next();
2665 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
2667 hrn.setAlpha( unshadow( hrn.getAlpha() ) );
2669 Iterator<RefEdge> itrEdges = hrn.iteratorToReferencers();
2670 while( itrEdges.hasNext() ) {
2671 RefEdge re = itrEdges.next();
2672 re.setBeta( unshadow( re.getBeta() ) );
2679 if( writeDebugDOTs ) {
2680 writeGraph( debugGraphPrefix+"caller50BeforeGlobalSweep",
2681 resolveMethodDebugDOTwriteLabels,
2682 resolveMethodDebugDOTselectTemps,
2683 resolveMethodDebugDOTpruneGarbage,
2684 resolveMethodDebugDOThideSubsetReach,
2685 resolveMethodDebugDOThideEdgeTaints );
2690 if( !DISABLE_GLOBAL_SWEEP ) {
2698 if( writeDebugDOTs ) {
2699 writeGraph( debugGraphPrefix+"caller90AfterTransfer",
2700 resolveMethodDebugDOTwriteLabels,
2701 resolveMethodDebugDOTselectTemps,
2702 resolveMethodDebugDOTpruneGarbage,
2703 resolveMethodDebugDOThideSubsetReach,
2704 resolveMethodDebugDOThideEdgeTaints );
2706 ++debugCallSiteVisits;
2707 if( debugCallSiteVisits >= debugCallSiteVisitsUntilExit ) {
2708 System.out.println( "!!! Exiting after requested visits to call site. !!!" );
2716 ////////////////////////////////////////////////////
2718 // Abstract garbage collection simply removes
2719 // heap region nodes that are not mechanically
2720 // reachable from a root set. This step is
2721 // essential for testing node and edge existence
2722 // predicates efficiently
2724 ////////////////////////////////////////////////////
2725 public void abstractGarbageCollect( Set<TempDescriptor> liveSet ) {
2727 // calculate a root set, will be different for Java
2728 // version of analysis versus Bamboo version
2729 Set<RefSrcNode> toVisit = new HashSet<RefSrcNode>();
2731 // visit every variable in graph while building root
2732 // set, and do iterating on a copy, so we can remove
2733 // dead variables while we're at this
2734 Iterator makeCopyItr = td2vn.entrySet().iterator();
2735 Set entrysCopy = new HashSet();
2736 while( makeCopyItr.hasNext() ) {
2737 entrysCopy.add( makeCopyItr.next() );
2740 Iterator eItr = entrysCopy.iterator();
2741 while( eItr.hasNext() ) {
2742 Map.Entry me = (Map.Entry) eItr.next();
2743 TempDescriptor td = (TempDescriptor) me.getKey();
2744 VariableNode vn = (VariableNode) me.getValue();
2746 if( liveSet.contains( td ) ) {
2750 // dead var, remove completely from graph
2752 clearRefEdgesFrom( vn, null, null, true );
2756 // everything visited in a traversal is
2757 // considered abstractly live
2758 Set<RefSrcNode> visited = new HashSet<RefSrcNode>();
2760 while( !toVisit.isEmpty() ) {
2761 RefSrcNode rsn = toVisit.iterator().next();
2762 toVisit.remove( rsn );
2765 Iterator<RefEdge> hrnItr = rsn.iteratorToReferencees();
2766 while( hrnItr.hasNext() ) {
2767 RefEdge edge = hrnItr.next();
2768 HeapRegionNode hrn = edge.getDst();
2770 if( !visited.contains( hrn ) ) {
2776 // get a copy of the set to iterate over because
2777 // we're going to monkey with the graph when we
2778 // identify a garbage node
2779 Set<HeapRegionNode> hrnAllPrior = new HashSet<HeapRegionNode>();
2780 Iterator<HeapRegionNode> hrnItr = id2hrn.values().iterator();
2781 while( hrnItr.hasNext() ) {
2782 hrnAllPrior.add( hrnItr.next() );
2785 Iterator<HeapRegionNode> hrnAllItr = hrnAllPrior.iterator();
2786 while( hrnAllItr.hasNext() ) {
2787 HeapRegionNode hrn = hrnAllItr.next();
2789 if( !visited.contains( hrn ) ) {
2791 // heap region nodes are compared across ReachGraph
2792 // objects by their integer ID, so when discarding
2793 // garbage nodes we must also discard entries in
2794 // the ID -> heap region hashtable.
2795 id2hrn.remove( hrn.getID() );
2797 // RefEdge objects are two-way linked between
2798 // nodes, so when a node is identified as garbage,
2799 // actively clear references to and from it so
2800 // live nodes won't have dangling RefEdge's
2801 wipeOut( hrn, true );
2803 // if we just removed the last node from an allocation
2804 // site, it should be taken out of the ReachGraph's list
2805 AllocSite as = hrn.getAllocSite();
2806 if( !hasNodesOf( as ) ) {
2807 allocSites.remove( as );
2813 protected boolean hasNodesOf( AllocSite as ) {
2814 if( id2hrn.containsKey( as.getSummary() ) ) {
2818 for( int i = 0; i < allocationDepth; ++i ) {
2819 if( id2hrn.containsKey( as.getIthOldest( i ) ) ) {
2827 ////////////////////////////////////////////////////
2829 // This global sweep is an optional step to prune
2830 // reachability sets that are not internally
2831 // consistent with the global graph. It should be
2832 // invoked after strong updates or method calls.
2834 ////////////////////////////////////////////////////
2835 public void globalSweep() {
2837 // boldB is part of the phase 1 sweep
2838 // it has an in-context table and an out-of-context table
2839 Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBic =
2840 new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();
2842 Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBooc =
2843 new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();
2845 // visit every heap region to initialize alphaNew and betaNew,
2846 // and make a map of every hrnID to the source nodes it should
2847 // propagate forward from. In-context flagged hrnID's propagate
2848 // from only the in-context node they name, but out-of-context
2849 // ID's may propagate from several out-of-context nodes
2850 Hashtable< Integer, Set<HeapRegionNode> > icID2srcs =
2851 new Hashtable< Integer, Set<HeapRegionNode> >();
2853 Hashtable< Integer, Set<HeapRegionNode> > oocID2srcs =
2854 new Hashtable< Integer, Set<HeapRegionNode> >();
2857 Iterator itrHrns = id2hrn.entrySet().iterator();
2858 while( itrHrns.hasNext() ) {
2859 Map.Entry me = (Map.Entry) itrHrns.next();
2860 Integer hrnID = (Integer) me.getKey();
2861 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
2863 // assert that this node and incoming edges have clean alphaNew
2864 // and betaNew sets, respectively
2865 assert rsetEmpty.equals( hrn.getAlphaNew() );
2867 Iterator<RefEdge> itrRers = hrn.iteratorToReferencers();
2868 while( itrRers.hasNext() ) {
2869 RefEdge edge = itrRers.next();
2870 assert rsetEmpty.equals( edge.getBetaNew() );
2873 // make a mapping of IDs to heap regions they propagate from
2874 if( hrn.isFlagged() ) {
2875 assert !hrn.isOutOfContext();
2876 assert !icID2srcs.containsKey( hrn.getID() );
2878 // in-context flagged node IDs simply propagate from the
2880 Set<HeapRegionNode> srcs = new HashSet<HeapRegionNode>();
2882 icID2srcs.put( hrn.getID(), srcs );
2885 if( hrn.isOutOfContext() ) {
2886 assert !hrn.isFlagged();
2888 // the reachability states on an out-of-context
2889 // node are not really important (combinations of
2890 // IDs or arity)--what matters is that the states
2891 // specify which nodes this out-of-context node
2892 // stands in for. For example, if the state [17?, 19*]
2893 // appears on the ooc node, it may serve as a source
2894 // for node 17? and a source for node 19.
2895 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
2896 while( stateItr.hasNext() ) {
2897 ReachState state = stateItr.next();
2899 Iterator<ReachTuple> rtItr = state.iterator();
2900 while( rtItr.hasNext() ) {
2901 ReachTuple rt = rtItr.next();
2902 assert rt.isOutOfContext();
2904 Set<HeapRegionNode> srcs = oocID2srcs.get( rt.getHrnID() );
2905 if( srcs == null ) {
2906 srcs = new HashSet<HeapRegionNode>();
2909 oocID2srcs.put( rt.getHrnID(), srcs );
2915 // calculate boldB for all hrnIDs identified by the above
2916 // node traversal, propagating from every source
2917 while( !icID2srcs.isEmpty() || !oocID2srcs.isEmpty() ) {
2920 Set<HeapRegionNode> srcs;
2923 if( !icID2srcs.isEmpty() ) {
2924 Map.Entry me = (Map.Entry) icID2srcs.entrySet().iterator().next();
2925 hrnID = (Integer) me.getKey();
2926 srcs = (Set<HeapRegionNode>) me.getValue();
2928 icID2srcs.remove( hrnID );
2931 assert !oocID2srcs.isEmpty();
2933 Map.Entry me = (Map.Entry) oocID2srcs.entrySet().iterator().next();
2934 hrnID = (Integer) me.getKey();
2935 srcs = (Set<HeapRegionNode>) me.getValue();
2937 oocID2srcs.remove( hrnID );
2941 Hashtable<RefEdge, ReachSet> boldB_f =
2942 new Hashtable<RefEdge, ReachSet>();
2944 Set<RefEdge> workSetEdges = new HashSet<RefEdge>();
2946 Iterator<HeapRegionNode> hrnItr = srcs.iterator();
2947 while( hrnItr.hasNext() ) {
2948 HeapRegionNode hrn = hrnItr.next();
2950 assert workSetEdges.isEmpty();
2952 // initial boldB_f constraints
2953 Iterator<RefEdge> itrRees = hrn.iteratorToReferencees();
2954 while( itrRees.hasNext() ) {
2955 RefEdge edge = itrRees.next();
2957 assert !boldB_f.containsKey( edge );
2958 boldB_f.put( edge, edge.getBeta() );
2960 assert !workSetEdges.contains( edge );
2961 workSetEdges.add( edge );
2964 // enforce the boldB_f constraint at edges until we reach a fixed point
2965 while( !workSetEdges.isEmpty() ) {
2966 RefEdge edge = workSetEdges.iterator().next();
2967 workSetEdges.remove( edge );
2969 Iterator<RefEdge> itrPrime = edge.getDst().iteratorToReferencees();
2970 while( itrPrime.hasNext() ) {
2971 RefEdge edgePrime = itrPrime.next();
2973 ReachSet prevResult = boldB_f.get( edgePrime );
2974 ReachSet intersection = Canonical.intersection( boldB_f.get( edge ),
2978 if( prevResult == null ||
2979 Canonical.unionORpreds( prevResult,
2980 intersection ).size()
2984 if( prevResult == null ) {
2985 boldB_f.put( edgePrime,
2986 Canonical.unionORpreds( edgePrime.getBeta(),
2991 boldB_f.put( edgePrime,
2992 Canonical.unionORpreds( prevResult,
2997 workSetEdges.add( edgePrime );
3004 boldBic.put( hrnID, boldB_f );
3006 boldBooc.put( hrnID, boldB_f );
3011 // use boldB to prune hrnIDs from alpha states that are impossible
3012 // and propagate the differences backwards across edges
3013 HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
3015 Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
3016 new Hashtable<RefEdge, ChangeSet>();
3019 itrHrns = id2hrn.entrySet().iterator();
3020 while( itrHrns.hasNext() ) {
3021 Map.Entry me = (Map.Entry) itrHrns.next();
3022 Integer hrnID = (Integer) me.getKey();
3023 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3025 // out-of-context nodes don't participate in the
3026 // global sweep, they serve as sources for the pass
3028 if( hrn.isOutOfContext() ) {
3032 // the inherent states of a region are the exception
3033 // to removal as the global sweep prunes
3034 ReachTuple rtException = ReachTuple.factory( hrnID,
3035 !hrn.isSingleObject(),
3036 ReachTuple.ARITY_ONE,
3037 false // out-of-context
3040 ChangeSet cts = ChangeSet.factory();
3042 // mark hrnIDs for removal
3043 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3044 while( stateItr.hasNext() ) {
3045 ReachState stateOld = stateItr.next();
3047 ReachState markedHrnIDs = ReachState.factory();
3049 Iterator<ReachTuple> rtItr = stateOld.iterator();
3050 while( rtItr.hasNext() ) {
3051 ReachTuple rtOld = rtItr.next();
3053 // never remove the inherent hrnID from a flagged region
3054 // because it is trivially satisfied
3055 if( hrn.isFlagged() ) {
3056 if( rtOld == rtException ) {
3061 // does boldB allow this hrnID?
3062 boolean foundState = false;
3063 Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3064 while( incidentEdgeItr.hasNext() ) {
3065 RefEdge incidentEdge = incidentEdgeItr.next();
3067 Hashtable<RefEdge, ReachSet> B;
3068 if( rtOld.isOutOfContext() ) {
3069 B = boldBooc.get( rtOld.getHrnID() );
3072 if( !id2hrn.containsKey( rtOld.getHrnID() ) ) {
3073 // let symbols not in the graph get pruned
3077 B = boldBic.get( rtOld.getHrnID() );
3081 ReachSet boldB_rtOld_incident = B.get( incidentEdge );
3082 if( boldB_rtOld_incident != null &&
3083 boldB_rtOld_incident.containsIgnorePreds( stateOld ) != null
3091 markedHrnIDs = Canonical.add( markedHrnIDs, rtOld );
3095 // if there is nothing marked, just move on
3096 if( markedHrnIDs.isEmpty() ) {
3097 hrn.setAlphaNew( Canonical.add( hrn.getAlphaNew(),
3104 // remove all marked hrnIDs and establish a change set that should
3105 // propagate backwards over edges from this node
3106 ReachState statePruned = ReachState.factory();
3107 rtItr = stateOld.iterator();
3108 while( rtItr.hasNext() ) {
3109 ReachTuple rtOld = rtItr.next();
3111 if( !markedHrnIDs.containsTuple( rtOld ) ) {
3112 statePruned = Canonical.add( statePruned, rtOld );
3115 assert !stateOld.equals( statePruned );
3117 hrn.setAlphaNew( Canonical.add( hrn.getAlphaNew(),
3121 ChangeTuple ct = ChangeTuple.factory( stateOld,
3124 cts = Canonical.add( cts, ct );
3127 // throw change tuple set on all incident edges
3128 if( !cts.isEmpty() ) {
3129 Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3130 while( incidentEdgeItr.hasNext() ) {
3131 RefEdge incidentEdge = incidentEdgeItr.next();
3133 edgesForPropagation.add( incidentEdge );
3135 if( edgePlannedChanges.get( incidentEdge ) == null ) {
3136 edgePlannedChanges.put( incidentEdge, cts );
3138 edgePlannedChanges.put(
3140 Canonical.union( edgePlannedChanges.get( incidentEdge ),
3149 HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
3151 propagateTokensOverEdges( edgesForPropagation,
3155 // at the end of the 1st phase reference edges have
3156 // beta, betaNew that correspond to beta and betaR
3158 // commit beta<-betaNew, so beta=betaR and betaNew
3159 // will represent the beta' calculation in 2nd phase
3161 // commit alpha<-alphaNew because it won't change
3162 HashSet<RefEdge> res = new HashSet<RefEdge>();
3164 Iterator<HeapRegionNode> nodeItr = id2hrn.values().iterator();
3165 while( nodeItr.hasNext() ) {
3166 HeapRegionNode hrn = nodeItr.next();
3168 // as mentioned above, out-of-context nodes only serve
3169 // as sources of reach states for the sweep, not part
3171 if( hrn.isOutOfContext() ) {
3172 assert hrn.getAlphaNew().equals( rsetEmpty );
3174 hrn.applyAlphaNew();
3177 Iterator<RefEdge> itrRes = hrn.iteratorToReferencers();
3178 while( itrRes.hasNext() ) {
3179 res.add( itrRes.next() );
3185 Iterator<RefEdge> edgeItr = res.iterator();
3186 while( edgeItr.hasNext() ) {
3187 RefEdge edge = edgeItr.next();
3188 HeapRegionNode hrn = edge.getDst();
3190 // commit results of last phase
3191 if( edgesUpdated.contains( edge ) ) {
3192 edge.applyBetaNew();
3195 // compute intial condition of 2nd phase
3196 edge.setBetaNew( Canonical.intersection( edge.getBeta(),
3202 // every edge in the graph is the initial workset
3203 Set<RefEdge> edgeWorkSet = (Set) res.clone();
3204 while( !edgeWorkSet.isEmpty() ) {
3205 RefEdge edgePrime = edgeWorkSet.iterator().next();
3206 edgeWorkSet.remove( edgePrime );
3208 RefSrcNode rsn = edgePrime.getSrc();
3209 if( !(rsn instanceof HeapRegionNode) ) {
3212 HeapRegionNode hrn = (HeapRegionNode) rsn;
3214 Iterator<RefEdge> itrEdge = hrn.iteratorToReferencers();
3215 while( itrEdge.hasNext() ) {
3216 RefEdge edge = itrEdge.next();
3218 ReachSet prevResult = edge.getBetaNew();
3219 assert prevResult != null;
3221 ReachSet intersection =
3222 Canonical.intersection( edge.getBeta(),
3223 edgePrime.getBetaNew()
3226 if( Canonical.unionORpreds( prevResult,
3233 Canonical.unionORpreds( prevResult,
3237 edgeWorkSet.add( edge );
3242 // commit beta' (beta<-betaNew)
3243 edgeItr = res.iterator();
3244 while( edgeItr.hasNext() ) {
3245 edgeItr.next().applyBetaNew();
3250 // a useful assertion for debugging:
3251 // every in-context tuple on any edge or
3252 // any node should name a node that is
3253 // part of the graph
3254 public boolean inContextTuplesInGraph() {
3256 Iterator hrnItr = id2hrn.entrySet().iterator();
3257 while( hrnItr.hasNext() ) {
3258 Map.Entry me = (Map.Entry) hrnItr.next();
3259 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3262 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3263 while( stateItr.hasNext() ) {
3264 ReachState state = stateItr.next();
3266 Iterator<ReachTuple> rtItr = state.iterator();
3267 while( rtItr.hasNext() ) {
3268 ReachTuple rt = rtItr.next();
3270 if( !rt.isOutOfContext() ) {
3271 if( !id2hrn.containsKey( rt.getHrnID() ) ) {
3272 System.out.println( rt.getHrnID()+" is missing" );
3280 Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3281 while( edgeItr.hasNext() ) {
3282 RefEdge edge = edgeItr.next();
3284 Iterator<ReachState> stateItr = edge.getBeta().iterator();
3285 while( stateItr.hasNext() ) {
3286 ReachState state = stateItr.next();
3288 Iterator<ReachTuple> rtItr = state.iterator();
3289 while( rtItr.hasNext() ) {
3290 ReachTuple rt = rtItr.next();
3292 if( !rt.isOutOfContext() ) {
3293 if( !id2hrn.containsKey( rt.getHrnID() ) ) {
3294 System.out.println( rt.getHrnID()+" is missing" );
3307 // another useful assertion for debugging
3308 public boolean noEmptyReachSetsInGraph() {
3310 Iterator hrnItr = id2hrn.entrySet().iterator();
3311 while( hrnItr.hasNext() ) {
3312 Map.Entry me = (Map.Entry) hrnItr.next();
3313 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3315 if( !hrn.isOutOfContext() &&
3317 hrn.getAlpha().isEmpty()
3319 System.out.println( "!!! "+hrn+" has an empty ReachSet !!!" );
3323 Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3324 while( edgeItr.hasNext() ) {
3325 RefEdge edge = edgeItr.next();
3327 if( edge.getBeta().isEmpty() ) {
3328 System.out.println( "!!! "+edge+" has an empty ReachSet !!!" );
3338 public boolean everyReachStateWTrue() {
3340 Iterator hrnItr = id2hrn.entrySet().iterator();
3341 while( hrnItr.hasNext() ) {
3342 Map.Entry me = (Map.Entry) hrnItr.next();
3343 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3346 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3347 while( stateItr.hasNext() ) {
3348 ReachState state = stateItr.next();
3350 if( !state.getPreds().equals( predsTrue ) ) {
3356 Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3357 while( edgeItr.hasNext() ) {
3358 RefEdge edge = edgeItr.next();
3360 Iterator<ReachState> stateItr = edge.getBeta().iterator();
3361 while( stateItr.hasNext() ) {
3362 ReachState state = stateItr.next();
3364 if( !state.getPreds().equals( predsTrue ) ) {
3377 ////////////////////////////////////////////////////
3378 // high-level merge operations
3379 ////////////////////////////////////////////////////
3380 public void merge_sameMethodContext( ReachGraph rg ) {
3381 // when merging two graphs that abstract the heap
3382 // of the same method context, we just call the
3383 // basic merge operation
3387 public void merge_diffMethodContext( ReachGraph rg ) {
3388 // when merging graphs for abstract heaps in
3389 // different method contexts we should:
3390 // 1) age the allocation sites?
3394 ////////////////////////////////////////////////////
3395 // in merge() and equals() methods the suffix A
3396 // represents the passed in graph and the suffix
3397 // B refers to the graph in this object
3398 // Merging means to take the incoming graph A and
3399 // merge it into B, so after the operation graph B
3400 // is the final result.
3401 ////////////////////////////////////////////////////
3402 protected void merge( ReachGraph rg ) {
3409 mergeRefEdges ( rg );
3410 mergeAllocSites( rg );
3413 protected void mergeNodes( ReachGraph rg ) {
3415 // start with heap region nodes
3416 Set sA = rg.id2hrn.entrySet();
3417 Iterator iA = sA.iterator();
3418 while( iA.hasNext() ) {
3419 Map.Entry meA = (Map.Entry) iA.next();
3420 Integer idA = (Integer) meA.getKey();
3421 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3423 // if this graph doesn't have a node the
3424 // incoming graph has, allocate it
3425 if( !id2hrn.containsKey( idA ) ) {
3426 HeapRegionNode hrnB = hrnA.copy();
3427 id2hrn.put( idA, hrnB );
3430 // otherwise this is a node present in both graphs
3431 // so make the new reachability set a union of the
3432 // nodes' reachability sets
3433 HeapRegionNode hrnB = id2hrn.get( idA );
3434 hrnB.setAlpha( Canonical.unionORpreds( hrnB.getAlpha(),
3439 hrnB.setPreds( Canonical.join( hrnB.getPreds(),
3446 // now add any variable nodes that are in graph B but
3448 sA = rg.td2vn.entrySet();
3450 while( iA.hasNext() ) {
3451 Map.Entry meA = (Map.Entry) iA.next();
3452 TempDescriptor tdA = (TempDescriptor) meA.getKey();
3453 VariableNode lnA = (VariableNode) meA.getValue();
3455 // if the variable doesn't exist in B, allocate and add it
3456 VariableNode lnB = getVariableNodeFromTemp( tdA );
3460 protected void mergeRefEdges( ReachGraph rg ) {
3462 // between heap regions
3463 Set sA = rg.id2hrn.entrySet();
3464 Iterator iA = sA.iterator();
3465 while( iA.hasNext() ) {
3466 Map.Entry meA = (Map.Entry) iA.next();
3467 Integer idA = (Integer) meA.getKey();
3468 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3470 Iterator<RefEdge> heapRegionsItrA = hrnA.iteratorToReferencees();
3471 while( heapRegionsItrA.hasNext() ) {
3472 RefEdge edgeA = heapRegionsItrA.next();
3473 HeapRegionNode hrnChildA = edgeA.getDst();
3474 Integer idChildA = hrnChildA.getID();
3476 // at this point we know an edge in graph A exists
3477 // idA -> idChildA, does this exist in B?
3478 assert id2hrn.containsKey( idA );
3479 HeapRegionNode hrnB = id2hrn.get( idA );
3480 RefEdge edgeToMerge = null;
3482 Iterator<RefEdge> heapRegionsItrB = hrnB.iteratorToReferencees();
3483 while( heapRegionsItrB.hasNext() &&
3484 edgeToMerge == null ) {
3486 RefEdge edgeB = heapRegionsItrB.next();
3487 HeapRegionNode hrnChildB = edgeB.getDst();
3488 Integer idChildB = hrnChildB.getID();
3490 // don't use the RefEdge.equals() here because
3491 // we're talking about existence between graphs,
3492 // not intragraph equal
3493 if( idChildB.equals( idChildA ) &&
3494 edgeB.typeAndFieldEquals( edgeA ) ) {
3496 edgeToMerge = edgeB;
3500 // if the edge from A was not found in B,
3502 if( edgeToMerge == null ) {
3503 assert id2hrn.containsKey( idChildA );
3504 HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3505 edgeToMerge = edgeA.copy();
3506 edgeToMerge.setSrc( hrnB );
3507 edgeToMerge.setDst( hrnChildB );
3508 addRefEdge( hrnB, hrnChildB, edgeToMerge );
3510 // otherwise, the edge already existed in both graphs
3511 // so merge their reachability sets
3513 // just replace this beta set with the union
3514 assert edgeToMerge != null;
3515 edgeToMerge.setBeta(
3516 Canonical.unionORpreds( edgeToMerge.getBeta(),
3520 edgeToMerge.setPreds(
3521 Canonical.join( edgeToMerge.getPreds(),
3529 // and then again from variable nodes
3530 sA = rg.td2vn.entrySet();
3532 while( iA.hasNext() ) {
3533 Map.Entry meA = (Map.Entry) iA.next();
3534 TempDescriptor tdA = (TempDescriptor) meA.getKey();
3535 VariableNode vnA = (VariableNode) meA.getValue();
3537 Iterator<RefEdge> heapRegionsItrA = vnA.iteratorToReferencees();
3538 while( heapRegionsItrA.hasNext() ) {
3539 RefEdge edgeA = heapRegionsItrA.next();
3540 HeapRegionNode hrnChildA = edgeA.getDst();
3541 Integer idChildA = hrnChildA.getID();
3543 // at this point we know an edge in graph A exists
3544 // tdA -> idChildA, does this exist in B?
3545 assert td2vn.containsKey( tdA );
3546 VariableNode vnB = td2vn.get( tdA );
3547 RefEdge edgeToMerge = null;
3549 Iterator<RefEdge> heapRegionsItrB = vnB.iteratorToReferencees();
3550 while( heapRegionsItrB.hasNext() &&
3551 edgeToMerge == null ) {
3553 RefEdge edgeB = heapRegionsItrB.next();
3554 HeapRegionNode hrnChildB = edgeB.getDst();
3555 Integer idChildB = hrnChildB.getID();
3557 // don't use the RefEdge.equals() here because
3558 // we're talking about existence between graphs
3559 if( idChildB.equals( idChildA ) &&
3560 edgeB.typeAndFieldEquals( edgeA ) ) {
3562 edgeToMerge = edgeB;
3566 // if the edge from A was not found in B,
3568 if( edgeToMerge == null ) {
3569 assert id2hrn.containsKey( idChildA );
3570 HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3571 edgeToMerge = edgeA.copy();
3572 edgeToMerge.setSrc( vnB );
3573 edgeToMerge.setDst( hrnChildB );
3574 addRefEdge( vnB, hrnChildB, edgeToMerge );
3576 // otherwise, the edge already existed in both graphs
3577 // so merge their reachability sets
3579 // just replace this beta set with the union
3580 edgeToMerge.setBeta( Canonical.unionORpreds( edgeToMerge.getBeta(),
3584 edgeToMerge.setPreds( Canonical.join( edgeToMerge.getPreds(),
3593 protected void mergeAllocSites( ReachGraph rg ) {
3594 allocSites.addAll( rg.allocSites );
3599 static boolean dbgEquals = false;
3602 // it is necessary in the equals() member functions
3603 // to "check both ways" when comparing the data
3604 // structures of two graphs. For instance, if all
3605 // edges between heap region nodes in graph A are
3606 // present and equal in graph B it is not sufficient
3607 // to say the graphs are equal. Consider that there
3608 // may be edges in graph B that are not in graph A.
3609 // the only way to know that all edges in both graphs
3610 // are equally present is to iterate over both data
3611 // structures and compare against the other graph.
3612 public boolean equals( ReachGraph rg ) {
3616 System.out.println( "rg is null" );
3621 if( !areHeapRegionNodesEqual( rg ) ) {
3623 System.out.println( "hrn not equal" );
3628 if( !areVariableNodesEqual( rg ) ) {
3630 System.out.println( "vars not equal" );
3635 if( !areRefEdgesEqual( rg ) ) {
3637 System.out.println( "edges not equal" );
3642 // if everything is equal up to this point,
3643 // assert that allocSites is also equal--
3644 // this data is redundant but kept for efficiency
3645 assert allocSites.equals( rg.allocSites );
3651 protected boolean areHeapRegionNodesEqual( ReachGraph rg ) {
3653 if( !areallHRNinAalsoinBandequal( this, rg ) ) {
3657 if( !areallHRNinAalsoinBandequal( rg, this ) ) {
3664 static protected boolean areallHRNinAalsoinBandequal( ReachGraph rgA,
3666 Set sA = rgA.id2hrn.entrySet();
3667 Iterator iA = sA.iterator();
3668 while( iA.hasNext() ) {
3669 Map.Entry meA = (Map.Entry) iA.next();
3670 Integer idA = (Integer) meA.getKey();
3671 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3673 if( !rgB.id2hrn.containsKey( idA ) ) {
3677 HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3678 if( !hrnA.equalsIncludingAlphaAndPreds( hrnB ) ) {
3686 protected boolean areVariableNodesEqual( ReachGraph rg ) {
3688 if( !areallVNinAalsoinBandequal( this, rg ) ) {
3692 if( !areallVNinAalsoinBandequal( rg, this ) ) {
3699 static protected boolean areallVNinAalsoinBandequal( ReachGraph rgA,
3701 Set sA = rgA.td2vn.entrySet();
3702 Iterator iA = sA.iterator();
3703 while( iA.hasNext() ) {
3704 Map.Entry meA = (Map.Entry) iA.next();
3705 TempDescriptor tdA = (TempDescriptor) meA.getKey();
3707 if( !rgB.td2vn.containsKey( tdA ) ) {
3716 protected boolean areRefEdgesEqual( ReachGraph rg ) {
3717 if( !areallREinAandBequal( this, rg ) ) {
3726 static protected boolean areallREinAandBequal( ReachGraph rgA,
3729 // check all the heap region->heap region edges
3730 Set sA = rgA.id2hrn.entrySet();
3731 Iterator iA = sA.iterator();
3732 while( iA.hasNext() ) {
3733 Map.Entry meA = (Map.Entry) iA.next();
3734 Integer idA = (Integer) meA.getKey();
3735 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3737 // we should have already checked that the same
3738 // heap regions exist in both graphs
3739 assert rgB.id2hrn.containsKey( idA );
3741 if( !areallREfromAequaltoB( rgA, hrnA, rgB ) ) {
3745 // then check every edge in B for presence in A, starting
3746 // from the same parent HeapRegionNode
3747 HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3749 if( !areallREfromAequaltoB( rgB, hrnB, rgA ) ) {
3754 // then check all the variable->heap region edges
3755 sA = rgA.td2vn.entrySet();
3757 while( iA.hasNext() ) {
3758 Map.Entry meA = (Map.Entry) iA.next();
3759 TempDescriptor tdA = (TempDescriptor) meA.getKey();
3760 VariableNode vnA = (VariableNode) meA.getValue();
3762 // we should have already checked that the same
3763 // label nodes exist in both graphs
3764 assert rgB.td2vn.containsKey( tdA );
3766 if( !areallREfromAequaltoB( rgA, vnA, rgB ) ) {
3770 // then check every edge in B for presence in A, starting
3771 // from the same parent VariableNode
3772 VariableNode vnB = rgB.td2vn.get( tdA );
3774 if( !areallREfromAequaltoB( rgB, vnB, rgA ) ) {
3783 static protected boolean areallREfromAequaltoB( ReachGraph rgA,
3787 Iterator<RefEdge> itrA = rnA.iteratorToReferencees();
3788 while( itrA.hasNext() ) {
3789 RefEdge edgeA = itrA.next();
3790 HeapRegionNode hrnChildA = edgeA.getDst();
3791 Integer idChildA = hrnChildA.getID();
3793 assert rgB.id2hrn.containsKey( idChildA );
3795 // at this point we know an edge in graph A exists
3796 // rnA -> idChildA, does this exact edge exist in B?
3797 boolean edgeFound = false;
3799 RefSrcNode rnB = null;
3800 if( rnA instanceof HeapRegionNode ) {
3801 HeapRegionNode hrnA = (HeapRegionNode) rnA;
3802 rnB = rgB.id2hrn.get( hrnA.getID() );
3804 VariableNode vnA = (VariableNode) rnA;
3805 rnB = rgB.td2vn.get( vnA.getTempDescriptor() );
3808 Iterator<RefEdge> itrB = rnB.iteratorToReferencees();
3809 while( itrB.hasNext() ) {
3810 RefEdge edgeB = itrB.next();
3811 HeapRegionNode hrnChildB = edgeB.getDst();
3812 Integer idChildB = hrnChildB.getID();
3814 if( idChildA.equals( idChildB ) &&
3815 edgeA.typeAndFieldEquals( edgeB ) ) {
3817 // there is an edge in the right place with the right field,
3818 // but do they have the same attributes?
3819 if( edgeA.getBeta().equals( edgeB.getBeta() ) &&
3820 edgeA.equalsPreds( edgeB )
3836 // can be used to assert monotonicity
3837 static public boolean isNoSmallerThan( ReachGraph rgA,
3840 //System.out.println( "*** Asking if A is no smaller than B ***" );
3843 Iterator iA = rgA.id2hrn.entrySet().iterator();
3844 while( iA.hasNext() ) {
3845 Map.Entry meA = (Map.Entry) iA.next();
3846 Integer idA = (Integer) meA.getKey();
3847 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3849 if( !rgB.id2hrn.containsKey( idA ) ) {
3850 System.out.println( " regions smaller" );
3854 //HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3855 /* NOT EQUALS, NO SMALLER THAN!
3856 if( !hrnA.equalsIncludingAlphaAndPreds( hrnB ) ) {
3857 System.out.println( " regions smaller" );
3863 // this works just fine, no smaller than
3864 if( !areallVNinAalsoinBandequal( rgA, rgB ) ) {
3865 System.out.println( " vars smaller:" );
3866 System.out.println( " A:"+rgA.td2vn.keySet() );
3867 System.out.println( " B:"+rgB.td2vn.keySet() );
3872 iA = rgA.id2hrn.entrySet().iterator();
3873 while( iA.hasNext() ) {
3874 Map.Entry meA = (Map.Entry) iA.next();
3875 Integer idA = (Integer) meA.getKey();
3876 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3878 Iterator<RefEdge> reItr = hrnA.iteratorToReferencers();
3879 while( reItr.hasNext() ) {
3880 RefEdge edgeA = reItr.next();
3881 RefSrcNode rsnA = edgeA.getSrc();
3883 // we already checked that nodes were present
3884 HeapRegionNode hrnB = rgB.id2hrn.get( hrnA.getID() );
3885 assert hrnB != null;
3888 if( rsnA instanceof VariableNode ) {
3889 VariableNode vnA = (VariableNode) rsnA;
3890 rsnB = rgB.td2vn.get( vnA.getTempDescriptor() );
3893 HeapRegionNode hrnSrcA = (HeapRegionNode) rsnA;
3894 rsnB = rgB.id2hrn.get( hrnSrcA.getID() );
3896 assert rsnB != null;
3898 RefEdge edgeB = rsnB.getReferenceTo( hrnB,
3902 if( edgeB == null ) {
3903 System.out.println( " edges smaller:" );
3907 // REMEMBER, IS NO SMALLER THAN
3909 System.out.println( " edges smaller" );
3925 // this analysis no longer has the "match anything"
3926 // type which was represented by null
3927 protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3928 TypeDescriptor td2 ) {
3932 if( td1.isNull() ) {
3935 if( td2.isNull() ) {
3938 return typeUtil.mostSpecific( td1, td2 );
3941 protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3943 TypeDescriptor td3 ) {
3945 return mostSpecificType( td1,
3946 mostSpecificType( td2, td3 )
3950 protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
3953 TypeDescriptor td4 ) {
3955 return mostSpecificType( mostSpecificType( td1, td2 ),
3956 mostSpecificType( td3, td4 )
3960 protected boolean isSuperiorType( TypeDescriptor possibleSuper,
3961 TypeDescriptor possibleChild ) {
3962 assert possibleSuper != null;
3963 assert possibleChild != null;
3965 if( possibleSuper.isNull() ||
3966 possibleChild.isNull() ) {
3970 return typeUtil.isSuperorType( possibleSuper, possibleChild );
3974 protected boolean hasMatchingField( HeapRegionNode src,
3977 TypeDescriptor tdSrc = src.getType();
3978 assert tdSrc != null;
3980 if( tdSrc.isArray() ) {
3981 TypeDescriptor td = edge.getType();
3984 TypeDescriptor tdSrcDeref = tdSrc.dereference();
3985 assert tdSrcDeref != null;
3987 if( !typeUtil.isSuperorType( tdSrcDeref, td ) ) {
3991 return edge.getField().equals( DisjointAnalysis.arrayElementFieldName );
3994 // if it's not a class, it doesn't have any fields to match
3995 if( !tdSrc.isClass() ) {
3999 ClassDescriptor cd = tdSrc.getClassDesc();
4000 while( cd != null ) {
4001 Iterator fieldItr = cd.getFields();
4003 while( fieldItr.hasNext() ) {
4004 FieldDescriptor fd = (FieldDescriptor) fieldItr.next();
4006 if( fd.getType().equals( edge.getType() ) &&
4007 fd.getSymbol().equals( edge.getField() ) ) {
4012 cd = cd.getSuperDesc();
4015 // otherwise it is a class with fields
4016 // but we didn't find a match
4020 protected boolean hasMatchingType( RefEdge edge,
4021 HeapRegionNode dst ) {
4023 // if the region has no type, matches everything
4024 TypeDescriptor tdDst = dst.getType();
4025 assert tdDst != null;
4027 // if the type is not a class or an array, don't
4028 // match because primitives are copied, no aliases
4029 ClassDescriptor cdDst = tdDst.getClassDesc();
4030 if( cdDst == null && !tdDst.isArray() ) {
4034 // if the edge type is null, it matches everything
4035 TypeDescriptor tdEdge = edge.getType();
4036 assert tdEdge != null;
4038 return typeUtil.isSuperorType( tdEdge, tdDst );
4043 // the default signature for quick-and-dirty debugging
4044 public void writeGraph( String graphName ) {
4045 writeGraph( graphName,
4046 true, // write labels
4047 true, // label select
4048 true, // prune garbage
4049 true, // hide subset reachability
4050 true, // hide edge taints
4051 null // in-context boundary
4055 public void writeGraph( String graphName,
4056 boolean writeLabels,
4057 boolean labelSelect,
4058 boolean pruneGarbage,
4059 boolean hideSubsetReachability,
4060 boolean hideEdgeTaints
4062 writeGraph( graphName,
4066 hideSubsetReachability,
4071 public void writeGraph( String graphName,
4072 boolean writeLabels,
4073 boolean labelSelect,
4074 boolean pruneGarbage,
4075 boolean hideSubsetReachability,
4076 boolean hideEdgeTaints,
4077 Set<Integer> callerNodeIDsCopiedToCallee
4081 // remove all non-word characters from the graph name so
4082 // the filename and identifier in dot don't cause errors
4083 graphName = graphName.replaceAll( "[\\W]", "" );
4086 new BufferedWriter( new FileWriter( graphName+".dot" ) );
4088 bw.write( "digraph "+graphName+" {\n" );
4091 // this is an optional step to form the callee-reachable
4092 // "cut-out" into a DOT cluster for visualization
4093 if( callerNodeIDsCopiedToCallee != null ) {
4095 bw.write( " subgraph cluster0 {\n" );
4096 bw.write( " color=blue;\n" );
4098 Iterator i = id2hrn.entrySet().iterator();
4099 while( i.hasNext() ) {
4100 Map.Entry me = (Map.Entry) i.next();
4101 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4103 if( callerNodeIDsCopiedToCallee.contains( hrn.getID() ) ) {
4104 bw.write( " "+hrn.toString()+
4105 hrn.toStringDOT( hideSubsetReachability )+
4115 Set<HeapRegionNode> visited = new HashSet<HeapRegionNode>();
4117 // then visit every heap region node
4118 Iterator i = id2hrn.entrySet().iterator();
4119 while( i.hasNext() ) {
4120 Map.Entry me = (Map.Entry) i.next();
4121 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4123 // only visit nodes worth writing out--for instance
4124 // not every node at an allocation is referenced
4125 // (think of it as garbage-collected), etc.
4126 if( !pruneGarbage ||
4127 hrn.isOutOfContext()
4130 if( !visited.contains( hrn ) ) {
4131 traverseHeapRegionNodes( hrn,
4135 hideSubsetReachability,
4137 callerNodeIDsCopiedToCallee );
4142 bw.write( " graphTitle[label=\""+graphName+"\",shape=box];\n" );
4145 // then visit every label node, useful for debugging
4147 i = td2vn.entrySet().iterator();
4148 while( i.hasNext() ) {
4149 Map.Entry me = (Map.Entry) i.next();
4150 VariableNode vn = (VariableNode) me.getValue();
4153 String labelStr = vn.getTempDescriptorString();
4154 if( labelStr.startsWith( "___temp" ) ||
4155 labelStr.startsWith( "___dst" ) ||
4156 labelStr.startsWith( "___srctmp" ) ||
4157 labelStr.startsWith( "___neverused" )
4163 Iterator<RefEdge> heapRegionsItr = vn.iteratorToReferencees();
4164 while( heapRegionsItr.hasNext() ) {
4165 RefEdge edge = heapRegionsItr.next();
4166 HeapRegionNode hrn = edge.getDst();
4168 if( !visited.contains( hrn ) ) {
4169 traverseHeapRegionNodes( hrn,
4173 hideSubsetReachability,
4175 callerNodeIDsCopiedToCallee );
4178 bw.write( " "+vn.toString()+
4179 " -> "+hrn.toString()+
4180 edge.toStringDOT( hideSubsetReachability, "" )+
4189 } catch( IOException e ) {
4190 throw new Error( "Error writing out DOT graph "+graphName );
4194 protected void traverseHeapRegionNodes( HeapRegionNode hrn,
4197 Set<HeapRegionNode> visited,
4198 boolean hideSubsetReachability,
4199 boolean hideEdgeTaints,
4200 Set<Integer> callerNodeIDsCopiedToCallee
4201 ) throws java.io.IOException {
4203 if( visited.contains( hrn ) ) {
4208 // if we're drawing the callee-view subgraph, only
4209 // write out the node info if it hasn't already been
4211 if( callerNodeIDsCopiedToCallee == null ||
4212 !callerNodeIDsCopiedToCallee.contains( hrn.getID() )
4214 bw.write( " "+hrn.toString()+
4215 hrn.toStringDOT( hideSubsetReachability )+
4219 Iterator<RefEdge> childRegionsItr = hrn.iteratorToReferencees();
4220 while( childRegionsItr.hasNext() ) {
4221 RefEdge edge = childRegionsItr.next();
4222 HeapRegionNode hrnChild = edge.getDst();
4224 if( callerNodeIDsCopiedToCallee != null &&
4225 (edge.getSrc() instanceof HeapRegionNode) ) {
4226 HeapRegionNode hrnSrc = (HeapRegionNode) edge.getSrc();
4227 if( callerNodeIDsCopiedToCallee.contains( hrnSrc.getID() ) &&
4228 callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
4230 bw.write( " "+hrn.toString()+
4231 " -> "+hrnChild.toString()+
4232 edge.toStringDOT( hideSubsetReachability, ",color=blue" )+
4234 } else if( !callerNodeIDsCopiedToCallee.contains( hrnSrc.getID() ) &&
4235 callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
4237 bw.write( " "+hrn.toString()+
4238 " -> "+hrnChild.toString()+
4239 edge.toStringDOT( hideSubsetReachability, ",color=blue,style=dashed" )+
4242 bw.write( " "+hrn.toString()+
4243 " -> "+hrnChild.toString()+
4244 edge.toStringDOT( hideSubsetReachability, "" )+
4248 bw.write( " "+hrn.toString()+
4249 " -> "+hrnChild.toString()+
4250 edge.toStringDOT( hideSubsetReachability, "" )+
4254 traverseHeapRegionNodes( hrnChild,
4258 hideSubsetReachability,
4260 callerNodeIDsCopiedToCallee );
4268 public Set<HeapRegionNode> findCommonReachableNodes( ReachSet proofOfSharing ) {
4270 Set<HeapRegionNode> exhibitProofState =
4271 new HashSet<HeapRegionNode>();
4273 Iterator hrnItr = id2hrn.entrySet().iterator();
4274 while( hrnItr.hasNext() ) {
4275 Map.Entry me = (Map.Entry) hrnItr.next();
4276 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4278 ReachSet intersection =
4279 Canonical.intersection( proofOfSharing,
4282 if( !intersection.isEmpty() ) {
4283 assert !hrn.isOutOfContext();
4284 exhibitProofState.add( hrn );
4288 return exhibitProofState;
4292 public Set<HeapRegionNode> mayReachSharedObjects(HeapRegionNode hrn1,
4293 HeapRegionNode hrn2) {
4294 assert hrn1 != null;
4295 assert hrn2 != null;
4297 assert !hrn1.isOutOfContext();
4298 assert !hrn2.isOutOfContext();
4300 assert belongsToThis( hrn1 );
4301 assert belongsToThis( hrn2 );
4303 assert !hrn1.getID().equals( hrn2.getID() );
4306 // then get the various tokens for these heap regions
4308 ReachTuple.factory( hrn1.getID(),
4309 !hrn1.isSingleObject(), // multi?
4310 ReachTuple.ARITY_ONE,
4313 ReachTuple h1star = null;
4314 if( !hrn1.isSingleObject() ) {
4316 ReachTuple.factory( hrn1.getID(),
4317 !hrn1.isSingleObject(),
4318 ReachTuple.ARITY_ZEROORMORE,
4323 ReachTuple.factory( hrn2.getID(),
4324 !hrn2.isSingleObject(),
4325 ReachTuple.ARITY_ONE,
4328 ReachTuple h2star = null;
4329 if( !hrn2.isSingleObject() ) {
4331 ReachTuple.factory( hrn2.getID(),
4332 !hrn2.isSingleObject(),
4333 ReachTuple.ARITY_ZEROORMORE,
4337 // then get the merged beta of all out-going edges from these heap
4340 ReachSet beta1 = ReachSet.factory();
4341 Iterator<RefEdge> itrEdge = hrn1.iteratorToReferencees();
4342 while (itrEdge.hasNext()) {
4343 RefEdge edge = itrEdge.next();
4344 beta1 = Canonical.unionORpreds(beta1, edge.getBeta());
4347 ReachSet beta2 = ReachSet.factory();
4348 itrEdge = hrn2.iteratorToReferencees();
4349 while (itrEdge.hasNext()) {
4350 RefEdge edge = itrEdge.next();
4351 beta2 = Canonical.unionORpreds(beta2, edge.getBeta());
4354 ReachSet proofOfSharing = ReachSet.factory();
4357 Canonical.unionORpreds( proofOfSharing,
4358 beta1.getStatesWithBoth( h1, h2 )
4361 Canonical.unionORpreds( proofOfSharing,
4362 beta2.getStatesWithBoth( h1, h2 )
4365 if( !hrn1.isSingleObject() ) {
4367 Canonical.unionORpreds( proofOfSharing,
4368 beta1.getStatesWithBoth( h1star, h2 )
4371 Canonical.unionORpreds( proofOfSharing,
4372 beta2.getStatesWithBoth( h1star, h2 )
4376 if( !hrn2.isSingleObject() ) {
4378 Canonical.unionORpreds( proofOfSharing,
4379 beta1.getStatesWithBoth( h1, h2star )
4382 Canonical.unionORpreds( proofOfSharing,
4383 beta2.getStatesWithBoth( h1, h2star )
4387 if( !hrn1.isSingleObject() &&
4388 !hrn2.isSingleObject()
4391 Canonical.unionORpreds( proofOfSharing,
4392 beta1.getStatesWithBoth( h1star, h2star )
4395 Canonical.unionORpreds( proofOfSharing,
4396 beta2.getStatesWithBoth( h1star, h2star )
4400 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4401 if( !proofOfSharing.isEmpty() ) {
4402 common = findCommonReachableNodes( proofOfSharing );
4403 if( !DISABLE_STRONG_UPDATES &&
4404 !DISABLE_GLOBAL_SWEEP
4406 assert !common.isEmpty();
4413 // this version of the above method checks whether there is sharing
4414 // among the objects in a summary node
4415 public Set<HeapRegionNode> mayReachSharedObjects(HeapRegionNode hrn) {
4417 assert hrn.isNewSummary();
4418 assert !hrn.isOutOfContext();
4419 assert belongsToThis( hrn );
4422 ReachTuple.factory( hrn.getID(),
4424 ReachTuple.ARITY_ZEROORMORE,
4427 // then get the merged beta of all out-going edges from
4430 ReachSet beta = ReachSet.factory();
4431 Iterator<RefEdge> itrEdge = hrn.iteratorToReferencees();
4432 while (itrEdge.hasNext()) {
4433 RefEdge edge = itrEdge.next();
4434 beta = Canonical.unionORpreds(beta, edge.getBeta());
4437 ReachSet proofOfSharing = ReachSet.factory();
4440 Canonical.unionORpreds( proofOfSharing,
4441 beta.getStatesWithBoth( hstar, hstar )
4444 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4445 if( !proofOfSharing.isEmpty() ) {
4446 common = findCommonReachableNodes( proofOfSharing );
4447 if( !DISABLE_STRONG_UPDATES &&
4448 !DISABLE_GLOBAL_SWEEP
4450 assert !common.isEmpty();
4458 public Set<HeapRegionNode> mayReachSharedObjects(FlatMethod fm,
4459 Integer paramIndex1,
4460 Integer paramIndex2) {
4462 // get parameter's heap regions
4463 TempDescriptor paramTemp1 = fm.getParameter(paramIndex1.intValue());
4464 assert this.hasVariable( paramTemp1 );
4465 VariableNode paramVar1 = getVariableNodeFromTemp(paramTemp1);
4468 if( !(paramVar1.getNumReferencees() == 1) ) {
4469 System.out.println( "\n fm="+fm+"\n param="+paramTemp1 );
4470 writeGraph( "whatup" );
4474 assert paramVar1.getNumReferencees() == 1;
4475 RefEdge paramEdge1 = paramVar1.iteratorToReferencees().next();
4476 HeapRegionNode hrnParam1 = paramEdge1.getDst();
4478 TempDescriptor paramTemp2 = fm.getParameter(paramIndex2.intValue());
4479 assert this.hasVariable( paramTemp2 );
4480 VariableNode paramVar2 = getVariableNodeFromTemp(paramTemp2);
4482 if( !(paramVar2.getNumReferencees() == 1) ) {
4483 System.out.println( "\n fm="+fm+"\n param="+paramTemp2 );
4484 writeGraph( "whatup" );
4487 assert paramVar2.getNumReferencees() == 1;
4488 RefEdge paramEdge2 = paramVar2.iteratorToReferencees().next();
4489 HeapRegionNode hrnParam2 = paramEdge2.getDst();
4491 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4492 common.addAll(mayReachSharedObjects(hrnParam1, hrnParam2));
4497 public Set<HeapRegionNode> mayReachSharedObjects(FlatMethod fm,
4501 // get parameter's heap regions
4502 TempDescriptor paramTemp = fm.getParameter(paramIndex.intValue());
4503 assert this.hasVariable( paramTemp );
4504 VariableNode paramVar = getVariableNodeFromTemp(paramTemp);
4505 assert paramVar.getNumReferencees() == 1;
4506 RefEdge paramEdge = paramVar.iteratorToReferencees().next();
4507 HeapRegionNode hrnParam = paramEdge.getDst();
4510 HeapRegionNode hrnSummary=null;
4511 if(id2hrn.containsKey(as.getSummary())){
4512 // if summary node doesn't exist, ignore this case
4513 hrnSummary = id2hrn.get(as.getSummary());
4514 assert hrnSummary != null;
4517 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4518 if(hrnSummary!=null){
4519 common.addAll( mayReachSharedObjects(hrnParam, hrnSummary) );
4522 // check for other nodes
4523 for (int i = 0; i < as.getAllocationDepth(); ++i) {
4525 assert id2hrn.containsKey(as.getIthOldest(i));
4526 HeapRegionNode hrnIthOldest = id2hrn.get(as.getIthOldest(i));
4527 assert hrnIthOldest != null;
4529 common.addAll(mayReachSharedObjects(hrnParam, hrnIthOldest));
4536 public Set<HeapRegionNode> mayReachSharedObjects(AllocSite as1,
4539 // get summary node 1's alpha
4540 Integer idSum1 = as1.getSummary();
4541 HeapRegionNode hrnSum1=null;
4542 if(id2hrn.containsKey(idSum1)){
4543 hrnSum1 = id2hrn.get(idSum1);
4546 // get summary node 2's alpha
4547 Integer idSum2 = as2.getSummary();
4548 HeapRegionNode hrnSum2=null;
4549 if(id2hrn.containsKey(idSum2)){
4550 hrnSum2 = id2hrn.get(idSum2);
4553 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4554 if(hrnSum1!=null && hrnSum2!=null && hrnSum1!=hrnSum2){
4555 common.addAll(mayReachSharedObjects(hrnSum1, hrnSum2));
4559 // ask if objects from this summary share among each other
4560 common.addAll(mayReachSharedObjects(hrnSum1));
4563 // check sum2 against alloc1 nodes
4565 for (int i = 0; i < as1.getAllocationDepth(); ++i) {
4566 Integer idI1 = as1.getIthOldest(i);
4567 assert id2hrn.containsKey(idI1);
4568 HeapRegionNode hrnI1 = id2hrn.get(idI1);
4569 assert hrnI1 != null;
4570 common.addAll(mayReachSharedObjects(hrnI1, hrnSum2));
4573 // also ask if objects from this summary share among each other
4574 common.addAll(mayReachSharedObjects(hrnSum2));
4577 // check sum1 against alloc2 nodes
4578 for (int i = 0; i < as2.getAllocationDepth(); ++i) {
4579 Integer idI2 = as2.getIthOldest(i);
4580 assert id2hrn.containsKey(idI2);
4581 HeapRegionNode hrnI2 = id2hrn.get(idI2);
4582 assert hrnI2 != null;
4585 common.addAll(mayReachSharedObjects(hrnSum1, hrnI2));
4588 // while we're at it, do an inner loop for alloc2 vs alloc1 nodes
4589 for (int j = 0; j < as1.getAllocationDepth(); ++j) {
4590 Integer idI1 = as1.getIthOldest(j);
4592 // if these are the same site, don't look for the same token, no
4594 // different tokens of the same site could alias together though
4595 if (idI1.equals(idI2)) {
4599 HeapRegionNode hrnI1 = id2hrn.get(idI1);
4601 common.addAll(mayReachSharedObjects(hrnI1, hrnI2));