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 = false;
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,
95 boolean isOutOfContext,
104 TypeDescriptor typeToUse = null;
105 if( allocSite != null ) {
106 typeToUse = allocSite.getType();
107 allocSites.add( allocSite );
112 boolean markForAnalysis = false;
113 if( allocSite != null && allocSite.isFlagged() ) {
114 markForAnalysis = true;
117 if( allocSite == null ) {
118 assert !markForAnalysis;
120 } else if( markForAnalysis != allocSite.isFlagged() ) {
126 id = DisjointAnalysis.generateUniqueHeapRegionNodeID();
129 if( inherent == null ) {
130 if( markForAnalysis ) {
132 Canonical.makePredsTrue(
135 ReachTuple.factory( id,
137 ReachTuple.ARITY_ONE,
138 false // out-of-context
144 inherent = rsetWithEmptyState;
148 if( alpha == null ) {
152 assert preds != null;
154 HeapRegionNode hrn = new HeapRegionNode( id,
165 id2hrn.put( id, hrn );
171 ////////////////////////////////////////////////
173 // Low-level referencee and referencer methods
175 // These methods provide the lowest level for
176 // creating references between reachability nodes
177 // and handling the details of maintaining both
178 // list of referencers and referencees.
180 ////////////////////////////////////////////////
181 protected void addRefEdge( RefSrcNode referencer,
182 HeapRegionNode referencee,
184 assert referencer != null;
185 assert referencee != null;
187 assert edge.getSrc() == referencer;
188 assert edge.getDst() == referencee;
189 assert belongsToThis( referencer );
190 assert belongsToThis( referencee );
192 // edges are getting added twice to graphs now, the
193 // kind that should have abstract facts merged--use
194 // this check to prevent that
195 assert referencer.getReferenceTo( referencee,
200 referencer.addReferencee( edge );
201 referencee.addReferencer( edge );
204 protected void removeRefEdge( RefEdge e ) {
205 removeRefEdge( e.getSrc(),
211 protected void removeRefEdge( RefSrcNode referencer,
212 HeapRegionNode referencee,
215 assert referencer != null;
216 assert referencee != null;
218 RefEdge edge = referencer.getReferenceTo( referencee,
222 assert edge == referencee.getReferenceFrom( referencer,
226 referencer.removeReferencee( edge );
227 referencee.removeReferencer( edge );
231 // int oldTaint=edge.getTaintIdentifier();
232 // if(referencer instanceof HeapRegionNode){
233 // depropagateTaintIdentifier((HeapRegionNode)referencer,oldTaint,new HashSet<HeapRegionNode>());
239 protected void clearRefEdgesFrom( RefSrcNode referencer,
242 boolean removeAll ) {
243 assert referencer != null;
245 // get a copy of the set to iterate over, otherwise
246 // we will be trying to take apart the set as we
247 // are iterating over it, which won't work
248 Iterator<RefEdge> i = referencer.iteratorToReferenceesClone();
249 while( i.hasNext() ) {
250 RefEdge edge = i.next();
253 (edge.typeEquals( type ) && edge.fieldEquals( field ))
256 HeapRegionNode referencee = edge.getDst();
258 removeRefEdge( referencer,
266 protected void clearRefEdgesTo( HeapRegionNode referencee,
269 boolean removeAll ) {
270 assert referencee != null;
272 // get a copy of the set to iterate over, otherwise
273 // we will be trying to take apart the set as we
274 // are iterating over it, which won't work
275 Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
276 while( i.hasNext() ) {
277 RefEdge edge = i.next();
280 (edge.typeEquals( type ) && edge.fieldEquals( field ))
283 RefSrcNode referencer = edge.getSrc();
285 removeRefEdge( referencer,
293 protected void clearNonVarRefEdgesTo( HeapRegionNode referencee ) {
294 assert referencee != null;
296 // get a copy of the set to iterate over, otherwise
297 // we will be trying to take apart the set as we
298 // are iterating over it, which won't work
299 Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
300 while( i.hasNext() ) {
301 RefEdge edge = i.next();
302 RefSrcNode referencer = edge.getSrc();
303 if( !(referencer instanceof VariableNode) ) {
304 removeRefEdge( referencer,
312 // this is a common operation in many transfer functions: we want
313 // to add an edge, but if there is already such an edge we should
314 // merge the properties of the existing and the new edges
315 protected void addEdgeOrMergeWithExisting( RefEdge edgeNew ) {
317 RefSrcNode src = edgeNew.getSrc();
318 assert belongsToThis( src );
320 HeapRegionNode dst = edgeNew.getDst();
321 assert belongsToThis( dst );
323 // look to see if an edge with same field exists
324 // and merge with it, otherwise just add the edge
325 RefEdge edgeExisting = src.getReferenceTo( dst,
330 if( edgeExisting != null ) {
331 edgeExisting.setBeta(
332 Canonical.unionORpreds( edgeExisting.getBeta(),
336 edgeExisting.setPreds(
337 Canonical.join( edgeExisting.getPreds(),
341 edgeExisting.setTaints(
342 Canonical.unionORpreds( edgeExisting.getTaints(),
348 addRefEdge( src, dst, edgeNew );
354 ////////////////////////////////////////////////////
356 // Assignment Operation Methods
358 // These methods are high-level operations for
359 // modeling program assignment statements using
360 // the low-level reference create/remove methods
363 ////////////////////////////////////////////////////
365 public void assignTempXEqualToTempY( TempDescriptor x,
367 assignTempXEqualToCastedTempY( x, y, null );
370 public void assignTempXEqualToCastedTempY( TempDescriptor x,
372 TypeDescriptor tdCast ) {
374 VariableNode lnX = getVariableNodeFromTemp( x );
375 VariableNode lnY = getVariableNodeFromTemp( y );
377 clearRefEdgesFrom( lnX, null, null, true );
379 // note it is possible that the types of temps in the
380 // flat node to analyze will reveal that some typed
381 // edges in the reachability graph are impossible
382 Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
384 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
385 while( itrYhrn.hasNext() ) {
386 RefEdge edgeY = itrYhrn.next();
387 HeapRegionNode referencee = edgeY.getDst();
388 RefEdge edgeNew = edgeY.copy();
390 if( !isSuperiorType( x.getType(), edgeY.getType() ) ) {
391 impossibleEdges.add( edgeY );
395 edgeNew.setSrc( lnX );
397 if( tdCast == null ) {
398 edgeNew.setType( mostSpecificType( y.getType(),
404 edgeNew.setType( mostSpecificType( y.getType(),
406 referencee.getType(),
412 edgeNew.setField( null );
414 addRefEdge( lnX, referencee, edgeNew );
417 Iterator<RefEdge> itrImp = impossibleEdges.iterator();
418 while( itrImp.hasNext() ) {
419 RefEdge edgeImp = itrImp.next();
420 removeRefEdge( edgeImp );
425 public void assignTempXEqualToTempYFieldF( TempDescriptor x,
427 FieldDescriptor f ) {
428 VariableNode lnX = getVariableNodeFromTemp( x );
429 VariableNode lnY = getVariableNodeFromTemp( y );
431 clearRefEdgesFrom( lnX, null, null, true );
433 // note it is possible that the types of temps in the
434 // flat node to analyze will reveal that some typed
435 // edges in the reachability graph are impossible
436 Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
438 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
439 while( itrYhrn.hasNext() ) {
440 RefEdge edgeY = itrYhrn.next();
441 HeapRegionNode hrnY = edgeY.getDst();
442 ReachSet betaY = edgeY.getBeta();
444 Iterator<RefEdge> itrHrnFhrn = hrnY.iteratorToReferencees();
445 while( itrHrnFhrn.hasNext() ) {
446 RefEdge edgeHrn = itrHrnFhrn.next();
447 HeapRegionNode hrnHrn = edgeHrn.getDst();
448 ReachSet betaHrn = edgeHrn.getBeta();
450 // prune edges that are not a matching field
451 if( edgeHrn.getType() != null &&
452 !edgeHrn.getField().equals( f.getSymbol() )
457 // check for impossible edges
458 if( !isSuperiorType( x.getType(), edgeHrn.getType() ) ) {
459 impossibleEdges.add( edgeHrn );
463 TypeDescriptor tdNewEdge =
464 mostSpecificType( edgeHrn.getType(),
468 RefEdge edgeNew = new RefEdge( lnX,
472 Canonical.intersection( betaY, betaHrn ),
477 addEdgeOrMergeWithExisting( edgeNew );
481 Iterator<RefEdge> itrImp = impossibleEdges.iterator();
482 while( itrImp.hasNext() ) {
483 RefEdge edgeImp = itrImp.next();
484 removeRefEdge( edgeImp );
487 // anytime you might remove edges between heap regions
488 // you must global sweep to clean up broken reachability
489 if( !impossibleEdges.isEmpty() ) {
490 if( !DISABLE_GLOBAL_SWEEP ) {
497 public void assignTempXFieldFEqualToTempY( TempDescriptor x,
501 VariableNode lnX = getVariableNodeFromTemp( x );
502 VariableNode lnY = getVariableNodeFromTemp( y );
504 HashSet<HeapRegionNode> nodesWithNewAlpha = new HashSet<HeapRegionNode>();
505 HashSet<RefEdge> edgesWithNewBeta = new HashSet<RefEdge>();
507 // note it is possible that the types of temps in the
508 // flat node to analyze will reveal that some typed
509 // edges in the reachability graph are impossible
510 Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
512 // first look for possible strong updates and remove those edges
513 boolean strongUpdate = false;
515 Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
516 while( itrXhrn.hasNext() ) {
517 RefEdge edgeX = itrXhrn.next();
518 HeapRegionNode hrnX = edgeX.getDst();
520 // we can do a strong update here if one of two cases holds
522 f != DisjointAnalysis.getArrayField( f.getType() ) &&
523 ( (hrnX.getNumReferencers() == 1) || // case 1
524 (hrnX.isSingleObject() && lnX.getNumReferencees() == 1) // case 2
527 if( !DISABLE_STRONG_UPDATES ) {
529 clearRefEdgesFrom( hrnX, f.getType(), f.getSymbol(), false );
534 // then do all token propagation
535 itrXhrn = lnX.iteratorToReferencees();
536 while( itrXhrn.hasNext() ) {
537 RefEdge edgeX = itrXhrn.next();
538 HeapRegionNode hrnX = edgeX.getDst();
539 ReachSet betaX = edgeX.getBeta();
540 ReachSet R = Canonical.intersection( hrnX.getAlpha(),
544 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
545 while( itrYhrn.hasNext() ) {
546 RefEdge edgeY = itrYhrn.next();
547 HeapRegionNode hrnY = edgeY.getDst();
548 ReachSet O = edgeY.getBeta();
550 // check for impossible edges
551 if( !isSuperiorType( f.getType(), edgeY.getType() ) ) {
552 impossibleEdges.add( edgeY );
556 // propagate tokens over nodes starting from hrnSrc, and it will
557 // take care of propagating back up edges from any touched nodes
558 ChangeSet Cy = Canonical.unionUpArityToChangeSet( O, R );
559 propagateTokensOverNodes( hrnY, Cy, nodesWithNewAlpha, edgesWithNewBeta );
561 // then propagate back just up the edges from hrn
562 ChangeSet Cx = Canonical.unionUpArityToChangeSet( R, O );
563 HashSet<RefEdge> todoEdges = new HashSet<RefEdge>();
565 Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
566 new Hashtable<RefEdge, ChangeSet>();
568 Iterator<RefEdge> referItr = hrnX.iteratorToReferencers();
569 while( referItr.hasNext() ) {
570 RefEdge edgeUpstream = referItr.next();
571 todoEdges.add( edgeUpstream );
572 edgePlannedChanges.put( edgeUpstream, Cx );
575 propagateTokensOverEdges( todoEdges,
582 // apply the updates to reachability
583 Iterator<HeapRegionNode> nodeItr = nodesWithNewAlpha.iterator();
584 while( nodeItr.hasNext() ) {
585 nodeItr.next().applyAlphaNew();
588 Iterator<RefEdge> edgeItr = edgesWithNewBeta.iterator();
589 while( edgeItr.hasNext() ) {
590 edgeItr.next().applyBetaNew();
594 // then go back through and add the new edges
595 itrXhrn = lnX.iteratorToReferencees();
596 while( itrXhrn.hasNext() ) {
597 RefEdge edgeX = itrXhrn.next();
598 HeapRegionNode hrnX = edgeX.getDst();
600 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
601 while( itrYhrn.hasNext() ) {
602 RefEdge edgeY = itrYhrn.next();
603 HeapRegionNode hrnY = edgeY.getDst();
605 // skip impossible edges here, we already marked them
606 // when computing reachability propagations above
607 if( !isSuperiorType( f.getType(), edgeY.getType() ) ) {
611 // prepare the new reference edge hrnX.f -> hrnY
612 TypeDescriptor tdNewEdge =
613 mostSpecificType( y.getType(),
623 Canonical.makePredsTrue(
624 Canonical.pruneBy( edgeY.getBeta(),
632 addEdgeOrMergeWithExisting( edgeNew );
636 Iterator<RefEdge> itrImp = impossibleEdges.iterator();
637 while( itrImp.hasNext() ) {
638 RefEdge edgeImp = itrImp.next();
639 removeRefEdge( edgeImp );
642 // if there was a strong update, make sure to improve
643 // reachability with a global sweep
644 if( strongUpdate || !impossibleEdges.isEmpty() ) {
645 if( !DISABLE_GLOBAL_SWEEP ) {
652 public void assignReturnEqualToTemp( TempDescriptor x ) {
654 VariableNode lnR = getVariableNodeFromTemp( tdReturn );
655 VariableNode lnX = getVariableNodeFromTemp( x );
657 clearRefEdgesFrom( lnR, null, null, true );
659 Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
660 while( itrXhrn.hasNext() ) {
661 RefEdge edgeX = itrXhrn.next();
662 HeapRegionNode referencee = edgeX.getDst();
663 RefEdge edgeNew = edgeX.copy();
664 edgeNew.setSrc( lnR );
666 addRefEdge( lnR, referencee, edgeNew );
671 public void assignTempEqualToNewAlloc( TempDescriptor x,
678 // after the age operation the newest (or zero-ith oldest)
679 // node associated with the allocation site should have
680 // no references to it as if it were a newly allocated
682 Integer idNewest = as.getIthOldest( 0 );
683 HeapRegionNode hrnNewest = id2hrn.get( idNewest );
684 assert hrnNewest != null;
686 VariableNode lnX = getVariableNodeFromTemp( x );
687 clearRefEdgesFrom( lnX, null, null, true );
689 // make a new reference to allocated node
690 TypeDescriptor type = as.getType();
693 new RefEdge( lnX, // source
697 hrnNewest.getAlpha(), // beta
698 predsTrue, // predicates
699 TaintSet.factory() // taints
702 addRefEdge( lnX, hrnNewest, edgeNew );
706 // use the allocation site (unique to entire analysis) to
707 // locate the heap region nodes in this reachability graph
708 // that should be aged. The process models the allocation
709 // of new objects and collects all the oldest allocations
710 // in a summary node to allow for a finite analysis
712 // There is an additional property of this method. After
713 // running it on a particular reachability graph (many graphs
714 // may have heap regions related to the same allocation site)
715 // the heap region node objects in this reachability graph will be
716 // allocated. Therefore, after aging a graph for an allocation
717 // site, attempts to retrieve the heap region nodes using the
718 // integer id's contained in the allocation site should always
719 // return non-null heap regions.
720 public void age( AllocSite as ) {
722 // keep track of allocation sites that are represented
723 // in this graph for efficiency with other operations
724 allocSites.add( as );
726 // if there is a k-th oldest node, it merges into
728 Integer idK = as.getOldest();
729 if( id2hrn.containsKey( idK ) ) {
730 HeapRegionNode hrnK = id2hrn.get( idK );
732 // retrieve the summary node, or make it
734 HeapRegionNode hrnSummary = getSummaryNode( as, false );
736 mergeIntoSummary( hrnK, hrnSummary );
739 // move down the line of heap region nodes
740 // clobbering the ith and transferring all references
741 // to and from i-1 to node i.
742 for( int i = allocationDepth - 1; i > 0; --i ) {
744 // only do the transfer if the i-1 node exists
745 Integer idImin1th = as.getIthOldest( i - 1 );
746 if( id2hrn.containsKey( idImin1th ) ) {
747 HeapRegionNode hrnImin1 = id2hrn.get( idImin1th );
748 if( hrnImin1.isWiped() ) {
749 // there is no info on this node, just skip
753 // either retrieve or make target of transfer
754 HeapRegionNode hrnI = getIthNode( as, i, false );
756 transferOnto( hrnImin1, hrnI );
761 // as stated above, the newest node should have had its
762 // references moved over to the second oldest, so we wipe newest
763 // in preparation for being the new object to assign something to
764 HeapRegionNode hrn0 = getIthNode( as, 0, false );
765 wipeOut( hrn0, true );
767 // now tokens in reachability sets need to "age" also
768 Iterator itrAllVariableNodes = td2vn.entrySet().iterator();
769 while( itrAllVariableNodes.hasNext() ) {
770 Map.Entry me = (Map.Entry) itrAllVariableNodes.next();
771 VariableNode ln = (VariableNode) me.getValue();
773 Iterator<RefEdge> itrEdges = ln.iteratorToReferencees();
774 while( itrEdges.hasNext() ) {
775 ageTuplesFrom( as, itrEdges.next() );
779 Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
780 while( itrAllHRNodes.hasNext() ) {
781 Map.Entry me = (Map.Entry) itrAllHRNodes.next();
782 HeapRegionNode hrnToAge = (HeapRegionNode) me.getValue();
784 ageTuplesFrom( as, hrnToAge );
786 Iterator<RefEdge> itrEdges = hrnToAge.iteratorToReferencees();
787 while( itrEdges.hasNext() ) {
788 ageTuplesFrom( as, itrEdges.next() );
793 // after tokens have been aged, reset newest node's reachability
794 // and a brand new node has a "true" predicate
795 hrn0.setAlpha( hrn0.getInherent() );
796 hrn0.setPreds( predsTrue );
800 // either retrieve or create the needed heap region node
801 protected HeapRegionNode getSummaryNode( AllocSite as,
806 idSummary = as.getSummaryShadow();
808 idSummary = as.getSummary();
811 HeapRegionNode hrnSummary = id2hrn.get( idSummary );
813 if( hrnSummary == null ) {
815 String strDesc = as.toStringForDOT()+"\\nsummary";
818 createNewHeapRegionNode( idSummary, // id or null to generate a new one
819 false, // single object?
821 false, // out-of-context?
822 as.getType(), // type
823 as, // allocation site
824 null, // inherent reach
825 null, // current reach
826 predsEmpty, // predicates
827 strDesc // description
834 // either retrieve or create the needed heap region node
835 protected HeapRegionNode getIthNode( AllocSite as,
841 idIth = as.getIthOldestShadow( i );
843 idIth = as.getIthOldest( i );
846 HeapRegionNode hrnIth = id2hrn.get( idIth );
848 if( hrnIth == null ) {
850 String strDesc = as.toStringForDOT()+"\\n"+i+" oldest";
852 hrnIth = createNewHeapRegionNode( idIth, // id or null to generate a new one
853 true, // single object?
855 false, // out-of-context?
856 as.getType(), // type
857 as, // allocation site
858 null, // inherent reach
859 null, // current reach
860 predsEmpty, // predicates
861 strDesc // description
869 protected void mergeIntoSummary( HeapRegionNode hrn,
870 HeapRegionNode hrnSummary ) {
871 assert hrnSummary.isNewSummary();
873 // assert that these nodes belong to THIS graph
874 assert belongsToThis( hrn );
875 assert belongsToThis( hrnSummary );
877 assert hrn != hrnSummary;
879 // transfer references _from_ hrn over to hrnSummary
880 Iterator<RefEdge> itrReferencee = hrn.iteratorToReferencees();
881 while( itrReferencee.hasNext() ) {
882 RefEdge edge = itrReferencee.next();
883 RefEdge edgeMerged = edge.copy();
884 edgeMerged.setSrc( hrnSummary );
886 HeapRegionNode hrnReferencee = edge.getDst();
887 RefEdge edgeSummary =
888 hrnSummary.getReferenceTo( hrnReferencee,
893 if( edgeSummary == null ) {
894 // the merge is trivial, nothing to be done
895 addRefEdge( hrnSummary, hrnReferencee, edgeMerged );
898 // otherwise an edge from the referencer to hrnSummary exists already
899 // and the edge referencer->hrn should be merged with it
901 Canonical.unionORpreds( edgeMerged.getBeta(),
902 edgeSummary.getBeta()
905 edgeSummary.setPreds(
906 Canonical.join( edgeMerged.getPreds(),
907 edgeSummary.getPreds()
913 // next transfer references _to_ hrn over to hrnSummary
914 Iterator<RefEdge> itrReferencer = hrn.iteratorToReferencers();
915 while( itrReferencer.hasNext() ) {
916 RefEdge edge = itrReferencer.next();
917 RefEdge edgeMerged = edge.copy();
918 edgeMerged.setDst( hrnSummary );
920 RefSrcNode onReferencer = edge.getSrc();
921 RefEdge edgeSummary =
922 onReferencer.getReferenceTo( hrnSummary,
927 if( edgeSummary == null ) {
928 // the merge is trivial, nothing to be done
929 addRefEdge( onReferencer, hrnSummary, edgeMerged );
932 // otherwise an edge from the referencer to alpha_S exists already
933 // and the edge referencer->alpha_K should be merged with it
935 Canonical.unionORpreds( edgeMerged.getBeta(),
936 edgeSummary.getBeta()
939 edgeSummary.setPreds(
940 Canonical.join( edgeMerged.getPreds(),
941 edgeSummary.getPreds()
947 // then merge hrn reachability into hrnSummary
949 Canonical.unionORpreds( hrnSummary.getAlpha(),
955 Canonical.join( hrnSummary.getPreds(),
960 // and afterward, this node is gone
961 wipeOut( hrn, true );
965 protected void transferOnto( HeapRegionNode hrnA,
966 HeapRegionNode hrnB ) {
968 assert belongsToThis( hrnA );
969 assert belongsToThis( hrnB );
972 // clear references in and out of node b?
973 assert hrnB.isWiped();
975 // copy each: (edge in and out of A) to B
976 Iterator<RefEdge> itrReferencee = hrnA.iteratorToReferencees();
977 while( itrReferencee.hasNext() ) {
978 RefEdge edge = itrReferencee.next();
979 HeapRegionNode hrnReferencee = edge.getDst();
980 RefEdge edgeNew = edge.copy();
981 edgeNew.setSrc( hrnB );
982 edgeNew.setDst( hrnReferencee );
984 addRefEdge( hrnB, hrnReferencee, edgeNew );
987 Iterator<RefEdge> itrReferencer = hrnA.iteratorToReferencers();
988 while( itrReferencer.hasNext() ) {
989 RefEdge edge = itrReferencer.next();
990 RefSrcNode rsnReferencer = edge.getSrc();
991 RefEdge edgeNew = edge.copy();
992 edgeNew.setSrc( rsnReferencer );
993 edgeNew.setDst( hrnB );
995 addRefEdge( rsnReferencer, hrnB, edgeNew );
998 // replace hrnB reachability and preds with hrnA's
999 hrnB.setAlpha( hrnA.getAlpha() );
1000 hrnB.setPreds( hrnA.getPreds() );
1002 // after transfer, wipe out source
1003 wipeOut( hrnA, true );
1007 // the purpose of this method is to conceptually "wipe out"
1008 // a heap region from the graph--purposefully not called REMOVE
1009 // because the node is still hanging around in the graph, just
1010 // not mechanically connected or have any reach or predicate
1011 // information on it anymore--lots of ops can use this
1012 protected void wipeOut( HeapRegionNode hrn,
1013 boolean wipeVariableReferences ) {
1015 assert belongsToThis( hrn );
1017 clearRefEdgesFrom( hrn, null, null, true );
1019 if( wipeVariableReferences ) {
1020 clearRefEdgesTo( hrn, null, null, true );
1022 clearNonVarRefEdgesTo( hrn );
1025 hrn.setAlpha( rsetEmpty );
1026 hrn.setPreds( predsEmpty );
1030 protected void ageTuplesFrom( AllocSite as, RefEdge edge ) {
1032 Canonical.ageTuplesFrom( edge.getBeta(),
1038 protected void ageTuplesFrom( AllocSite as, HeapRegionNode hrn ) {
1040 Canonical.ageTuplesFrom( hrn.getAlpha(),
1048 protected void propagateTokensOverNodes( HeapRegionNode nPrime,
1050 HashSet<HeapRegionNode> nodesWithNewAlpha,
1051 HashSet<RefEdge> edgesWithNewBeta ) {
1053 HashSet<HeapRegionNode> todoNodes
1054 = new HashSet<HeapRegionNode>();
1055 todoNodes.add( nPrime );
1057 HashSet<RefEdge> todoEdges
1058 = new HashSet<RefEdge>();
1060 Hashtable<HeapRegionNode, ChangeSet> nodePlannedChanges
1061 = new Hashtable<HeapRegionNode, ChangeSet>();
1062 nodePlannedChanges.put( nPrime, c0 );
1064 Hashtable<RefEdge, ChangeSet> edgePlannedChanges
1065 = new Hashtable<RefEdge, ChangeSet>();
1067 // first propagate change sets everywhere they can go
1068 while( !todoNodes.isEmpty() ) {
1069 HeapRegionNode n = todoNodes.iterator().next();
1070 ChangeSet C = nodePlannedChanges.get( n );
1072 Iterator<RefEdge> referItr = n.iteratorToReferencers();
1073 while( referItr.hasNext() ) {
1074 RefEdge edge = referItr.next();
1075 todoEdges.add( edge );
1077 if( !edgePlannedChanges.containsKey( edge ) ) {
1078 edgePlannedChanges.put( edge,
1083 edgePlannedChanges.put( edge,
1084 Canonical.union( edgePlannedChanges.get( edge ),
1090 Iterator<RefEdge> refeeItr = n.iteratorToReferencees();
1091 while( refeeItr.hasNext() ) {
1092 RefEdge edgeF = refeeItr.next();
1093 HeapRegionNode m = edgeF.getDst();
1095 ChangeSet changesToPass = ChangeSet.factory();
1097 Iterator<ChangeTuple> itrCprime = C.iterator();
1098 while( itrCprime.hasNext() ) {
1099 ChangeTuple c = itrCprime.next();
1100 if( edgeF.getBeta().containsIgnorePreds( c.getStateToMatch() )
1103 changesToPass = Canonical.add( changesToPass, c );
1107 if( !changesToPass.isEmpty() ) {
1108 if( !nodePlannedChanges.containsKey( m ) ) {
1109 nodePlannedChanges.put( m, ChangeSet.factory() );
1112 ChangeSet currentChanges = nodePlannedChanges.get( m );
1114 if( !changesToPass.isSubset( currentChanges ) ) {
1116 nodePlannedChanges.put( m,
1117 Canonical.union( currentChanges,
1126 todoNodes.remove( n );
1129 // then apply all of the changes for each node at once
1130 Iterator itrMap = nodePlannedChanges.entrySet().iterator();
1131 while( itrMap.hasNext() ) {
1132 Map.Entry me = (Map.Entry) itrMap.next();
1133 HeapRegionNode n = (HeapRegionNode) me.getKey();
1134 ChangeSet C = (ChangeSet) me.getValue();
1136 // this propagation step is with respect to one change,
1137 // so we capture the full change from the old alpha:
1138 ReachSet localDelta = Canonical.applyChangeSet( n.getAlpha(),
1142 // but this propagation may be only one of many concurrent
1143 // possible changes, so keep a running union with the node's
1144 // partially updated new alpha set
1145 n.setAlphaNew( Canonical.unionORpreds( n.getAlphaNew(),
1150 nodesWithNewAlpha.add( n );
1153 propagateTokensOverEdges( todoEdges,
1160 protected void propagateTokensOverEdges( HashSet <RefEdge> todoEdges,
1161 Hashtable<RefEdge, ChangeSet> edgePlannedChanges,
1162 HashSet <RefEdge> edgesWithNewBeta ) {
1164 // first propagate all change tuples everywhere they can go
1165 while( !todoEdges.isEmpty() ) {
1166 RefEdge edgeE = todoEdges.iterator().next();
1167 todoEdges.remove( edgeE );
1169 if( !edgePlannedChanges.containsKey( edgeE ) ) {
1170 edgePlannedChanges.put( edgeE,
1175 ChangeSet C = edgePlannedChanges.get( edgeE );
1177 ChangeSet changesToPass = ChangeSet.factory();
1179 Iterator<ChangeTuple> itrC = C.iterator();
1180 while( itrC.hasNext() ) {
1181 ChangeTuple c = itrC.next();
1182 if( edgeE.getBeta().containsIgnorePreds( c.getStateToMatch() )
1185 changesToPass = Canonical.add( changesToPass, c );
1189 RefSrcNode rsn = edgeE.getSrc();
1191 if( !changesToPass.isEmpty() && rsn instanceof HeapRegionNode ) {
1192 HeapRegionNode n = (HeapRegionNode) rsn;
1194 Iterator<RefEdge> referItr = n.iteratorToReferencers();
1195 while( referItr.hasNext() ) {
1196 RefEdge edgeF = referItr.next();
1198 if( !edgePlannedChanges.containsKey( edgeF ) ) {
1199 edgePlannedChanges.put( edgeF,
1204 ChangeSet currentChanges = edgePlannedChanges.get( edgeF );
1206 if( !changesToPass.isSubset( currentChanges ) ) {
1207 todoEdges.add( edgeF );
1208 edgePlannedChanges.put( edgeF,
1209 Canonical.union( currentChanges,
1218 // then apply all of the changes for each edge at once
1219 Iterator itrMap = edgePlannedChanges.entrySet().iterator();
1220 while( itrMap.hasNext() ) {
1221 Map.Entry me = (Map.Entry) itrMap.next();
1222 RefEdge e = (RefEdge) me.getKey();
1223 ChangeSet C = (ChangeSet) me.getValue();
1225 // this propagation step is with respect to one change,
1226 // so we capture the full change from the old beta:
1227 ReachSet localDelta =
1228 Canonical.applyChangeSet( e.getBeta(),
1233 // but this propagation may be only one of many concurrent
1234 // possible changes, so keep a running union with the edge's
1235 // partially updated new beta set
1236 e.setBetaNew( Canonical.unionORpreds( e.getBetaNew(),
1241 edgesWithNewBeta.add( e );
1246 public void taintLiveTemps( FlatSESEEnterNode sese,
1247 Set<TempDescriptor> liveTemps
1250 System.out.println( "At "+sese+" with: "+liveTemps );
1252 Iterator<TempDescriptor> tdItr = liveTemps.iterator();
1253 while( tdItr.hasNext() ) {
1254 TempDescriptor td = tdItr.next();
1255 VariableNode vn = td2vn.get( td );
1257 Iterator<RefEdge> reItr = vn.iteratorToReferencees();
1258 while( reItr.hasNext() ) {
1259 RefEdge re = reItr.next();
1261 // these new sese (rblock) taints should
1262 // have empty predicates so they never propagate
1264 Taint t = Taint.factory( sese,
1266 re.getDst().getAllocSite(),
1267 ExistPredSet.factory()
1270 re.setTaints( Canonical.add( re.getTaints(),
1278 public void removeInContextTaints( FlatSESEEnterNode sese ) {
1283 // used in makeCalleeView below to decide if there is
1284 // already an appropriate out-of-context edge in a callee
1285 // view graph for merging, or null if a new one will be added
1287 getOutOfContextReferenceTo( HeapRegionNode hrn,
1288 TypeDescriptor srcType,
1289 TypeDescriptor refType,
1291 assert belongsToThis( hrn );
1293 HeapRegionNode hrnInContext = id2hrn.get( hrn.getID() );
1294 if( hrnInContext == null ) {
1298 Iterator<RefEdge> refItr = hrnInContext.iteratorToReferencers();
1299 while( refItr.hasNext() ) {
1300 RefEdge re = refItr.next();
1302 assert belongsToThis( re.getSrc() );
1303 assert belongsToThis( re.getDst() );
1305 if( !(re.getSrc() instanceof HeapRegionNode) ) {
1309 HeapRegionNode hrnSrc = (HeapRegionNode) re.getSrc();
1310 if( !hrnSrc.isOutOfContext() ) {
1314 if( srcType == null ) {
1315 if( hrnSrc.getType() != null ) {
1319 if( !srcType.equals( hrnSrc.getType() ) ) {
1324 if( !re.typeEquals( refType ) ) {
1328 if( !re.fieldEquals( refField ) ) {
1332 // tada! We found it!
1339 // used below to convert a ReachSet to its callee-context
1340 // equivalent with respect to allocation sites in this graph
1341 protected ReachSet toCalleeContext( ReachSet rs,
1343 Set<HrnIdOoc> oocHrnIdOoc2callee
1345 ReachSet out = ReachSet.factory();
1347 Iterator<ReachState> itr = rs.iterator();
1348 while( itr.hasNext() ) {
1349 ReachState stateCaller = itr.next();
1351 ReachState stateCallee = stateCaller;
1353 Iterator<AllocSite> asItr = allocSites.iterator();
1354 while( asItr.hasNext() ) {
1355 AllocSite as = asItr.next();
1357 ReachState stateNew = ReachState.factory();
1358 Iterator<ReachTuple> rtItr = stateCallee.iterator();
1359 while( rtItr.hasNext() ) {
1360 ReachTuple rt = rtItr.next();
1362 // only translate this tuple if it is
1363 // in the out-callee-context bag
1364 HrnIdOoc hio = new HrnIdOoc( rt.getHrnID(),
1367 if( !oocHrnIdOoc2callee.contains( hio ) ) {
1368 stateNew = Canonical.add( stateNew, rt );
1372 int age = as.getAgeCategory( rt.getHrnID() );
1374 // this is the current mapping, where 0, 1, 2S were allocated
1375 // in the current context, 0?, 1? and 2S? were allocated in a
1376 // previous context, and we're translating to a future context
1388 if( age == AllocSite.AGE_notInThisSite ) {
1389 // things not from the site just go back in
1390 stateNew = Canonical.add( stateNew, rt );
1392 } else if( age == AllocSite.AGE_summary ||
1395 // the in-context summary and all existing out-of-context
1397 stateNew = Canonical.add( stateNew,
1398 ReachTuple.factory( as.getSummary(),
1401 true // out-of-context
1405 // otherwise everything else just goes to an out-of-context
1406 // version, everything else the same
1407 Integer I = as.getAge( rt.getHrnID() );
1410 assert !rt.isMultiObject();
1412 stateNew = Canonical.add( stateNew,
1413 ReachTuple.factory( rt.getHrnID(),
1416 true // out-of-context
1422 stateCallee = stateNew;
1425 // attach the passed in preds
1426 stateCallee = Canonical.attach( stateCallee,
1429 out = Canonical.add( out,
1434 assert out.isCanonical();
1438 // used below to convert a ReachSet to its caller-context
1439 // equivalent with respect to allocation sites in this graph
1441 toCallerContext( ReachSet rs,
1442 Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied
1444 ReachSet out = ReachSet.factory();
1446 // when the mapping is null it means there were no
1447 // predicates satisfied
1448 if( calleeStatesSatisfied == null ) {
1452 Iterator<ReachState> itr = rs.iterator();
1453 while( itr.hasNext() ) {
1454 ReachState stateCallee = itr.next();
1456 if( calleeStatesSatisfied.containsKey( stateCallee ) ) {
1458 // starting from one callee state...
1459 ReachSet rsCaller = ReachSet.factory( stateCallee );
1461 // possibly branch it into many states, which any
1462 // allocation site might do, so lots of derived states
1463 Iterator<AllocSite> asItr = allocSites.iterator();
1464 while( asItr.hasNext() ) {
1465 AllocSite as = asItr.next();
1466 rsCaller = Canonical.toCallerContext( rsCaller, as );
1469 // then before adding each derived, now caller-context
1470 // states to the output, attach the appropriate pred
1471 // based on the source callee state
1472 Iterator<ReachState> stateItr = rsCaller.iterator();
1473 while( stateItr.hasNext() ) {
1474 ReachState stateCaller = stateItr.next();
1475 stateCaller = Canonical.attach( stateCaller,
1476 calleeStatesSatisfied.get( stateCallee )
1478 out = Canonical.add( out,
1485 assert out.isCanonical();
1490 // used below to convert a ReachSet to an equivalent
1491 // version with shadow IDs merged into unshadowed IDs
1492 protected ReachSet unshadow( ReachSet rs ) {
1494 Iterator<AllocSite> asItr = allocSites.iterator();
1495 while( asItr.hasNext() ) {
1496 AllocSite as = asItr.next();
1497 out = Canonical.unshadow( out, as );
1499 assert out.isCanonical();
1504 // used below to convert a TaintSet to its caller-context
1505 // equivalent, just eliminate Taints with bad preds
1507 toCallerContext( TaintSet ts,
1508 Hashtable<Taint, ExistPredSet> calleeTaintsSatisfied
1510 TaintSet out = TaintSet.factory();
1512 // when the mapping is null it means there were no
1513 // predicates satisfied
1514 if( calleeTaintsSatisfied == null ) {
1518 Iterator<Taint> itr = ts.iterator();
1519 while( itr.hasNext() ) {
1520 Taint tCallee = itr.next();
1522 if( calleeTaintsSatisfied.containsKey( tCallee ) ) {
1525 Canonical.attach( Taint.factory( tCallee.sese,
1527 tCallee.allocSite ),
1528 calleeTaintsSatisfied.get( tCallee )
1530 out = Canonical.add( out,
1536 assert out.isCanonical();
1543 // use this method to make a new reach graph that is
1544 // what heap the FlatMethod callee from the FlatCall
1545 // would start with reaching from its arguments in
1548 makeCalleeView( FlatCall fc,
1549 FlatMethod fmCallee,
1550 Set<Integer> callerNodeIDsCopiedToCallee,
1551 boolean writeDebugDOTs
1555 // first traverse this context to find nodes and edges
1556 // that will be callee-reachable
1557 Set<HeapRegionNode> reachableCallerNodes =
1558 new HashSet<HeapRegionNode>();
1560 // caller edges between callee-reachable nodes
1561 Set<RefEdge> reachableCallerEdges =
1562 new HashSet<RefEdge>();
1564 // caller edges from arg vars, and the matching param index
1565 // because these become a special edge in callee
1566 Hashtable<RefEdge, Integer> reachableCallerArgEdges2paramIndex =
1567 new Hashtable<RefEdge, Integer>();
1569 // caller edges from local vars or callee-unreachable nodes
1570 // (out-of-context sources) to callee-reachable nodes
1571 Set<RefEdge> oocCallerEdges =
1572 new HashSet<RefEdge>();
1575 for( int i = 0; i < fmCallee.numParameters(); ++i ) {
1577 TempDescriptor tdArg = fc.getArgMatchingParamIndex( fmCallee, i );
1578 VariableNode vnArgCaller = this.getVariableNodeFromTemp( tdArg );
1580 Set<RefSrcNode> toVisitInCaller = new HashSet<RefSrcNode>();
1581 Set<RefSrcNode> visitedInCaller = new HashSet<RefSrcNode>();
1583 toVisitInCaller.add( vnArgCaller );
1585 while( !toVisitInCaller.isEmpty() ) {
1586 RefSrcNode rsnCaller = toVisitInCaller.iterator().next();
1587 toVisitInCaller.remove( rsnCaller );
1588 visitedInCaller.add( rsnCaller );
1590 Iterator<RefEdge> itrRefEdges = rsnCaller.iteratorToReferencees();
1591 while( itrRefEdges.hasNext() ) {
1592 RefEdge reCaller = itrRefEdges.next();
1593 HeapRegionNode hrnCaller = reCaller.getDst();
1595 callerNodeIDsCopiedToCallee.add( hrnCaller.getID() );
1596 reachableCallerNodes.add( hrnCaller );
1598 if( reCaller.getSrc() instanceof HeapRegionNode ) {
1599 reachableCallerEdges.add( reCaller );
1601 if( rsnCaller.equals( vnArgCaller ) ) {
1602 reachableCallerArgEdges2paramIndex.put( reCaller, i );
1604 oocCallerEdges.add( reCaller );
1608 if( !visitedInCaller.contains( hrnCaller ) ) {
1609 toVisitInCaller.add( hrnCaller );
1612 } // end edge iteration
1613 } // end visiting heap nodes in caller
1614 } // end iterating over parameters as starting points
1617 // now collect out-of-callee-context IDs and
1618 // map them to whether the ID is out of the caller
1620 Set<HrnIdOoc> oocHrnIdOoc2callee = new HashSet<HrnIdOoc>();
1622 Iterator<Integer> itrInContext =
1623 callerNodeIDsCopiedToCallee.iterator();
1624 while( itrInContext.hasNext() ) {
1625 Integer hrnID = itrInContext.next();
1626 HeapRegionNode hrnCallerAndInContext = id2hrn.get( hrnID );
1628 Iterator<RefEdge> itrMightCross =
1629 hrnCallerAndInContext.iteratorToReferencers();
1630 while( itrMightCross.hasNext() ) {
1631 RefEdge edgeMightCross = itrMightCross.next();
1633 RefSrcNode rsnCallerAndOutContext =
1634 edgeMightCross.getSrc();
1636 if( rsnCallerAndOutContext instanceof VariableNode ) {
1637 // variables do not have out-of-context reach states,
1639 oocCallerEdges.add( edgeMightCross );
1643 HeapRegionNode hrnCallerAndOutContext =
1644 (HeapRegionNode) rsnCallerAndOutContext;
1646 // is this source node out-of-context?
1647 if( callerNodeIDsCopiedToCallee.contains( hrnCallerAndOutContext.getID() ) ) {
1648 // no, skip this edge
1653 oocCallerEdges.add( edgeMightCross );
1655 // add all reach tuples on the node to list
1656 // of things that are out-of-context: insight
1657 // if this node is reachable from someting that WAS
1658 // in-context, then this node should already be in-context
1659 Iterator<ReachState> stateItr = hrnCallerAndOutContext.getAlpha().iterator();
1660 while( stateItr.hasNext() ) {
1661 ReachState state = stateItr.next();
1663 Iterator<ReachTuple> rtItr = state.iterator();
1664 while( rtItr.hasNext() ) {
1665 ReachTuple rt = rtItr.next();
1667 oocHrnIdOoc2callee.add( new HrnIdOoc( rt.getHrnID(),
1676 // the callee view is a new graph: DON'T MODIFY *THIS* graph
1677 ReachGraph rg = new ReachGraph();
1679 // add nodes to callee graph
1680 Iterator<HeapRegionNode> hrnItr = reachableCallerNodes.iterator();
1681 while( hrnItr.hasNext() ) {
1682 HeapRegionNode hrnCaller = hrnItr.next();
1684 assert callerNodeIDsCopiedToCallee.contains( hrnCaller.getID() );
1685 assert !rg.id2hrn.containsKey( hrnCaller.getID() );
1687 ExistPred pred = ExistPred.factory( hrnCaller.getID(), null );
1688 ExistPredSet preds = ExistPredSet.factory( pred );
1690 rg.createNewHeapRegionNode( hrnCaller.getID(),
1691 hrnCaller.isSingleObject(),
1692 hrnCaller.isNewSummary(),
1693 false, // out-of-context?
1694 hrnCaller.getType(),
1695 hrnCaller.getAllocSite(),
1696 toCalleeContext( hrnCaller.getInherent(),
1700 toCalleeContext( hrnCaller.getAlpha(),
1705 hrnCaller.getDescription()
1709 // add param edges to callee graph
1711 reachableCallerArgEdges2paramIndex.entrySet().iterator();
1712 while( argEdges.hasNext() ) {
1713 Map.Entry me = (Map.Entry) argEdges.next();
1714 RefEdge reArg = (RefEdge) me.getKey();
1715 Integer index = (Integer) me.getValue();
1717 VariableNode vnCaller = (VariableNode) reArg.getSrc();
1718 TempDescriptor argCaller = vnCaller.getTempDescriptor();
1720 TempDescriptor paramCallee = fmCallee.getParameter( index );
1721 VariableNode vnCallee = rg.getVariableNodeFromTemp( paramCallee );
1723 HeapRegionNode hrnDstCaller = reArg.getDst();
1724 HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1725 assert hrnDstCallee != null;
1728 ExistPred.factory( argCaller,
1730 hrnDstCallee.getID(),
1734 true, // out-of-callee-context
1735 false // out-of-caller-context
1738 ExistPredSet preds =
1739 ExistPredSet.factory( pred );
1745 new RefEdge( vnCallee,
1749 toCalleeContext( reArg.getBeta(),
1757 rg.addRefEdge( vnCallee,
1763 // add in-context edges to callee graph
1764 Iterator<RefEdge> reItr = reachableCallerEdges.iterator();
1765 while( reItr.hasNext() ) {
1766 RefEdge reCaller = reItr.next();
1767 RefSrcNode rsnCaller = reCaller.getSrc();
1768 assert rsnCaller instanceof HeapRegionNode;
1769 HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1770 HeapRegionNode hrnDstCaller = reCaller.getDst();
1772 HeapRegionNode hrnSrcCallee = rg.id2hrn.get( hrnSrcCaller.getID() );
1773 HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1774 assert hrnSrcCallee != null;
1775 assert hrnDstCallee != null;
1778 ExistPred.factory( null,
1779 hrnSrcCallee.getID(),
1780 hrnDstCallee.getID(),
1782 reCaller.getField(),
1784 false, // out-of-callee-context
1785 false // out-of-caller-context
1788 ExistPredSet preds =
1789 ExistPredSet.factory( pred );
1792 new RefEdge( hrnSrcCallee,
1795 reCaller.getField(),
1796 toCalleeContext( reCaller.getBeta(),
1801 TaintSet.factory() // no taints for in-context edges
1804 rg.addRefEdge( hrnSrcCallee,
1810 // add out-of-context edges to callee graph
1811 reItr = oocCallerEdges.iterator();
1812 while( reItr.hasNext() ) {
1813 RefEdge reCaller = reItr.next();
1814 RefSrcNode rsnCaller = reCaller.getSrc();
1815 HeapRegionNode hrnDstCaller = reCaller.getDst();
1816 HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1817 assert hrnDstCallee != null;
1819 TypeDescriptor oocNodeType;
1821 TempDescriptor oocPredSrcTemp = null;
1822 Integer oocPredSrcID = null;
1823 boolean outOfCalleeContext;
1824 boolean outOfCallerContext;
1826 if( rsnCaller instanceof VariableNode ) {
1827 VariableNode vnCaller = (VariableNode) rsnCaller;
1829 oocReach = rsetEmpty;
1830 oocPredSrcTemp = vnCaller.getTempDescriptor();
1831 outOfCalleeContext = true;
1832 outOfCallerContext = false;
1835 HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1836 assert !callerNodeIDsCopiedToCallee.contains( hrnSrcCaller.getID() );
1837 oocNodeType = hrnSrcCaller.getType();
1838 oocReach = hrnSrcCaller.getAlpha();
1839 oocPredSrcID = hrnSrcCaller.getID();
1840 if( hrnSrcCaller.isOutOfContext() ) {
1841 outOfCalleeContext = false;
1842 outOfCallerContext = true;
1844 outOfCalleeContext = true;
1845 outOfCallerContext = false;
1850 ExistPred.factory( oocPredSrcTemp,
1852 hrnDstCallee.getID(),
1854 reCaller.getField(),
1860 ExistPredSet preds =
1861 ExistPredSet.factory( pred );
1863 RefEdge oocEdgeExisting =
1864 rg.getOutOfContextReferenceTo( hrnDstCallee,
1870 if( oocEdgeExisting == null ) {
1871 // for consistency, map one out-of-context "identifier"
1872 // to one heap region node id, otherwise no convergence
1873 String oocid = "oocid"+
1875 hrnDstCallee.getIDString()+
1878 reCaller.getField();
1880 Integer oocHrnID = oocid2hrnid.get( oocid );
1882 HeapRegionNode hrnCalleeAndOutContext;
1884 if( oocHrnID == null ) {
1886 hrnCalleeAndOutContext =
1887 rg.createNewHeapRegionNode( null, // ID
1888 false, // single object?
1889 false, // new summary?
1890 true, // out-of-context?
1892 null, // alloc site, shouldn't be used
1893 toCalleeContext( oocReach,
1897 toCalleeContext( oocReach,
1905 oocid2hrnid.put( oocid, hrnCalleeAndOutContext.getID() );
1909 // the mapping already exists, so see if node is there
1910 hrnCalleeAndOutContext = rg.id2hrn.get( oocHrnID );
1912 if( hrnCalleeAndOutContext == null ) {
1914 hrnCalleeAndOutContext =
1915 rg.createNewHeapRegionNode( oocHrnID, // ID
1916 false, // single object?
1917 false, // new summary?
1918 true, // out-of-context?
1920 null, // alloc site, shouldn't be used
1921 toCalleeContext( oocReach,
1925 toCalleeContext( oocReach,
1934 // otherwise it is there, so merge reachability
1935 hrnCalleeAndOutContext.setAlpha( Canonical.unionORpreds( hrnCalleeAndOutContext.getAlpha(),
1936 toCalleeContext( oocReach,
1945 assert hrnCalleeAndOutContext.reachHasOnlyOOC();
1947 rg.addRefEdge( hrnCalleeAndOutContext,
1949 new RefEdge( hrnCalleeAndOutContext,
1952 reCaller.getField(),
1953 toCalleeContext( reCaller.getBeta(),
1958 TaintSet.factory() // no taints
1963 // the out-of-context edge already exists
1964 oocEdgeExisting.setBeta( Canonical.unionORpreds( oocEdgeExisting.getBeta(),
1965 toCalleeContext( reCaller.getBeta(),
1972 oocEdgeExisting.setPreds( Canonical.join( oocEdgeExisting.getPreds(),
1977 HeapRegionNode hrnCalleeAndOutContext =
1978 (HeapRegionNode) oocEdgeExisting.getSrc();
1979 hrnCalleeAndOutContext.setAlpha( Canonical.unionORpreds( hrnCalleeAndOutContext.getAlpha(),
1980 toCalleeContext( oocReach,
1987 assert hrnCalleeAndOutContext.reachHasOnlyOOC();
1992 if( writeDebugDOTs ) {
1993 debugGraphPrefix = String.format( "call%03d", debugCallSiteVisitCounter );
1994 rg.writeGraph( debugGraphPrefix+"calleeview",
1995 resolveMethodDebugDOTwriteLabels,
1996 resolveMethodDebugDOTselectTemps,
1997 resolveMethodDebugDOTpruneGarbage,
1998 resolveMethodDebugDOThideReach,
1999 resolveMethodDebugDOThideSubsetReach,
2000 resolveMethodDebugDOThidePreds,
2001 resolveMethodDebugDOThideEdgeTaints );
2007 private static Hashtable<String, Integer> oocid2hrnid =
2008 new Hashtable<String, Integer>();
2011 // useful since many graphs writes in the method call debug code
2012 private static boolean resolveMethodDebugDOTwriteLabels = true;
2013 private static boolean resolveMethodDebugDOTselectTemps = true;
2014 private static boolean resolveMethodDebugDOTpruneGarbage = true;
2015 private static boolean resolveMethodDebugDOThideReach = true;
2016 private static boolean resolveMethodDebugDOThideSubsetReach = true;
2017 private static boolean resolveMethodDebugDOThidePreds = true;
2018 private static boolean resolveMethodDebugDOThideEdgeTaints = true;
2020 static String debugGraphPrefix;
2021 static int debugCallSiteVisitCounter;
2022 static int debugCallSiteVisitStartCapture;
2023 static int debugCallSiteNumVisitsToCapture;
2024 static boolean debugCallSiteStopAfter;
2028 resolveMethodCall( FlatCall fc,
2029 FlatMethod fmCallee,
2030 ReachGraph rgCallee,
2031 Set<Integer> callerNodeIDsCopiedToCallee,
2032 boolean writeDebugDOTs
2035 if( writeDebugDOTs ) {
2036 System.out.println( " Writing out visit "+
2037 debugCallSiteVisitCounter+
2038 " to debug call site" );
2040 debugGraphPrefix = String.format( "call%03d",
2041 debugCallSiteVisitCounter );
2043 rgCallee.writeGraph( debugGraphPrefix+"callee",
2044 resolveMethodDebugDOTwriteLabels,
2045 resolveMethodDebugDOTselectTemps,
2046 resolveMethodDebugDOTpruneGarbage,
2047 resolveMethodDebugDOThideReach,
2048 resolveMethodDebugDOThideSubsetReach,
2049 resolveMethodDebugDOThidePreds,
2050 resolveMethodDebugDOThideEdgeTaints );
2052 writeGraph( debugGraphPrefix+"caller00In",
2053 resolveMethodDebugDOTwriteLabels,
2054 resolveMethodDebugDOTselectTemps,
2055 resolveMethodDebugDOTpruneGarbage,
2056 resolveMethodDebugDOThideReach,
2057 resolveMethodDebugDOThideSubsetReach,
2058 resolveMethodDebugDOThidePreds,
2059 resolveMethodDebugDOThideEdgeTaints,
2060 callerNodeIDsCopiedToCallee );
2065 // method call transfer function steps:
2066 // 1. Use current callee-reachable heap (CRH) to test callee
2067 // predicates and mark what will be coming in.
2068 // 2. Wipe CRH out of caller.
2069 // 3. Transplant marked callee parts in:
2070 // a) bring in nodes
2071 // b) bring in callee -> callee edges
2072 // c) resolve out-of-context -> callee edges
2073 // d) assign return value
2074 // 4. Collapse shadow nodes down
2075 // 5. Global sweep it.
2078 // 1. mark what callee elements have satisfied predicates
2079 Hashtable<HeapRegionNode, ExistPredSet> calleeNodesSatisfied =
2080 new Hashtable<HeapRegionNode, ExistPredSet>();
2082 Hashtable<RefEdge, ExistPredSet> calleeEdgesSatisfied =
2083 new Hashtable<RefEdge, ExistPredSet>();
2085 Hashtable< HeapRegionNode, Hashtable<ReachState, ExistPredSet> >
2086 calleeNode2calleeStatesSatisfied =
2087 new Hashtable< HeapRegionNode, Hashtable<ReachState, ExistPredSet> >();
2089 Hashtable< RefEdge, Hashtable<ReachState, ExistPredSet> >
2090 calleeEdge2calleeStatesSatisfied =
2091 new Hashtable< RefEdge, Hashtable<ReachState, ExistPredSet> >();
2093 Hashtable< RefEdge, Hashtable<Taint, ExistPredSet> >
2094 calleeEdge2calleeTaintsSatisfied =
2095 new Hashtable< RefEdge, Hashtable<Taint, ExistPredSet> >();
2097 Hashtable< RefEdge, Set<RefSrcNode> > calleeEdges2oocCallerSrcMatches =
2098 new Hashtable< RefEdge, Set<RefSrcNode> >();
2101 Iterator meItr = rgCallee.id2hrn.entrySet().iterator();
2102 while( meItr.hasNext() ) {
2103 Map.Entry me = (Map.Entry) meItr.next();
2104 Integer id = (Integer) me.getKey();
2105 HeapRegionNode hrnCallee = (HeapRegionNode) me.getValue();
2107 // if a callee element's predicates are satisfied then a set
2108 // of CALLER predicates is returned: they are the predicates
2109 // that the callee element moved into the caller context
2110 // should have, and it is inefficient to find this again later
2111 ExistPredSet predsIfSatis =
2112 hrnCallee.getPreds().isSatisfiedBy( this,
2113 callerNodeIDsCopiedToCallee
2116 if( predsIfSatis != null ) {
2117 calleeNodesSatisfied.put( hrnCallee, predsIfSatis );
2119 // otherwise don't bother looking at edges to this node
2123 // since the node is coming over, find out which reach
2124 // states on it should come over, too
2125 Iterator<ReachState> stateItr = hrnCallee.getAlpha().iterator();
2126 while( stateItr.hasNext() ) {
2127 ReachState stateCallee = stateItr.next();
2130 stateCallee.getPreds().isSatisfiedBy( this,
2131 callerNodeIDsCopiedToCallee
2133 if( predsIfSatis != null ) {
2134 assert calleeNode2calleeStatesSatisfied.get( hrnCallee ) == null;
2136 Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied =
2137 new Hashtable<ReachState, ExistPredSet>();
2138 calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2140 calleeNode2calleeStatesSatisfied.put( hrnCallee, calleeStatesSatisfied );
2144 // then look at edges to the node
2145 Iterator<RefEdge> reItr = hrnCallee.iteratorToReferencers();
2146 while( reItr.hasNext() ) {
2147 RefEdge reCallee = reItr.next();
2148 RefSrcNode rsnCallee = reCallee.getSrc();
2150 // (caller local variables to in-context heap regions)
2151 // have an (out-of-context heap region -> in-context heap region)
2152 // abstraction in the callEE, so its true we never need to
2153 // look at a (var node -> heap region) edge in callee to bring
2154 // those over for the call site transfer, except for the special
2155 // case of *RETURN var* -> heap region edges.
2156 // What about (param var->heap region)
2157 // edges in callee? They are dealt with below this loop.
2159 if( rsnCallee instanceof VariableNode ) {
2161 // looking for the return-value variable only
2162 VariableNode vnCallee = (VariableNode) rsnCallee;
2163 if( vnCallee.getTempDescriptor() != tdReturn ) {
2167 TempDescriptor returnTemp = fc.getReturnTemp();
2168 if( returnTemp == null ||
2169 !DisjointAnalysis.shouldAnalysisTrack( returnTemp.getType() )
2174 // note that the assignment of the return value is to a
2175 // variable in the caller which is out-of-context with
2176 // respect to the callee
2177 VariableNode vnLhsCaller = getVariableNodeFromTemp( returnTemp );
2178 Set<RefSrcNode> rsnCallers = new HashSet<RefSrcNode>();
2179 rsnCallers.add( vnLhsCaller );
2180 calleeEdges2oocCallerSrcMatches.put( reCallee, rsnCallers );
2184 // for HeapRegionNode callee sources...
2186 // first see if the source is out-of-context, and only
2187 // proceed with this edge if we find some caller-context
2189 HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2190 boolean matchedOutOfContext = false;
2192 if( !hrnSrcCallee.isOutOfContext() ) {
2195 hrnSrcCallee.getPreds().isSatisfiedBy( this,
2196 callerNodeIDsCopiedToCallee
2198 if( predsIfSatis != null ) {
2199 calleeNodesSatisfied.put( hrnSrcCallee, predsIfSatis );
2201 // otherwise forget this edge
2206 // hrnSrcCallee is out-of-context
2208 assert !calleeEdges2oocCallerSrcMatches.containsKey( reCallee );
2210 Set<RefSrcNode> rsnCallers = new HashSet<RefSrcNode>();
2212 // is the target node in the caller?
2213 HeapRegionNode hrnDstCaller = this.id2hrn.get( hrnCallee.getID() );
2214 if( hrnDstCaller == null ) {
2218 Iterator<RefEdge> reDstItr = hrnDstCaller.iteratorToReferencers();
2219 while( reDstItr.hasNext() ) {
2220 // the edge and field (either possibly null) must match
2221 RefEdge reCaller = reDstItr.next();
2223 if( !reCaller.typeEquals ( reCallee.getType() ) ||
2224 !reCaller.fieldEquals( reCallee.getField() )
2229 RefSrcNode rsnCaller = reCaller.getSrc();
2230 if( rsnCaller instanceof VariableNode ) {
2232 // a variable node matches an OOC region with null type
2233 if( hrnSrcCallee.getType() != null ) {
2238 // otherwise types should match
2239 HeapRegionNode hrnCallerSrc = (HeapRegionNode) rsnCaller;
2240 if( hrnSrcCallee.getType() == null ) {
2241 if( hrnCallerSrc.getType() != null ) {
2245 if( !hrnSrcCallee.getType().equals( hrnCallerSrc.getType() ) ) {
2251 rsnCallers.add( rsnCaller );
2252 matchedOutOfContext = true;
2255 if( !rsnCallers.isEmpty() ) {
2256 calleeEdges2oocCallerSrcMatches.put( reCallee, rsnCallers );
2260 if( hrnSrcCallee.isOutOfContext() &&
2261 !matchedOutOfContext ) {
2268 reCallee.getPreds().isSatisfiedBy( this,
2269 callerNodeIDsCopiedToCallee
2272 if( predsIfSatis != null ) {
2273 calleeEdgesSatisfied.put( reCallee, predsIfSatis );
2275 // since the edge is coming over, find out which reach
2276 // states on it should come over, too
2277 stateItr = reCallee.getBeta().iterator();
2278 while( stateItr.hasNext() ) {
2279 ReachState stateCallee = stateItr.next();
2282 stateCallee.getPreds().isSatisfiedBy( this,
2283 callerNodeIDsCopiedToCallee
2285 if( predsIfSatis != null ) {
2286 assert calleeEdge2calleeStatesSatisfied.get( reCallee ) == null;
2288 Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied =
2289 new Hashtable<ReachState, ExistPredSet>();
2290 calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2292 calleeEdge2calleeStatesSatisfied.put( reCallee, calleeStatesSatisfied );
2296 // since the edge is coming over, find out which taints
2297 // on it should come over, too
2298 Iterator<Taint> tItr = reCallee.getTaints().iterator();
2299 while( tItr.hasNext() ) {
2300 Taint tCallee = tItr.next();
2303 tCallee.getPreds().isSatisfiedBy( this,
2304 callerNodeIDsCopiedToCallee
2306 if( predsIfSatis != null ) {
2307 assert calleeEdge2calleeTaintsSatisfied.get( reCallee ) == null;
2309 Hashtable<Taint, ExistPredSet> calleeTaintsSatisfied =
2310 new Hashtable<Taint, ExistPredSet>();
2311 calleeTaintsSatisfied.put( tCallee, predsIfSatis );
2313 calleeEdge2calleeTaintsSatisfied.put( reCallee, calleeTaintsSatisfied );
2320 if( writeDebugDOTs ) {
2321 writeGraph( debugGraphPrefix+"caller20BeforeWipe",
2322 resolveMethodDebugDOTwriteLabels,
2323 resolveMethodDebugDOTselectTemps,
2324 resolveMethodDebugDOTpruneGarbage,
2325 resolveMethodDebugDOThideReach,
2326 resolveMethodDebugDOThideSubsetReach,
2327 resolveMethodDebugDOThidePreds,
2328 resolveMethodDebugDOThideEdgeTaints );
2332 // 2. predicates tested, ok to wipe out caller part
2333 Iterator<Integer> hrnItr = callerNodeIDsCopiedToCallee.iterator();
2334 while( hrnItr.hasNext() ) {
2335 Integer hrnID = hrnItr.next();
2336 HeapRegionNode hrnCaller = id2hrn.get( hrnID );
2337 assert hrnCaller != null;
2339 // when clearing off nodes, also eliminate variable
2341 wipeOut( hrnCaller, true );
2344 // if we are assigning the return value to something, clobber now
2345 // as part of the wipe
2346 TempDescriptor returnTemp = fc.getReturnTemp();
2347 if( returnTemp != null &&
2348 DisjointAnalysis.shouldAnalysisTrack( returnTemp.getType() )
2351 VariableNode vnLhsCaller = getVariableNodeFromTemp( returnTemp );
2352 clearRefEdgesFrom( vnLhsCaller, null, null, true );
2358 if( writeDebugDOTs ) {
2359 writeGraph( debugGraphPrefix+"caller30BeforeAddingNodes",
2360 resolveMethodDebugDOTwriteLabels,
2361 resolveMethodDebugDOTselectTemps,
2362 resolveMethodDebugDOTpruneGarbage,
2363 resolveMethodDebugDOThideReach,
2364 resolveMethodDebugDOThideSubsetReach,
2365 resolveMethodDebugDOThidePreds,
2366 resolveMethodDebugDOThideEdgeTaints );
2372 // 3. callee elements with satisfied preds come in, note that
2373 // the mapping of elements satisfied to preds is like this:
2374 // A callee element EE has preds EEp that are satisfied by
2375 // some caller element ER. We bring EE into the caller
2376 // context as ERee with the preds of ER, namely ERp, which
2377 // in the following algorithm is the value in the mapping
2380 Iterator satisItr = calleeNodesSatisfied.entrySet().iterator();
2381 while( satisItr.hasNext() ) {
2382 Map.Entry me = (Map.Entry) satisItr.next();
2383 HeapRegionNode hrnCallee = (HeapRegionNode) me.getKey();
2384 ExistPredSet preds = (ExistPredSet) me.getValue();
2386 // TODO: I think its true that the current implementation uses
2387 // the type of the OOC region and the predicates OF THE EDGE from
2388 // it to link everything up in caller context, so that's why we're
2389 // skipping this... maybe that's a sillier way to do it?
2390 if( hrnCallee.isOutOfContext() ) {
2394 AllocSite as = hrnCallee.getAllocSite();
2395 allocSites.add( as );
2397 Integer hrnIDshadow = as.getShadowIDfromID( hrnCallee.getID() );
2399 HeapRegionNode hrnCaller = id2hrn.get( hrnIDshadow );
2400 if( hrnCaller == null ) {
2402 createNewHeapRegionNode( hrnIDshadow, // id or null to generate a new one
2403 hrnCallee.isSingleObject(), // single object?
2404 hrnCallee.isNewSummary(), // summary?
2405 false, // out-of-context?
2406 hrnCallee.getType(), // type
2407 hrnCallee.getAllocSite(), // allocation site
2408 toCallerContext( hrnCallee.getInherent(),
2409 calleeNode2calleeStatesSatisfied.get( hrnCallee ) ), // inherent reach
2410 null, // current reach
2411 predsEmpty, // predicates
2412 hrnCallee.getDescription() // description
2415 assert hrnCaller.isWiped();
2418 hrnCaller.setAlpha( toCallerContext( hrnCallee.getAlpha(),
2419 calleeNode2calleeStatesSatisfied.get( hrnCallee )
2423 hrnCaller.setPreds( preds );
2430 if( writeDebugDOTs ) {
2431 writeGraph( debugGraphPrefix+"caller31BeforeAddingEdges",
2432 resolveMethodDebugDOTwriteLabels,
2433 resolveMethodDebugDOTselectTemps,
2434 resolveMethodDebugDOTpruneGarbage,
2435 resolveMethodDebugDOThideReach,
2436 resolveMethodDebugDOThideSubsetReach,
2437 resolveMethodDebugDOThidePreds,
2438 resolveMethodDebugDOThideEdgeTaints );
2442 // set these up during the next procedure so after
2443 // the caller has all of its nodes and edges put
2444 // back together we can propagate the callee's
2445 // reach changes backwards into the caller graph
2446 HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
2448 Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
2449 new Hashtable<RefEdge, ChangeSet>();
2452 // 3.b) callee -> callee edges AND out-of-context -> callee
2453 // which includes return temp -> callee edges now, too
2454 satisItr = calleeEdgesSatisfied.entrySet().iterator();
2455 while( satisItr.hasNext() ) {
2456 Map.Entry me = (Map.Entry) satisItr.next();
2457 RefEdge reCallee = (RefEdge) me.getKey();
2458 ExistPredSet preds = (ExistPredSet) me.getValue();
2460 HeapRegionNode hrnDstCallee = reCallee.getDst();
2461 AllocSite asDst = hrnDstCallee.getAllocSite();
2462 allocSites.add( asDst );
2464 Integer hrnIDDstShadow =
2465 asDst.getShadowIDfromID( hrnDstCallee.getID() );
2467 HeapRegionNode hrnDstCaller = id2hrn.get( hrnIDDstShadow );
2468 assert hrnDstCaller != null;
2471 RefSrcNode rsnCallee = reCallee.getSrc();
2473 Set<RefSrcNode> rsnCallers =
2474 new HashSet<RefSrcNode>();
2476 Set<RefSrcNode> oocCallers =
2477 calleeEdges2oocCallerSrcMatches.get( reCallee );
2479 if( rsnCallee instanceof HeapRegionNode ) {
2480 HeapRegionNode hrnCalleeSrc = (HeapRegionNode) rsnCallee;
2481 if( hrnCalleeSrc.isOutOfContext() ) {
2482 assert oocCallers != null;
2487 if( oocCallers == null ) {
2488 // there are no out-of-context matches, so it's
2489 // either a param/arg var or one in-context heap region
2490 if( rsnCallee instanceof VariableNode ) {
2491 // variable -> node in the callee should only
2492 // come into the caller if its from a param var
2493 VariableNode vnCallee = (VariableNode) rsnCallee;
2494 TempDescriptor tdParam = vnCallee.getTempDescriptor();
2495 TempDescriptor tdArg = fc.getArgMatchingParam( fmCallee,
2497 if( tdArg == null ) {
2498 // this means the variable isn't a parameter, its local
2499 // to the callee so we ignore it in call site transfer
2500 // shouldn't this NEVER HAPPEN?
2504 rsnCallers.add( this.getVariableNodeFromTemp( tdArg ) );
2507 // otherwise source is in context, one region
2509 HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2511 // translate an in-context node to shadow
2512 AllocSite asSrc = hrnSrcCallee.getAllocSite();
2513 allocSites.add( asSrc );
2515 Integer hrnIDSrcShadow =
2516 asSrc.getShadowIDfromID( hrnSrcCallee.getID() );
2518 HeapRegionNode hrnSrcCallerShadow =
2519 this.id2hrn.get( hrnIDSrcShadow );
2521 assert hrnSrcCallerShadow != null;
2523 rsnCallers.add( hrnSrcCallerShadow );
2527 // otherwise we have a set of out-of-context srcs
2528 // that should NOT be translated to shadow nodes
2529 assert !oocCallers.isEmpty();
2530 rsnCallers.addAll( oocCallers );
2533 // now make all caller edges we've identified from
2534 // this callee edge with a satisfied predicate
2535 assert !rsnCallers.isEmpty();
2536 Iterator<RefSrcNode> rsnItr = rsnCallers.iterator();
2537 while( rsnItr.hasNext() ) {
2538 RefSrcNode rsnCaller = rsnItr.next();
2540 RefEdge reCaller = new RefEdge( rsnCaller,
2543 reCallee.getField(),
2544 toCallerContext( reCallee.getBeta(),
2545 calleeEdge2calleeStatesSatisfied.get( reCallee ) ),
2547 toCallerContext( reCallee.getTaints(),
2548 calleeEdge2calleeTaintsSatisfied.get( reCallee ) )
2551 ChangeSet cs = ChangeSet.factory();
2552 Iterator<ReachState> rsItr = reCaller.getBeta().iterator();
2553 while( rsItr.hasNext() ) {
2554 ReachState state = rsItr.next();
2555 ExistPredSet predsPreCallee = state.getPreds();
2557 if( state.isEmpty() ) {
2561 Iterator<ExistPred> predItr = predsPreCallee.iterator();
2562 while( predItr.hasNext() ) {
2563 ExistPred pred = predItr.next();
2564 ReachState old = pred.ne_state;
2570 cs = Canonical.add( cs,
2571 ChangeTuple.factory( old,
2578 // we're just going to use the convenient "merge-if-exists"
2579 // edge call below, but still take a separate look if there
2580 // is an existing caller edge to build change sets properly
2581 if( !cs.isEmpty() ) {
2582 RefEdge edgeExisting = rsnCaller.getReferenceTo( hrnDstCaller,
2586 if( edgeExisting != null ) {
2587 ChangeSet csExisting = edgePlannedChanges.get( edgeExisting );
2588 if( csExisting == null ) {
2589 csExisting = ChangeSet.factory();
2591 edgePlannedChanges.put( edgeExisting,
2592 Canonical.union( csExisting,
2597 edgesForPropagation.add( reCaller );
2598 assert !edgePlannedChanges.containsKey( reCaller );
2599 edgePlannedChanges.put( reCaller, cs );
2603 // then add new caller edge or merge
2604 addEdgeOrMergeWithExisting( reCaller );
2612 if( writeDebugDOTs ) {
2613 writeGraph( debugGraphPrefix+"caller38propagateReach",
2614 resolveMethodDebugDOTwriteLabels,
2615 resolveMethodDebugDOTselectTemps,
2616 resolveMethodDebugDOTpruneGarbage,
2617 resolveMethodDebugDOThideReach,
2618 resolveMethodDebugDOThideSubsetReach,
2619 resolveMethodDebugDOThidePreds,
2620 resolveMethodDebugDOThideEdgeTaints );
2623 // propagate callee reachability changes to the rest
2624 // of the caller graph edges
2625 HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
2627 propagateTokensOverEdges( edgesForPropagation, // source edges
2628 edgePlannedChanges, // map src edge to change set
2629 edgesUpdated ); // list of updated edges
2631 // commit beta' (beta<-betaNew)
2632 Iterator<RefEdge> edgeItr = edgesUpdated.iterator();
2633 while( edgeItr.hasNext() ) {
2634 edgeItr.next().applyBetaNew();
2643 if( writeDebugDOTs ) {
2644 writeGraph( debugGraphPrefix+"caller40BeforeShadowMerge",
2645 resolveMethodDebugDOTwriteLabels,
2646 resolveMethodDebugDOTselectTemps,
2647 resolveMethodDebugDOTpruneGarbage,
2648 resolveMethodDebugDOThideReach,
2649 resolveMethodDebugDOThideSubsetReach,
2650 resolveMethodDebugDOThidePreds,
2651 resolveMethodDebugDOThideEdgeTaints );
2655 // 4) merge shadow nodes so alloc sites are back to k
2656 Iterator<AllocSite> asItr = rgCallee.allocSites.iterator();
2657 while( asItr.hasNext() ) {
2658 // for each allocation site do the following to merge
2659 // shadow nodes (newest from callee) with any existing
2660 // look for the newest normal and newest shadow "slot"
2661 // not being used, transfer normal to shadow. Keep
2662 // doing this until there are no more normal nodes, or
2663 // no empty shadow slots: then merge all remaining normal
2664 // nodes into the shadow summary. Finally, convert all
2665 // shadow to their normal versions.
2666 AllocSite as = asItr.next();
2669 while( ageNorm < allocationDepth &&
2670 ageShad < allocationDepth ) {
2672 // first, are there any normal nodes left?
2673 Integer idNorm = as.getIthOldest( ageNorm );
2674 HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2675 if( hrnNorm == null ) {
2676 // no, this age of normal node not in the caller graph
2681 // yes, a normal node exists, is there an empty shadow
2682 // "slot" to transfer it onto?
2683 HeapRegionNode hrnShad = getIthNode( as, ageShad, true );
2684 if( !hrnShad.isWiped() ) {
2685 // no, this age of shadow node is not empty
2690 // yes, this shadow node is empty
2691 transferOnto( hrnNorm, hrnShad );
2696 // now, while there are still normal nodes but no shadow
2697 // slots, merge normal nodes into the shadow summary
2698 while( ageNorm < allocationDepth ) {
2700 // first, are there any normal nodes left?
2701 Integer idNorm = as.getIthOldest( ageNorm );
2702 HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2703 if( hrnNorm == null ) {
2704 // no, this age of normal node not in the caller graph
2709 // yes, a normal node exists, so get the shadow summary
2710 HeapRegionNode summShad = getSummaryNode( as, true );
2711 mergeIntoSummary( hrnNorm, summShad );
2715 // if there is a normal summary, merge it into shadow summary
2716 Integer idNorm = as.getSummary();
2717 HeapRegionNode summNorm = id2hrn.get( idNorm );
2718 if( summNorm != null ) {
2719 HeapRegionNode summShad = getSummaryNode( as, true );
2720 mergeIntoSummary( summNorm, summShad );
2723 // finally, flip all existing shadow nodes onto the normal
2724 for( int i = 0; i < allocationDepth; ++i ) {
2725 Integer idShad = as.getIthOldestShadow( i );
2726 HeapRegionNode hrnShad = id2hrn.get( idShad );
2727 if( hrnShad != null ) {
2729 HeapRegionNode hrnNorm = getIthNode( as, i, false );
2730 assert hrnNorm.isWiped();
2731 transferOnto( hrnShad, hrnNorm );
2735 Integer idShad = as.getSummaryShadow();
2736 HeapRegionNode summShad = id2hrn.get( idShad );
2737 if( summShad != null ) {
2738 summNorm = getSummaryNode( as, false );
2739 transferOnto( summShad, summNorm );
2748 if( writeDebugDOTs ) {
2749 writeGraph( debugGraphPrefix+"caller45BeforeUnshadow",
2750 resolveMethodDebugDOTwriteLabels,
2751 resolveMethodDebugDOTselectTemps,
2752 resolveMethodDebugDOTpruneGarbage,
2753 resolveMethodDebugDOThideReach,
2754 resolveMethodDebugDOThideSubsetReach,
2755 resolveMethodDebugDOThidePreds,
2756 resolveMethodDebugDOThideEdgeTaints );
2760 Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
2761 while( itrAllHRNodes.hasNext() ) {
2762 Map.Entry me = (Map.Entry) itrAllHRNodes.next();
2763 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
2765 hrn.setAlpha( unshadow( hrn.getAlpha() ) );
2767 Iterator<RefEdge> itrEdges = hrn.iteratorToReferencers();
2768 while( itrEdges.hasNext() ) {
2769 RefEdge re = itrEdges.next();
2770 re.setBeta( unshadow( re.getBeta() ) );
2777 if( writeDebugDOTs ) {
2778 writeGraph( debugGraphPrefix+"caller50BeforeGlobalSweep",
2779 resolveMethodDebugDOTwriteLabels,
2780 resolveMethodDebugDOTselectTemps,
2781 resolveMethodDebugDOTpruneGarbage,
2782 resolveMethodDebugDOThideReach,
2783 resolveMethodDebugDOThideSubsetReach,
2784 resolveMethodDebugDOThidePreds,
2785 resolveMethodDebugDOThideEdgeTaints );
2790 if( !DISABLE_GLOBAL_SWEEP ) {
2795 if( writeDebugDOTs ) {
2796 writeGraph( debugGraphPrefix+"caller90AfterTransfer",
2797 resolveMethodDebugDOTwriteLabels,
2798 resolveMethodDebugDOTselectTemps,
2799 resolveMethodDebugDOTpruneGarbage,
2800 resolveMethodDebugDOThideReach,
2801 resolveMethodDebugDOThideSubsetReach,
2802 resolveMethodDebugDOThidePreds,
2803 resolveMethodDebugDOThideEdgeTaints );
2809 ////////////////////////////////////////////////////
2811 // Abstract garbage collection simply removes
2812 // heap region nodes that are not mechanically
2813 // reachable from a root set. This step is
2814 // essential for testing node and edge existence
2815 // predicates efficiently
2817 ////////////////////////////////////////////////////
2818 public void abstractGarbageCollect( Set<TempDescriptor> liveSet ) {
2820 // calculate a root set, will be different for Java
2821 // version of analysis versus Bamboo version
2822 Set<RefSrcNode> toVisit = new HashSet<RefSrcNode>();
2824 // visit every variable in graph while building root
2825 // set, and do iterating on a copy, so we can remove
2826 // dead variables while we're at this
2827 Iterator makeCopyItr = td2vn.entrySet().iterator();
2828 Set entrysCopy = new HashSet();
2829 while( makeCopyItr.hasNext() ) {
2830 entrysCopy.add( makeCopyItr.next() );
2833 Iterator eItr = entrysCopy.iterator();
2834 while( eItr.hasNext() ) {
2835 Map.Entry me = (Map.Entry) eItr.next();
2836 TempDescriptor td = (TempDescriptor) me.getKey();
2837 VariableNode vn = (VariableNode) me.getValue();
2839 if( liveSet.contains( td ) ) {
2843 // dead var, remove completely from graph
2845 clearRefEdgesFrom( vn, null, null, true );
2849 // everything visited in a traversal is
2850 // considered abstractly live
2851 Set<RefSrcNode> visited = new HashSet<RefSrcNode>();
2853 while( !toVisit.isEmpty() ) {
2854 RefSrcNode rsn = toVisit.iterator().next();
2855 toVisit.remove( rsn );
2858 Iterator<RefEdge> hrnItr = rsn.iteratorToReferencees();
2859 while( hrnItr.hasNext() ) {
2860 RefEdge edge = hrnItr.next();
2861 HeapRegionNode hrn = edge.getDst();
2863 if( !visited.contains( hrn ) ) {
2869 // get a copy of the set to iterate over because
2870 // we're going to monkey with the graph when we
2871 // identify a garbage node
2872 Set<HeapRegionNode> hrnAllPrior = new HashSet<HeapRegionNode>();
2873 Iterator<HeapRegionNode> hrnItr = id2hrn.values().iterator();
2874 while( hrnItr.hasNext() ) {
2875 hrnAllPrior.add( hrnItr.next() );
2878 Iterator<HeapRegionNode> hrnAllItr = hrnAllPrior.iterator();
2879 while( hrnAllItr.hasNext() ) {
2880 HeapRegionNode hrn = hrnAllItr.next();
2882 if( !visited.contains( hrn ) ) {
2884 // heap region nodes are compared across ReachGraph
2885 // objects by their integer ID, so when discarding
2886 // garbage nodes we must also discard entries in
2887 // the ID -> heap region hashtable.
2888 id2hrn.remove( hrn.getID() );
2890 // RefEdge objects are two-way linked between
2891 // nodes, so when a node is identified as garbage,
2892 // actively clear references to and from it so
2893 // live nodes won't have dangling RefEdge's
2894 wipeOut( hrn, true );
2896 // if we just removed the last node from an allocation
2897 // site, it should be taken out of the ReachGraph's list
2898 AllocSite as = hrn.getAllocSite();
2899 if( !hasNodesOf( as ) ) {
2900 allocSites.remove( as );
2906 protected boolean hasNodesOf( AllocSite as ) {
2907 if( id2hrn.containsKey( as.getSummary() ) ) {
2911 for( int i = 0; i < allocationDepth; ++i ) {
2912 if( id2hrn.containsKey( as.getIthOldest( i ) ) ) {
2920 ////////////////////////////////////////////////////
2922 // This global sweep is an optional step to prune
2923 // reachability sets that are not internally
2924 // consistent with the global graph. It should be
2925 // invoked after strong updates or method calls.
2927 ////////////////////////////////////////////////////
2928 public void globalSweep() {
2930 // boldB is part of the phase 1 sweep
2931 // it has an in-context table and an out-of-context table
2932 Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBic =
2933 new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();
2935 Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBooc =
2936 new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();
2938 // visit every heap region to initialize alphaNew and betaNew,
2939 // and make a map of every hrnID to the source nodes it should
2940 // propagate forward from. In-context flagged hrnID's propagate
2941 // from only the in-context node they name, but out-of-context
2942 // ID's may propagate from several out-of-context nodes
2943 Hashtable< Integer, Set<HeapRegionNode> > icID2srcs =
2944 new Hashtable< Integer, Set<HeapRegionNode> >();
2946 Hashtable< Integer, Set<HeapRegionNode> > oocID2srcs =
2947 new Hashtable< Integer, Set<HeapRegionNode> >();
2950 Iterator itrHrns = id2hrn.entrySet().iterator();
2951 while( itrHrns.hasNext() ) {
2952 Map.Entry me = (Map.Entry) itrHrns.next();
2953 Integer hrnID = (Integer) me.getKey();
2954 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
2956 // assert that this node and incoming edges have clean alphaNew
2957 // and betaNew sets, respectively
2958 assert rsetEmpty.equals( hrn.getAlphaNew() );
2960 Iterator<RefEdge> itrRers = hrn.iteratorToReferencers();
2961 while( itrRers.hasNext() ) {
2962 RefEdge edge = itrRers.next();
2963 assert rsetEmpty.equals( edge.getBetaNew() );
2966 // make a mapping of IDs to heap regions they propagate from
2967 if( hrn.isFlagged() ) {
2968 assert !hrn.isOutOfContext();
2969 assert !icID2srcs.containsKey( hrn.getID() );
2971 // in-context flagged node IDs simply propagate from the
2973 Set<HeapRegionNode> srcs = new HashSet<HeapRegionNode>();
2975 icID2srcs.put( hrn.getID(), srcs );
2978 if( hrn.isOutOfContext() ) {
2979 assert !hrn.isFlagged();
2981 // the reachability states on an out-of-context
2982 // node are not really important (combinations of
2983 // IDs or arity)--what matters is that the states
2984 // specify which nodes this out-of-context node
2985 // stands in for. For example, if the state [17?, 19*]
2986 // appears on the ooc node, it may serve as a source
2987 // for node 17? and a source for node 19.
2988 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
2989 while( stateItr.hasNext() ) {
2990 ReachState state = stateItr.next();
2992 Iterator<ReachTuple> rtItr = state.iterator();
2993 while( rtItr.hasNext() ) {
2994 ReachTuple rt = rtItr.next();
2995 assert rt.isOutOfContext();
2997 Set<HeapRegionNode> srcs = oocID2srcs.get( rt.getHrnID() );
2998 if( srcs == null ) {
2999 srcs = new HashSet<HeapRegionNode>();
3002 oocID2srcs.put( rt.getHrnID(), srcs );
3008 // calculate boldB for all hrnIDs identified by the above
3009 // node traversal, propagating from every source
3010 while( !icID2srcs.isEmpty() || !oocID2srcs.isEmpty() ) {
3013 Set<HeapRegionNode> srcs;
3016 if( !icID2srcs.isEmpty() ) {
3017 Map.Entry me = (Map.Entry) icID2srcs.entrySet().iterator().next();
3018 hrnID = (Integer) me.getKey();
3019 srcs = (Set<HeapRegionNode>) me.getValue();
3021 icID2srcs.remove( hrnID );
3024 assert !oocID2srcs.isEmpty();
3026 Map.Entry me = (Map.Entry) oocID2srcs.entrySet().iterator().next();
3027 hrnID = (Integer) me.getKey();
3028 srcs = (Set<HeapRegionNode>) me.getValue();
3030 oocID2srcs.remove( hrnID );
3034 Hashtable<RefEdge, ReachSet> boldB_f =
3035 new Hashtable<RefEdge, ReachSet>();
3037 Set<RefEdge> workSetEdges = new HashSet<RefEdge>();
3039 Iterator<HeapRegionNode> hrnItr = srcs.iterator();
3040 while( hrnItr.hasNext() ) {
3041 HeapRegionNode hrn = hrnItr.next();
3043 assert workSetEdges.isEmpty();
3045 // initial boldB_f constraints
3046 Iterator<RefEdge> itrRees = hrn.iteratorToReferencees();
3047 while( itrRees.hasNext() ) {
3048 RefEdge edge = itrRees.next();
3050 assert !boldB_f.containsKey( edge );
3051 boldB_f.put( edge, edge.getBeta() );
3053 assert !workSetEdges.contains( edge );
3054 workSetEdges.add( edge );
3057 // enforce the boldB_f constraint at edges until we reach a fixed point
3058 while( !workSetEdges.isEmpty() ) {
3059 RefEdge edge = workSetEdges.iterator().next();
3060 workSetEdges.remove( edge );
3062 Iterator<RefEdge> itrPrime = edge.getDst().iteratorToReferencees();
3063 while( itrPrime.hasNext() ) {
3064 RefEdge edgePrime = itrPrime.next();
3066 ReachSet prevResult = boldB_f.get( edgePrime );
3067 ReachSet intersection = Canonical.intersection( boldB_f.get( edge ),
3071 if( prevResult == null ||
3072 Canonical.unionORpreds( prevResult,
3073 intersection ).size()
3077 if( prevResult == null ) {
3078 boldB_f.put( edgePrime,
3079 Canonical.unionORpreds( edgePrime.getBeta(),
3084 boldB_f.put( edgePrime,
3085 Canonical.unionORpreds( prevResult,
3090 workSetEdges.add( edgePrime );
3097 boldBic.put( hrnID, boldB_f );
3099 boldBooc.put( hrnID, boldB_f );
3104 // use boldB to prune hrnIDs from alpha states that are impossible
3105 // and propagate the differences backwards across edges
3106 HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
3108 Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
3109 new Hashtable<RefEdge, ChangeSet>();
3112 itrHrns = id2hrn.entrySet().iterator();
3113 while( itrHrns.hasNext() ) {
3114 Map.Entry me = (Map.Entry) itrHrns.next();
3115 Integer hrnID = (Integer) me.getKey();
3116 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3118 // out-of-context nodes don't participate in the
3119 // global sweep, they serve as sources for the pass
3121 if( hrn.isOutOfContext() ) {
3125 // the inherent states of a region are the exception
3126 // to removal as the global sweep prunes
3127 ReachTuple rtException = ReachTuple.factory( hrnID,
3128 !hrn.isSingleObject(),
3129 ReachTuple.ARITY_ONE,
3130 false // out-of-context
3133 ChangeSet cts = ChangeSet.factory();
3135 // mark hrnIDs for removal
3136 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3137 while( stateItr.hasNext() ) {
3138 ReachState stateOld = stateItr.next();
3140 ReachState markedHrnIDs = ReachState.factory();
3142 Iterator<ReachTuple> rtItr = stateOld.iterator();
3143 while( rtItr.hasNext() ) {
3144 ReachTuple rtOld = rtItr.next();
3146 // never remove the inherent hrnID from a flagged region
3147 // because it is trivially satisfied
3148 if( hrn.isFlagged() ) {
3149 if( rtOld == rtException ) {
3154 // does boldB allow this hrnID?
3155 boolean foundState = false;
3156 Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3157 while( incidentEdgeItr.hasNext() ) {
3158 RefEdge incidentEdge = incidentEdgeItr.next();
3160 Hashtable<RefEdge, ReachSet> B;
3161 if( rtOld.isOutOfContext() ) {
3162 B = boldBooc.get( rtOld.getHrnID() );
3165 if( !id2hrn.containsKey( rtOld.getHrnID() ) ) {
3166 // let symbols not in the graph get pruned
3170 B = boldBic.get( rtOld.getHrnID() );
3174 ReachSet boldB_rtOld_incident = B.get( incidentEdge );
3175 if( boldB_rtOld_incident != null &&
3176 boldB_rtOld_incident.containsIgnorePreds( stateOld ) != null
3184 markedHrnIDs = Canonical.add( markedHrnIDs, rtOld );
3188 // if there is nothing marked, just move on
3189 if( markedHrnIDs.isEmpty() ) {
3190 hrn.setAlphaNew( Canonical.add( hrn.getAlphaNew(),
3197 // remove all marked hrnIDs and establish a change set that should
3198 // propagate backwards over edges from this node
3199 ReachState statePruned = ReachState.factory();
3200 rtItr = stateOld.iterator();
3201 while( rtItr.hasNext() ) {
3202 ReachTuple rtOld = rtItr.next();
3204 if( !markedHrnIDs.containsTuple( rtOld ) ) {
3205 statePruned = Canonical.add( statePruned, rtOld );
3208 assert !stateOld.equals( statePruned );
3210 hrn.setAlphaNew( Canonical.add( hrn.getAlphaNew(),
3214 ChangeTuple ct = ChangeTuple.factory( stateOld,
3217 cts = Canonical.add( cts, ct );
3220 // throw change tuple set on all incident edges
3221 if( !cts.isEmpty() ) {
3222 Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3223 while( incidentEdgeItr.hasNext() ) {
3224 RefEdge incidentEdge = incidentEdgeItr.next();
3226 edgesForPropagation.add( incidentEdge );
3228 if( edgePlannedChanges.get( incidentEdge ) == null ) {
3229 edgePlannedChanges.put( incidentEdge, cts );
3231 edgePlannedChanges.put(
3233 Canonical.union( edgePlannedChanges.get( incidentEdge ),
3242 HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
3244 propagateTokensOverEdges( edgesForPropagation,
3248 // at the end of the 1st phase reference edges have
3249 // beta, betaNew that correspond to beta and betaR
3251 // commit beta<-betaNew, so beta=betaR and betaNew
3252 // will represent the beta' calculation in 2nd phase
3254 // commit alpha<-alphaNew because it won't change
3255 HashSet<RefEdge> res = new HashSet<RefEdge>();
3257 Iterator<HeapRegionNode> nodeItr = id2hrn.values().iterator();
3258 while( nodeItr.hasNext() ) {
3259 HeapRegionNode hrn = nodeItr.next();
3261 // as mentioned above, out-of-context nodes only serve
3262 // as sources of reach states for the sweep, not part
3264 if( hrn.isOutOfContext() ) {
3265 assert hrn.getAlphaNew().equals( rsetEmpty );
3267 hrn.applyAlphaNew();
3270 Iterator<RefEdge> itrRes = hrn.iteratorToReferencers();
3271 while( itrRes.hasNext() ) {
3272 res.add( itrRes.next() );
3278 Iterator<RefEdge> edgeItr = res.iterator();
3279 while( edgeItr.hasNext() ) {
3280 RefEdge edge = edgeItr.next();
3281 HeapRegionNode hrn = edge.getDst();
3283 // commit results of last phase
3284 if( edgesUpdated.contains( edge ) ) {
3285 edge.applyBetaNew();
3288 // compute intial condition of 2nd phase
3289 edge.setBetaNew( Canonical.intersection( edge.getBeta(),
3295 // every edge in the graph is the initial workset
3296 Set<RefEdge> edgeWorkSet = (Set) res.clone();
3297 while( !edgeWorkSet.isEmpty() ) {
3298 RefEdge edgePrime = edgeWorkSet.iterator().next();
3299 edgeWorkSet.remove( edgePrime );
3301 RefSrcNode rsn = edgePrime.getSrc();
3302 if( !(rsn instanceof HeapRegionNode) ) {
3305 HeapRegionNode hrn = (HeapRegionNode) rsn;
3307 Iterator<RefEdge> itrEdge = hrn.iteratorToReferencers();
3308 while( itrEdge.hasNext() ) {
3309 RefEdge edge = itrEdge.next();
3311 ReachSet prevResult = edge.getBetaNew();
3312 assert prevResult != null;
3314 ReachSet intersection =
3315 Canonical.intersection( edge.getBeta(),
3316 edgePrime.getBetaNew()
3319 if( Canonical.unionORpreds( prevResult,
3326 Canonical.unionORpreds( prevResult,
3330 edgeWorkSet.add( edge );
3335 // commit beta' (beta<-betaNew)
3336 edgeItr = res.iterator();
3337 while( edgeItr.hasNext() ) {
3338 edgeItr.next().applyBetaNew();
3343 // a useful assertion for debugging:
3344 // every in-context tuple on any edge or
3345 // any node should name a node that is
3346 // part of the graph
3347 public boolean inContextTuplesInGraph() {
3349 Iterator hrnItr = id2hrn.entrySet().iterator();
3350 while( hrnItr.hasNext() ) {
3351 Map.Entry me = (Map.Entry) hrnItr.next();
3352 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3355 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3356 while( stateItr.hasNext() ) {
3357 ReachState state = stateItr.next();
3359 Iterator<ReachTuple> rtItr = state.iterator();
3360 while( rtItr.hasNext() ) {
3361 ReachTuple rt = rtItr.next();
3363 if( !rt.isOutOfContext() ) {
3364 if( !id2hrn.containsKey( rt.getHrnID() ) ) {
3365 System.out.println( rt.getHrnID()+" is missing" );
3373 Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3374 while( edgeItr.hasNext() ) {
3375 RefEdge edge = edgeItr.next();
3377 Iterator<ReachState> stateItr = edge.getBeta().iterator();
3378 while( stateItr.hasNext() ) {
3379 ReachState state = stateItr.next();
3381 Iterator<ReachTuple> rtItr = state.iterator();
3382 while( rtItr.hasNext() ) {
3383 ReachTuple rt = rtItr.next();
3385 if( !rt.isOutOfContext() ) {
3386 if( !id2hrn.containsKey( rt.getHrnID() ) ) {
3387 System.out.println( rt.getHrnID()+" is missing" );
3400 // another useful assertion for debugging
3401 public boolean noEmptyReachSetsInGraph() {
3403 Iterator hrnItr = id2hrn.entrySet().iterator();
3404 while( hrnItr.hasNext() ) {
3405 Map.Entry me = (Map.Entry) hrnItr.next();
3406 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3408 if( !hrn.isOutOfContext() &&
3410 hrn.getAlpha().isEmpty()
3412 System.out.println( "!!! "+hrn+" has an empty ReachSet !!!" );
3416 Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3417 while( edgeItr.hasNext() ) {
3418 RefEdge edge = edgeItr.next();
3420 if( edge.getBeta().isEmpty() ) {
3421 System.out.println( "!!! "+edge+" has an empty ReachSet !!!" );
3431 public boolean everyReachStateWTrue() {
3433 Iterator hrnItr = id2hrn.entrySet().iterator();
3434 while( hrnItr.hasNext() ) {
3435 Map.Entry me = (Map.Entry) hrnItr.next();
3436 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3439 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3440 while( stateItr.hasNext() ) {
3441 ReachState state = stateItr.next();
3443 if( !state.getPreds().equals( predsTrue ) ) {
3449 Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3450 while( edgeItr.hasNext() ) {
3451 RefEdge edge = edgeItr.next();
3453 Iterator<ReachState> stateItr = edge.getBeta().iterator();
3454 while( stateItr.hasNext() ) {
3455 ReachState state = stateItr.next();
3457 if( !state.getPreds().equals( predsTrue ) ) {
3470 ////////////////////////////////////////////////////
3471 // in merge() and equals() methods the suffix A
3472 // represents the passed in graph and the suffix
3473 // B refers to the graph in this object
3474 // Merging means to take the incoming graph A and
3475 // merge it into B, so after the operation graph B
3476 // is the final result.
3477 ////////////////////////////////////////////////////
3478 protected void merge( ReachGraph rg ) {
3485 mergeRefEdges ( rg );
3486 mergeAllocSites( rg );
3489 protected void mergeNodes( ReachGraph rg ) {
3491 // start with heap region nodes
3492 Set sA = rg.id2hrn.entrySet();
3493 Iterator iA = sA.iterator();
3494 while( iA.hasNext() ) {
3495 Map.Entry meA = (Map.Entry) iA.next();
3496 Integer idA = (Integer) meA.getKey();
3497 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3499 // if this graph doesn't have a node the
3500 // incoming graph has, allocate it
3501 if( !id2hrn.containsKey( idA ) ) {
3502 HeapRegionNode hrnB = hrnA.copy();
3503 id2hrn.put( idA, hrnB );
3506 // otherwise this is a node present in both graphs
3507 // so make the new reachability set a union of the
3508 // nodes' reachability sets
3509 HeapRegionNode hrnB = id2hrn.get( idA );
3510 hrnB.setAlpha( Canonical.unionORpreds( hrnB.getAlpha(),
3515 hrnB.setPreds( Canonical.join( hrnB.getPreds(),
3522 if( !hrnA.equals( hrnB ) ) {
3523 rg.writeGraph( "graphA" );
3524 this.writeGraph( "graphB" );
3525 throw new Error( "flagged not matching" );
3533 // now add any variable nodes that are in graph B but
3535 sA = rg.td2vn.entrySet();
3537 while( iA.hasNext() ) {
3538 Map.Entry meA = (Map.Entry) iA.next();
3539 TempDescriptor tdA = (TempDescriptor) meA.getKey();
3540 VariableNode lnA = (VariableNode) meA.getValue();
3542 // if the variable doesn't exist in B, allocate and add it
3543 VariableNode lnB = getVariableNodeFromTemp( tdA );
3547 protected void mergeRefEdges( ReachGraph rg ) {
3549 // between heap regions
3550 Set sA = rg.id2hrn.entrySet();
3551 Iterator iA = sA.iterator();
3552 while( iA.hasNext() ) {
3553 Map.Entry meA = (Map.Entry) iA.next();
3554 Integer idA = (Integer) meA.getKey();
3555 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3557 Iterator<RefEdge> heapRegionsItrA = hrnA.iteratorToReferencees();
3558 while( heapRegionsItrA.hasNext() ) {
3559 RefEdge edgeA = heapRegionsItrA.next();
3560 HeapRegionNode hrnChildA = edgeA.getDst();
3561 Integer idChildA = hrnChildA.getID();
3563 // at this point we know an edge in graph A exists
3564 // idA -> idChildA, does this exist in B?
3565 assert id2hrn.containsKey( idA );
3566 HeapRegionNode hrnB = id2hrn.get( idA );
3567 RefEdge edgeToMerge = null;
3569 Iterator<RefEdge> heapRegionsItrB = hrnB.iteratorToReferencees();
3570 while( heapRegionsItrB.hasNext() &&
3571 edgeToMerge == null ) {
3573 RefEdge edgeB = heapRegionsItrB.next();
3574 HeapRegionNode hrnChildB = edgeB.getDst();
3575 Integer idChildB = hrnChildB.getID();
3577 // don't use the RefEdge.equals() here because
3578 // we're talking about existence between graphs,
3579 // not intragraph equal
3580 if( idChildB.equals( idChildA ) &&
3581 edgeB.typeAndFieldEquals( edgeA ) ) {
3583 edgeToMerge = edgeB;
3587 // if the edge from A was not found in B,
3589 if( edgeToMerge == null ) {
3590 assert id2hrn.containsKey( idChildA );
3591 HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3592 edgeToMerge = edgeA.copy();
3593 edgeToMerge.setSrc( hrnB );
3594 edgeToMerge.setDst( hrnChildB );
3595 addRefEdge( hrnB, hrnChildB, edgeToMerge );
3597 // otherwise, the edge already existed in both graphs
3598 // so merge their reachability sets
3600 // just replace this beta set with the union
3601 assert edgeToMerge != null;
3602 edgeToMerge.setBeta(
3603 Canonical.unionORpreds( edgeToMerge.getBeta(),
3607 edgeToMerge.setPreds(
3608 Canonical.join( edgeToMerge.getPreds(),
3612 edgeToMerge.setTaints(
3613 Canonical.union( edgeToMerge.getTaints(),
3621 // and then again from variable nodes
3622 sA = rg.td2vn.entrySet();
3624 while( iA.hasNext() ) {
3625 Map.Entry meA = (Map.Entry) iA.next();
3626 TempDescriptor tdA = (TempDescriptor) meA.getKey();
3627 VariableNode vnA = (VariableNode) meA.getValue();
3629 Iterator<RefEdge> heapRegionsItrA = vnA.iteratorToReferencees();
3630 while( heapRegionsItrA.hasNext() ) {
3631 RefEdge edgeA = heapRegionsItrA.next();
3632 HeapRegionNode hrnChildA = edgeA.getDst();
3633 Integer idChildA = hrnChildA.getID();
3635 // at this point we know an edge in graph A exists
3636 // tdA -> idChildA, does this exist in B?
3637 assert td2vn.containsKey( tdA );
3638 VariableNode vnB = td2vn.get( tdA );
3639 RefEdge edgeToMerge = null;
3641 Iterator<RefEdge> heapRegionsItrB = vnB.iteratorToReferencees();
3642 while( heapRegionsItrB.hasNext() &&
3643 edgeToMerge == null ) {
3645 RefEdge edgeB = heapRegionsItrB.next();
3646 HeapRegionNode hrnChildB = edgeB.getDst();
3647 Integer idChildB = hrnChildB.getID();
3649 // don't use the RefEdge.equals() here because
3650 // we're talking about existence between graphs
3651 if( idChildB.equals( idChildA ) &&
3652 edgeB.typeAndFieldEquals( edgeA ) ) {
3654 edgeToMerge = edgeB;
3658 // if the edge from A was not found in B,
3660 if( edgeToMerge == null ) {
3661 assert id2hrn.containsKey( idChildA );
3662 HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3663 edgeToMerge = edgeA.copy();
3664 edgeToMerge.setSrc( vnB );
3665 edgeToMerge.setDst( hrnChildB );
3666 addRefEdge( vnB, hrnChildB, edgeToMerge );
3668 // otherwise, the edge already existed in both graphs
3669 // so merge their reachability sets
3671 // just replace this beta set with the union
3672 edgeToMerge.setBeta( Canonical.unionORpreds( edgeToMerge.getBeta(),
3676 edgeToMerge.setPreds( Canonical.join( edgeToMerge.getPreds(),
3680 edgeToMerge.setTaints(
3681 Canonical.union( edgeToMerge.getTaints(),
3690 protected void mergeAllocSites( ReachGraph rg ) {
3691 allocSites.addAll( rg.allocSites );
3696 static boolean dbgEquals = false;
3699 // it is necessary in the equals() member functions
3700 // to "check both ways" when comparing the data
3701 // structures of two graphs. For instance, if all
3702 // edges between heap region nodes in graph A are
3703 // present and equal in graph B it is not sufficient
3704 // to say the graphs are equal. Consider that there
3705 // may be edges in graph B that are not in graph A.
3706 // the only way to know that all edges in both graphs
3707 // are equally present is to iterate over both data
3708 // structures and compare against the other graph.
3709 public boolean equals( ReachGraph rg ) {
3713 System.out.println( "rg is null" );
3718 if( !areHeapRegionNodesEqual( rg ) ) {
3720 System.out.println( "hrn not equal" );
3725 if( !areVariableNodesEqual( rg ) ) {
3727 System.out.println( "vars not equal" );
3732 if( !areRefEdgesEqual( rg ) ) {
3734 System.out.println( "edges not equal" );
3739 // if everything is equal up to this point,
3740 // assert that allocSites is also equal--
3741 // this data is redundant but kept for efficiency
3742 assert allocSites.equals( rg.allocSites );
3748 protected boolean areHeapRegionNodesEqual( ReachGraph rg ) {
3750 if( !areallHRNinAalsoinBandequal( this, rg ) ) {
3754 if( !areallHRNinAalsoinBandequal( rg, this ) ) {
3761 static protected boolean areallHRNinAalsoinBandequal( ReachGraph rgA,
3763 Set sA = rgA.id2hrn.entrySet();
3764 Iterator iA = sA.iterator();
3765 while( iA.hasNext() ) {
3766 Map.Entry meA = (Map.Entry) iA.next();
3767 Integer idA = (Integer) meA.getKey();
3768 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3770 if( !rgB.id2hrn.containsKey( idA ) ) {
3774 HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3775 if( !hrnA.equalsIncludingAlphaAndPreds( hrnB ) ) {
3783 protected boolean areVariableNodesEqual( ReachGraph rg ) {
3785 if( !areallVNinAalsoinBandequal( this, rg ) ) {
3789 if( !areallVNinAalsoinBandequal( rg, this ) ) {
3796 static protected boolean areallVNinAalsoinBandequal( ReachGraph rgA,
3798 Set sA = rgA.td2vn.entrySet();
3799 Iterator iA = sA.iterator();
3800 while( iA.hasNext() ) {
3801 Map.Entry meA = (Map.Entry) iA.next();
3802 TempDescriptor tdA = (TempDescriptor) meA.getKey();
3804 if( !rgB.td2vn.containsKey( tdA ) ) {
3813 protected boolean areRefEdgesEqual( ReachGraph rg ) {
3814 if( !areallREinAandBequal( this, rg ) ) {
3818 if( !areallREinAandBequal( rg, this ) ) {
3825 static protected boolean areallREinAandBequal( ReachGraph rgA,
3828 // check all the heap region->heap region edges
3829 Set sA = rgA.id2hrn.entrySet();
3830 Iterator iA = sA.iterator();
3831 while( iA.hasNext() ) {
3832 Map.Entry meA = (Map.Entry) iA.next();
3833 Integer idA = (Integer) meA.getKey();
3834 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3836 // we should have already checked that the same
3837 // heap regions exist in both graphs
3838 assert rgB.id2hrn.containsKey( idA );
3840 if( !areallREfromAequaltoB( rgA, hrnA, rgB ) ) {
3844 // then check every edge in B for presence in A, starting
3845 // from the same parent HeapRegionNode
3846 HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3848 if( !areallREfromAequaltoB( rgB, hrnB, rgA ) ) {
3853 // then check all the variable->heap region edges
3854 sA = rgA.td2vn.entrySet();
3856 while( iA.hasNext() ) {
3857 Map.Entry meA = (Map.Entry) iA.next();
3858 TempDescriptor tdA = (TempDescriptor) meA.getKey();
3859 VariableNode vnA = (VariableNode) meA.getValue();
3861 // we should have already checked that the same
3862 // label nodes exist in both graphs
3863 assert rgB.td2vn.containsKey( tdA );
3865 if( !areallREfromAequaltoB( rgA, vnA, rgB ) ) {
3869 // then check every edge in B for presence in A, starting
3870 // from the same parent VariableNode
3871 VariableNode vnB = rgB.td2vn.get( tdA );
3873 if( !areallREfromAequaltoB( rgB, vnB, rgA ) ) {
3882 static protected boolean areallREfromAequaltoB( ReachGraph rgA,
3886 Iterator<RefEdge> itrA = rnA.iteratorToReferencees();
3887 while( itrA.hasNext() ) {
3888 RefEdge edgeA = itrA.next();
3889 HeapRegionNode hrnChildA = edgeA.getDst();
3890 Integer idChildA = hrnChildA.getID();
3892 assert rgB.id2hrn.containsKey( idChildA );
3894 // at this point we know an edge in graph A exists
3895 // rnA -> idChildA, does this exact edge exist in B?
3896 boolean edgeFound = false;
3898 RefSrcNode rnB = null;
3899 if( rnA instanceof HeapRegionNode ) {
3900 HeapRegionNode hrnA = (HeapRegionNode) rnA;
3901 rnB = rgB.id2hrn.get( hrnA.getID() );
3903 VariableNode vnA = (VariableNode) rnA;
3904 rnB = rgB.td2vn.get( vnA.getTempDescriptor() );
3907 Iterator<RefEdge> itrB = rnB.iteratorToReferencees();
3908 while( itrB.hasNext() ) {
3909 RefEdge edgeB = itrB.next();
3910 HeapRegionNode hrnChildB = edgeB.getDst();
3911 Integer idChildB = hrnChildB.getID();
3913 if( idChildA.equals( idChildB ) &&
3914 edgeA.typeAndFieldEquals( edgeB ) ) {
3916 // there is an edge in the right place with the right field,
3917 // but do they have the same attributes?
3918 if( edgeA.getBeta().equals( edgeB.getBeta() ) &&
3919 edgeA.equalsPreds( edgeB )
3935 // can be used to assert monotonicity
3936 static public boolean isNoSmallerThan( ReachGraph rgA,
3939 //System.out.println( "*** Asking if A is no smaller than B ***" );
3942 Iterator iA = rgA.id2hrn.entrySet().iterator();
3943 while( iA.hasNext() ) {
3944 Map.Entry meA = (Map.Entry) iA.next();
3945 Integer idA = (Integer) meA.getKey();
3946 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3948 if( !rgB.id2hrn.containsKey( idA ) ) {
3949 System.out.println( " regions smaller" );
3953 //HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3954 /* NOT EQUALS, NO SMALLER THAN!
3955 if( !hrnA.equalsIncludingAlphaAndPreds( hrnB ) ) {
3956 System.out.println( " regions smaller" );
3962 // this works just fine, no smaller than
3963 if( !areallVNinAalsoinBandequal( rgA, rgB ) ) {
3964 System.out.println( " vars smaller:" );
3965 System.out.println( " A:"+rgA.td2vn.keySet() );
3966 System.out.println( " B:"+rgB.td2vn.keySet() );
3971 iA = rgA.id2hrn.entrySet().iterator();
3972 while( iA.hasNext() ) {
3973 Map.Entry meA = (Map.Entry) iA.next();
3974 Integer idA = (Integer) meA.getKey();
3975 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3977 Iterator<RefEdge> reItr = hrnA.iteratorToReferencers();
3978 while( reItr.hasNext() ) {
3979 RefEdge edgeA = reItr.next();
3980 RefSrcNode rsnA = edgeA.getSrc();
3982 // we already checked that nodes were present
3983 HeapRegionNode hrnB = rgB.id2hrn.get( hrnA.getID() );
3984 assert hrnB != null;
3987 if( rsnA instanceof VariableNode ) {
3988 VariableNode vnA = (VariableNode) rsnA;
3989 rsnB = rgB.td2vn.get( vnA.getTempDescriptor() );
3992 HeapRegionNode hrnSrcA = (HeapRegionNode) rsnA;
3993 rsnB = rgB.id2hrn.get( hrnSrcA.getID() );
3995 assert rsnB != null;
3997 RefEdge edgeB = rsnB.getReferenceTo( hrnB,
4001 if( edgeB == null ) {
4002 System.out.println( " edges smaller:" );
4006 // REMEMBER, IS NO SMALLER THAN
4008 System.out.println( " edges smaller" );
4024 // this analysis no longer has the "match anything"
4025 // type which was represented by null
4026 protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
4027 TypeDescriptor td2 ) {
4031 if( td1.isNull() ) {
4034 if( td2.isNull() ) {
4037 return typeUtil.mostSpecific( td1, td2 );
4040 protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
4042 TypeDescriptor td3 ) {
4044 return mostSpecificType( td1,
4045 mostSpecificType( td2, td3 )
4049 protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
4052 TypeDescriptor td4 ) {
4054 return mostSpecificType( mostSpecificType( td1, td2 ),
4055 mostSpecificType( td3, td4 )
4059 protected boolean isSuperiorType( TypeDescriptor possibleSuper,
4060 TypeDescriptor possibleChild ) {
4061 assert possibleSuper != null;
4062 assert possibleChild != null;
4064 if( possibleSuper.isNull() ||
4065 possibleChild.isNull() ) {
4069 return typeUtil.isSuperorType( possibleSuper, possibleChild );
4073 protected boolean hasMatchingField( HeapRegionNode src,
4076 TypeDescriptor tdSrc = src.getType();
4077 assert tdSrc != null;
4079 if( tdSrc.isArray() ) {
4080 TypeDescriptor td = edge.getType();
4083 TypeDescriptor tdSrcDeref = tdSrc.dereference();
4084 assert tdSrcDeref != null;
4086 if( !typeUtil.isSuperorType( tdSrcDeref, td ) ) {
4090 return edge.getField().equals( DisjointAnalysis.arrayElementFieldName );
4093 // if it's not a class, it doesn't have any fields to match
4094 if( !tdSrc.isClass() ) {
4098 ClassDescriptor cd = tdSrc.getClassDesc();
4099 while( cd != null ) {
4100 Iterator fieldItr = cd.getFields();
4102 while( fieldItr.hasNext() ) {
4103 FieldDescriptor fd = (FieldDescriptor) fieldItr.next();
4105 if( fd.getType().equals( edge.getType() ) &&
4106 fd.getSymbol().equals( edge.getField() ) ) {
4111 cd = cd.getSuperDesc();
4114 // otherwise it is a class with fields
4115 // but we didn't find a match
4119 protected boolean hasMatchingType( RefEdge edge,
4120 HeapRegionNode dst ) {
4122 // if the region has no type, matches everything
4123 TypeDescriptor tdDst = dst.getType();
4124 assert tdDst != null;
4126 // if the type is not a class or an array, don't
4127 // match because primitives are copied, no aliases
4128 ClassDescriptor cdDst = tdDst.getClassDesc();
4129 if( cdDst == null && !tdDst.isArray() ) {
4133 // if the edge type is null, it matches everything
4134 TypeDescriptor tdEdge = edge.getType();
4135 assert tdEdge != null;
4137 return typeUtil.isSuperorType( tdEdge, tdDst );
4142 // the default signature for quick-and-dirty debugging
4143 public void writeGraph( String graphName ) {
4144 writeGraph( graphName,
4145 true, // write labels
4146 true, // label select
4147 true, // prune garbage
4148 false, // hide reachability
4149 true, // hide subset reachability
4150 true, // hide predicates
4151 true, // hide edge taints
4152 null // in-context boundary
4156 public void writeGraph( String graphName,
4157 boolean writeLabels,
4158 boolean labelSelect,
4159 boolean pruneGarbage,
4160 boolean hideReachability,
4161 boolean hideSubsetReachability,
4162 boolean hidePredicates,
4163 boolean hideEdgeTaints
4165 writeGraph( graphName,
4170 hideSubsetReachability,
4176 public void writeGraph( String graphName,
4177 boolean writeLabels,
4178 boolean labelSelect,
4179 boolean pruneGarbage,
4180 boolean hideReachability,
4181 boolean hideSubsetReachability,
4182 boolean hidePredicates,
4183 boolean hideEdgeTaints,
4184 Set<Integer> callerNodeIDsCopiedToCallee
4188 // remove all non-word characters from the graph name so
4189 // the filename and identifier in dot don't cause errors
4190 graphName = graphName.replaceAll( "[\\W]", "" );
4193 new BufferedWriter( new FileWriter( graphName+".dot" ) );
4195 bw.write( "digraph "+graphName+" {\n" );
4198 // this is an optional step to form the callee-reachable
4199 // "cut-out" into a DOT cluster for visualization
4200 if( callerNodeIDsCopiedToCallee != null ) {
4202 bw.write( " subgraph cluster0 {\n" );
4203 bw.write( " color=blue;\n" );
4205 Iterator i = id2hrn.entrySet().iterator();
4206 while( i.hasNext() ) {
4207 Map.Entry me = (Map.Entry) i.next();
4208 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4210 if( callerNodeIDsCopiedToCallee.contains( hrn.getID() ) ) {
4213 hrn.toStringDOT( hideReachability,
4214 hideSubsetReachability,
4224 Set<HeapRegionNode> visited = new HashSet<HeapRegionNode>();
4226 // then visit every heap region node
4227 Iterator i = id2hrn.entrySet().iterator();
4228 while( i.hasNext() ) {
4229 Map.Entry me = (Map.Entry) i.next();
4230 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4232 // only visit nodes worth writing out--for instance
4233 // not every node at an allocation is referenced
4234 // (think of it as garbage-collected), etc.
4235 if( !pruneGarbage ||
4236 hrn.isOutOfContext() ||
4237 (hrn.isFlagged() && hrn.getID() > 0 && !hrn.isWiped()) // a non-shadow flagged node
4240 if( !visited.contains( hrn ) ) {
4241 traverseHeapRegionNodes( hrn,
4246 hideSubsetReachability,
4249 callerNodeIDsCopiedToCallee );
4254 bw.write( " graphTitle[label=\""+graphName+"\",shape=box];\n" );
4257 // then visit every label node, useful for debugging
4259 i = td2vn.entrySet().iterator();
4260 while( i.hasNext() ) {
4261 Map.Entry me = (Map.Entry) i.next();
4262 VariableNode vn = (VariableNode) me.getValue();
4265 String labelStr = vn.getTempDescriptorString();
4266 if( labelStr.startsWith( "___temp" ) ||
4267 labelStr.startsWith( "___dst" ) ||
4268 labelStr.startsWith( "___srctmp" ) ||
4269 labelStr.startsWith( "___neverused" )
4275 Iterator<RefEdge> heapRegionsItr = vn.iteratorToReferencees();
4276 while( heapRegionsItr.hasNext() ) {
4277 RefEdge edge = heapRegionsItr.next();
4278 HeapRegionNode hrn = edge.getDst();
4280 if( !visited.contains( hrn ) ) {
4281 traverseHeapRegionNodes( hrn,
4286 hideSubsetReachability,
4289 callerNodeIDsCopiedToCallee );
4292 bw.write( " "+vn.toString()+
4293 " -> "+hrn.toString()+
4294 edge.toStringDOT( hideReachability,
4295 hideSubsetReachability,
4307 } catch( IOException e ) {
4308 throw new Error( "Error writing out DOT graph "+graphName );
4313 traverseHeapRegionNodes( HeapRegionNode hrn,
4316 Set<HeapRegionNode> visited,
4317 boolean hideReachability,
4318 boolean hideSubsetReachability,
4319 boolean hidePredicates,
4320 boolean hideEdgeTaints,
4321 Set<Integer> callerNodeIDsCopiedToCallee
4322 ) throws java.io.IOException {
4324 if( visited.contains( hrn ) ) {
4329 // if we're drawing the callee-view subgraph, only
4330 // write out the node info if it hasn't already been
4332 if( callerNodeIDsCopiedToCallee == null ||
4333 !callerNodeIDsCopiedToCallee.contains( hrn.getID() )
4337 hrn.toStringDOT( hideReachability,
4338 hideSubsetReachability,
4343 Iterator<RefEdge> childRegionsItr = hrn.iteratorToReferencees();
4344 while( childRegionsItr.hasNext() ) {
4345 RefEdge edge = childRegionsItr.next();
4346 HeapRegionNode hrnChild = edge.getDst();
4348 if( callerNodeIDsCopiedToCallee != null &&
4349 (edge.getSrc() instanceof HeapRegionNode) ) {
4350 HeapRegionNode hrnSrc = (HeapRegionNode) edge.getSrc();
4351 if( callerNodeIDsCopiedToCallee.contains( hrnSrc.getID() ) &&
4352 callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
4354 bw.write( " "+hrn.toString()+
4355 " -> "+hrnChild.toString()+
4356 edge.toStringDOT( hideReachability,
4357 hideSubsetReachability,
4362 } else if( !callerNodeIDsCopiedToCallee.contains( hrnSrc.getID() ) &&
4363 callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
4365 bw.write( " "+hrn.toString()+
4366 " -> "+hrnChild.toString()+
4367 edge.toStringDOT( hideReachability,
4368 hideSubsetReachability,
4371 ",color=blue,style=dashed" )+
4374 bw.write( " "+hrn.toString()+
4375 " -> "+hrnChild.toString()+
4376 edge.toStringDOT( hideReachability,
4377 hideSubsetReachability,
4384 bw.write( " "+hrn.toString()+
4385 " -> "+hrnChild.toString()+
4386 edge.toStringDOT( hideReachability,
4387 hideSubsetReachability,
4394 traverseHeapRegionNodes( hrnChild,
4399 hideSubsetReachability,
4402 callerNodeIDsCopiedToCallee );
4410 public Set<HeapRegionNode> findCommonReachableNodes( ReachSet proofOfSharing ) {
4412 Set<HeapRegionNode> exhibitProofState =
4413 new HashSet<HeapRegionNode>();
4415 Iterator hrnItr = id2hrn.entrySet().iterator();
4416 while( hrnItr.hasNext() ) {
4417 Map.Entry me = (Map.Entry) hrnItr.next();
4418 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4420 ReachSet intersection =
4421 Canonical.intersection( proofOfSharing,
4424 if( !intersection.isEmpty() ) {
4425 assert !hrn.isOutOfContext();
4426 exhibitProofState.add( hrn );
4430 return exhibitProofState;
4434 public Set<HeapRegionNode> mayReachSharedObjects(HeapRegionNode hrn1,
4435 HeapRegionNode hrn2) {
4436 assert hrn1 != null;
4437 assert hrn2 != null;
4439 assert !hrn1.isOutOfContext();
4440 assert !hrn2.isOutOfContext();
4442 assert belongsToThis( hrn1 );
4443 assert belongsToThis( hrn2 );
4445 assert !hrn1.getID().equals( hrn2.getID() );
4448 // then get the various tokens for these heap regions
4450 ReachTuple.factory( hrn1.getID(),
4451 !hrn1.isSingleObject(), // multi?
4452 ReachTuple.ARITY_ONE,
4455 ReachTuple h1star = null;
4456 if( !hrn1.isSingleObject() ) {
4458 ReachTuple.factory( hrn1.getID(),
4459 !hrn1.isSingleObject(),
4460 ReachTuple.ARITY_ZEROORMORE,
4465 ReachTuple.factory( hrn2.getID(),
4466 !hrn2.isSingleObject(),
4467 ReachTuple.ARITY_ONE,
4470 ReachTuple h2star = null;
4471 if( !hrn2.isSingleObject() ) {
4473 ReachTuple.factory( hrn2.getID(),
4474 !hrn2.isSingleObject(),
4475 ReachTuple.ARITY_ZEROORMORE,
4479 // then get the merged beta of all out-going edges from these heap
4482 ReachSet beta1 = ReachSet.factory();
4483 Iterator<RefEdge> itrEdge = hrn1.iteratorToReferencees();
4484 while (itrEdge.hasNext()) {
4485 RefEdge edge = itrEdge.next();
4486 beta1 = Canonical.unionORpreds(beta1, edge.getBeta());
4489 ReachSet beta2 = ReachSet.factory();
4490 itrEdge = hrn2.iteratorToReferencees();
4491 while (itrEdge.hasNext()) {
4492 RefEdge edge = itrEdge.next();
4493 beta2 = Canonical.unionORpreds(beta2, edge.getBeta());
4496 ReachSet proofOfSharing = ReachSet.factory();
4499 Canonical.unionORpreds( proofOfSharing,
4500 beta1.getStatesWithBoth( h1, h2 )
4503 Canonical.unionORpreds( proofOfSharing,
4504 beta2.getStatesWithBoth( h1, h2 )
4507 if( !hrn1.isSingleObject() ) {
4509 Canonical.unionORpreds( proofOfSharing,
4510 beta1.getStatesWithBoth( h1star, h2 )
4513 Canonical.unionORpreds( proofOfSharing,
4514 beta2.getStatesWithBoth( h1star, h2 )
4518 if( !hrn2.isSingleObject() ) {
4520 Canonical.unionORpreds( proofOfSharing,
4521 beta1.getStatesWithBoth( h1, h2star )
4524 Canonical.unionORpreds( proofOfSharing,
4525 beta2.getStatesWithBoth( h1, h2star )
4529 if( !hrn1.isSingleObject() &&
4530 !hrn2.isSingleObject()
4533 Canonical.unionORpreds( proofOfSharing,
4534 beta1.getStatesWithBoth( h1star, h2star )
4537 Canonical.unionORpreds( proofOfSharing,
4538 beta2.getStatesWithBoth( h1star, h2star )
4542 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4543 if( !proofOfSharing.isEmpty() ) {
4544 common = findCommonReachableNodes( proofOfSharing );
4545 if( !DISABLE_STRONG_UPDATES &&
4546 !DISABLE_GLOBAL_SWEEP
4548 assert !common.isEmpty();
4555 // this version of the above method checks whether there is sharing
4556 // among the objects in a summary node
4557 public Set<HeapRegionNode> mayReachSharedObjects(HeapRegionNode hrn) {
4559 assert hrn.isNewSummary();
4560 assert !hrn.isOutOfContext();
4561 assert belongsToThis( hrn );
4564 ReachTuple.factory( hrn.getID(),
4566 ReachTuple.ARITY_ZEROORMORE,
4569 // then get the merged beta of all out-going edges from
4572 ReachSet beta = ReachSet.factory();
4573 Iterator<RefEdge> itrEdge = hrn.iteratorToReferencees();
4574 while (itrEdge.hasNext()) {
4575 RefEdge edge = itrEdge.next();
4576 beta = Canonical.unionORpreds(beta, edge.getBeta());
4579 ReachSet proofOfSharing = ReachSet.factory();
4582 Canonical.unionORpreds( proofOfSharing,
4583 beta.getStatesWithBoth( hstar, hstar )
4586 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4587 if( !proofOfSharing.isEmpty() ) {
4588 common = findCommonReachableNodes( proofOfSharing );
4589 if( !DISABLE_STRONG_UPDATES &&
4590 !DISABLE_GLOBAL_SWEEP
4592 assert !common.isEmpty();
4600 public Set<HeapRegionNode> mayReachSharedObjects(FlatMethod fm,
4601 Integer paramIndex1,
4602 Integer paramIndex2) {
4604 // get parameter's heap regions
4605 TempDescriptor paramTemp1 = fm.getParameter(paramIndex1.intValue());
4606 assert this.hasVariable( paramTemp1 );
4607 VariableNode paramVar1 = getVariableNodeFromTemp(paramTemp1);
4610 if( !(paramVar1.getNumReferencees() == 1) ) {
4611 System.out.println( "\n fm="+fm+"\n param="+paramTemp1 );
4612 writeGraph( "whatup" );
4616 assert paramVar1.getNumReferencees() == 1;
4617 RefEdge paramEdge1 = paramVar1.iteratorToReferencees().next();
4618 HeapRegionNode hrnParam1 = paramEdge1.getDst();
4620 TempDescriptor paramTemp2 = fm.getParameter(paramIndex2.intValue());
4621 assert this.hasVariable( paramTemp2 );
4622 VariableNode paramVar2 = getVariableNodeFromTemp(paramTemp2);
4624 if( !(paramVar2.getNumReferencees() == 1) ) {
4625 System.out.println( "\n fm="+fm+"\n param="+paramTemp2 );
4626 writeGraph( "whatup" );
4629 assert paramVar2.getNumReferencees() == 1;
4630 RefEdge paramEdge2 = paramVar2.iteratorToReferencees().next();
4631 HeapRegionNode hrnParam2 = paramEdge2.getDst();
4633 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4634 common.addAll(mayReachSharedObjects(hrnParam1, hrnParam2));
4639 public Set<HeapRegionNode> mayReachSharedObjects(FlatMethod fm,
4643 // get parameter's heap regions
4644 TempDescriptor paramTemp = fm.getParameter(paramIndex.intValue());
4645 assert this.hasVariable( paramTemp );
4646 VariableNode paramVar = getVariableNodeFromTemp(paramTemp);
4647 assert paramVar.getNumReferencees() == 1;
4648 RefEdge paramEdge = paramVar.iteratorToReferencees().next();
4649 HeapRegionNode hrnParam = paramEdge.getDst();
4652 HeapRegionNode hrnSummary=null;
4653 if(id2hrn.containsKey(as.getSummary())){
4654 // if summary node doesn't exist, ignore this case
4655 hrnSummary = id2hrn.get(as.getSummary());
4656 assert hrnSummary != null;
4659 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4660 if(hrnSummary!=null){
4661 common.addAll( mayReachSharedObjects(hrnParam, hrnSummary) );
4664 // check for other nodes
4665 for (int i = 0; i < as.getAllocationDepth(); ++i) {
4667 assert id2hrn.containsKey(as.getIthOldest(i));
4668 HeapRegionNode hrnIthOldest = id2hrn.get(as.getIthOldest(i));
4669 assert hrnIthOldest != null;
4671 common.addAll(mayReachSharedObjects(hrnParam, hrnIthOldest));
4678 public Set<HeapRegionNode> mayReachSharedObjects(AllocSite as1,
4681 // get summary node 1's alpha
4682 Integer idSum1 = as1.getSummary();
4683 HeapRegionNode hrnSum1=null;
4684 if(id2hrn.containsKey(idSum1)){
4685 hrnSum1 = id2hrn.get(idSum1);
4688 // get summary node 2's alpha
4689 Integer idSum2 = as2.getSummary();
4690 HeapRegionNode hrnSum2=null;
4691 if(id2hrn.containsKey(idSum2)){
4692 hrnSum2 = id2hrn.get(idSum2);
4695 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4696 if(hrnSum1!=null && hrnSum2!=null && hrnSum1!=hrnSum2){
4697 common.addAll(mayReachSharedObjects(hrnSum1, hrnSum2));
4701 // ask if objects from this summary share among each other
4702 common.addAll(mayReachSharedObjects(hrnSum1));
4705 // check sum2 against alloc1 nodes
4707 for (int i = 0; i < as1.getAllocationDepth(); ++i) {
4708 Integer idI1 = as1.getIthOldest(i);
4709 assert id2hrn.containsKey(idI1);
4710 HeapRegionNode hrnI1 = id2hrn.get(idI1);
4711 assert hrnI1 != null;
4712 common.addAll(mayReachSharedObjects(hrnI1, hrnSum2));
4715 // also ask if objects from this summary share among each other
4716 common.addAll(mayReachSharedObjects(hrnSum2));
4719 // check sum1 against alloc2 nodes
4720 for (int i = 0; i < as2.getAllocationDepth(); ++i) {
4721 Integer idI2 = as2.getIthOldest(i);
4722 assert id2hrn.containsKey(idI2);
4723 HeapRegionNode hrnI2 = id2hrn.get(idI2);
4724 assert hrnI2 != null;
4727 common.addAll(mayReachSharedObjects(hrnSum1, hrnI2));
4730 // while we're at it, do an inner loop for alloc2 vs alloc1 nodes
4731 for (int j = 0; j < as1.getAllocationDepth(); ++j) {
4732 Integer idI1 = as1.getIthOldest(j);
4734 // if these are the same site, don't look for the same token, no
4736 // different tokens of the same site could alias together though
4737 if (idI1.equals(idI2)) {
4741 HeapRegionNode hrnI1 = id2hrn.get(idI1);
4743 common.addAll(mayReachSharedObjects(hrnI1, hrnI2));