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 // predicate constants
19 public static final ExistPred predTrue = ExistPred.factory(); // if no args, true
20 public static final ExistPredSet predsEmpty = ExistPredSet.factory();
21 public static final ExistPredSet predsTrue = ExistPredSet.factory( predTrue );
23 // some frequently used reachability constants
24 protected static final ReachState rstateEmpty = ReachState.factory();
25 protected static final ReachSet rsetEmpty = ReachSet.factory();
26 protected static final ReachSet rsetWithEmptyState = Canonical.changePredsTo( ReachSet.factory( rstateEmpty ),
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;
42 // set of accessible variables for current program statement
43 // if not contains, it is an inaccessible variable
44 public HashSet<TempDescriptor> accessibleVars;
47 id2hrn = new Hashtable<Integer, HeapRegionNode>();
48 td2vn = new Hashtable<TempDescriptor, VariableNode >();
49 allocSites = new HashSet<AllocSite>();
50 accessibleVars = new HashSet<TempDescriptor>();
54 // temp descriptors are globally unique and map to
55 // exactly one variable node, easy
56 protected VariableNode getVariableNodeFromTemp( TempDescriptor td ) {
59 if( !td2vn.containsKey( td ) ) {
60 td2vn.put( td, new VariableNode( td ) );
63 return td2vn.get( td );
66 public boolean hasVariable( TempDescriptor td ) {
67 return td2vn.containsKey( td );
71 // this suite of methods can be used to assert a
72 // very important property of ReachGraph objects:
73 // some element, HeapRegionNode, RefEdge etc.
74 // should be referenced by at most ONE ReachGraph!!
75 // If a heap region or edge or variable should be
76 // in another graph, make a new object with
77 // equivalent properties for a new graph
78 public boolean belongsToThis( RefSrcNode rsn ) {
79 if( rsn instanceof VariableNode ) {
80 VariableNode vn = (VariableNode) rsn;
81 return this.td2vn.get( vn.getTempDescriptor() ) == vn;
83 HeapRegionNode hrn = (HeapRegionNode) rsn;
84 return this.id2hrn.get( hrn.getID() ) == hrn;
91 // the reason for this method is to have the option
92 // of creating new heap regions with specific IDs, or
93 // duplicating heap regions with specific IDs (especially
94 // in the merge() operation) or to create new heap
95 // regions with a new unique ID
96 protected HeapRegionNode
97 createNewHeapRegionNode( Integer id,
98 boolean isSingleObject,
100 boolean isOutOfContext,
109 TypeDescriptor typeToUse = null;
110 if( allocSite != null ) {
111 typeToUse = allocSite.getType();
112 allocSites.add( allocSite );
117 boolean markForAnalysis = false;
118 if( allocSite != null && allocSite.isFlagged() ) {
119 markForAnalysis = true;
122 if( allocSite == null ) {
123 assert !markForAnalysis;
125 } else if( markForAnalysis != allocSite.isFlagged() ) {
131 id = DisjointAnalysis.generateUniqueHeapRegionNodeID();
134 if( inherent == null ) {
135 if( markForAnalysis ) {
137 Canonical.changePredsTo(
140 ReachTuple.factory( id,
142 ReachTuple.ARITY_ONE,
143 false // out-of-context
150 inherent = rsetWithEmptyState;
154 if( alpha == null ) {
158 assert preds != null;
160 HeapRegionNode hrn = new HeapRegionNode( id,
171 id2hrn.put( id, hrn );
177 ////////////////////////////////////////////////
179 // Low-level referencee and referencer methods
181 // These methods provide the lowest level for
182 // creating references between reachability nodes
183 // and handling the details of maintaining both
184 // list of referencers and referencees.
186 ////////////////////////////////////////////////
187 protected void addRefEdge( RefSrcNode referencer,
188 HeapRegionNode referencee,
190 assert referencer != null;
191 assert referencee != null;
193 assert edge.getSrc() == referencer;
194 assert edge.getDst() == referencee;
195 assert belongsToThis( referencer );
196 assert belongsToThis( referencee );
198 // edges are getting added twice to graphs now, the
199 // kind that should have abstract facts merged--use
200 // this check to prevent that
201 assert referencer.getReferenceTo( referencee,
206 referencer.addReferencee( edge );
207 referencee.addReferencer( edge );
210 protected void removeRefEdge( RefEdge e ) {
211 removeRefEdge( e.getSrc(),
217 protected void removeRefEdge( RefSrcNode referencer,
218 HeapRegionNode referencee,
221 assert referencer != null;
222 assert referencee != null;
224 RefEdge edge = referencer.getReferenceTo( referencee,
228 assert edge == referencee.getReferenceFrom( referencer,
232 referencer.removeReferencee( edge );
233 referencee.removeReferencer( edge );
237 // int oldTaint=edge.getTaintIdentifier();
238 // if(referencer instanceof HeapRegionNode){
239 // depropagateTaintIdentifier((HeapRegionNode)referencer,oldTaint,new HashSet<HeapRegionNode>());
245 // return whether at least one edge was removed
246 protected boolean clearRefEdgesFrom( RefSrcNode referencer,
249 boolean removeAll ) {
250 assert referencer != null;
252 boolean atLeastOneEdgeRemoved = false;
254 // get a copy of the set to iterate over, otherwise
255 // we will be trying to take apart the set as we
256 // are iterating over it, which won't work
257 Iterator<RefEdge> i = referencer.iteratorToReferenceesClone();
258 while( i.hasNext() ) {
259 RefEdge edge = i.next();
262 (edge.typeEquals( type ) && edge.fieldEquals( field ))
265 HeapRegionNode referencee = edge.getDst();
267 removeRefEdge( referencer,
272 atLeastOneEdgeRemoved = true;
276 return atLeastOneEdgeRemoved;
279 protected void clearRefEdgesTo( HeapRegionNode referencee,
282 boolean removeAll ) {
283 assert referencee != null;
285 // get a copy of the set to iterate over, otherwise
286 // we will be trying to take apart the set as we
287 // are iterating over it, which won't work
288 Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
289 while( i.hasNext() ) {
290 RefEdge edge = i.next();
293 (edge.typeEquals( type ) && edge.fieldEquals( field ))
296 RefSrcNode referencer = edge.getSrc();
298 removeRefEdge( referencer,
306 protected void clearNonVarRefEdgesTo( HeapRegionNode referencee ) {
307 assert referencee != null;
309 // get a copy of the set to iterate over, otherwise
310 // we will be trying to take apart the set as we
311 // are iterating over it, which won't work
312 Iterator<RefEdge> i = referencee.iteratorToReferencersClone();
313 while( i.hasNext() ) {
314 RefEdge edge = i.next();
315 RefSrcNode referencer = edge.getSrc();
316 if( !(referencer instanceof VariableNode) ) {
317 removeRefEdge( referencer,
325 // this is a common operation in many transfer functions: we want
326 // to add an edge, but if there is already such an edge we should
327 // merge the properties of the existing and the new edges
328 protected void addEdgeOrMergeWithExisting( RefEdge edgeNew ) {
330 RefSrcNode src = edgeNew.getSrc();
331 assert belongsToThis( src );
333 HeapRegionNode dst = edgeNew.getDst();
334 assert belongsToThis( dst );
336 // look to see if an edge with same field exists
337 // and merge with it, otherwise just add the edge
338 RefEdge edgeExisting = src.getReferenceTo( dst,
343 if( edgeExisting != null ) {
344 edgeExisting.setBeta(
345 Canonical.unionORpreds( edgeExisting.getBeta(),
349 edgeExisting.setPreds(
350 Canonical.join( edgeExisting.getPreds(),
354 edgeExisting.setTaints(
355 Canonical.unionORpreds( edgeExisting.getTaints(),
361 addRefEdge( src, dst, edgeNew );
367 ////////////////////////////////////////////////////
369 // Assignment Operation Methods
371 // These methods are high-level operations for
372 // modeling program assignment statements using
373 // the low-level reference create/remove methods
376 ////////////////////////////////////////////////////
378 public void assignTempXEqualToTempY( TempDescriptor x,
380 assignTempXEqualToCastedTempY( x, y, null );
384 public void assignTempXEqualToCastedTempY( TempDescriptor x,
386 TypeDescriptor tdCast ) {
388 VariableNode lnX = getVariableNodeFromTemp( x );
389 VariableNode lnY = getVariableNodeFromTemp( y );
391 clearRefEdgesFrom( lnX, null, null, true );
393 // note it is possible that the types of temps in the
394 // flat node to analyze will reveal that some typed
395 // edges in the reachability graph are impossible
396 Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
398 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
399 while( itrYhrn.hasNext() ) {
400 RefEdge edgeY = itrYhrn.next();
401 HeapRegionNode referencee = edgeY.getDst();
402 RefEdge edgeNew = edgeY.copy();
404 if( !isSuperiorType( x.getType(), edgeY.getType() ) ) {
405 impossibleEdges.add( edgeY );
409 edgeNew.setSrc( lnX );
411 if( tdCast == null ) {
412 edgeNew.setType( mostSpecificType( y.getType(),
418 edgeNew.setType( mostSpecificType( y.getType(),
420 referencee.getType(),
426 edgeNew.setField( null );
428 addRefEdge( lnX, referencee, edgeNew );
431 Iterator<RefEdge> itrImp = impossibleEdges.iterator();
432 while( itrImp.hasNext() ) {
433 RefEdge edgeImp = itrImp.next();
434 removeRefEdge( edgeImp );
439 public void assignTempXEqualToTempYFieldF( TempDescriptor x,
441 FieldDescriptor f ) {
442 VariableNode lnX = getVariableNodeFromTemp( x );
443 VariableNode lnY = getVariableNodeFromTemp( y );
445 clearRefEdgesFrom( lnX, null, null, true );
447 // note it is possible that the types of temps in the
448 // flat node to analyze will reveal that some typed
449 // edges in the reachability graph are impossible
450 Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
452 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
453 while( itrYhrn.hasNext() ) {
454 RefEdge edgeY = itrYhrn.next();
455 HeapRegionNode hrnY = edgeY.getDst();
456 ReachSet betaY = edgeY.getBeta();
458 Iterator<RefEdge> itrHrnFhrn = hrnY.iteratorToReferencees();
459 while( itrHrnFhrn.hasNext() ) {
460 RefEdge edgeHrn = itrHrnFhrn.next();
461 HeapRegionNode hrnHrn = edgeHrn.getDst();
462 ReachSet betaHrn = edgeHrn.getBeta();
464 // prune edges that are not a matching field
465 if( edgeHrn.getType() != null &&
466 !edgeHrn.getField().equals( f.getSymbol() )
471 // check for impossible edges
472 if( !isSuperiorType( x.getType(), edgeHrn.getType() ) ) {
473 impossibleEdges.add( edgeHrn );
477 TypeDescriptor tdNewEdge =
478 mostSpecificType( edgeHrn.getType(),
482 RefEdge edgeNew = new RefEdge( lnX,
486 Canonical.intersection( betaY, betaHrn ),
491 addEdgeOrMergeWithExisting( edgeNew );
495 Iterator<RefEdge> itrImp = impossibleEdges.iterator();
496 while( itrImp.hasNext() ) {
497 RefEdge edgeImp = itrImp.next();
498 removeRefEdge( edgeImp );
501 // anytime you might remove edges between heap regions
502 // you must global sweep to clean up broken reachability
503 if( !impossibleEdges.isEmpty() ) {
504 if( !DISABLE_GLOBAL_SWEEP ) {
512 // return whether a strong update was actually effected
513 public boolean assignTempXFieldFEqualToTempY( TempDescriptor x,
517 VariableNode lnX = getVariableNodeFromTemp( x );
518 VariableNode lnY = getVariableNodeFromTemp( y );
520 HashSet<HeapRegionNode> nodesWithNewAlpha = new HashSet<HeapRegionNode>();
521 HashSet<RefEdge> edgesWithNewBeta = new HashSet<RefEdge>();
523 // note it is possible that the types of temps in the
524 // flat node to analyze will reveal that some typed
525 // edges in the reachability graph are impossible
526 Set<RefEdge> impossibleEdges = new HashSet<RefEdge>();
528 // first look for possible strong updates and remove those edges
529 boolean strongUpdateCond = false;
530 boolean edgeRemovedByStrongUpdate = false;
532 Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
533 while( itrXhrn.hasNext() ) {
534 RefEdge edgeX = itrXhrn.next();
535 HeapRegionNode hrnX = edgeX.getDst();
537 // we can do a strong update here if one of two cases holds
539 f != DisjointAnalysis.getArrayField( f.getType() ) &&
540 ( (hrnX.getNumReferencers() == 1) || // case 1
541 (hrnX.isSingleObject() && lnX.getNumReferencees() == 1) // case 2
544 if( !DISABLE_STRONG_UPDATES ) {
545 strongUpdateCond = true;
548 clearRefEdgesFrom( hrnX,
553 edgeRemovedByStrongUpdate = true;
559 // then do all token propagation
560 itrXhrn = lnX.iteratorToReferencees();
561 while( itrXhrn.hasNext() ) {
562 RefEdge edgeX = itrXhrn.next();
563 HeapRegionNode hrnX = edgeX.getDst();
564 ReachSet betaX = edgeX.getBeta();
565 ReachSet R = Canonical.intersection( hrnX.getAlpha(),
569 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
570 while( itrYhrn.hasNext() ) {
571 RefEdge edgeY = itrYhrn.next();
572 HeapRegionNode hrnY = edgeY.getDst();
573 ReachSet O = edgeY.getBeta();
575 // check for impossible edges
576 if( !isSuperiorType( f.getType(), edgeY.getType() ) ) {
577 impossibleEdges.add( edgeY );
581 // propagate tokens over nodes starting from hrnSrc, and it will
582 // take care of propagating back up edges from any touched nodes
583 ChangeSet Cy = Canonical.unionUpArityToChangeSet( O, R );
584 propagateTokensOverNodes( hrnY, Cy, nodesWithNewAlpha, edgesWithNewBeta );
586 // then propagate back just up the edges from hrn
587 ChangeSet Cx = Canonical.unionUpArityToChangeSet( R, O );
588 HashSet<RefEdge> todoEdges = new HashSet<RefEdge>();
590 Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
591 new Hashtable<RefEdge, ChangeSet>();
593 Iterator<RefEdge> referItr = hrnX.iteratorToReferencers();
594 while( referItr.hasNext() ) {
595 RefEdge edgeUpstream = referItr.next();
596 todoEdges.add( edgeUpstream );
597 edgePlannedChanges.put( edgeUpstream, Cx );
600 propagateTokensOverEdges( todoEdges,
607 // apply the updates to reachability
608 Iterator<HeapRegionNode> nodeItr = nodesWithNewAlpha.iterator();
609 while( nodeItr.hasNext() ) {
610 nodeItr.next().applyAlphaNew();
613 Iterator<RefEdge> edgeItr = edgesWithNewBeta.iterator();
614 while( edgeItr.hasNext() ) {
615 edgeItr.next().applyBetaNew();
619 // then go back through and add the new edges
620 itrXhrn = lnX.iteratorToReferencees();
621 while( itrXhrn.hasNext() ) {
622 RefEdge edgeX = itrXhrn.next();
623 HeapRegionNode hrnX = edgeX.getDst();
625 Iterator<RefEdge> itrYhrn = lnY.iteratorToReferencees();
626 while( itrYhrn.hasNext() ) {
627 RefEdge edgeY = itrYhrn.next();
628 HeapRegionNode hrnY = edgeY.getDst();
630 // skip impossible edges here, we already marked them
631 // when computing reachability propagations above
632 if( !isSuperiorType( f.getType(), edgeY.getType() ) ) {
636 // prepare the new reference edge hrnX.f -> hrnY
637 TypeDescriptor tdNewEdge =
638 mostSpecificType( y.getType(),
648 Canonical.changePredsTo(
649 Canonical.pruneBy( edgeY.getBeta(),
655 Canonical.changePredsTo( edgeY.getTaints(),
659 addEdgeOrMergeWithExisting( edgeNew );
663 Iterator<RefEdge> itrImp = impossibleEdges.iterator();
664 while( itrImp.hasNext() ) {
665 RefEdge edgeImp = itrImp.next();
666 removeRefEdge( edgeImp );
669 // if there was a strong update, make sure to improve
670 // reachability with a global sweep
671 if( edgeRemovedByStrongUpdate || !impossibleEdges.isEmpty() ) {
672 if( !DISABLE_GLOBAL_SWEEP ) {
677 return edgeRemovedByStrongUpdate;
681 public void assignReturnEqualToTemp( TempDescriptor x ) {
683 VariableNode lnR = getVariableNodeFromTemp( tdReturn );
684 VariableNode lnX = getVariableNodeFromTemp( x );
686 clearRefEdgesFrom( lnR, null, null, true );
688 Iterator<RefEdge> itrXhrn = lnX.iteratorToReferencees();
689 while( itrXhrn.hasNext() ) {
690 RefEdge edgeX = itrXhrn.next();
691 HeapRegionNode referencee = edgeX.getDst();
692 RefEdge edgeNew = edgeX.copy();
693 edgeNew.setSrc( lnR );
694 edgeNew.setTaints( Canonical.changePredsTo( edgeNew.getTaints(),
699 addRefEdge( lnR, referencee, edgeNew );
704 public void assignTempEqualToNewAlloc( TempDescriptor x,
711 // after the age operation the newest (or zero-ith oldest)
712 // node associated with the allocation site should have
713 // no references to it as if it were a newly allocated
715 Integer idNewest = as.getIthOldest( 0 );
716 HeapRegionNode hrnNewest = id2hrn.get( idNewest );
717 assert hrnNewest != null;
719 VariableNode lnX = getVariableNodeFromTemp( x );
720 clearRefEdgesFrom( lnX, null, null, true );
722 // make a new reference to allocated node
723 TypeDescriptor type = as.getType();
726 new RefEdge( lnX, // source
730 hrnNewest.getAlpha(), // beta
731 predsTrue, // predicates
732 TaintSet.factory() // taints
735 addRefEdge( lnX, hrnNewest, edgeNew );
737 // after x=new , x is accessible
738 // if (isInRegion()) {
739 //accessibleVars.add(x);
743 // use the allocation site (unique to entire analysis) to
744 // locate the heap region nodes in this reachability graph
745 // that should be aged. The process models the allocation
746 // of new objects and collects all the oldest allocations
747 // in a summary node to allow for a finite analysis
749 // There is an additional property of this method. After
750 // running it on a particular reachability graph (many graphs
751 // may have heap regions related to the same allocation site)
752 // the heap region node objects in this reachability graph will be
753 // allocated. Therefore, after aging a graph for an allocation
754 // site, attempts to retrieve the heap region nodes using the
755 // integer id's contained in the allocation site should always
756 // return non-null heap regions.
757 public void age( AllocSite as ) {
759 // keep track of allocation sites that are represented
760 // in this graph for efficiency with other operations
761 allocSites.add( as );
763 // if there is a k-th oldest node, it merges into
765 Integer idK = as.getOldest();
766 if( id2hrn.containsKey( idK ) ) {
767 HeapRegionNode hrnK = id2hrn.get( idK );
769 // retrieve the summary node, or make it
771 HeapRegionNode hrnSummary = getSummaryNode( as, false );
773 mergeIntoSummary( hrnK, hrnSummary );
776 // move down the line of heap region nodes
777 // clobbering the ith and transferring all references
778 // to and from i-1 to node i.
779 for( int i = allocationDepth - 1; i > 0; --i ) {
781 // only do the transfer if the i-1 node exists
782 Integer idImin1th = as.getIthOldest( i - 1 );
783 if( id2hrn.containsKey( idImin1th ) ) {
784 HeapRegionNode hrnImin1 = id2hrn.get( idImin1th );
785 if( hrnImin1.isWiped() ) {
786 // there is no info on this node, just skip
790 // either retrieve or make target of transfer
791 HeapRegionNode hrnI = getIthNode( as, i, false );
793 transferOnto( hrnImin1, hrnI );
798 // as stated above, the newest node should have had its
799 // references moved over to the second oldest, so we wipe newest
800 // in preparation for being the new object to assign something to
801 HeapRegionNode hrn0 = getIthNode( as, 0, false );
802 wipeOut( hrn0, true );
804 // now tokens in reachability sets need to "age" also
805 Iterator itrAllVariableNodes = td2vn.entrySet().iterator();
806 while( itrAllVariableNodes.hasNext() ) {
807 Map.Entry me = (Map.Entry) itrAllVariableNodes.next();
808 VariableNode ln = (VariableNode) me.getValue();
810 Iterator<RefEdge> itrEdges = ln.iteratorToReferencees();
811 while( itrEdges.hasNext() ) {
812 ageTuplesFrom( as, itrEdges.next() );
816 Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
817 while( itrAllHRNodes.hasNext() ) {
818 Map.Entry me = (Map.Entry) itrAllHRNodes.next();
819 HeapRegionNode hrnToAge = (HeapRegionNode) me.getValue();
821 ageTuplesFrom( as, hrnToAge );
823 Iterator<RefEdge> itrEdges = hrnToAge.iteratorToReferencees();
824 while( itrEdges.hasNext() ) {
825 ageTuplesFrom( as, itrEdges.next() );
830 // after tokens have been aged, reset newest node's reachability
831 // and a brand new node has a "true" predicate
832 hrn0.setAlpha( hrn0.getInherent() );
833 hrn0.setPreds( predsTrue );
837 // either retrieve or create the needed heap region node
838 protected HeapRegionNode getSummaryNode( AllocSite as,
843 idSummary = as.getSummaryShadow();
845 idSummary = as.getSummary();
848 HeapRegionNode hrnSummary = id2hrn.get( idSummary );
850 if( hrnSummary == null ) {
852 String strDesc = as.toStringForDOT()+"\\nsummary";
855 createNewHeapRegionNode( idSummary, // id or null to generate a new one
856 false, // single object?
858 false, // out-of-context?
859 as.getType(), // type
860 as, // allocation site
861 null, // inherent reach
862 null, // current reach
863 predsEmpty, // predicates
864 strDesc // description
871 // either retrieve or create the needed heap region node
872 protected HeapRegionNode getIthNode( AllocSite as,
878 idIth = as.getIthOldestShadow( i );
880 idIth = as.getIthOldest( i );
883 HeapRegionNode hrnIth = id2hrn.get( idIth );
885 if( hrnIth == null ) {
887 String strDesc = as.toStringForDOT()+"\\n"+i+" oldest";
889 hrnIth = createNewHeapRegionNode( idIth, // id or null to generate a new one
890 true, // single object?
892 false, // out-of-context?
893 as.getType(), // type
894 as, // allocation site
895 null, // inherent reach
896 null, // current reach
897 predsEmpty, // predicates
898 strDesc // description
906 protected void mergeIntoSummary( HeapRegionNode hrn,
907 HeapRegionNode hrnSummary ) {
908 assert hrnSummary.isNewSummary();
910 // assert that these nodes belong to THIS graph
911 assert belongsToThis( hrn );
912 assert belongsToThis( hrnSummary );
914 assert hrn != hrnSummary;
916 // transfer references _from_ hrn over to hrnSummary
917 Iterator<RefEdge> itrReferencee = hrn.iteratorToReferencees();
918 while( itrReferencee.hasNext() ) {
919 RefEdge edge = itrReferencee.next();
920 RefEdge edgeMerged = edge.copy();
921 edgeMerged.setSrc( hrnSummary );
923 HeapRegionNode hrnReferencee = edge.getDst();
924 RefEdge edgeSummary =
925 hrnSummary.getReferenceTo( hrnReferencee,
930 if( edgeSummary == null ) {
931 // the merge is trivial, nothing to be done
932 addRefEdge( hrnSummary, hrnReferencee, edgeMerged );
935 // otherwise an edge from the referencer to hrnSummary exists already
936 // and the edge referencer->hrn should be merged with it
938 Canonical.unionORpreds( edgeMerged.getBeta(),
939 edgeSummary.getBeta()
942 edgeSummary.setPreds(
943 Canonical.join( edgeMerged.getPreds(),
944 edgeSummary.getPreds()
950 // next transfer references _to_ hrn over to hrnSummary
951 Iterator<RefEdge> itrReferencer = hrn.iteratorToReferencers();
952 while( itrReferencer.hasNext() ) {
953 RefEdge edge = itrReferencer.next();
954 RefEdge edgeMerged = edge.copy();
955 edgeMerged.setDst( hrnSummary );
957 RefSrcNode onReferencer = edge.getSrc();
958 RefEdge edgeSummary =
959 onReferencer.getReferenceTo( hrnSummary,
964 if( edgeSummary == null ) {
965 // the merge is trivial, nothing to be done
966 addRefEdge( onReferencer, hrnSummary, edgeMerged );
969 // otherwise an edge from the referencer to alpha_S exists already
970 // and the edge referencer->alpha_K should be merged with it
972 Canonical.unionORpreds( edgeMerged.getBeta(),
973 edgeSummary.getBeta()
976 edgeSummary.setPreds(
977 Canonical.join( edgeMerged.getPreds(),
978 edgeSummary.getPreds()
984 // then merge hrn reachability into hrnSummary
986 Canonical.unionORpreds( hrnSummary.getAlpha(),
992 Canonical.join( hrnSummary.getPreds(),
997 // and afterward, this node is gone
998 wipeOut( hrn, true );
1002 protected void transferOnto( HeapRegionNode hrnA,
1003 HeapRegionNode hrnB ) {
1005 assert belongsToThis( hrnA );
1006 assert belongsToThis( hrnB );
1007 assert hrnA != hrnB;
1009 // clear references in and out of node b?
1010 assert hrnB.isWiped();
1012 // copy each: (edge in and out of A) to B
1013 Iterator<RefEdge> itrReferencee = hrnA.iteratorToReferencees();
1014 while( itrReferencee.hasNext() ) {
1015 RefEdge edge = itrReferencee.next();
1016 HeapRegionNode hrnReferencee = edge.getDst();
1017 RefEdge edgeNew = edge.copy();
1018 edgeNew.setSrc( hrnB );
1019 edgeNew.setDst( hrnReferencee );
1021 addRefEdge( hrnB, hrnReferencee, edgeNew );
1024 Iterator<RefEdge> itrReferencer = hrnA.iteratorToReferencers();
1025 while( itrReferencer.hasNext() ) {
1026 RefEdge edge = itrReferencer.next();
1027 RefSrcNode rsnReferencer = edge.getSrc();
1028 RefEdge edgeNew = edge.copy();
1029 edgeNew.setSrc( rsnReferencer );
1030 edgeNew.setDst( hrnB );
1032 addRefEdge( rsnReferencer, hrnB, edgeNew );
1035 // replace hrnB reachability and preds with hrnA's
1036 hrnB.setAlpha( hrnA.getAlpha() );
1037 hrnB.setPreds( hrnA.getPreds() );
1039 // after transfer, wipe out source
1040 wipeOut( hrnA, true );
1044 // the purpose of this method is to conceptually "wipe out"
1045 // a heap region from the graph--purposefully not called REMOVE
1046 // because the node is still hanging around in the graph, just
1047 // not mechanically connected or have any reach or predicate
1048 // information on it anymore--lots of ops can use this
1049 protected void wipeOut( HeapRegionNode hrn,
1050 boolean wipeVariableReferences ) {
1052 assert belongsToThis( hrn );
1054 clearRefEdgesFrom( hrn, null, null, true );
1056 if( wipeVariableReferences ) {
1057 clearRefEdgesTo( hrn, null, null, true );
1059 clearNonVarRefEdgesTo( hrn );
1062 hrn.setAlpha( rsetEmpty );
1063 hrn.setPreds( predsEmpty );
1067 protected void ageTuplesFrom( AllocSite as, RefEdge edge ) {
1069 Canonical.ageTuplesFrom( edge.getBeta(),
1075 protected void ageTuplesFrom( AllocSite as, HeapRegionNode hrn ) {
1077 Canonical.ageTuplesFrom( hrn.getAlpha(),
1085 protected void propagateTokensOverNodes( HeapRegionNode nPrime,
1087 HashSet<HeapRegionNode> nodesWithNewAlpha,
1088 HashSet<RefEdge> edgesWithNewBeta ) {
1090 HashSet<HeapRegionNode> todoNodes
1091 = new HashSet<HeapRegionNode>();
1092 todoNodes.add( nPrime );
1094 HashSet<RefEdge> todoEdges
1095 = new HashSet<RefEdge>();
1097 Hashtable<HeapRegionNode, ChangeSet> nodePlannedChanges
1098 = new Hashtable<HeapRegionNode, ChangeSet>();
1099 nodePlannedChanges.put( nPrime, c0 );
1101 Hashtable<RefEdge, ChangeSet> edgePlannedChanges
1102 = new Hashtable<RefEdge, ChangeSet>();
1104 // first propagate change sets everywhere they can go
1105 while( !todoNodes.isEmpty() ) {
1106 HeapRegionNode n = todoNodes.iterator().next();
1107 ChangeSet C = nodePlannedChanges.get( n );
1109 Iterator<RefEdge> referItr = n.iteratorToReferencers();
1110 while( referItr.hasNext() ) {
1111 RefEdge edge = referItr.next();
1112 todoEdges.add( edge );
1114 if( !edgePlannedChanges.containsKey( edge ) ) {
1115 edgePlannedChanges.put( edge,
1120 edgePlannedChanges.put( edge,
1121 Canonical.union( edgePlannedChanges.get( edge ),
1127 Iterator<RefEdge> refeeItr = n.iteratorToReferencees();
1128 while( refeeItr.hasNext() ) {
1129 RefEdge edgeF = refeeItr.next();
1130 HeapRegionNode m = edgeF.getDst();
1132 ChangeSet changesToPass = ChangeSet.factory();
1134 Iterator<ChangeTuple> itrCprime = C.iterator();
1135 while( itrCprime.hasNext() ) {
1136 ChangeTuple c = itrCprime.next();
1137 if( edgeF.getBeta().containsIgnorePreds( c.getStateToMatch() )
1140 changesToPass = Canonical.add( changesToPass, c );
1144 if( !changesToPass.isEmpty() ) {
1145 if( !nodePlannedChanges.containsKey( m ) ) {
1146 nodePlannedChanges.put( m, ChangeSet.factory() );
1149 ChangeSet currentChanges = nodePlannedChanges.get( m );
1151 if( !changesToPass.isSubset( currentChanges ) ) {
1153 nodePlannedChanges.put( m,
1154 Canonical.union( currentChanges,
1163 todoNodes.remove( n );
1166 // then apply all of the changes for each node at once
1167 Iterator itrMap = nodePlannedChanges.entrySet().iterator();
1168 while( itrMap.hasNext() ) {
1169 Map.Entry me = (Map.Entry) itrMap.next();
1170 HeapRegionNode n = (HeapRegionNode) me.getKey();
1171 ChangeSet C = (ChangeSet) me.getValue();
1173 // this propagation step is with respect to one change,
1174 // so we capture the full change from the old alpha:
1175 ReachSet localDelta = Canonical.applyChangeSet( n.getAlpha(),
1179 // but this propagation may be only one of many concurrent
1180 // possible changes, so keep a running union with the node's
1181 // partially updated new alpha set
1182 n.setAlphaNew( Canonical.unionORpreds( n.getAlphaNew(),
1187 nodesWithNewAlpha.add( n );
1190 propagateTokensOverEdges( todoEdges,
1197 protected void propagateTokensOverEdges( HashSet <RefEdge> todoEdges,
1198 Hashtable<RefEdge, ChangeSet> edgePlannedChanges,
1199 HashSet <RefEdge> edgesWithNewBeta ) {
1201 // first propagate all change tuples everywhere they can go
1202 while( !todoEdges.isEmpty() ) {
1203 RefEdge edgeE = todoEdges.iterator().next();
1204 todoEdges.remove( edgeE );
1206 if( !edgePlannedChanges.containsKey( edgeE ) ) {
1207 edgePlannedChanges.put( edgeE,
1212 ChangeSet C = edgePlannedChanges.get( edgeE );
1214 ChangeSet changesToPass = ChangeSet.factory();
1216 Iterator<ChangeTuple> itrC = C.iterator();
1217 while( itrC.hasNext() ) {
1218 ChangeTuple c = itrC.next();
1219 if( edgeE.getBeta().containsIgnorePreds( c.getStateToMatch() )
1222 changesToPass = Canonical.add( changesToPass, c );
1226 RefSrcNode rsn = edgeE.getSrc();
1228 if( !changesToPass.isEmpty() && rsn instanceof HeapRegionNode ) {
1229 HeapRegionNode n = (HeapRegionNode) rsn;
1231 Iterator<RefEdge> referItr = n.iteratorToReferencers();
1232 while( referItr.hasNext() ) {
1233 RefEdge edgeF = referItr.next();
1235 if( !edgePlannedChanges.containsKey( edgeF ) ) {
1236 edgePlannedChanges.put( edgeF,
1241 ChangeSet currentChanges = edgePlannedChanges.get( edgeF );
1243 if( !changesToPass.isSubset( currentChanges ) ) {
1244 todoEdges.add( edgeF );
1245 edgePlannedChanges.put( edgeF,
1246 Canonical.union( currentChanges,
1255 // then apply all of the changes for each edge at once
1256 Iterator itrMap = edgePlannedChanges.entrySet().iterator();
1257 while( itrMap.hasNext() ) {
1258 Map.Entry me = (Map.Entry) itrMap.next();
1259 RefEdge e = (RefEdge) me.getKey();
1260 ChangeSet C = (ChangeSet) me.getValue();
1262 // this propagation step is with respect to one change,
1263 // so we capture the full change from the old beta:
1264 ReachSet localDelta =
1265 Canonical.applyChangeSet( e.getBeta(),
1270 // but this propagation may be only one of many concurrent
1271 // possible changes, so keep a running union with the edge's
1272 // partially updated new beta set
1273 e.setBetaNew( Canonical.unionORpreds( e.getBetaNew(),
1278 edgesWithNewBeta.add( e );
1283 public void taintInSetVars( FlatSESEEnterNode sese ) {
1284 if( sese.getIsCallerSESEplaceholder() ) {
1288 Iterator<TempDescriptor> isvItr = sese.getInVarSet().iterator();
1289 while( isvItr.hasNext() ) {
1290 TempDescriptor isv = isvItr.next();
1292 // in-set var taints should NOT propagate back into callers
1293 // so give it FALSE(EMPTY) predicates
1302 public void taintStallSite( FlatNode stallSite,
1303 TempDescriptor var ) {
1305 System.out.println( "Tainting stall site: "+stallSite+" and "+var );
1307 // stall site taint should propagate back into callers
1308 // so give it TRUE predicates
1316 protected void taintTemp( FlatSESEEnterNode sese,
1322 VariableNode vn = getVariableNodeFromTemp( var );
1324 Iterator<RefEdge> reItr = vn.iteratorToReferencees();
1325 while( reItr.hasNext() ) {
1326 RefEdge re = reItr.next();
1328 Taint taint = Taint.factory( sese,
1331 re.getDst().getAllocSite(),
1335 re.setTaints( Canonical.add( re.getTaints(),
1342 public void removeInContextTaints( FlatSESEEnterNode sese ) {
1343 if( sese.getIsCallerSESEplaceholder() ) {
1347 Iterator meItr = id2hrn.entrySet().iterator();
1348 while( meItr.hasNext() ) {
1349 Map.Entry me = (Map.Entry) meItr.next();
1350 Integer id = (Integer) me.getKey();
1351 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
1353 Iterator<RefEdge> reItr = hrn.iteratorToReferencers();
1354 while( reItr.hasNext() ) {
1355 RefEdge re = reItr.next();
1357 re.setTaints( Canonical.removeTaintsBy( re.getTaints(),
1366 // used in makeCalleeView below to decide if there is
1367 // already an appropriate out-of-context edge in a callee
1368 // view graph for merging, or null if a new one will be added
1370 getOutOfContextReferenceTo( HeapRegionNode hrn,
1371 TypeDescriptor srcType,
1372 TypeDescriptor refType,
1374 assert belongsToThis( hrn );
1376 HeapRegionNode hrnInContext = id2hrn.get( hrn.getID() );
1377 if( hrnInContext == null ) {
1381 Iterator<RefEdge> refItr = hrnInContext.iteratorToReferencers();
1382 while( refItr.hasNext() ) {
1383 RefEdge re = refItr.next();
1385 assert belongsToThis( re.getSrc() );
1386 assert belongsToThis( re.getDst() );
1388 if( !(re.getSrc() instanceof HeapRegionNode) ) {
1392 HeapRegionNode hrnSrc = (HeapRegionNode) re.getSrc();
1393 if( !hrnSrc.isOutOfContext() ) {
1397 if( srcType == null ) {
1398 if( hrnSrc.getType() != null ) {
1402 if( !srcType.equals( hrnSrc.getType() ) ) {
1407 if( !re.typeEquals( refType ) ) {
1411 if( !re.fieldEquals( refField ) ) {
1415 // tada! We found it!
1422 // used below to convert a ReachSet to its callee-context
1423 // equivalent with respect to allocation sites in this graph
1424 protected ReachSet toCalleeContext( ReachSet rs,
1425 ExistPredSet predsNodeOrEdge,
1426 Set<HrnIdOoc> oocHrnIdOoc2callee
1428 ReachSet out = ReachSet.factory();
1430 Iterator<ReachState> itr = rs.iterator();
1431 while( itr.hasNext() ) {
1432 ReachState stateCaller = itr.next();
1434 ReachState stateCallee = stateCaller;
1436 Iterator<AllocSite> asItr = allocSites.iterator();
1437 while( asItr.hasNext() ) {
1438 AllocSite as = asItr.next();
1440 ReachState stateNew = ReachState.factory();
1441 Iterator<ReachTuple> rtItr = stateCallee.iterator();
1442 while( rtItr.hasNext() ) {
1443 ReachTuple rt = rtItr.next();
1445 // only translate this tuple if it is
1446 // in the out-callee-context bag
1447 HrnIdOoc hio = new HrnIdOoc( rt.getHrnID(),
1450 if( !oocHrnIdOoc2callee.contains( hio ) ) {
1451 stateNew = Canonical.add( stateNew, rt );
1455 int age = as.getAgeCategory( rt.getHrnID() );
1457 // this is the current mapping, where 0, 1, 2S were allocated
1458 // in the current context, 0?, 1? and 2S? were allocated in a
1459 // previous context, and we're translating to a future context
1471 if( age == AllocSite.AGE_notInThisSite ) {
1472 // things not from the site just go back in
1473 stateNew = Canonical.add( stateNew, rt );
1475 } else if( age == AllocSite.AGE_summary ||
1478 // the in-context summary and all existing out-of-context
1480 stateNew = Canonical.add( stateNew,
1481 ReachTuple.factory( as.getSummary(),
1484 true // out-of-context
1488 // otherwise everything else just goes to an out-of-context
1489 // version, everything else the same
1490 Integer I = as.getAge( rt.getHrnID() );
1493 assert !rt.isMultiObject();
1495 stateNew = Canonical.add( stateNew,
1496 ReachTuple.factory( rt.getHrnID(),
1499 true // out-of-context
1505 stateCallee = stateNew;
1508 // make a predicate of the caller graph element
1509 // and the caller state we just converted
1510 ExistPredSet predsWithState = ExistPredSet.factory();
1512 Iterator<ExistPred> predItr = predsNodeOrEdge.iterator();
1513 while( predItr.hasNext() ) {
1514 ExistPred predNodeOrEdge = predItr.next();
1517 Canonical.add( predsWithState,
1518 ExistPred.factory( predNodeOrEdge.n_hrnID,
1519 predNodeOrEdge.e_tdSrc,
1520 predNodeOrEdge.e_hrnSrcID,
1521 predNodeOrEdge.e_hrnDstID,
1522 predNodeOrEdge.e_type,
1523 predNodeOrEdge.e_field,
1526 predNodeOrEdge.e_srcOutCalleeContext,
1527 predNodeOrEdge.e_srcOutCallerContext
1532 stateCallee = Canonical.changePredsTo( stateCallee,
1535 out = Canonical.add( out,
1539 assert out.isCanonical();
1543 // used below to convert a ReachSet to its caller-context
1544 // equivalent with respect to allocation sites in this graph
1546 toCallerContext( ReachSet rs,
1547 Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied
1549 ReachSet out = ReachSet.factory();
1551 // when the mapping is null it means there were no
1552 // predicates satisfied
1553 if( calleeStatesSatisfied == null ) {
1557 Iterator<ReachState> itr = rs.iterator();
1558 while( itr.hasNext() ) {
1559 ReachState stateCallee = itr.next();
1561 if( calleeStatesSatisfied.containsKey( stateCallee ) ) {
1563 // starting from one callee state...
1564 ReachSet rsCaller = ReachSet.factory( stateCallee );
1566 // possibly branch it into many states, which any
1567 // allocation site might do, so lots of derived states
1568 Iterator<AllocSite> asItr = allocSites.iterator();
1569 while( asItr.hasNext() ) {
1570 AllocSite as = asItr.next();
1571 rsCaller = Canonical.toCallerContext( rsCaller, as );
1574 // then before adding each derived, now caller-context
1575 // states to the output, attach the appropriate pred
1576 // based on the source callee state
1577 Iterator<ReachState> stateItr = rsCaller.iterator();
1578 while( stateItr.hasNext() ) {
1579 ReachState stateCaller = stateItr.next();
1580 stateCaller = Canonical.attach( stateCaller,
1581 calleeStatesSatisfied.get( stateCallee )
1583 out = Canonical.add( out,
1590 assert out.isCanonical();
1595 // used below to convert a ReachSet to an equivalent
1596 // version with shadow IDs merged into unshadowed IDs
1597 protected ReachSet unshadow( ReachSet rs ) {
1599 Iterator<AllocSite> asItr = allocSites.iterator();
1600 while( asItr.hasNext() ) {
1601 AllocSite as = asItr.next();
1602 out = Canonical.unshadow( out, as );
1604 assert out.isCanonical();
1609 // convert a caller taint set into a callee taint set
1611 toCalleeContext( TaintSet ts,
1612 ExistPredSet predsEdge ) {
1614 TaintSet out = TaintSet.factory();
1616 // the idea is easy, the taint identifier itself doesn't
1617 // change at all, but the predicates should be tautology:
1618 // start with the preds passed in from the caller edge
1619 // that host the taints, and alter them to have the taint
1620 // added, just becoming more specific than edge predicate alone
1622 Iterator<Taint> itr = ts.iterator();
1623 while( itr.hasNext() ) {
1624 Taint tCaller = itr.next();
1626 ExistPredSet predsWithTaint = ExistPredSet.factory();
1628 Iterator<ExistPred> predItr = predsEdge.iterator();
1629 while( predItr.hasNext() ) {
1630 ExistPred predEdge = predItr.next();
1633 Canonical.add( predsWithTaint,
1634 ExistPred.factory( predEdge.e_tdSrc,
1635 predEdge.e_hrnSrcID,
1636 predEdge.e_hrnDstID,
1641 predEdge.e_srcOutCalleeContext,
1642 predEdge.e_srcOutCallerContext
1647 Taint tCallee = Canonical.changePredsTo( tCaller,
1650 out = Canonical.add( out,
1655 assert out.isCanonical();
1660 // used below to convert a TaintSet to its caller-context
1661 // equivalent, just eliminate Taints with bad preds
1663 toCallerContext( TaintSet ts,
1664 Hashtable<Taint, ExistPredSet> calleeTaintsSatisfied
1667 TaintSet out = TaintSet.factory();
1669 // when the mapping is null it means there were no
1670 // predicates satisfied
1671 if( calleeTaintsSatisfied == null ) {
1675 Iterator<Taint> itr = ts.iterator();
1676 while( itr.hasNext() ) {
1677 Taint tCallee = itr.next();
1679 if( calleeTaintsSatisfied.containsKey( tCallee ) ) {
1682 Canonical.attach( Taint.factory( tCallee.sese,
1686 ExistPredSet.factory() ),
1687 calleeTaintsSatisfied.get( tCallee )
1689 out = Canonical.add( out,
1695 assert out.isCanonical();
1702 // use this method to make a new reach graph that is
1703 // what heap the FlatMethod callee from the FlatCall
1704 // would start with reaching from its arguments in
1707 makeCalleeView( FlatCall fc,
1708 FlatMethod fmCallee,
1709 Set<Integer> callerNodeIDsCopiedToCallee,
1710 boolean writeDebugDOTs
1714 // first traverse this context to find nodes and edges
1715 // that will be callee-reachable
1716 Set<HeapRegionNode> reachableCallerNodes =
1717 new HashSet<HeapRegionNode>();
1719 // caller edges between callee-reachable nodes
1720 Set<RefEdge> reachableCallerEdges =
1721 new HashSet<RefEdge>();
1723 // caller edges from arg vars, and the matching param index
1724 // because these become a special edge in callee
1725 Hashtable<RefEdge, Integer> reachableCallerArgEdges2paramIndex =
1726 new Hashtable<RefEdge, Integer>();
1728 // caller edges from local vars or callee-unreachable nodes
1729 // (out-of-context sources) to callee-reachable nodes
1730 Set<RefEdge> oocCallerEdges =
1731 new HashSet<RefEdge>();
1734 for( int i = 0; i < fmCallee.numParameters(); ++i ) {
1736 TempDescriptor tdArg = fc.getArgMatchingParamIndex( fmCallee, i );
1737 VariableNode vnArgCaller = this.getVariableNodeFromTemp( tdArg );
1739 Set<RefSrcNode> toVisitInCaller = new HashSet<RefSrcNode>();
1740 Set<RefSrcNode> visitedInCaller = new HashSet<RefSrcNode>();
1742 toVisitInCaller.add( vnArgCaller );
1744 while( !toVisitInCaller.isEmpty() ) {
1745 RefSrcNode rsnCaller = toVisitInCaller.iterator().next();
1746 toVisitInCaller.remove( rsnCaller );
1747 visitedInCaller.add( rsnCaller );
1749 Iterator<RefEdge> itrRefEdges = rsnCaller.iteratorToReferencees();
1750 while( itrRefEdges.hasNext() ) {
1751 RefEdge reCaller = itrRefEdges.next();
1752 HeapRegionNode hrnCaller = reCaller.getDst();
1754 callerNodeIDsCopiedToCallee.add( hrnCaller.getID() );
1755 reachableCallerNodes.add( hrnCaller );
1757 if( reCaller.getSrc() instanceof HeapRegionNode ) {
1758 reachableCallerEdges.add( reCaller );
1760 if( rsnCaller.equals( vnArgCaller ) ) {
1761 reachableCallerArgEdges2paramIndex.put( reCaller, i );
1763 oocCallerEdges.add( reCaller );
1767 if( !visitedInCaller.contains( hrnCaller ) ) {
1768 toVisitInCaller.add( hrnCaller );
1771 } // end edge iteration
1772 } // end visiting heap nodes in caller
1773 } // end iterating over parameters as starting points
1776 // now collect out-of-callee-context IDs and
1777 // map them to whether the ID is out of the caller
1779 Set<HrnIdOoc> oocHrnIdOoc2callee = new HashSet<HrnIdOoc>();
1781 Iterator<Integer> itrInContext =
1782 callerNodeIDsCopiedToCallee.iterator();
1783 while( itrInContext.hasNext() ) {
1784 Integer hrnID = itrInContext.next();
1785 HeapRegionNode hrnCallerAndInContext = id2hrn.get( hrnID );
1787 Iterator<RefEdge> itrMightCross =
1788 hrnCallerAndInContext.iteratorToReferencers();
1789 while( itrMightCross.hasNext() ) {
1790 RefEdge edgeMightCross = itrMightCross.next();
1792 RefSrcNode rsnCallerAndOutContext =
1793 edgeMightCross.getSrc();
1795 if( rsnCallerAndOutContext instanceof VariableNode ) {
1796 // variables do not have out-of-context reach states,
1798 oocCallerEdges.add( edgeMightCross );
1802 HeapRegionNode hrnCallerAndOutContext =
1803 (HeapRegionNode) rsnCallerAndOutContext;
1805 // is this source node out-of-context?
1806 if( callerNodeIDsCopiedToCallee.contains( hrnCallerAndOutContext.getID() ) ) {
1807 // no, skip this edge
1812 oocCallerEdges.add( edgeMightCross );
1814 // add all reach tuples on the node to list
1815 // of things that are out-of-context: insight
1816 // if this node is reachable from someting that WAS
1817 // in-context, then this node should already be in-context
1818 Iterator<ReachState> stateItr = hrnCallerAndOutContext.getAlpha().iterator();
1819 while( stateItr.hasNext() ) {
1820 ReachState state = stateItr.next();
1822 Iterator<ReachTuple> rtItr = state.iterator();
1823 while( rtItr.hasNext() ) {
1824 ReachTuple rt = rtItr.next();
1826 oocHrnIdOoc2callee.add( new HrnIdOoc( rt.getHrnID(),
1835 // the callee view is a new graph: DON'T MODIFY *THIS* graph
1836 ReachGraph rg = new ReachGraph();
1838 // add nodes to callee graph
1839 Iterator<HeapRegionNode> hrnItr = reachableCallerNodes.iterator();
1840 while( hrnItr.hasNext() ) {
1841 HeapRegionNode hrnCaller = hrnItr.next();
1843 assert callerNodeIDsCopiedToCallee.contains( hrnCaller.getID() );
1844 assert !rg.id2hrn.containsKey( hrnCaller.getID() );
1846 ExistPred pred = ExistPred.factory( hrnCaller.getID(), null );
1847 ExistPredSet preds = ExistPredSet.factory( pred );
1849 rg.createNewHeapRegionNode( hrnCaller.getID(),
1850 hrnCaller.isSingleObject(),
1851 hrnCaller.isNewSummary(),
1852 false, // out-of-context?
1853 hrnCaller.getType(),
1854 hrnCaller.getAllocSite(),
1855 toCalleeContext( hrnCaller.getInherent(),
1859 toCalleeContext( hrnCaller.getAlpha(),
1864 hrnCaller.getDescription()
1868 // add param edges to callee graph
1870 reachableCallerArgEdges2paramIndex.entrySet().iterator();
1871 while( argEdges.hasNext() ) {
1872 Map.Entry me = (Map.Entry) argEdges.next();
1873 RefEdge reArg = (RefEdge) me.getKey();
1874 Integer index = (Integer) me.getValue();
1876 VariableNode vnCaller = (VariableNode) reArg.getSrc();
1877 TempDescriptor argCaller = vnCaller.getTempDescriptor();
1879 TempDescriptor paramCallee = fmCallee.getParameter( index );
1880 VariableNode vnCallee = rg.getVariableNodeFromTemp( paramCallee );
1882 HeapRegionNode hrnDstCaller = reArg.getDst();
1883 HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1884 assert hrnDstCallee != null;
1887 ExistPred.factory( argCaller,
1889 hrnDstCallee.getID(),
1894 true, // out-of-callee-context
1895 false // out-of-caller-context
1898 ExistPredSet preds =
1899 ExistPredSet.factory( pred );
1902 new RefEdge( vnCallee,
1906 toCalleeContext( reArg.getBeta(),
1911 toCalleeContext( reArg.getTaints(),
1915 rg.addRefEdge( vnCallee,
1921 // add in-context edges to callee graph
1922 Iterator<RefEdge> reItr = reachableCallerEdges.iterator();
1923 while( reItr.hasNext() ) {
1924 RefEdge reCaller = reItr.next();
1925 RefSrcNode rsnCaller = reCaller.getSrc();
1926 assert rsnCaller instanceof HeapRegionNode;
1927 HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1928 HeapRegionNode hrnDstCaller = reCaller.getDst();
1930 HeapRegionNode hrnSrcCallee = rg.id2hrn.get( hrnSrcCaller.getID() );
1931 HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1932 assert hrnSrcCallee != null;
1933 assert hrnDstCallee != null;
1936 ExistPred.factory( null,
1937 hrnSrcCallee.getID(),
1938 hrnDstCallee.getID(),
1940 reCaller.getField(),
1943 false, // out-of-callee-context
1944 false // out-of-caller-context
1947 ExistPredSet preds =
1948 ExistPredSet.factory( pred );
1951 new RefEdge( hrnSrcCallee,
1954 reCaller.getField(),
1955 toCalleeContext( reCaller.getBeta(),
1960 TaintSet.factory() // no taints for in-context edges
1963 rg.addRefEdge( hrnSrcCallee,
1969 // add out-of-context edges to callee graph
1970 reItr = oocCallerEdges.iterator();
1971 while( reItr.hasNext() ) {
1972 RefEdge reCaller = reItr.next();
1973 RefSrcNode rsnCaller = reCaller.getSrc();
1974 HeapRegionNode hrnDstCaller = reCaller.getDst();
1975 HeapRegionNode hrnDstCallee = rg.id2hrn.get( hrnDstCaller.getID() );
1976 assert hrnDstCallee != null;
1978 TypeDescriptor oocNodeType;
1980 TempDescriptor oocPredSrcTemp = null;
1981 Integer oocPredSrcID = null;
1982 boolean outOfCalleeContext;
1983 boolean outOfCallerContext;
1985 if( rsnCaller instanceof VariableNode ) {
1986 VariableNode vnCaller = (VariableNode) rsnCaller;
1988 oocReach = rsetEmpty;
1989 oocPredSrcTemp = vnCaller.getTempDescriptor();
1990 outOfCalleeContext = true;
1991 outOfCallerContext = false;
1994 HeapRegionNode hrnSrcCaller = (HeapRegionNode) rsnCaller;
1995 assert !callerNodeIDsCopiedToCallee.contains( hrnSrcCaller.getID() );
1996 oocNodeType = hrnSrcCaller.getType();
1997 oocReach = hrnSrcCaller.getAlpha();
1998 oocPredSrcID = hrnSrcCaller.getID();
1999 if( hrnSrcCaller.isOutOfContext() ) {
2000 outOfCalleeContext = false;
2001 outOfCallerContext = true;
2003 outOfCalleeContext = true;
2004 outOfCallerContext = false;
2009 ExistPred.factory( oocPredSrcTemp,
2011 hrnDstCallee.getID(),
2013 reCaller.getField(),
2020 ExistPredSet preds =
2021 ExistPredSet.factory( pred );
2023 RefEdge oocEdgeExisting =
2024 rg.getOutOfContextReferenceTo( hrnDstCallee,
2030 if( oocEdgeExisting == null ) {
2031 // for consistency, map one out-of-context "identifier"
2032 // to one heap region node id, otherwise no convergence
2033 String oocid = "oocid"+
2035 hrnDstCallee.getIDString()+
2038 reCaller.getField();
2040 Integer oocHrnID = oocid2hrnid.get( oocid );
2042 HeapRegionNode hrnCalleeAndOutContext;
2044 if( oocHrnID == null ) {
2046 hrnCalleeAndOutContext =
2047 rg.createNewHeapRegionNode( null, // ID
2048 false, // single object?
2049 false, // new summary?
2050 true, // out-of-context?
2052 null, // alloc site, shouldn't be used
2053 toCalleeContext( oocReach,
2057 toCalleeContext( oocReach,
2065 oocid2hrnid.put( oocid, hrnCalleeAndOutContext.getID() );
2069 // the mapping already exists, so see if node is there
2070 hrnCalleeAndOutContext = rg.id2hrn.get( oocHrnID );
2072 if( hrnCalleeAndOutContext == null ) {
2074 hrnCalleeAndOutContext =
2075 rg.createNewHeapRegionNode( oocHrnID, // ID
2076 false, // single object?
2077 false, // new summary?
2078 true, // out-of-context?
2080 null, // alloc site, shouldn't be used
2081 toCalleeContext( oocReach,
2085 toCalleeContext( oocReach,
2094 // otherwise it is there, so merge reachability
2095 hrnCalleeAndOutContext.setAlpha( Canonical.unionORpreds( hrnCalleeAndOutContext.getAlpha(),
2096 toCalleeContext( oocReach,
2105 assert hrnCalleeAndOutContext.reachHasOnlyOOC();
2107 rg.addRefEdge( hrnCalleeAndOutContext,
2109 new RefEdge( hrnCalleeAndOutContext,
2112 reCaller.getField(),
2113 toCalleeContext( reCaller.getBeta(),
2118 toCalleeContext( reCaller.getTaints(),
2124 // the out-of-context edge already exists
2125 oocEdgeExisting.setBeta( Canonical.unionORpreds( oocEdgeExisting.getBeta(),
2126 toCalleeContext( reCaller.getBeta(),
2133 oocEdgeExisting.setPreds( Canonical.join( oocEdgeExisting.getPreds(),
2138 oocEdgeExisting.setTaints( Canonical.unionORpreds( oocEdgeExisting.getTaints(),
2139 toCalleeContext( reCaller.getTaints(),
2145 HeapRegionNode hrnCalleeAndOutContext =
2146 (HeapRegionNode) oocEdgeExisting.getSrc();
2147 hrnCalleeAndOutContext.setAlpha( Canonical.unionORpreds( hrnCalleeAndOutContext.getAlpha(),
2148 toCalleeContext( oocReach,
2155 assert hrnCalleeAndOutContext.reachHasOnlyOOC();
2160 if( writeDebugDOTs ) {
2161 debugGraphPrefix = String.format( "call%03d", debugCallSiteVisitCounter );
2162 rg.writeGraph( debugGraphPrefix+"calleeview",
2163 resolveMethodDebugDOTwriteLabels,
2164 resolveMethodDebugDOTselectTemps,
2165 resolveMethodDebugDOTpruneGarbage,
2166 resolveMethodDebugDOThideReach,
2167 resolveMethodDebugDOThideSubsetReach,
2168 resolveMethodDebugDOThidePreds,
2169 resolveMethodDebugDOThideEdgeTaints );
2175 private static Hashtable<String, Integer> oocid2hrnid =
2176 new Hashtable<String, Integer>();
2179 // useful since many graphs writes in the method call debug code
2180 private static boolean resolveMethodDebugDOTwriteLabels = true;
2181 private static boolean resolveMethodDebugDOTselectTemps = true;
2182 private static boolean resolveMethodDebugDOTpruneGarbage = true;
2183 private static boolean resolveMethodDebugDOThideReach = true;
2184 private static boolean resolveMethodDebugDOThideSubsetReach = true;
2185 private static boolean resolveMethodDebugDOThidePreds = true;
2186 private static boolean resolveMethodDebugDOThideEdgeTaints = true;
2188 static String debugGraphPrefix;
2189 static int debugCallSiteVisitCounter;
2190 static int debugCallSiteVisitStartCapture;
2191 static int debugCallSiteNumVisitsToCapture;
2192 static boolean debugCallSiteStopAfter;
2196 resolveMethodCall( FlatCall fc,
2197 FlatMethod fmCallee,
2198 ReachGraph rgCallee,
2199 Set<Integer> callerNodeIDsCopiedToCallee,
2200 boolean writeDebugDOTs
2203 if( writeDebugDOTs ) {
2204 System.out.println( " Writing out visit "+
2205 debugCallSiteVisitCounter+
2206 " to debug call site" );
2208 debugGraphPrefix = String.format( "call%03d",
2209 debugCallSiteVisitCounter );
2211 rgCallee.writeGraph( debugGraphPrefix+"callee",
2212 resolveMethodDebugDOTwriteLabels,
2213 resolveMethodDebugDOTselectTemps,
2214 resolveMethodDebugDOTpruneGarbage,
2215 resolveMethodDebugDOThideReach,
2216 resolveMethodDebugDOThideSubsetReach,
2217 resolveMethodDebugDOThidePreds,
2218 resolveMethodDebugDOThideEdgeTaints );
2220 writeGraph( debugGraphPrefix+"caller00In",
2221 resolveMethodDebugDOTwriteLabels,
2222 resolveMethodDebugDOTselectTemps,
2223 resolveMethodDebugDOTpruneGarbage,
2224 resolveMethodDebugDOThideReach,
2225 resolveMethodDebugDOThideSubsetReach,
2226 resolveMethodDebugDOThidePreds,
2227 resolveMethodDebugDOThideEdgeTaints,
2228 callerNodeIDsCopiedToCallee );
2233 // method call transfer function steps:
2234 // 1. Use current callee-reachable heap (CRH) to test callee
2235 // predicates and mark what will be coming in.
2236 // 2. Wipe CRH out of caller.
2237 // 3. Transplant marked callee parts in:
2238 // a) bring in nodes
2239 // b) bring in callee -> callee edges
2240 // c) resolve out-of-context -> callee edges
2241 // d) assign return value
2242 // 4. Collapse shadow nodes down
2243 // 5. Global sweep it.
2246 // 1. mark what callee elements have satisfied predicates
2247 Hashtable<HeapRegionNode, ExistPredSet> calleeNodesSatisfied =
2248 new Hashtable<HeapRegionNode, ExistPredSet>();
2250 Hashtable<RefEdge, ExistPredSet> calleeEdgesSatisfied =
2251 new Hashtable<RefEdge, ExistPredSet>();
2253 Hashtable< HeapRegionNode, Hashtable<ReachState, ExistPredSet> >
2254 calleeNode2calleeStatesSatisfied =
2255 new Hashtable< HeapRegionNode, Hashtable<ReachState, ExistPredSet> >();
2257 Hashtable< RefEdge, Hashtable<ReachState, ExistPredSet> >
2258 calleeEdge2calleeStatesSatisfied =
2259 new Hashtable< RefEdge, Hashtable<ReachState, ExistPredSet> >();
2261 Hashtable< RefEdge, Hashtable<Taint, ExistPredSet> >
2262 calleeEdge2calleeTaintsSatisfied =
2263 new Hashtable< RefEdge, Hashtable<Taint, ExistPredSet> >();
2265 Hashtable< RefEdge, Set<RefSrcNode> > calleeEdges2oocCallerSrcMatches =
2266 new Hashtable< RefEdge, Set<RefSrcNode> >();
2269 Iterator meItr = rgCallee.id2hrn.entrySet().iterator();
2270 while( meItr.hasNext() ) {
2271 Map.Entry me = (Map.Entry) meItr.next();
2272 Integer id = (Integer) me.getKey();
2273 HeapRegionNode hrnCallee = (HeapRegionNode) me.getValue();
2275 // if a callee element's predicates are satisfied then a set
2276 // of CALLER predicates is returned: they are the predicates
2277 // that the callee element moved into the caller context
2278 // should have, and it is inefficient to find this again later
2279 ExistPredSet predsIfSatis =
2280 hrnCallee.getPreds().isSatisfiedBy( this,
2281 callerNodeIDsCopiedToCallee
2284 if( predsIfSatis != null ) {
2285 calleeNodesSatisfied.put( hrnCallee, predsIfSatis );
2287 // otherwise don't bother looking at edges to this node
2291 // since the node is coming over, find out which reach
2292 // states on it should come over, too
2293 assert calleeNode2calleeStatesSatisfied.get( hrnCallee ) == null;
2295 Iterator<ReachState> stateItr = hrnCallee.getAlpha().iterator();
2296 while( stateItr.hasNext() ) {
2297 ReachState stateCallee = stateItr.next();
2300 stateCallee.getPreds().isSatisfiedBy( this,
2301 callerNodeIDsCopiedToCallee
2303 if( predsIfSatis != null ) {
2305 Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied =
2306 calleeNode2calleeStatesSatisfied.get( hrnCallee );
2308 if( calleeStatesSatisfied == null ) {
2309 calleeStatesSatisfied =
2310 new Hashtable<ReachState, ExistPredSet>();
2312 calleeNode2calleeStatesSatisfied.put( hrnCallee, calleeStatesSatisfied );
2315 calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2319 // then look at edges to the node
2320 Iterator<RefEdge> reItr = hrnCallee.iteratorToReferencers();
2321 while( reItr.hasNext() ) {
2322 RefEdge reCallee = reItr.next();
2323 RefSrcNode rsnCallee = reCallee.getSrc();
2325 // (caller local variables to in-context heap regions)
2326 // have an (out-of-context heap region -> in-context heap region)
2327 // abstraction in the callEE, so its true we never need to
2328 // look at a (var node -> heap region) edge in callee to bring
2329 // those over for the call site transfer, except for the special
2330 // case of *RETURN var* -> heap region edges.
2331 // What about (param var->heap region)
2332 // edges in callee? They are dealt with below this loop.
2334 if( rsnCallee instanceof VariableNode ) {
2336 // looking for the return-value variable only
2337 VariableNode vnCallee = (VariableNode) rsnCallee;
2338 if( vnCallee.getTempDescriptor() != tdReturn ) {
2342 TempDescriptor returnTemp = fc.getReturnTemp();
2343 if( returnTemp == null ||
2344 !DisjointAnalysis.shouldAnalysisTrack( returnTemp.getType() )
2349 // note that the assignment of the return value is to a
2350 // variable in the caller which is out-of-context with
2351 // respect to the callee
2352 VariableNode vnLhsCaller = getVariableNodeFromTemp( returnTemp );
2353 Set<RefSrcNode> rsnCallers = new HashSet<RefSrcNode>();
2354 rsnCallers.add( vnLhsCaller );
2355 calleeEdges2oocCallerSrcMatches.put( reCallee, rsnCallers );
2359 // for HeapRegionNode callee sources...
2361 // first see if the source is out-of-context, and only
2362 // proceed with this edge if we find some caller-context
2364 HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2365 boolean matchedOutOfContext = false;
2367 if( !hrnSrcCallee.isOutOfContext() ) {
2370 hrnSrcCallee.getPreds().isSatisfiedBy( this,
2371 callerNodeIDsCopiedToCallee
2373 if( predsIfSatis != null ) {
2374 calleeNodesSatisfied.put( hrnSrcCallee, predsIfSatis );
2376 // otherwise forget this edge
2381 // hrnSrcCallee is out-of-context
2383 assert !calleeEdges2oocCallerSrcMatches.containsKey( reCallee );
2385 Set<RefSrcNode> rsnCallers = new HashSet<RefSrcNode>();
2387 // is the target node in the caller?
2388 HeapRegionNode hrnDstCaller = this.id2hrn.get( hrnCallee.getID() );
2389 if( hrnDstCaller == null ) {
2393 Iterator<RefEdge> reDstItr = hrnDstCaller.iteratorToReferencers();
2394 while( reDstItr.hasNext() ) {
2395 // the edge and field (either possibly null) must match
2396 RefEdge reCaller = reDstItr.next();
2398 if( !reCaller.typeEquals ( reCallee.getType() ) ||
2399 !reCaller.fieldEquals( reCallee.getField() )
2404 RefSrcNode rsnCaller = reCaller.getSrc();
2405 if( rsnCaller instanceof VariableNode ) {
2407 // a variable node matches an OOC region with null type
2408 if( hrnSrcCallee.getType() != null ) {
2413 // otherwise types should match
2414 HeapRegionNode hrnCallerSrc = (HeapRegionNode) rsnCaller;
2415 if( hrnSrcCallee.getType() == null ) {
2416 if( hrnCallerSrc.getType() != null ) {
2420 if( !hrnSrcCallee.getType().equals( hrnCallerSrc.getType() ) ) {
2426 rsnCallers.add( rsnCaller );
2427 matchedOutOfContext = true;
2430 if( !rsnCallers.isEmpty() ) {
2431 calleeEdges2oocCallerSrcMatches.put( reCallee, rsnCallers );
2435 if( hrnSrcCallee.isOutOfContext() &&
2436 !matchedOutOfContext ) {
2443 reCallee.getPreds().isSatisfiedBy( this,
2444 callerNodeIDsCopiedToCallee
2447 if( predsIfSatis != null ) {
2448 calleeEdgesSatisfied.put( reCallee, predsIfSatis );
2450 // since the edge is coming over, find out which reach
2451 // states on it should come over, too
2452 assert calleeEdge2calleeStatesSatisfied.get( reCallee ) == null;
2454 stateItr = reCallee.getBeta().iterator();
2455 while( stateItr.hasNext() ) {
2456 ReachState stateCallee = stateItr.next();
2459 stateCallee.getPreds().isSatisfiedBy( this,
2460 callerNodeIDsCopiedToCallee
2462 if( predsIfSatis != null ) {
2464 Hashtable<ReachState, ExistPredSet> calleeStatesSatisfied =
2465 calleeEdge2calleeStatesSatisfied.get( reCallee );
2467 if( calleeStatesSatisfied == null ) {
2468 calleeStatesSatisfied =
2469 new Hashtable<ReachState, ExistPredSet>();
2471 calleeEdge2calleeStatesSatisfied.put( reCallee, calleeStatesSatisfied );
2474 calleeStatesSatisfied.put( stateCallee, predsIfSatis );
2478 // since the edge is coming over, find out which taints
2479 // on it should come over, too
2480 assert calleeEdge2calleeTaintsSatisfied.get( reCallee ) == null;
2482 Iterator<Taint> tItr = reCallee.getTaints().iterator();
2483 while( tItr.hasNext() ) {
2484 Taint tCallee = tItr.next();
2487 tCallee.getPreds().isSatisfiedBy( this,
2488 callerNodeIDsCopiedToCallee
2490 if( predsIfSatis != null ) {
2492 Hashtable<Taint, ExistPredSet> calleeTaintsSatisfied =
2493 calleeEdge2calleeTaintsSatisfied.get( reCallee );
2495 if( calleeTaintsSatisfied == null ) {
2496 calleeTaintsSatisfied =
2497 new Hashtable<Taint, ExistPredSet>();
2499 calleeEdge2calleeTaintsSatisfied.put( reCallee, calleeTaintsSatisfied );
2502 calleeTaintsSatisfied.put( tCallee, predsIfSatis );
2509 if( writeDebugDOTs ) {
2510 writeGraph( debugGraphPrefix+"caller20BeforeWipe",
2511 resolveMethodDebugDOTwriteLabels,
2512 resolveMethodDebugDOTselectTemps,
2513 resolveMethodDebugDOTpruneGarbage,
2514 resolveMethodDebugDOThideReach,
2515 resolveMethodDebugDOThideSubsetReach,
2516 resolveMethodDebugDOThidePreds,
2517 resolveMethodDebugDOThideEdgeTaints );
2521 // 2. predicates tested, ok to wipe out caller part
2522 Iterator<Integer> hrnItr = callerNodeIDsCopiedToCallee.iterator();
2523 while( hrnItr.hasNext() ) {
2524 Integer hrnID = hrnItr.next();
2525 HeapRegionNode hrnCaller = id2hrn.get( hrnID );
2526 assert hrnCaller != null;
2528 // when clearing off nodes, also eliminate variable
2530 wipeOut( hrnCaller, true );
2533 // if we are assigning the return value to something, clobber now
2534 // as part of the wipe
2535 TempDescriptor returnTemp = fc.getReturnTemp();
2536 if( returnTemp != null &&
2537 DisjointAnalysis.shouldAnalysisTrack( returnTemp.getType() )
2540 VariableNode vnLhsCaller = getVariableNodeFromTemp( returnTemp );
2541 clearRefEdgesFrom( vnLhsCaller, null, null, true );
2547 if( writeDebugDOTs ) {
2548 writeGraph( debugGraphPrefix+"caller30BeforeAddingNodes",
2549 resolveMethodDebugDOTwriteLabels,
2550 resolveMethodDebugDOTselectTemps,
2551 resolveMethodDebugDOTpruneGarbage,
2552 resolveMethodDebugDOThideReach,
2553 resolveMethodDebugDOThideSubsetReach,
2554 resolveMethodDebugDOThidePreds,
2555 resolveMethodDebugDOThideEdgeTaints );
2561 // 3. callee elements with satisfied preds come in, note that
2562 // the mapping of elements satisfied to preds is like this:
2563 // A callee element EE has preds EEp that are satisfied by
2564 // some caller element ER. We bring EE into the caller
2565 // context as ERee with the preds of ER, namely ERp, which
2566 // in the following algorithm is the value in the mapping
2569 Iterator satisItr = calleeNodesSatisfied.entrySet().iterator();
2570 while( satisItr.hasNext() ) {
2571 Map.Entry me = (Map.Entry) satisItr.next();
2572 HeapRegionNode hrnCallee = (HeapRegionNode) me.getKey();
2573 ExistPredSet preds = (ExistPredSet) me.getValue();
2575 // TODO: I think its true that the current implementation uses
2576 // the type of the OOC region and the predicates OF THE EDGE from
2577 // it to link everything up in caller context, so that's why we're
2578 // skipping this... maybe that's a sillier way to do it?
2579 if( hrnCallee.isOutOfContext() ) {
2583 AllocSite as = hrnCallee.getAllocSite();
2584 allocSites.add( as );
2586 Integer hrnIDshadow = as.getShadowIDfromID( hrnCallee.getID() );
2588 HeapRegionNode hrnCaller = id2hrn.get( hrnIDshadow );
2589 if( hrnCaller == null ) {
2591 createNewHeapRegionNode( hrnIDshadow, // id or null to generate a new one
2592 hrnCallee.isSingleObject(), // single object?
2593 hrnCallee.isNewSummary(), // summary?
2594 false, // out-of-context?
2595 hrnCallee.getType(), // type
2596 hrnCallee.getAllocSite(), // allocation site
2597 toCallerContext( hrnCallee.getInherent(),
2598 calleeNode2calleeStatesSatisfied.get( hrnCallee ) ), // inherent reach
2599 null, // current reach
2600 predsEmpty, // predicates
2601 hrnCallee.getDescription() // description
2604 assert hrnCaller.isWiped();
2607 hrnCaller.setAlpha( toCallerContext( hrnCallee.getAlpha(),
2608 calleeNode2calleeStatesSatisfied.get( hrnCallee )
2612 hrnCaller.setPreds( preds );
2619 if( writeDebugDOTs ) {
2620 writeGraph( debugGraphPrefix+"caller31BeforeAddingEdges",
2621 resolveMethodDebugDOTwriteLabels,
2622 resolveMethodDebugDOTselectTemps,
2623 resolveMethodDebugDOTpruneGarbage,
2624 resolveMethodDebugDOThideReach,
2625 resolveMethodDebugDOThideSubsetReach,
2626 resolveMethodDebugDOThidePreds,
2627 resolveMethodDebugDOThideEdgeTaints );
2631 // set these up during the next procedure so after
2632 // the caller has all of its nodes and edges put
2633 // back together we can propagate the callee's
2634 // reach changes backwards into the caller graph
2635 HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
2637 Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
2638 new Hashtable<RefEdge, ChangeSet>();
2641 // 3.b) callee -> callee edges AND out-of-context -> callee
2642 // which includes return temp -> callee edges now, too
2643 satisItr = calleeEdgesSatisfied.entrySet().iterator();
2644 while( satisItr.hasNext() ) {
2645 Map.Entry me = (Map.Entry) satisItr.next();
2646 RefEdge reCallee = (RefEdge) me.getKey();
2647 ExistPredSet preds = (ExistPredSet) me.getValue();
2649 HeapRegionNode hrnDstCallee = reCallee.getDst();
2650 AllocSite asDst = hrnDstCallee.getAllocSite();
2651 allocSites.add( asDst );
2653 Integer hrnIDDstShadow =
2654 asDst.getShadowIDfromID( hrnDstCallee.getID() );
2656 HeapRegionNode hrnDstCaller = id2hrn.get( hrnIDDstShadow );
2657 assert hrnDstCaller != null;
2660 RefSrcNode rsnCallee = reCallee.getSrc();
2662 Set<RefSrcNode> rsnCallers =
2663 new HashSet<RefSrcNode>();
2665 Set<RefSrcNode> oocCallers =
2666 calleeEdges2oocCallerSrcMatches.get( reCallee );
2668 if( rsnCallee instanceof HeapRegionNode ) {
2669 HeapRegionNode hrnCalleeSrc = (HeapRegionNode) rsnCallee;
2670 if( hrnCalleeSrc.isOutOfContext() ) {
2671 assert oocCallers != null;
2676 if( oocCallers == null ) {
2677 // there are no out-of-context matches, so it's
2678 // either a param/arg var or one in-context heap region
2679 if( rsnCallee instanceof VariableNode ) {
2680 // variable -> node in the callee should only
2681 // come into the caller if its from a param var
2682 VariableNode vnCallee = (VariableNode) rsnCallee;
2683 TempDescriptor tdParam = vnCallee.getTempDescriptor();
2684 TempDescriptor tdArg = fc.getArgMatchingParam( fmCallee,
2686 if( tdArg == null ) {
2687 // this means the variable isn't a parameter, its local
2688 // to the callee so we ignore it in call site transfer
2689 // shouldn't this NEVER HAPPEN?
2693 rsnCallers.add( this.getVariableNodeFromTemp( tdArg ) );
2696 // otherwise source is in context, one region
2698 HeapRegionNode hrnSrcCallee = (HeapRegionNode) rsnCallee;
2700 // translate an in-context node to shadow
2701 AllocSite asSrc = hrnSrcCallee.getAllocSite();
2702 allocSites.add( asSrc );
2704 Integer hrnIDSrcShadow =
2705 asSrc.getShadowIDfromID( hrnSrcCallee.getID() );
2707 HeapRegionNode hrnSrcCallerShadow =
2708 this.id2hrn.get( hrnIDSrcShadow );
2710 assert hrnSrcCallerShadow != null;
2712 rsnCallers.add( hrnSrcCallerShadow );
2716 // otherwise we have a set of out-of-context srcs
2717 // that should NOT be translated to shadow nodes
2718 assert !oocCallers.isEmpty();
2719 rsnCallers.addAll( oocCallers );
2722 // now make all caller edges we've identified from
2723 // this callee edge with a satisfied predicate
2724 assert !rsnCallers.isEmpty();
2725 Iterator<RefSrcNode> rsnItr = rsnCallers.iterator();
2726 while( rsnItr.hasNext() ) {
2727 RefSrcNode rsnCaller = rsnItr.next();
2729 RefEdge reCaller = new RefEdge( rsnCaller,
2732 reCallee.getField(),
2733 toCallerContext( reCallee.getBeta(),
2734 calleeEdge2calleeStatesSatisfied.get( reCallee ) ),
2736 toCallerContext( reCallee.getTaints(),
2737 calleeEdge2calleeTaintsSatisfied.get( reCallee ) )
2740 ChangeSet cs = ChangeSet.factory();
2741 Iterator<ReachState> rsItr = reCaller.getBeta().iterator();
2742 while( rsItr.hasNext() ) {
2743 ReachState state = rsItr.next();
2744 ExistPredSet predsPreCallee = state.getPreds();
2746 if( state.isEmpty() ) {
2750 Iterator<ExistPred> predItr = predsPreCallee.iterator();
2751 while( predItr.hasNext() ) {
2752 ExistPred pred = predItr.next();
2753 ReachState old = pred.ne_state;
2759 cs = Canonical.add( cs,
2760 ChangeTuple.factory( old,
2767 // we're just going to use the convenient "merge-if-exists"
2768 // edge call below, but still take a separate look if there
2769 // is an existing caller edge to build change sets properly
2770 if( !cs.isEmpty() ) {
2771 RefEdge edgeExisting = rsnCaller.getReferenceTo( hrnDstCaller,
2775 if( edgeExisting != null ) {
2776 ChangeSet csExisting = edgePlannedChanges.get( edgeExisting );
2777 if( csExisting == null ) {
2778 csExisting = ChangeSet.factory();
2780 edgePlannedChanges.put( edgeExisting,
2781 Canonical.union( csExisting,
2786 edgesForPropagation.add( reCaller );
2787 assert !edgePlannedChanges.containsKey( reCaller );
2788 edgePlannedChanges.put( reCaller, cs );
2792 // then add new caller edge or merge
2793 addEdgeOrMergeWithExisting( reCaller );
2801 if( writeDebugDOTs ) {
2802 writeGraph( debugGraphPrefix+"caller38propagateReach",
2803 resolveMethodDebugDOTwriteLabels,
2804 resolveMethodDebugDOTselectTemps,
2805 resolveMethodDebugDOTpruneGarbage,
2806 resolveMethodDebugDOThideReach,
2807 resolveMethodDebugDOThideSubsetReach,
2808 resolveMethodDebugDOThidePreds,
2809 resolveMethodDebugDOThideEdgeTaints );
2812 // propagate callee reachability changes to the rest
2813 // of the caller graph edges
2814 HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
2816 propagateTokensOverEdges( edgesForPropagation, // source edges
2817 edgePlannedChanges, // map src edge to change set
2818 edgesUpdated ); // list of updated edges
2820 // commit beta' (beta<-betaNew)
2821 Iterator<RefEdge> edgeItr = edgesUpdated.iterator();
2822 while( edgeItr.hasNext() ) {
2823 edgeItr.next().applyBetaNew();
2832 if( writeDebugDOTs ) {
2833 writeGraph( debugGraphPrefix+"caller40BeforeShadowMerge",
2834 resolveMethodDebugDOTwriteLabels,
2835 resolveMethodDebugDOTselectTemps,
2836 resolveMethodDebugDOTpruneGarbage,
2837 resolveMethodDebugDOThideReach,
2838 resolveMethodDebugDOThideSubsetReach,
2839 resolveMethodDebugDOThidePreds,
2840 resolveMethodDebugDOThideEdgeTaints );
2844 // 4) merge shadow nodes so alloc sites are back to k
2845 Iterator<AllocSite> asItr = rgCallee.allocSites.iterator();
2846 while( asItr.hasNext() ) {
2847 // for each allocation site do the following to merge
2848 // shadow nodes (newest from callee) with any existing
2849 // look for the newest normal and newest shadow "slot"
2850 // not being used, transfer normal to shadow. Keep
2851 // doing this until there are no more normal nodes, or
2852 // no empty shadow slots: then merge all remaining normal
2853 // nodes into the shadow summary. Finally, convert all
2854 // shadow to their normal versions.
2855 AllocSite as = asItr.next();
2858 while( ageNorm < allocationDepth &&
2859 ageShad < allocationDepth ) {
2861 // first, are there any normal nodes left?
2862 Integer idNorm = as.getIthOldest( ageNorm );
2863 HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2864 if( hrnNorm == null ) {
2865 // no, this age of normal node not in the caller graph
2870 // yes, a normal node exists, is there an empty shadow
2871 // "slot" to transfer it onto?
2872 HeapRegionNode hrnShad = getIthNode( as, ageShad, true );
2873 if( !hrnShad.isWiped() ) {
2874 // no, this age of shadow node is not empty
2879 // yes, this shadow node is empty
2880 transferOnto( hrnNorm, hrnShad );
2885 // now, while there are still normal nodes but no shadow
2886 // slots, merge normal nodes into the shadow summary
2887 while( ageNorm < allocationDepth ) {
2889 // first, are there any normal nodes left?
2890 Integer idNorm = as.getIthOldest( ageNorm );
2891 HeapRegionNode hrnNorm = id2hrn.get( idNorm );
2892 if( hrnNorm == null ) {
2893 // no, this age of normal node not in the caller graph
2898 // yes, a normal node exists, so get the shadow summary
2899 HeapRegionNode summShad = getSummaryNode( as, true );
2900 mergeIntoSummary( hrnNorm, summShad );
2904 // if there is a normal summary, merge it into shadow summary
2905 Integer idNorm = as.getSummary();
2906 HeapRegionNode summNorm = id2hrn.get( idNorm );
2907 if( summNorm != null ) {
2908 HeapRegionNode summShad = getSummaryNode( as, true );
2909 mergeIntoSummary( summNorm, summShad );
2912 // finally, flip all existing shadow nodes onto the normal
2913 for( int i = 0; i < allocationDepth; ++i ) {
2914 Integer idShad = as.getIthOldestShadow( i );
2915 HeapRegionNode hrnShad = id2hrn.get( idShad );
2916 if( hrnShad != null ) {
2918 HeapRegionNode hrnNorm = getIthNode( as, i, false );
2919 assert hrnNorm.isWiped();
2920 transferOnto( hrnShad, hrnNorm );
2924 Integer idShad = as.getSummaryShadow();
2925 HeapRegionNode summShad = id2hrn.get( idShad );
2926 if( summShad != null ) {
2927 summNorm = getSummaryNode( as, false );
2928 transferOnto( summShad, summNorm );
2937 if( writeDebugDOTs ) {
2938 writeGraph( debugGraphPrefix+"caller45BeforeUnshadow",
2939 resolveMethodDebugDOTwriteLabels,
2940 resolveMethodDebugDOTselectTemps,
2941 resolveMethodDebugDOTpruneGarbage,
2942 resolveMethodDebugDOThideReach,
2943 resolveMethodDebugDOThideSubsetReach,
2944 resolveMethodDebugDOThidePreds,
2945 resolveMethodDebugDOThideEdgeTaints );
2949 Iterator itrAllHRNodes = id2hrn.entrySet().iterator();
2950 while( itrAllHRNodes.hasNext() ) {
2951 Map.Entry me = (Map.Entry) itrAllHRNodes.next();
2952 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
2954 hrn.setAlpha( unshadow( hrn.getAlpha() ) );
2956 Iterator<RefEdge> itrEdges = hrn.iteratorToReferencers();
2957 while( itrEdges.hasNext() ) {
2958 RefEdge re = itrEdges.next();
2959 re.setBeta( unshadow( re.getBeta() ) );
2966 if( writeDebugDOTs ) {
2967 writeGraph( debugGraphPrefix+"caller50BeforeGlobalSweep",
2968 resolveMethodDebugDOTwriteLabels,
2969 resolveMethodDebugDOTselectTemps,
2970 resolveMethodDebugDOTpruneGarbage,
2971 resolveMethodDebugDOThideReach,
2972 resolveMethodDebugDOThideSubsetReach,
2973 resolveMethodDebugDOThidePreds,
2974 resolveMethodDebugDOThideEdgeTaints );
2979 if( !DISABLE_GLOBAL_SWEEP ) {
2984 if( writeDebugDOTs ) {
2985 writeGraph( debugGraphPrefix+"caller90AfterTransfer",
2986 resolveMethodDebugDOTwriteLabels,
2987 resolveMethodDebugDOTselectTemps,
2988 resolveMethodDebugDOTpruneGarbage,
2989 resolveMethodDebugDOThideReach,
2990 resolveMethodDebugDOThideSubsetReach,
2991 resolveMethodDebugDOThidePreds,
2992 resolveMethodDebugDOThideEdgeTaints );
2998 ////////////////////////////////////////////////////
3000 // Abstract garbage collection simply removes
3001 // heap region nodes that are not mechanically
3002 // reachable from a root set. This step is
3003 // essential for testing node and edge existence
3004 // predicates efficiently
3006 ////////////////////////////////////////////////////
3007 public void abstractGarbageCollect( Set<TempDescriptor> liveSet ) {
3009 // calculate a root set, will be different for Java
3010 // version of analysis versus Bamboo version
3011 Set<RefSrcNode> toVisit = new HashSet<RefSrcNode>();
3013 // visit every variable in graph while building root
3014 // set, and do iterating on a copy, so we can remove
3015 // dead variables while we're at this
3016 Iterator makeCopyItr = td2vn.entrySet().iterator();
3017 Set entrysCopy = new HashSet();
3018 while( makeCopyItr.hasNext() ) {
3019 entrysCopy.add( makeCopyItr.next() );
3022 Iterator eItr = entrysCopy.iterator();
3023 while( eItr.hasNext() ) {
3024 Map.Entry me = (Map.Entry) eItr.next();
3025 TempDescriptor td = (TempDescriptor) me.getKey();
3026 VariableNode vn = (VariableNode) me.getValue();
3028 if( liveSet.contains( td ) ) {
3032 // dead var, remove completely from graph
3034 clearRefEdgesFrom( vn, null, null, true );
3038 // everything visited in a traversal is
3039 // considered abstractly live
3040 Set<RefSrcNode> visited = new HashSet<RefSrcNode>();
3042 while( !toVisit.isEmpty() ) {
3043 RefSrcNode rsn = toVisit.iterator().next();
3044 toVisit.remove( rsn );
3047 Iterator<RefEdge> hrnItr = rsn.iteratorToReferencees();
3048 while( hrnItr.hasNext() ) {
3049 RefEdge edge = hrnItr.next();
3050 HeapRegionNode hrn = edge.getDst();
3052 if( !visited.contains( hrn ) ) {
3058 // get a copy of the set to iterate over because
3059 // we're going to monkey with the graph when we
3060 // identify a garbage node
3061 Set<HeapRegionNode> hrnAllPrior = new HashSet<HeapRegionNode>();
3062 Iterator<HeapRegionNode> hrnItr = id2hrn.values().iterator();
3063 while( hrnItr.hasNext() ) {
3064 hrnAllPrior.add( hrnItr.next() );
3067 Iterator<HeapRegionNode> hrnAllItr = hrnAllPrior.iterator();
3068 while( hrnAllItr.hasNext() ) {
3069 HeapRegionNode hrn = hrnAllItr.next();
3071 if( !visited.contains( hrn ) ) {
3073 // heap region nodes are compared across ReachGraph
3074 // objects by their integer ID, so when discarding
3075 // garbage nodes we must also discard entries in
3076 // the ID -> heap region hashtable.
3077 id2hrn.remove( hrn.getID() );
3079 // RefEdge objects are two-way linked between
3080 // nodes, so when a node is identified as garbage,
3081 // actively clear references to and from it so
3082 // live nodes won't have dangling RefEdge's
3083 wipeOut( hrn, true );
3085 // if we just removed the last node from an allocation
3086 // site, it should be taken out of the ReachGraph's list
3087 AllocSite as = hrn.getAllocSite();
3088 if( !hasNodesOf( as ) ) {
3089 allocSites.remove( as );
3095 protected boolean hasNodesOf( AllocSite as ) {
3096 if( id2hrn.containsKey( as.getSummary() ) ) {
3100 for( int i = 0; i < allocationDepth; ++i ) {
3101 if( id2hrn.containsKey( as.getIthOldest( i ) ) ) {
3109 ////////////////////////////////////////////////////
3111 // This global sweep is an optional step to prune
3112 // reachability sets that are not internally
3113 // consistent with the global graph. It should be
3114 // invoked after strong updates or method calls.
3116 ////////////////////////////////////////////////////
3117 public void globalSweep() {
3119 // boldB is part of the phase 1 sweep
3120 // it has an in-context table and an out-of-context table
3121 Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBic =
3122 new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();
3124 Hashtable< Integer, Hashtable<RefEdge, ReachSet> > boldBooc =
3125 new Hashtable< Integer, Hashtable<RefEdge, ReachSet> >();
3127 // visit every heap region to initialize alphaNew and betaNew,
3128 // and make a map of every hrnID to the source nodes it should
3129 // propagate forward from. In-context flagged hrnID's propagate
3130 // from only the in-context node they name, but out-of-context
3131 // ID's may propagate from several out-of-context nodes
3132 Hashtable< Integer, Set<HeapRegionNode> > icID2srcs =
3133 new Hashtable< Integer, Set<HeapRegionNode> >();
3135 Hashtable< Integer, Set<HeapRegionNode> > oocID2srcs =
3136 new Hashtable< Integer, Set<HeapRegionNode> >();
3139 Iterator itrHrns = id2hrn.entrySet().iterator();
3140 while( itrHrns.hasNext() ) {
3141 Map.Entry me = (Map.Entry) itrHrns.next();
3142 Integer hrnID = (Integer) me.getKey();
3143 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3145 // assert that this node and incoming edges have clean alphaNew
3146 // and betaNew sets, respectively
3147 assert rsetEmpty.equals( hrn.getAlphaNew() );
3149 Iterator<RefEdge> itrRers = hrn.iteratorToReferencers();
3150 while( itrRers.hasNext() ) {
3151 RefEdge edge = itrRers.next();
3152 assert rsetEmpty.equals( edge.getBetaNew() );
3155 // make a mapping of IDs to heap regions they propagate from
3156 if( hrn.isFlagged() ) {
3157 assert !hrn.isOutOfContext();
3158 assert !icID2srcs.containsKey( hrn.getID() );
3160 // in-context flagged node IDs simply propagate from the
3162 Set<HeapRegionNode> srcs = new HashSet<HeapRegionNode>();
3164 icID2srcs.put( hrn.getID(), srcs );
3167 if( hrn.isOutOfContext() ) {
3168 assert !hrn.isFlagged();
3170 // the reachability states on an out-of-context
3171 // node are not really important (combinations of
3172 // IDs or arity)--what matters is that the states
3173 // specify which nodes this out-of-context node
3174 // stands in for. For example, if the state [17?, 19*]
3175 // appears on the ooc node, it may serve as a source
3176 // for node 17? and a source for node 19.
3177 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3178 while( stateItr.hasNext() ) {
3179 ReachState state = stateItr.next();
3181 Iterator<ReachTuple> rtItr = state.iterator();
3182 while( rtItr.hasNext() ) {
3183 ReachTuple rt = rtItr.next();
3184 assert rt.isOutOfContext();
3186 Set<HeapRegionNode> srcs = oocID2srcs.get( rt.getHrnID() );
3187 if( srcs == null ) {
3188 srcs = new HashSet<HeapRegionNode>();
3191 oocID2srcs.put( rt.getHrnID(), srcs );
3197 // calculate boldB for all hrnIDs identified by the above
3198 // node traversal, propagating from every source
3199 while( !icID2srcs.isEmpty() || !oocID2srcs.isEmpty() ) {
3202 Set<HeapRegionNode> srcs;
3205 if( !icID2srcs.isEmpty() ) {
3206 Map.Entry me = (Map.Entry) icID2srcs.entrySet().iterator().next();
3207 hrnID = (Integer) me.getKey();
3208 srcs = (Set<HeapRegionNode>) me.getValue();
3210 icID2srcs.remove( hrnID );
3213 assert !oocID2srcs.isEmpty();
3215 Map.Entry me = (Map.Entry) oocID2srcs.entrySet().iterator().next();
3216 hrnID = (Integer) me.getKey();
3217 srcs = (Set<HeapRegionNode>) me.getValue();
3219 oocID2srcs.remove( hrnID );
3223 Hashtable<RefEdge, ReachSet> boldB_f =
3224 new Hashtable<RefEdge, ReachSet>();
3226 Set<RefEdge> workSetEdges = new HashSet<RefEdge>();
3228 Iterator<HeapRegionNode> hrnItr = srcs.iterator();
3229 while( hrnItr.hasNext() ) {
3230 HeapRegionNode hrn = hrnItr.next();
3232 assert workSetEdges.isEmpty();
3234 // initial boldB_f constraints
3235 Iterator<RefEdge> itrRees = hrn.iteratorToReferencees();
3236 while( itrRees.hasNext() ) {
3237 RefEdge edge = itrRees.next();
3239 assert !boldB_f.containsKey( edge );
3240 boldB_f.put( edge, edge.getBeta() );
3242 assert !workSetEdges.contains( edge );
3243 workSetEdges.add( edge );
3246 // enforce the boldB_f constraint at edges until we reach a fixed point
3247 while( !workSetEdges.isEmpty() ) {
3248 RefEdge edge = workSetEdges.iterator().next();
3249 workSetEdges.remove( edge );
3251 Iterator<RefEdge> itrPrime = edge.getDst().iteratorToReferencees();
3252 while( itrPrime.hasNext() ) {
3253 RefEdge edgePrime = itrPrime.next();
3255 ReachSet prevResult = boldB_f.get( edgePrime );
3256 ReachSet intersection = Canonical.intersection( boldB_f.get( edge ),
3260 if( prevResult == null ||
3261 Canonical.unionORpreds( prevResult,
3262 intersection ).size()
3266 if( prevResult == null ) {
3267 boldB_f.put( edgePrime,
3268 Canonical.unionORpreds( edgePrime.getBeta(),
3273 boldB_f.put( edgePrime,
3274 Canonical.unionORpreds( prevResult,
3279 workSetEdges.add( edgePrime );
3286 boldBic.put( hrnID, boldB_f );
3288 boldBooc.put( hrnID, boldB_f );
3293 // use boldB to prune hrnIDs from alpha states that are impossible
3294 // and propagate the differences backwards across edges
3295 HashSet<RefEdge> edgesForPropagation = new HashSet<RefEdge>();
3297 Hashtable<RefEdge, ChangeSet> edgePlannedChanges =
3298 new Hashtable<RefEdge, ChangeSet>();
3301 itrHrns = id2hrn.entrySet().iterator();
3302 while( itrHrns.hasNext() ) {
3303 Map.Entry me = (Map.Entry) itrHrns.next();
3304 Integer hrnID = (Integer) me.getKey();
3305 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3307 // out-of-context nodes don't participate in the
3308 // global sweep, they serve as sources for the pass
3310 if( hrn.isOutOfContext() ) {
3314 // the inherent states of a region are the exception
3315 // to removal as the global sweep prunes
3316 ReachTuple rtException = ReachTuple.factory( hrnID,
3317 !hrn.isSingleObject(),
3318 ReachTuple.ARITY_ONE,
3319 false // out-of-context
3322 ChangeSet cts = ChangeSet.factory();
3324 // mark hrnIDs for removal
3325 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3326 while( stateItr.hasNext() ) {
3327 ReachState stateOld = stateItr.next();
3329 ReachState markedHrnIDs = ReachState.factory();
3331 Iterator<ReachTuple> rtItr = stateOld.iterator();
3332 while( rtItr.hasNext() ) {
3333 ReachTuple rtOld = rtItr.next();
3335 // never remove the inherent hrnID from a flagged region
3336 // because it is trivially satisfied
3337 if( hrn.isFlagged() ) {
3338 if( rtOld == rtException ) {
3343 // does boldB allow this hrnID?
3344 boolean foundState = false;
3345 Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3346 while( incidentEdgeItr.hasNext() ) {
3347 RefEdge incidentEdge = incidentEdgeItr.next();
3349 Hashtable<RefEdge, ReachSet> B;
3350 if( rtOld.isOutOfContext() ) {
3351 B = boldBooc.get( rtOld.getHrnID() );
3354 if( !id2hrn.containsKey( rtOld.getHrnID() ) ) {
3355 // let symbols not in the graph get pruned
3359 B = boldBic.get( rtOld.getHrnID() );
3363 ReachSet boldB_rtOld_incident = B.get( incidentEdge );
3364 if( boldB_rtOld_incident != null &&
3365 boldB_rtOld_incident.containsIgnorePreds( stateOld ) != null
3373 markedHrnIDs = Canonical.add( markedHrnIDs, rtOld );
3377 // if there is nothing marked, just move on
3378 if( markedHrnIDs.isEmpty() ) {
3379 hrn.setAlphaNew( Canonical.add( hrn.getAlphaNew(),
3386 // remove all marked hrnIDs and establish a change set that should
3387 // propagate backwards over edges from this node
3388 ReachState statePruned = ReachState.factory();
3389 rtItr = stateOld.iterator();
3390 while( rtItr.hasNext() ) {
3391 ReachTuple rtOld = rtItr.next();
3393 if( !markedHrnIDs.containsTuple( rtOld ) ) {
3394 statePruned = Canonical.add( statePruned, rtOld );
3397 assert !stateOld.equals( statePruned );
3399 hrn.setAlphaNew( Canonical.add( hrn.getAlphaNew(),
3403 ChangeTuple ct = ChangeTuple.factory( stateOld,
3406 cts = Canonical.add( cts, ct );
3409 // throw change tuple set on all incident edges
3410 if( !cts.isEmpty() ) {
3411 Iterator<RefEdge> incidentEdgeItr = hrn.iteratorToReferencers();
3412 while( incidentEdgeItr.hasNext() ) {
3413 RefEdge incidentEdge = incidentEdgeItr.next();
3415 edgesForPropagation.add( incidentEdge );
3417 if( edgePlannedChanges.get( incidentEdge ) == null ) {
3418 edgePlannedChanges.put( incidentEdge, cts );
3420 edgePlannedChanges.put(
3422 Canonical.union( edgePlannedChanges.get( incidentEdge ),
3431 HashSet<RefEdge> edgesUpdated = new HashSet<RefEdge>();
3433 propagateTokensOverEdges( edgesForPropagation,
3437 // at the end of the 1st phase reference edges have
3438 // beta, betaNew that correspond to beta and betaR
3440 // commit beta<-betaNew, so beta=betaR and betaNew
3441 // will represent the beta' calculation in 2nd phase
3443 // commit alpha<-alphaNew because it won't change
3444 HashSet<RefEdge> res = new HashSet<RefEdge>();
3446 Iterator<HeapRegionNode> nodeItr = id2hrn.values().iterator();
3447 while( nodeItr.hasNext() ) {
3448 HeapRegionNode hrn = nodeItr.next();
3450 // as mentioned above, out-of-context nodes only serve
3451 // as sources of reach states for the sweep, not part
3453 if( hrn.isOutOfContext() ) {
3454 assert hrn.getAlphaNew().equals( rsetEmpty );
3456 hrn.applyAlphaNew();
3459 Iterator<RefEdge> itrRes = hrn.iteratorToReferencers();
3460 while( itrRes.hasNext() ) {
3461 res.add( itrRes.next() );
3467 Iterator<RefEdge> edgeItr = res.iterator();
3468 while( edgeItr.hasNext() ) {
3469 RefEdge edge = edgeItr.next();
3470 HeapRegionNode hrn = edge.getDst();
3472 // commit results of last phase
3473 if( edgesUpdated.contains( edge ) ) {
3474 edge.applyBetaNew();
3477 // compute intial condition of 2nd phase
3478 edge.setBetaNew( Canonical.intersection( edge.getBeta(),
3484 // every edge in the graph is the initial workset
3485 Set<RefEdge> edgeWorkSet = (Set) res.clone();
3486 while( !edgeWorkSet.isEmpty() ) {
3487 RefEdge edgePrime = edgeWorkSet.iterator().next();
3488 edgeWorkSet.remove( edgePrime );
3490 RefSrcNode rsn = edgePrime.getSrc();
3491 if( !(rsn instanceof HeapRegionNode) ) {
3494 HeapRegionNode hrn = (HeapRegionNode) rsn;
3496 Iterator<RefEdge> itrEdge = hrn.iteratorToReferencers();
3497 while( itrEdge.hasNext() ) {
3498 RefEdge edge = itrEdge.next();
3500 ReachSet prevResult = edge.getBetaNew();
3501 assert prevResult != null;
3503 ReachSet intersection =
3504 Canonical.intersection( edge.getBeta(),
3505 edgePrime.getBetaNew()
3508 if( Canonical.unionORpreds( prevResult,
3515 Canonical.unionORpreds( prevResult,
3519 edgeWorkSet.add( edge );
3524 // commit beta' (beta<-betaNew)
3525 edgeItr = res.iterator();
3526 while( edgeItr.hasNext() ) {
3527 edgeItr.next().applyBetaNew();
3532 // a useful assertion for debugging:
3533 // every in-context tuple on any edge or
3534 // any node should name a node that is
3535 // part of the graph
3536 public boolean inContextTuplesInGraph() {
3538 Iterator hrnItr = id2hrn.entrySet().iterator();
3539 while( hrnItr.hasNext() ) {
3540 Map.Entry me = (Map.Entry) hrnItr.next();
3541 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3544 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3545 while( stateItr.hasNext() ) {
3546 ReachState state = stateItr.next();
3548 Iterator<ReachTuple> rtItr = state.iterator();
3549 while( rtItr.hasNext() ) {
3550 ReachTuple rt = rtItr.next();
3552 if( !rt.isOutOfContext() ) {
3553 if( !id2hrn.containsKey( rt.getHrnID() ) ) {
3554 System.out.println( rt.getHrnID()+" is missing" );
3562 Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3563 while( edgeItr.hasNext() ) {
3564 RefEdge edge = edgeItr.next();
3566 Iterator<ReachState> stateItr = edge.getBeta().iterator();
3567 while( stateItr.hasNext() ) {
3568 ReachState state = stateItr.next();
3570 Iterator<ReachTuple> rtItr = state.iterator();
3571 while( rtItr.hasNext() ) {
3572 ReachTuple rt = rtItr.next();
3574 if( !rt.isOutOfContext() ) {
3575 if( !id2hrn.containsKey( rt.getHrnID() ) ) {
3576 System.out.println( rt.getHrnID()+" is missing" );
3589 // another useful assertion for debugging
3590 public boolean noEmptyReachSetsInGraph() {
3592 Iterator hrnItr = id2hrn.entrySet().iterator();
3593 while( hrnItr.hasNext() ) {
3594 Map.Entry me = (Map.Entry) hrnItr.next();
3595 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3597 if( !hrn.isOutOfContext() &&
3599 hrn.getAlpha().isEmpty()
3601 System.out.println( "!!! "+hrn+" has an empty ReachSet !!!" );
3605 Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3606 while( edgeItr.hasNext() ) {
3607 RefEdge edge = edgeItr.next();
3609 if( edge.getBeta().isEmpty() ) {
3610 System.out.println( "!!! "+edge+" has an empty ReachSet !!!" );
3620 public boolean everyReachStateWTrue() {
3622 Iterator hrnItr = id2hrn.entrySet().iterator();
3623 while( hrnItr.hasNext() ) {
3624 Map.Entry me = (Map.Entry) hrnItr.next();
3625 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
3628 Iterator<ReachState> stateItr = hrn.getAlpha().iterator();
3629 while( stateItr.hasNext() ) {
3630 ReachState state = stateItr.next();
3632 if( !state.getPreds().equals( predsTrue ) ) {
3638 Iterator<RefEdge> edgeItr = hrn.iteratorToReferencers();
3639 while( edgeItr.hasNext() ) {
3640 RefEdge edge = edgeItr.next();
3642 Iterator<ReachState> stateItr = edge.getBeta().iterator();
3643 while( stateItr.hasNext() ) {
3644 ReachState state = stateItr.next();
3646 if( !state.getPreds().equals( predsTrue ) ) {
3659 ////////////////////////////////////////////////////
3660 // in merge() and equals() methods the suffix A
3661 // represents the passed in graph and the suffix
3662 // B refers to the graph in this object
3663 // Merging means to take the incoming graph A and
3664 // merge it into B, so after the operation graph B
3665 // is the final result.
3666 ////////////////////////////////////////////////////
3667 protected void merge( ReachGraph rg ) {
3674 mergeRefEdges ( rg );
3675 mergeAllocSites( rg );
3676 mergeAccessibleSet( rg );
3679 protected void mergeNodes( ReachGraph rg ) {
3681 // start with heap region nodes
3682 Set sA = rg.id2hrn.entrySet();
3683 Iterator iA = sA.iterator();
3684 while( iA.hasNext() ) {
3685 Map.Entry meA = (Map.Entry) iA.next();
3686 Integer idA = (Integer) meA.getKey();
3687 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3689 // if this graph doesn't have a node the
3690 // incoming graph has, allocate it
3691 if( !id2hrn.containsKey( idA ) ) {
3692 HeapRegionNode hrnB = hrnA.copy();
3693 id2hrn.put( idA, hrnB );
3696 // otherwise this is a node present in both graphs
3697 // so make the new reachability set a union of the
3698 // nodes' reachability sets
3699 HeapRegionNode hrnB = id2hrn.get( idA );
3700 hrnB.setAlpha( Canonical.unionORpreds( hrnB.getAlpha(),
3705 hrnB.setPreds( Canonical.join( hrnB.getPreds(),
3712 if( !hrnA.equals( hrnB ) ) {
3713 rg.writeGraph( "graphA" );
3714 this.writeGraph( "graphB" );
3715 throw new Error( "flagged not matching" );
3723 // now add any variable nodes that are in graph B but
3725 sA = rg.td2vn.entrySet();
3727 while( iA.hasNext() ) {
3728 Map.Entry meA = (Map.Entry) iA.next();
3729 TempDescriptor tdA = (TempDescriptor) meA.getKey();
3730 VariableNode lnA = (VariableNode) meA.getValue();
3732 // if the variable doesn't exist in B, allocate and add it
3733 VariableNode lnB = getVariableNodeFromTemp( tdA );
3737 protected void mergeRefEdges( ReachGraph rg ) {
3739 // between heap regions
3740 Set sA = rg.id2hrn.entrySet();
3741 Iterator iA = sA.iterator();
3742 while( iA.hasNext() ) {
3743 Map.Entry meA = (Map.Entry) iA.next();
3744 Integer idA = (Integer) meA.getKey();
3745 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3747 Iterator<RefEdge> heapRegionsItrA = hrnA.iteratorToReferencees();
3748 while( heapRegionsItrA.hasNext() ) {
3749 RefEdge edgeA = heapRegionsItrA.next();
3750 HeapRegionNode hrnChildA = edgeA.getDst();
3751 Integer idChildA = hrnChildA.getID();
3753 // at this point we know an edge in graph A exists
3754 // idA -> idChildA, does this exist in B?
3755 assert id2hrn.containsKey( idA );
3756 HeapRegionNode hrnB = id2hrn.get( idA );
3757 RefEdge edgeToMerge = null;
3759 Iterator<RefEdge> heapRegionsItrB = hrnB.iteratorToReferencees();
3760 while( heapRegionsItrB.hasNext() &&
3761 edgeToMerge == null ) {
3763 RefEdge edgeB = heapRegionsItrB.next();
3764 HeapRegionNode hrnChildB = edgeB.getDst();
3765 Integer idChildB = hrnChildB.getID();
3767 // don't use the RefEdge.equals() here because
3768 // we're talking about existence between graphs,
3769 // not intragraph equal
3770 if( idChildB.equals( idChildA ) &&
3771 edgeB.typeAndFieldEquals( edgeA ) ) {
3773 edgeToMerge = edgeB;
3777 // if the edge from A was not found in B,
3779 if( edgeToMerge == null ) {
3780 assert id2hrn.containsKey( idChildA );
3781 HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3782 edgeToMerge = edgeA.copy();
3783 edgeToMerge.setSrc( hrnB );
3784 edgeToMerge.setDst( hrnChildB );
3785 addRefEdge( hrnB, hrnChildB, edgeToMerge );
3787 // otherwise, the edge already existed in both graphs
3788 // so merge their reachability sets
3790 // just replace this beta set with the union
3791 assert edgeToMerge != null;
3792 edgeToMerge.setBeta(
3793 Canonical.unionORpreds( edgeToMerge.getBeta(),
3797 edgeToMerge.setPreds(
3798 Canonical.join( edgeToMerge.getPreds(),
3802 edgeToMerge.setTaints(
3803 Canonical.union( edgeToMerge.getTaints(),
3811 // and then again from variable nodes
3812 sA = rg.td2vn.entrySet();
3814 while( iA.hasNext() ) {
3815 Map.Entry meA = (Map.Entry) iA.next();
3816 TempDescriptor tdA = (TempDescriptor) meA.getKey();
3817 VariableNode vnA = (VariableNode) meA.getValue();
3819 Iterator<RefEdge> heapRegionsItrA = vnA.iteratorToReferencees();
3820 while( heapRegionsItrA.hasNext() ) {
3821 RefEdge edgeA = heapRegionsItrA.next();
3822 HeapRegionNode hrnChildA = edgeA.getDst();
3823 Integer idChildA = hrnChildA.getID();
3825 // at this point we know an edge in graph A exists
3826 // tdA -> idChildA, does this exist in B?
3827 assert td2vn.containsKey( tdA );
3828 VariableNode vnB = td2vn.get( tdA );
3829 RefEdge edgeToMerge = null;
3831 Iterator<RefEdge> heapRegionsItrB = vnB.iteratorToReferencees();
3832 while( heapRegionsItrB.hasNext() &&
3833 edgeToMerge == null ) {
3835 RefEdge edgeB = heapRegionsItrB.next();
3836 HeapRegionNode hrnChildB = edgeB.getDst();
3837 Integer idChildB = hrnChildB.getID();
3839 // don't use the RefEdge.equals() here because
3840 // we're talking about existence between graphs
3841 if( idChildB.equals( idChildA ) &&
3842 edgeB.typeAndFieldEquals( edgeA ) ) {
3844 edgeToMerge = edgeB;
3848 // if the edge from A was not found in B,
3850 if( edgeToMerge == null ) {
3851 assert id2hrn.containsKey( idChildA );
3852 HeapRegionNode hrnChildB = id2hrn.get( idChildA );
3853 edgeToMerge = edgeA.copy();
3854 edgeToMerge.setSrc( vnB );
3855 edgeToMerge.setDst( hrnChildB );
3856 addRefEdge( vnB, hrnChildB, edgeToMerge );
3858 // otherwise, the edge already existed in both graphs
3859 // so merge their reachability sets
3861 // just replace this beta set with the union
3862 edgeToMerge.setBeta( Canonical.unionORpreds( edgeToMerge.getBeta(),
3866 edgeToMerge.setPreds( Canonical.join( edgeToMerge.getPreds(),
3870 edgeToMerge.setTaints(
3871 Canonical.union( edgeToMerge.getTaints(),
3880 protected void mergeAllocSites( ReachGraph rg ) {
3881 allocSites.addAll( rg.allocSites );
3884 protected void mergeAccessibleSet( ReachGraph rg ){
3885 // inaccesible status is prior to accessible status
3887 Set<TempDescriptor> varsToMerge=rg.getAccessibleVar();
3888 Set<TempDescriptor> varsRemoved=new HashSet<TempDescriptor>();
3890 for (Iterator iterator = accessibleVars.iterator(); iterator.hasNext();) {
3891 TempDescriptor accessibleVar = (TempDescriptor) iterator.next();
3892 if(!varsToMerge.contains(accessibleVar)){
3893 varsRemoved.add(accessibleVar);
3897 accessibleVars.removeAll(varsRemoved);
3903 static boolean dbgEquals = false;
3906 // it is necessary in the equals() member functions
3907 // to "check both ways" when comparing the data
3908 // structures of two graphs. For instance, if all
3909 // edges between heap region nodes in graph A are
3910 // present and equal in graph B it is not sufficient
3911 // to say the graphs are equal. Consider that there
3912 // may be edges in graph B that are not in graph A.
3913 // the only way to know that all edges in both graphs
3914 // are equally present is to iterate over both data
3915 // structures and compare against the other graph.
3916 public boolean equals( ReachGraph rg ) {
3920 System.out.println( "rg is null" );
3925 if( !areHeapRegionNodesEqual( rg ) ) {
3927 System.out.println( "hrn not equal" );
3932 if( !areVariableNodesEqual( rg ) ) {
3934 System.out.println( "vars not equal" );
3939 if( !areRefEdgesEqual( rg ) ) {
3941 System.out.println( "edges not equal" );
3946 if( !accessibleVars.equals( rg.accessibleVars) ){
3950 // if everything is equal up to this point,
3951 // assert that allocSites is also equal--
3952 // this data is redundant but kept for efficiency
3953 assert allocSites.equals( rg.allocSites );
3959 protected boolean areHeapRegionNodesEqual( ReachGraph rg ) {
3961 if( !areallHRNinAalsoinBandequal( this, rg ) ) {
3965 if( !areallHRNinAalsoinBandequal( rg, this ) ) {
3972 static protected boolean areallHRNinAalsoinBandequal( ReachGraph rgA,
3974 Set sA = rgA.id2hrn.entrySet();
3975 Iterator iA = sA.iterator();
3976 while( iA.hasNext() ) {
3977 Map.Entry meA = (Map.Entry) iA.next();
3978 Integer idA = (Integer) meA.getKey();
3979 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
3981 if( !rgB.id2hrn.containsKey( idA ) ) {
3985 HeapRegionNode hrnB = rgB.id2hrn.get( idA );
3986 if( !hrnA.equalsIncludingAlphaAndPreds( hrnB ) ) {
3994 protected boolean areVariableNodesEqual( ReachGraph rg ) {
3996 if( !areallVNinAalsoinBandequal( this, rg ) ) {
4000 if( !areallVNinAalsoinBandequal( rg, this ) ) {
4007 static protected boolean areallVNinAalsoinBandequal( ReachGraph rgA,
4009 Set sA = rgA.td2vn.entrySet();
4010 Iterator iA = sA.iterator();
4011 while( iA.hasNext() ) {
4012 Map.Entry meA = (Map.Entry) iA.next();
4013 TempDescriptor tdA = (TempDescriptor) meA.getKey();
4015 if( !rgB.td2vn.containsKey( tdA ) ) {
4024 protected boolean areRefEdgesEqual( ReachGraph rg ) {
4025 if( !areallREinAandBequal( this, rg ) ) {
4029 if( !areallREinAandBequal( rg, this ) ) {
4036 static protected boolean areallREinAandBequal( ReachGraph rgA,
4039 // check all the heap region->heap region edges
4040 Set sA = rgA.id2hrn.entrySet();
4041 Iterator iA = sA.iterator();
4042 while( iA.hasNext() ) {
4043 Map.Entry meA = (Map.Entry) iA.next();
4044 Integer idA = (Integer) meA.getKey();
4045 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
4047 // we should have already checked that the same
4048 // heap regions exist in both graphs
4049 assert rgB.id2hrn.containsKey( idA );
4051 if( !areallREfromAequaltoB( rgA, hrnA, rgB ) ) {
4055 // then check every edge in B for presence in A, starting
4056 // from the same parent HeapRegionNode
4057 HeapRegionNode hrnB = rgB.id2hrn.get( idA );
4059 if( !areallREfromAequaltoB( rgB, hrnB, rgA ) ) {
4064 // then check all the variable->heap region edges
4065 sA = rgA.td2vn.entrySet();
4067 while( iA.hasNext() ) {
4068 Map.Entry meA = (Map.Entry) iA.next();
4069 TempDescriptor tdA = (TempDescriptor) meA.getKey();
4070 VariableNode vnA = (VariableNode) meA.getValue();
4072 // we should have already checked that the same
4073 // label nodes exist in both graphs
4074 assert rgB.td2vn.containsKey( tdA );
4076 if( !areallREfromAequaltoB( rgA, vnA, rgB ) ) {
4080 // then check every edge in B for presence in A, starting
4081 // from the same parent VariableNode
4082 VariableNode vnB = rgB.td2vn.get( tdA );
4084 if( !areallREfromAequaltoB( rgB, vnB, rgA ) ) {
4093 static protected boolean areallREfromAequaltoB( ReachGraph rgA,
4097 Iterator<RefEdge> itrA = rnA.iteratorToReferencees();
4098 while( itrA.hasNext() ) {
4099 RefEdge edgeA = itrA.next();
4100 HeapRegionNode hrnChildA = edgeA.getDst();
4101 Integer idChildA = hrnChildA.getID();
4103 assert rgB.id2hrn.containsKey( idChildA );
4105 // at this point we know an edge in graph A exists
4106 // rnA -> idChildA, does this exact edge exist in B?
4107 boolean edgeFound = false;
4109 RefSrcNode rnB = null;
4110 if( rnA instanceof HeapRegionNode ) {
4111 HeapRegionNode hrnA = (HeapRegionNode) rnA;
4112 rnB = rgB.id2hrn.get( hrnA.getID() );
4114 VariableNode vnA = (VariableNode) rnA;
4115 rnB = rgB.td2vn.get( vnA.getTempDescriptor() );
4118 Iterator<RefEdge> itrB = rnB.iteratorToReferencees();
4119 while( itrB.hasNext() ) {
4120 RefEdge edgeB = itrB.next();
4121 HeapRegionNode hrnChildB = edgeB.getDst();
4122 Integer idChildB = hrnChildB.getID();
4124 if( idChildA.equals( idChildB ) &&
4125 edgeA.typeAndFieldEquals( edgeB ) ) {
4127 // there is an edge in the right place with the right field,
4128 // but do they have the same attributes?
4129 if( edgeA.getBeta().equals( edgeB.getBeta() ) &&
4130 edgeA.equalsPreds( edgeB )
4146 // can be used to assert monotonicity
4147 static public boolean isNoSmallerThan( ReachGraph rgA,
4150 //System.out.println( "*** Asking if A is no smaller than B ***" );
4153 Iterator iA = rgA.id2hrn.entrySet().iterator();
4154 while( iA.hasNext() ) {
4155 Map.Entry meA = (Map.Entry) iA.next();
4156 Integer idA = (Integer) meA.getKey();
4157 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
4159 if( !rgB.id2hrn.containsKey( idA ) ) {
4160 System.out.println( " regions smaller" );
4164 //HeapRegionNode hrnB = rgB.id2hrn.get( idA );
4165 /* NOT EQUALS, NO SMALLER THAN!
4166 if( !hrnA.equalsIncludingAlphaAndPreds( hrnB ) ) {
4167 System.out.println( " regions smaller" );
4173 // this works just fine, no smaller than
4174 if( !areallVNinAalsoinBandequal( rgA, rgB ) ) {
4175 System.out.println( " vars smaller:" );
4176 System.out.println( " A:"+rgA.td2vn.keySet() );
4177 System.out.println( " B:"+rgB.td2vn.keySet() );
4182 iA = rgA.id2hrn.entrySet().iterator();
4183 while( iA.hasNext() ) {
4184 Map.Entry meA = (Map.Entry) iA.next();
4185 Integer idA = (Integer) meA.getKey();
4186 HeapRegionNode hrnA = (HeapRegionNode) meA.getValue();
4188 Iterator<RefEdge> reItr = hrnA.iteratorToReferencers();
4189 while( reItr.hasNext() ) {
4190 RefEdge edgeA = reItr.next();
4191 RefSrcNode rsnA = edgeA.getSrc();
4193 // we already checked that nodes were present
4194 HeapRegionNode hrnB = rgB.id2hrn.get( hrnA.getID() );
4195 assert hrnB != null;
4198 if( rsnA instanceof VariableNode ) {
4199 VariableNode vnA = (VariableNode) rsnA;
4200 rsnB = rgB.td2vn.get( vnA.getTempDescriptor() );
4203 HeapRegionNode hrnSrcA = (HeapRegionNode) rsnA;
4204 rsnB = rgB.id2hrn.get( hrnSrcA.getID() );
4206 assert rsnB != null;
4208 RefEdge edgeB = rsnB.getReferenceTo( hrnB,
4212 if( edgeB == null ) {
4213 System.out.println( " edges smaller:" );
4217 // REMEMBER, IS NO SMALLER THAN
4219 System.out.println( " edges smaller" );
4235 // this analysis no longer has the "match anything"
4236 // type which was represented by null
4237 protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
4238 TypeDescriptor td2 ) {
4242 if( td1.isNull() ) {
4245 if( td2.isNull() ) {
4248 return typeUtil.mostSpecific( td1, td2 );
4251 protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
4253 TypeDescriptor td3 ) {
4255 return mostSpecificType( td1,
4256 mostSpecificType( td2, td3 )
4260 protected TypeDescriptor mostSpecificType( TypeDescriptor td1,
4263 TypeDescriptor td4 ) {
4265 return mostSpecificType( mostSpecificType( td1, td2 ),
4266 mostSpecificType( td3, td4 )
4270 protected boolean isSuperiorType( TypeDescriptor possibleSuper,
4271 TypeDescriptor possibleChild ) {
4272 assert possibleSuper != null;
4273 assert possibleChild != null;
4275 if( possibleSuper.isNull() ||
4276 possibleChild.isNull() ) {
4280 return typeUtil.isSuperorType( possibleSuper, possibleChild );
4284 protected boolean hasMatchingField( HeapRegionNode src,
4287 TypeDescriptor tdSrc = src.getType();
4288 assert tdSrc != null;
4290 if( tdSrc.isArray() ) {
4291 TypeDescriptor td = edge.getType();
4294 TypeDescriptor tdSrcDeref = tdSrc.dereference();
4295 assert tdSrcDeref != null;
4297 if( !typeUtil.isSuperorType( tdSrcDeref, td ) ) {
4301 return edge.getField().equals( DisjointAnalysis.arrayElementFieldName );
4304 // if it's not a class, it doesn't have any fields to match
4305 if( !tdSrc.isClass() ) {
4309 ClassDescriptor cd = tdSrc.getClassDesc();
4310 while( cd != null ) {
4311 Iterator fieldItr = cd.getFields();
4313 while( fieldItr.hasNext() ) {
4314 FieldDescriptor fd = (FieldDescriptor) fieldItr.next();
4316 if( fd.getType().equals( edge.getType() ) &&
4317 fd.getSymbol().equals( edge.getField() ) ) {
4322 cd = cd.getSuperDesc();
4325 // otherwise it is a class with fields
4326 // but we didn't find a match
4330 protected boolean hasMatchingType( RefEdge edge,
4331 HeapRegionNode dst ) {
4333 // if the region has no type, matches everything
4334 TypeDescriptor tdDst = dst.getType();
4335 assert tdDst != null;
4337 // if the type is not a class or an array, don't
4338 // match because primitives are copied, no aliases
4339 ClassDescriptor cdDst = tdDst.getClassDesc();
4340 if( cdDst == null && !tdDst.isArray() ) {
4344 // if the edge type is null, it matches everything
4345 TypeDescriptor tdEdge = edge.getType();
4346 assert tdEdge != null;
4348 return typeUtil.isSuperorType( tdEdge, tdDst );
4353 // the default signature for quick-and-dirty debugging
4354 public void writeGraph( String graphName ) {
4355 writeGraph( graphName,
4356 true, // write labels
4357 true, // label select
4358 true, // prune garbage
4359 false, // hide reachability
4360 true, // hide subset reachability
4361 true, // hide predicates
4362 true, // hide edge taints
4363 null // in-context boundary
4367 public void writeGraph( String graphName,
4368 boolean writeLabels,
4369 boolean labelSelect,
4370 boolean pruneGarbage,
4371 boolean hideReachability,
4372 boolean hideSubsetReachability,
4373 boolean hidePredicates,
4374 boolean hideEdgeTaints
4376 writeGraph( graphName,
4381 hideSubsetReachability,
4387 public void writeGraph( String graphName,
4388 boolean writeLabels,
4389 boolean labelSelect,
4390 boolean pruneGarbage,
4391 boolean hideReachability,
4392 boolean hideSubsetReachability,
4393 boolean hidePredicates,
4394 boolean hideEdgeTaints,
4395 Set<Integer> callerNodeIDsCopiedToCallee
4399 // remove all non-word characters from the graph name so
4400 // the filename and identifier in dot don't cause errors
4401 graphName = graphName.replaceAll( "[\\W]", "" );
4404 new BufferedWriter( new FileWriter( graphName+".dot" ) );
4406 bw.write( "digraph "+graphName+" {\n" );
4409 // this is an optional step to form the callee-reachable
4410 // "cut-out" into a DOT cluster for visualization
4411 if( callerNodeIDsCopiedToCallee != null ) {
4413 bw.write( " subgraph cluster0 {\n" );
4414 bw.write( " color=blue;\n" );
4416 Iterator i = id2hrn.entrySet().iterator();
4417 while( i.hasNext() ) {
4418 Map.Entry me = (Map.Entry) i.next();
4419 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4421 if( callerNodeIDsCopiedToCallee.contains( hrn.getID() ) ) {
4424 hrn.toStringDOT( hideReachability,
4425 hideSubsetReachability,
4435 Set<HeapRegionNode> visited = new HashSet<HeapRegionNode>();
4437 // then visit every heap region node
4438 Iterator i = id2hrn.entrySet().iterator();
4439 while( i.hasNext() ) {
4440 Map.Entry me = (Map.Entry) i.next();
4441 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4443 // only visit nodes worth writing out--for instance
4444 // not every node at an allocation is referenced
4445 // (think of it as garbage-collected), etc.
4446 if( !pruneGarbage ||
4447 hrn.isOutOfContext() ||
4448 (hrn.isFlagged() && hrn.getID() > 0 && !hrn.isWiped()) // a non-shadow flagged node
4451 if( !visited.contains( hrn ) ) {
4452 traverseHeapRegionNodes( hrn,
4457 hideSubsetReachability,
4460 callerNodeIDsCopiedToCallee );
4465 bw.write( " graphTitle[label=\""+graphName+"\",shape=box];\n" );
4468 // then visit every label node, useful for debugging
4470 i = td2vn.entrySet().iterator();
4471 while( i.hasNext() ) {
4472 Map.Entry me = (Map.Entry) i.next();
4473 VariableNode vn = (VariableNode) me.getValue();
4476 String labelStr = vn.getTempDescriptorString();
4477 if( labelStr.startsWith( "___temp" ) ||
4478 labelStr.startsWith( "___dst" ) ||
4479 labelStr.startsWith( "___srctmp" ) ||
4480 labelStr.startsWith( "___neverused" )
4486 Iterator<RefEdge> heapRegionsItr = vn.iteratorToReferencees();
4487 while( heapRegionsItr.hasNext() ) {
4488 RefEdge edge = heapRegionsItr.next();
4489 HeapRegionNode hrn = edge.getDst();
4491 if( !visited.contains( hrn ) ) {
4492 traverseHeapRegionNodes( hrn,
4497 hideSubsetReachability,
4500 callerNodeIDsCopiedToCallee );
4503 bw.write( " "+vn.toString()+
4504 " -> "+hrn.toString()+
4505 edge.toStringDOT( hideReachability,
4506 hideSubsetReachability,
4518 } catch( IOException e ) {
4519 throw new Error( "Error writing out DOT graph "+graphName );
4524 traverseHeapRegionNodes( HeapRegionNode hrn,
4527 Set<HeapRegionNode> visited,
4528 boolean hideReachability,
4529 boolean hideSubsetReachability,
4530 boolean hidePredicates,
4531 boolean hideEdgeTaints,
4532 Set<Integer> callerNodeIDsCopiedToCallee
4533 ) throws java.io.IOException {
4535 if( visited.contains( hrn ) ) {
4540 // if we're drawing the callee-view subgraph, only
4541 // write out the node info if it hasn't already been
4543 if( callerNodeIDsCopiedToCallee == null ||
4544 !callerNodeIDsCopiedToCallee.contains( hrn.getID() )
4548 hrn.toStringDOT( hideReachability,
4549 hideSubsetReachability,
4554 Iterator<RefEdge> childRegionsItr = hrn.iteratorToReferencees();
4555 while( childRegionsItr.hasNext() ) {
4556 RefEdge edge = childRegionsItr.next();
4557 HeapRegionNode hrnChild = edge.getDst();
4559 if( callerNodeIDsCopiedToCallee != null &&
4560 (edge.getSrc() instanceof HeapRegionNode) ) {
4561 HeapRegionNode hrnSrc = (HeapRegionNode) edge.getSrc();
4562 if( callerNodeIDsCopiedToCallee.contains( hrnSrc.getID() ) &&
4563 callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
4565 bw.write( " "+hrn.toString()+
4566 " -> "+hrnChild.toString()+
4567 edge.toStringDOT( hideReachability,
4568 hideSubsetReachability,
4573 } else if( !callerNodeIDsCopiedToCallee.contains( hrnSrc.getID() ) &&
4574 callerNodeIDsCopiedToCallee.contains( edge.getDst().getID() )
4576 bw.write( " "+hrn.toString()+
4577 " -> "+hrnChild.toString()+
4578 edge.toStringDOT( hideReachability,
4579 hideSubsetReachability,
4582 ",color=blue,style=dashed" )+
4585 bw.write( " "+hrn.toString()+
4586 " -> "+hrnChild.toString()+
4587 edge.toStringDOT( hideReachability,
4588 hideSubsetReachability,
4595 bw.write( " "+hrn.toString()+
4596 " -> "+hrnChild.toString()+
4597 edge.toStringDOT( hideReachability,
4598 hideSubsetReachability,
4605 traverseHeapRegionNodes( hrnChild,
4610 hideSubsetReachability,
4613 callerNodeIDsCopiedToCallee );
4621 public Set<HeapRegionNode> findCommonReachableNodes( ReachSet proofOfSharing ) {
4623 Set<HeapRegionNode> exhibitProofState =
4624 new HashSet<HeapRegionNode>();
4626 Iterator hrnItr = id2hrn.entrySet().iterator();
4627 while( hrnItr.hasNext() ) {
4628 Map.Entry me = (Map.Entry) hrnItr.next();
4629 HeapRegionNode hrn = (HeapRegionNode) me.getValue();
4631 ReachSet intersection =
4632 Canonical.intersection( proofOfSharing,
4635 if( !intersection.isEmpty() ) {
4636 assert !hrn.isOutOfContext();
4637 exhibitProofState.add( hrn );
4641 return exhibitProofState;
4645 public Set<HeapRegionNode> mayReachSharedObjects(HeapRegionNode hrn1,
4646 HeapRegionNode hrn2) {
4647 assert hrn1 != null;
4648 assert hrn2 != null;
4650 assert !hrn1.isOutOfContext();
4651 assert !hrn2.isOutOfContext();
4653 assert belongsToThis( hrn1 );
4654 assert belongsToThis( hrn2 );
4656 assert !hrn1.getID().equals( hrn2.getID() );
4659 // then get the various tokens for these heap regions
4661 ReachTuple.factory( hrn1.getID(),
4662 !hrn1.isSingleObject(), // multi?
4663 ReachTuple.ARITY_ONE,
4666 ReachTuple h1star = null;
4667 if( !hrn1.isSingleObject() ) {
4669 ReachTuple.factory( hrn1.getID(),
4670 !hrn1.isSingleObject(),
4671 ReachTuple.ARITY_ZEROORMORE,
4676 ReachTuple.factory( hrn2.getID(),
4677 !hrn2.isSingleObject(),
4678 ReachTuple.ARITY_ONE,
4681 ReachTuple h2star = null;
4682 if( !hrn2.isSingleObject() ) {
4684 ReachTuple.factory( hrn2.getID(),
4685 !hrn2.isSingleObject(),
4686 ReachTuple.ARITY_ZEROORMORE,
4690 // then get the merged beta of all out-going edges from these heap
4693 ReachSet beta1 = ReachSet.factory();
4694 Iterator<RefEdge> itrEdge = hrn1.iteratorToReferencees();
4695 while (itrEdge.hasNext()) {
4696 RefEdge edge = itrEdge.next();
4697 beta1 = Canonical.unionORpreds(beta1, edge.getBeta());
4700 ReachSet beta2 = ReachSet.factory();
4701 itrEdge = hrn2.iteratorToReferencees();
4702 while (itrEdge.hasNext()) {
4703 RefEdge edge = itrEdge.next();
4704 beta2 = Canonical.unionORpreds(beta2, edge.getBeta());
4707 ReachSet proofOfSharing = ReachSet.factory();
4710 Canonical.unionORpreds( proofOfSharing,
4711 beta1.getStatesWithBoth( h1, h2 )
4714 Canonical.unionORpreds( proofOfSharing,
4715 beta2.getStatesWithBoth( h1, h2 )
4718 if( !hrn1.isSingleObject() ) {
4720 Canonical.unionORpreds( proofOfSharing,
4721 beta1.getStatesWithBoth( h1star, h2 )
4724 Canonical.unionORpreds( proofOfSharing,
4725 beta2.getStatesWithBoth( h1star, h2 )
4729 if( !hrn2.isSingleObject() ) {
4731 Canonical.unionORpreds( proofOfSharing,
4732 beta1.getStatesWithBoth( h1, h2star )
4735 Canonical.unionORpreds( proofOfSharing,
4736 beta2.getStatesWithBoth( h1, h2star )
4740 if( !hrn1.isSingleObject() &&
4741 !hrn2.isSingleObject()
4744 Canonical.unionORpreds( proofOfSharing,
4745 beta1.getStatesWithBoth( h1star, h2star )
4748 Canonical.unionORpreds( proofOfSharing,
4749 beta2.getStatesWithBoth( h1star, h2star )
4753 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4754 if( !proofOfSharing.isEmpty() ) {
4755 common = findCommonReachableNodes( proofOfSharing );
4756 if( !DISABLE_STRONG_UPDATES &&
4757 !DISABLE_GLOBAL_SWEEP
4759 assert !common.isEmpty();
4766 // this version of the above method checks whether there is sharing
4767 // among the objects in a summary node
4768 public Set<HeapRegionNode> mayReachSharedObjects(HeapRegionNode hrn) {
4770 assert hrn.isNewSummary();
4771 assert !hrn.isOutOfContext();
4772 assert belongsToThis( hrn );
4775 ReachTuple.factory( hrn.getID(),
4777 ReachTuple.ARITY_ZEROORMORE,
4780 // then get the merged beta of all out-going edges from
4783 ReachSet beta = ReachSet.factory();
4784 Iterator<RefEdge> itrEdge = hrn.iteratorToReferencees();
4785 while (itrEdge.hasNext()) {
4786 RefEdge edge = itrEdge.next();
4787 beta = Canonical.unionORpreds(beta, edge.getBeta());
4790 ReachSet proofOfSharing = ReachSet.factory();
4793 Canonical.unionORpreds( proofOfSharing,
4794 beta.getStatesWithBoth( hstar, hstar )
4797 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4798 if( !proofOfSharing.isEmpty() ) {
4799 common = findCommonReachableNodes( proofOfSharing );
4800 if( !DISABLE_STRONG_UPDATES &&
4801 !DISABLE_GLOBAL_SWEEP
4803 assert !common.isEmpty();
4811 public Set<HeapRegionNode> mayReachSharedObjects(FlatMethod fm,
4812 Integer paramIndex1,
4813 Integer paramIndex2) {
4815 // get parameter's heap regions
4816 TempDescriptor paramTemp1 = fm.getParameter(paramIndex1.intValue());
4817 assert this.hasVariable( paramTemp1 );
4818 VariableNode paramVar1 = getVariableNodeFromTemp(paramTemp1);
4821 if( !(paramVar1.getNumReferencees() == 1) ) {
4822 System.out.println( "\n fm="+fm+"\n param="+paramTemp1 );
4823 writeGraph( "whatup" );
4827 assert paramVar1.getNumReferencees() == 1;
4828 RefEdge paramEdge1 = paramVar1.iteratorToReferencees().next();
4829 HeapRegionNode hrnParam1 = paramEdge1.getDst();
4831 TempDescriptor paramTemp2 = fm.getParameter(paramIndex2.intValue());
4832 assert this.hasVariable( paramTemp2 );
4833 VariableNode paramVar2 = getVariableNodeFromTemp(paramTemp2);
4835 if( !(paramVar2.getNumReferencees() == 1) ) {
4836 System.out.println( "\n fm="+fm+"\n param="+paramTemp2 );
4837 writeGraph( "whatup" );
4840 assert paramVar2.getNumReferencees() == 1;
4841 RefEdge paramEdge2 = paramVar2.iteratorToReferencees().next();
4842 HeapRegionNode hrnParam2 = paramEdge2.getDst();
4844 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4845 common.addAll(mayReachSharedObjects(hrnParam1, hrnParam2));
4850 public Set<HeapRegionNode> mayReachSharedObjects(FlatMethod fm,
4854 // get parameter's heap regions
4855 TempDescriptor paramTemp = fm.getParameter(paramIndex.intValue());
4856 assert this.hasVariable( paramTemp );
4857 VariableNode paramVar = getVariableNodeFromTemp(paramTemp);
4858 assert paramVar.getNumReferencees() == 1;
4859 RefEdge paramEdge = paramVar.iteratorToReferencees().next();
4860 HeapRegionNode hrnParam = paramEdge.getDst();
4863 HeapRegionNode hrnSummary=null;
4864 if(id2hrn.containsKey(as.getSummary())){
4865 // if summary node doesn't exist, ignore this case
4866 hrnSummary = id2hrn.get(as.getSummary());
4867 assert hrnSummary != null;
4870 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4871 if(hrnSummary!=null){
4872 common.addAll( mayReachSharedObjects(hrnParam, hrnSummary) );
4875 // check for other nodes
4876 for (int i = 0; i < as.getAllocationDepth(); ++i) {
4878 assert id2hrn.containsKey(as.getIthOldest(i));
4879 HeapRegionNode hrnIthOldest = id2hrn.get(as.getIthOldest(i));
4880 assert hrnIthOldest != null;
4882 common.addAll(mayReachSharedObjects(hrnParam, hrnIthOldest));
4889 public Set<HeapRegionNode> mayReachSharedObjects(AllocSite as1,
4892 // get summary node 1's alpha
4893 Integer idSum1 = as1.getSummary();
4894 HeapRegionNode hrnSum1=null;
4895 if(id2hrn.containsKey(idSum1)){
4896 hrnSum1 = id2hrn.get(idSum1);
4899 // get summary node 2's alpha
4900 Integer idSum2 = as2.getSummary();
4901 HeapRegionNode hrnSum2=null;
4902 if(id2hrn.containsKey(idSum2)){
4903 hrnSum2 = id2hrn.get(idSum2);
4906 Set<HeapRegionNode> common = new HashSet<HeapRegionNode>();
4907 if(hrnSum1!=null && hrnSum2!=null && hrnSum1!=hrnSum2){
4908 common.addAll(mayReachSharedObjects(hrnSum1, hrnSum2));
4912 // ask if objects from this summary share among each other
4913 common.addAll(mayReachSharedObjects(hrnSum1));
4916 // check sum2 against alloc1 nodes
4918 for (int i = 0; i < as1.getAllocationDepth(); ++i) {
4919 Integer idI1 = as1.getIthOldest(i);
4920 assert id2hrn.containsKey(idI1);
4921 HeapRegionNode hrnI1 = id2hrn.get(idI1);
4922 assert hrnI1 != null;
4923 common.addAll(mayReachSharedObjects(hrnI1, hrnSum2));
4926 // also ask if objects from this summary share among each other
4927 common.addAll(mayReachSharedObjects(hrnSum2));
4930 // check sum1 against alloc2 nodes
4931 for (int i = 0; i < as2.getAllocationDepth(); ++i) {
4932 Integer idI2 = as2.getIthOldest(i);
4933 assert id2hrn.containsKey(idI2);
4934 HeapRegionNode hrnI2 = id2hrn.get(idI2);
4935 assert hrnI2 != null;
4938 common.addAll(mayReachSharedObjects(hrnSum1, hrnI2));
4941 // while we're at it, do an inner loop for alloc2 vs alloc1 nodes
4942 for (int j = 0; j < as1.getAllocationDepth(); ++j) {
4943 Integer idI1 = as1.getIthOldest(j);
4945 // if these are the same site, don't look for the same token, no
4947 // different tokens of the same site could alias together though
4948 if (idI1.equals(idI2)) {
4952 HeapRegionNode hrnI1 = id2hrn.get(idI1);
4954 common.addAll(mayReachSharedObjects(hrnI1, hrnI2));
4961 public void addAccessibleVar(TempDescriptor td){
4962 accessibleVars.add(td);
4965 public Set<TempDescriptor> getAccessibleVar(){
4966 return accessibleVars;
4969 public boolean isAccessible(TempDescriptor td) {
4970 return accessibleVars.contains(td);
4973 public void clearAccessibleVarSet(){
4974 accessibleVars.clear();