1 package Analysis.OwnershipAnalysis;
3 import Analysis.CallGraph.*;
6 import IR.Tree.Modifiers;
11 public class OwnershipAnalysis {
14 ///////////////////////////////////////////
16 // Public interface to discover possible
17 // aliases in the program under analysis
19 ///////////////////////////////////////////
21 public HashSet<AllocationSite>
22 getFlaggedAllocationSitesReachableFromTask(TaskDescriptor td) {
23 return getFlaggedAllocationSitesReachableFromTaskPRIVATE(td);
26 public AllocationSite getAllocationSiteFromFlatNew(FlatNew fn) {
27 return getAllocationSiteFromFlatNewPRIVATE(fn);
31 public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
35 OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
37 return og.hasPotentialAlias(paramIndex1, paramIndex2);
40 public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
42 AllocationSite alloc) {
44 OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
46 return og.hasPotentialAlias(paramIndex, alloc);
49 public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
53 OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
55 return og.hasPotentialAlias(paramIndex, alloc);
58 public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
59 AllocationSite alloc1,
60 AllocationSite alloc2) {
62 OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
64 return og.hasPotentialAlias(alloc1, alloc2);
68 protected OwnershipGraph getGraphOfAllContextsFromDescriptor(Descriptor d) {
71 OwnershipGraph og = new OwnershipGraph( allocationDepth, typeUtil );
73 assert mapDescriptorToAllMethodContexts.containsKey( d );
74 HashSet<MethodContext> contexts = mapDescriptorToAllMethodContexts.get( d );
75 Iterator<MethodContext> mcItr = contexts.iterator();
76 while( mcItr.hasNext() ) {
77 MethodContext mc = mcItr.next();
79 OwnershipGraph ogContext = mapMethodContextToCompleteOwnershipGraph.get(mc);
80 assert ogContext != null;
82 og.merge( ogContext );
89 public String prettyPrintNodeSet( Set<HeapRegionNode> s ) {
92 Iterator<HeapRegionNode> i = s.iterator();
93 while( i.hasNext() ) {
94 HeapRegionNode n = i.next();
96 AllocationSite as = n.getAllocationSite();
98 out += " "+n.toString()+",\n";
100 out += " "+n.toString()+": "+as.toStringVerbose()+",\n";
109 // use the methods given above to check every possible alias
110 // between task parameters and flagged allocation sites reachable
112 public void writeAllAliases(String outputFile, String timeReport) throws java.io.IOException {
114 BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
116 bw.write("Conducting ownership analysis with allocation depth = "+allocationDepth+"\n");
117 bw.write(timeReport+"\n");
119 // look through every task for potential aliases
120 Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
121 while( taskItr.hasNext() ) {
122 TaskDescriptor td = (TaskDescriptor) taskItr.next();
124 bw.write("\n---------"+td+"--------\n");
126 HashSet<AllocationSite> allocSites = getFlaggedAllocationSitesReachableFromTask(td);
128 Set<HeapRegionNode> common;
130 // for each task parameter, check for aliases with
131 // other task parameters and every allocation site
132 // reachable from this task
133 boolean foundSomeAlias = false;
135 FlatMethod fm = state.getMethodFlat(td);
136 for( int i = 0; i < fm.numParameters(); ++i ) {
138 // for the ith parameter check for aliases to all
139 // higher numbered parameters
140 for( int j = i + 1; j < fm.numParameters(); ++j ) {
141 common = createsPotentialAliases(td, i, j);
142 if( !common.isEmpty() ) {
143 foundSomeAlias = true;
144 bw.write("Potential alias between parameters "+i+" and "+j+".\n");
145 bw.write(prettyPrintNodeSet( common )+"\n" );
149 // for the ith parameter, check for aliases against
150 // the set of allocation sites reachable from this
152 Iterator allocItr = allocSites.iterator();
153 while( allocItr.hasNext() ) {
154 AllocationSite as = (AllocationSite) allocItr.next();
155 common = createsPotentialAliases(td, i, as);
156 if( !common.isEmpty() ) {
157 foundSomeAlias = true;
158 bw.write("Potential alias between parameter "+i+" and "+as.getFlatNew()+".\n");
159 bw.write(prettyPrintNodeSet( common )+"\n" );
164 // for each allocation site check for aliases with
165 // other allocation sites in the context of execution
167 HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
168 Iterator allocItr1 = allocSites.iterator();
169 while( allocItr1.hasNext() ) {
170 AllocationSite as1 = (AllocationSite) allocItr1.next();
172 Iterator allocItr2 = allocSites.iterator();
173 while( allocItr2.hasNext() ) {
174 AllocationSite as2 = (AllocationSite) allocItr2.next();
176 if( !outerChecked.contains(as2) ) {
177 common = createsPotentialAliases(td, as1, as2);
179 if( !common.isEmpty() ) {
180 foundSomeAlias = true;
181 bw.write("Potential alias between "+as1.getFlatNew()+" and "+as2.getFlatNew()+".\n");
182 bw.write(prettyPrintNodeSet( common )+"\n" );
187 outerChecked.add(as1);
190 if( !foundSomeAlias ) {
191 bw.write("No aliases between flagged objects in Task "+td+".\n");
195 bw.write( "\n"+computeAliasContextHistogram() );
200 // this version of writeAllAliases is for Java programs that have no tasks
201 public void writeAllAliasesJava(String outputFile, String timeReport) throws java.io.IOException {
204 BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
206 bw.write("Conducting ownership analysis with allocation depth = "+allocationDepth+"\n");
207 bw.write(timeReport+"\n\n");
209 boolean foundSomeAlias = false;
211 Descriptor d = typeUtil.getMain();
212 HashSet<AllocationSite> allocSites = getFlaggedAllocationSites(d);
214 // for each allocation site check for aliases with
215 // other allocation sites in the context of execution
217 HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
218 Iterator allocItr1 = allocSites.iterator();
219 while( allocItr1.hasNext() ) {
220 AllocationSite as1 = (AllocationSite) allocItr1.next();
222 Iterator allocItr2 = allocSites.iterator();
223 while( allocItr2.hasNext() ) {
224 AllocationSite as2 = (AllocationSite) allocItr2.next();
226 if( !outerChecked.contains(as2) ) {
227 Set<HeapRegionNode> common = createsPotentialAliases(d, as1, as2);
229 if( !common.isEmpty() ) {
230 foundSomeAlias = true;
231 bw.write("Potential alias between "+as1.getDisjointId()+" and "+as2.getDisjointId()+".\n");
232 bw.write( prettyPrintNodeSet( common )+"\n" );
237 outerChecked.add(as1);
240 if( !foundSomeAlias ) {
241 bw.write("No aliases between flagged objects found.\n");
244 bw.write( "\n"+computeAliasContextHistogram() );
247 ///////////////////////////////////////////
249 // end public interface
251 ///////////////////////////////////////////
260 // data from the compiler
262 private TypeUtil typeUtil;
263 private CallGraph callGraph;
264 private int allocationDepth;
266 // used to identify HeapRegionNode objects
267 // A unique ID equates an object in one
268 // ownership graph with an object in another
269 // graph that logically represents the same
271 // start at 10 and increment to leave some
272 // reserved IDs for special purposes
273 static private int uniqueIDcount = 10;
276 // Use these data structures to track progress of
277 // processing all methods in the program, and by methods
278 // TaskDescriptor and MethodDescriptor are combined
279 // together, with a common parent class Descriptor
280 private Hashtable<MethodContext, OwnershipGraph> mapMethodContextToInitialParamAllocGraph;
281 private Hashtable<MethodContext, OwnershipGraph> mapMethodContextToCompleteOwnershipGraph;
282 private Hashtable<FlatNew, AllocationSite> mapFlatNewToAllocationSite;
283 private Hashtable<Descriptor, HashSet<AllocationSite> > mapDescriptorToAllocationSiteSet;
284 private Hashtable<MethodContext, Integer> mapMethodContextToNumUpdates;
285 private Hashtable<Descriptor, HashSet<MethodContext> > mapDescriptorToAllMethodContexts;
286 private Hashtable<MethodContext, HashSet<MethodContext> > mapMethodContextToDependentContexts;
288 // Use these data structures to track progress of one pass of
289 // processing the FlatNodes of a particular method
290 private HashSet <FlatNode> flatNodesToVisit;
291 private Hashtable<FlatNode, OwnershipGraph> mapFlatNodeToOwnershipGraph;
292 private HashSet <FlatReturnNode> returnNodesToCombineForCompleteOwnershipGraph;
294 // descriptorsToAnalyze identifies the set of tasks and methods
295 // that are reachable from the program tasks, this set is initialized
296 // and then remains static
297 private HashSet<Descriptor> descriptorsToAnalyze;
299 // descriptorsToVisit is initialized to descriptorsToAnalyze and is
300 // reduced by visiting a descriptor during analysis. When dependents
301 // must be scheduled, only those contained in descriptorsToAnalyze
302 // should be re-added to this queue
303 private PriorityQueue<MethodContextQWrapper> methodContextsToVisitQ;
304 private Set <MethodContext> methodContextsToVisitSet;
305 private Hashtable<Descriptor, Integer> mapDescriptorToPriority;
308 // special field descriptors for array elements
309 public static final String arrayElementFieldName = "___element_";
310 private static Hashtable<TypeDescriptor, FieldDescriptor> mapTypeToArrayField =
311 new Hashtable<TypeDescriptor, FieldDescriptor>();
314 // a special temp descriptor for setting more than one parameter label
315 // to the all-aliased-parameters heap region node
316 protected static TempDescriptor tdAliasedParams = new TempDescriptor("_AllAliasedParams___");
319 // for controlling DOT file output
320 private boolean writeDOTs;
321 private boolean writeAllDOTs;
325 // this analysis generates an ownership graph for every task
327 public OwnershipAnalysis(State state,
332 boolean writeAllDOTs,
333 String aliasFile) throws java.io.IOException {
335 double timeStartAnalysis = (double) System.nanoTime();
339 this.callGraph = callGraph;
340 this.allocationDepth = allocationDepth;
341 this.writeDOTs = writeDOTs;
342 this.writeAllDOTs = writeAllDOTs;
344 descriptorsToAnalyze = new HashSet<Descriptor>();
346 mapMethodContextToInitialParamAllocGraph =
347 new Hashtable<MethodContext, OwnershipGraph>();
349 mapMethodContextToCompleteOwnershipGraph =
350 new Hashtable<MethodContext, OwnershipGraph>();
352 mapFlatNewToAllocationSite =
353 new Hashtable<FlatNew, AllocationSite>();
355 mapDescriptorToAllocationSiteSet =
356 new Hashtable<Descriptor, HashSet<AllocationSite> >();
358 mapDescriptorToAllMethodContexts =
359 new Hashtable<Descriptor, HashSet<MethodContext> >();
361 mapMethodContextToDependentContexts =
362 new Hashtable<MethodContext, HashSet<MethodContext> >();
364 mapDescriptorToPriority =
365 new Hashtable<Descriptor, Integer>();
369 mapMethodContextToNumUpdates = new Hashtable<MethodContext, Integer>();
374 // initialize methods to visit as the set of all tasks in the
375 // program and then any method that could be called starting
377 Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
378 while( taskItr.hasNext() ) {
379 Descriptor d = (Descriptor) taskItr.next();
380 scheduleAllCallees(d);
384 // we are not in task mode, just normal Java, so start with
386 Descriptor d = typeUtil.getMain();
387 scheduleAllCallees(d);
391 // before beginning analysis, initialize every scheduled method
392 // with an ownership graph that has populated parameter index tables
393 // by analyzing the first node which is always a FlatMethod node
394 Iterator<Descriptor> dItr = descriptorsToAnalyze.iterator();
395 while( dItr.hasNext() ) {
396 Descriptor d = dItr.next();
397 OwnershipGraph og = new OwnershipGraph(allocationDepth, typeUtil);
400 if( d instanceof MethodDescriptor ) {
401 fm = state.getMethodFlat( (MethodDescriptor) d);
403 assert d instanceof TaskDescriptor;
404 fm = state.getMethodFlat( (TaskDescriptor) d);
407 MethodContext mc = new MethodContext( d );
408 assert !mapDescriptorToAllMethodContexts.containsKey( d );
409 HashSet<MethodContext> s = new HashSet<MethodContext>();
411 mapDescriptorToAllMethodContexts.put( d, s );
413 og = analyzeFlatNode(mc, fm, null, og);
414 setGraphForMethodContext(mc, og);
417 // as mentioned above, analyze methods one-by-one, possibly revisiting
418 // a method if the methods that it calls are updated
421 double timeEndAnalysis = (double) System.nanoTime();
422 double dt = (timeEndAnalysis - timeStartAnalysis)/(Math.pow( 10.0, 9.0 ) );
423 String treport = String.format( "The analysis took %.3f sec.", dt );
424 System.out.println( treport );
426 if( writeDOTs && !writeAllDOTs ) {
427 writeFinalContextGraphs();
430 if( aliasFile != null ) {
432 writeAllAliases(aliasFile, treport);
434 writeAllAliasesJava(aliasFile, treport);
439 // called from the constructor to help initialize the set
440 // of methods that needs to be analyzed by ownership analysis
441 private void scheduleAllCallees(Descriptor d) {
442 if( descriptorsToAnalyze.contains(d) ) {
445 descriptorsToAnalyze.add(d);
447 // start with all method calls to further schedule
448 Set moreMethodsToCheck = moreMethodsToCheck = callGraph.getMethodCalls(d);
450 if( d instanceof MethodDescriptor ) {
451 // see if this method has virtual dispatch
452 Set virtualMethods = callGraph.getMethods( (MethodDescriptor)d);
453 moreMethodsToCheck.addAll(virtualMethods);
456 // keep following any further methods identified in
458 Iterator methItr = moreMethodsToCheck.iterator();
459 while( methItr.hasNext() ) {
460 Descriptor m = (Descriptor) methItr.next();
461 scheduleAllCallees(m);
466 // manage the set of tasks and methods to be analyzed
467 // and be sure to reschedule tasks/methods when the methods
468 // they call are updated
469 private void analyzeMethods() throws java.io.IOException {
471 // first gather all of the method contexts to analyze
472 HashSet<MethodContext> allContexts = new HashSet<MethodContext>();
473 Iterator<Descriptor> itrd2a = descriptorsToAnalyze.iterator();
474 while( itrd2a.hasNext() ) {
475 HashSet<MethodContext> mcs = mapDescriptorToAllMethodContexts.get( itrd2a.next() );
478 Iterator<MethodContext> itrmc = mcs.iterator();
479 while( itrmc.hasNext() ) {
480 allContexts.add( itrmc.next() );
484 // topologically sort them according to the caller graph so leaf calls are
485 // ordered first; use that ordering to give method contexts priorities
486 LinkedList<MethodContext> sortedMethodContexts = topologicalSort( allContexts );
488 methodContextsToVisitQ = new PriorityQueue<MethodContextQWrapper>();
489 methodContextsToVisitSet = new HashSet<MethodContext>();
492 Iterator<MethodContext> mcItr = sortedMethodContexts.iterator();
493 while( mcItr.hasNext() ) {
494 MethodContext mc = mcItr.next();
495 mapDescriptorToPriority.put( mc.getDescriptor(), new Integer( p ) );
496 methodContextsToVisitQ.add( new MethodContextQWrapper( p, mc ) );
497 methodContextsToVisitSet.add( mc );
501 // analyze methods from the priority queue until it is empty
502 while( !methodContextsToVisitQ.isEmpty() ) {
503 MethodContext mc = methodContextsToVisitQ.poll().getMethodContext();
504 assert methodContextsToVisitSet.contains( mc );
505 methodContextsToVisitSet.remove( mc );
507 // because the task or method descriptor just extracted
508 // was in the "to visit" set it either hasn't been analyzed
509 // yet, or some method that it depends on has been
510 // updated. Recompute a complete ownership graph for
511 // this task/method and compare it to any previous result.
512 // If there is a change detected, add any methods/tasks
513 // that depend on this one to the "to visit" set.
515 System.out.println("Analyzing " + mc);
517 Descriptor d = mc.getDescriptor();
519 if( d instanceof MethodDescriptor ) {
520 fm = state.getMethodFlat( (MethodDescriptor) d);
522 assert d instanceof TaskDescriptor;
523 fm = state.getMethodFlat( (TaskDescriptor) d);
526 OwnershipGraph og = analyzeFlatMethod(mc, fm);
527 OwnershipGraph ogPrev = mapMethodContextToCompleteOwnershipGraph.get(mc);
528 if( !og.equals(ogPrev) ) {
529 setGraphForMethodContext(mc, og);
531 Iterator<MethodContext> depsItr = iteratorDependents( mc );
532 while( depsItr.hasNext() ) {
533 MethodContext mcNext = depsItr.next();
535 if( !methodContextsToVisitSet.contains( mcNext ) ) {
536 //System.out.println( " queuing "+mcNext );
537 methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( mcNext.getDescriptor() ),
539 methodContextsToVisitSet.add( mcNext );
548 // keep passing the Descriptor of the method along for debugging
549 // and dot file writing
550 private OwnershipGraph
551 analyzeFlatMethod(MethodContext mc,
552 FlatMethod flatm) throws java.io.IOException {
554 // initialize flat nodes to visit as the flat method
555 // because it is the entry point
557 flatNodesToVisit = new HashSet<FlatNode>();
558 flatNodesToVisit.add(flatm);
560 // initilize the mapping of flat nodes in this flat method to
561 // ownership graph results to an empty mapping
562 mapFlatNodeToOwnershipGraph = new Hashtable<FlatNode, OwnershipGraph>();
564 // initialize the set of return nodes that will be combined as
565 // the final ownership graph result to return as an empty set
566 returnNodesToCombineForCompleteOwnershipGraph = new HashSet<FlatReturnNode>();
569 while( !flatNodesToVisit.isEmpty() ) {
570 FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
571 flatNodesToVisit.remove(fn);
573 //System.out.println( " "+fn );
575 // perform this node's contributions to the ownership
576 // graph on a new copy, then compare it to the old graph
577 // at this node to see if anything was updated.
578 OwnershipGraph og = new OwnershipGraph(allocationDepth, typeUtil);
580 // start by merging all node's parents' graphs
581 for( int i = 0; i < fn.numPrev(); ++i ) {
582 FlatNode pn = fn.getPrev(i);
583 if( mapFlatNodeToOwnershipGraph.containsKey(pn) ) {
584 OwnershipGraph ogParent = mapFlatNodeToOwnershipGraph.get(pn);
589 // apply the analysis of the flat node to the
590 // ownership graph made from the merge of the
592 og = analyzeFlatNode(mc,
594 returnNodesToCombineForCompleteOwnershipGraph,
600 if( takeDebugSnapshots &&
601 mc.getDescriptor().getSymbol().equals( mcDescSymbolDebug ) ) {
602 debugSnapshot(og,fn);
607 // if the results of the new graph are different from
608 // the current graph at this node, replace the graph
609 // with the update and enqueue the children for
611 OwnershipGraph ogPrev = mapFlatNodeToOwnershipGraph.get(fn);
612 if( !og.equals(ogPrev) ) {
613 mapFlatNodeToOwnershipGraph.put(fn, og);
615 for( int i = 0; i < fn.numNext(); i++ ) {
616 FlatNode nn = fn.getNext(i);
617 flatNodesToVisit.add(nn);
622 // end by merging all return nodes into a complete
623 // ownership graph that represents all possible heap
624 // states after the flat method returns
625 OwnershipGraph completeGraph = new OwnershipGraph(allocationDepth, typeUtil);
626 Iterator retItr = returnNodesToCombineForCompleteOwnershipGraph.iterator();
627 while( retItr.hasNext() ) {
628 FlatReturnNode frn = (FlatReturnNode) retItr.next();
629 assert mapFlatNodeToOwnershipGraph.containsKey(frn);
630 OwnershipGraph ogr = mapFlatNodeToOwnershipGraph.get(frn);
631 completeGraph.merge(ogr);
634 return completeGraph;
638 private OwnershipGraph
639 analyzeFlatNode(MethodContext mc,
641 HashSet<FlatReturnNode> setRetNodes,
642 OwnershipGraph og) throws java.io.IOException {
648 // use node type to decide what alterations to make
649 // to the ownership graph
650 switch( fn.kind() ) {
652 case FKind.FlatMethod:
653 FlatMethod fm = (FlatMethod) fn;
655 // there should only be one FlatMethod node as the
656 // parent of all other FlatNode objects, so take
657 // the opportunity to construct the initial graph by
658 // adding parameters labels to new heap regions
659 // AND this should be done once globally so that the
660 // parameter IDs are consistent between analysis
661 // iterations, so if this step has been done already
662 // just merge in the cached version
663 OwnershipGraph ogInitParamAlloc = mapMethodContextToInitialParamAllocGraph.get(mc);
664 if( ogInitParamAlloc == null ) {
666 // if the method context has aliased parameters, make sure
667 // there is a blob region for all those param labels to
669 Set<Integer> aliasedParamIndices = mc.getAliasedParamIndices();
670 boolean aliasedPiIsSuperOfPj = false;
671 if( !aliasedParamIndices.isEmpty() ) {
672 og.makeAliasedParamHeapRegionNode( tdAliasedParams );
674 // if one parameter type is a super class of any other
675 // then we cannot separate the primary parameter objects
676 // from the alias blob, so look for it
677 boolean keepLooking = true;
679 Iterator<Integer> apItrI = aliasedParamIndices.iterator();
680 while( apItrI.hasNext() && keepLooking ) {
681 Integer i = apItrI.next();
683 Iterator<Integer> apItrJ = aliasedParamIndices.iterator();
684 while( apItrJ.hasNext() ) {
685 Integer j = apItrJ.next();
687 if( !i.equals( j ) ) {
688 TypeDescriptor typeI = fm.getParameter( i ).getType();
689 TypeDescriptor typeJ = fm.getParameter( j ).getType();
691 if( typeUtil.isSuperorType( typeI, typeJ ) ||
692 typeUtil.isSuperorType( typeJ, typeI ) ) {
694 aliasedPiIsSuperOfPj = true;
703 // set up each parameter
704 for( int i = 0; i < fm.numParameters(); ++i ) {
705 TempDescriptor tdParam = fm.getParameter( i );
706 TypeDescriptor typeParam = tdParam.getType();
707 Integer paramIndex = new Integer( i );
709 if( typeParam.isImmutable() && !typeParam.isArray() ) {
710 // don't bother with this primitive parameter, it
711 // cannot affect reachability
715 if( aliasedParamIndices.contains( paramIndex ) ) {
716 // use the alias blob but give parameters their
717 // own primary obj region if they are not super
718 // classes of one another
719 og.assignTempEqualToAliasedParam( tdParam,
722 aliasedPiIsSuperOfPj );
724 // this parameter is not aliased to others, give it
725 // a fresh parameter heap region
726 og.assignTempEqualToParamAlloc( tdParam,
727 mc.getDescriptor() instanceof TaskDescriptor,
732 // clean up reachability on initial parameter shapes
736 OwnershipGraph ogResult = new OwnershipGraph(allocationDepth, typeUtil);
738 mapMethodContextToInitialParamAllocGraph.put(mc, ogResult);
741 // or just leverage the cached copy
742 og.merge(ogInitParamAlloc);
746 case FKind.FlatOpNode:
747 FlatOpNode fon = (FlatOpNode) fn;
748 if( fon.getOp().getOp() == Operation.ASSIGN ) {
751 og.assignTempXEqualToTempY(lhs, rhs);
755 case FKind.FlatCastNode:
756 FlatCastNode fcn = (FlatCastNode) fn;
760 TypeDescriptor td = fcn.getType();
763 og.assignTypedTempXEqualToTempY(lhs, rhs, td);
766 case FKind.FlatFieldNode:
767 FlatFieldNode ffn = (FlatFieldNode) fn;
770 fld = ffn.getField();
771 if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
772 og.assignTempXEqualToTempYFieldF(lhs, rhs, fld);
776 case FKind.FlatSetFieldNode:
777 FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
779 fld = fsfn.getField();
781 if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
782 og.assignTempXFieldFEqualToTempY(lhs, fld, rhs);
786 case FKind.FlatElementNode:
787 FlatElementNode fen = (FlatElementNode) fn;
790 if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
792 assert rhs.getType() != null;
793 assert rhs.getType().isArray();
795 TypeDescriptor tdElement = rhs.getType().dereference();
796 FieldDescriptor fdElement = getArrayField( tdElement );
798 og.assignTempXEqualToTempYFieldF(lhs, rhs, fdElement);
802 case FKind.FlatSetElementNode:
803 FlatSetElementNode fsen = (FlatSetElementNode) fn;
806 if( !rhs.getType().isImmutable() || rhs.getType().isArray() ) {
808 assert lhs.getType() != null;
809 assert lhs.getType().isArray();
811 TypeDescriptor tdElement = lhs.getType().dereference();
812 FieldDescriptor fdElement = getArrayField( tdElement );
814 og.assignTempXFieldFEqualToTempY(lhs, fdElement, rhs);
819 FlatNew fnn = (FlatNew) fn;
821 if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
822 AllocationSite as = getAllocationSiteFromFlatNewPRIVATE(fnn);
823 og.assignTempEqualToNewAlloc(lhs, as);
828 FlatCall fc = (FlatCall) fn;
829 MethodDescriptor md = fc.getMethod();
830 FlatMethod flatm = state.getMethodFlat(md);
831 OwnershipGraph ogMergeOfAllPossibleCalleeResults = new OwnershipGraph(allocationDepth, typeUtil);
833 if( md.isStatic() ) {
834 // a static method is simply always the same, makes life easy
835 ogMergeOfAllPossibleCalleeResults = og;
837 Set<Integer> aliasedParamIndices =
838 ogMergeOfAllPossibleCalleeResults.calculateAliasedParamSet(fc, md.isStatic(), flatm);
840 MethodContext mcNew = new MethodContext( md, aliasedParamIndices );
841 Set contexts = mapDescriptorToAllMethodContexts.get( md );
842 assert contexts != null;
843 contexts.add( mcNew );
845 addDependent( mc, mcNew );
847 OwnershipGraph onlyPossibleCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
849 if( onlyPossibleCallee == null ) {
850 // if this method context has never been analyzed just schedule it for analysis
851 // and skip over this call site for now
852 if( !methodContextsToVisitSet.contains( mcNew ) ) {
853 methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( md ),
855 methodContextsToVisitSet.add( mcNew );
859 ogMergeOfAllPossibleCalleeResults.resolveMethodCall(fc, md.isStatic(), flatm, onlyPossibleCallee, mc);
863 // if the method descriptor is virtual, then there could be a
864 // set of possible methods that will actually be invoked, so
865 // find all of them and merge all of their results together
866 TypeDescriptor typeDesc = fc.getThis().getType();
867 Set possibleCallees = callGraph.getMethods(md, typeDesc);
869 Iterator i = possibleCallees.iterator();
870 while( i.hasNext() ) {
871 MethodDescriptor possibleMd = (MethodDescriptor) i.next();
872 FlatMethod pflatm = state.getMethodFlat(possibleMd);
874 // don't alter the working graph (og) until we compute a result for every
875 // possible callee, merge them all together, then set og to that
876 OwnershipGraph ogCopy = new OwnershipGraph(allocationDepth, typeUtil);
879 Set<Integer> aliasedParamIndices =
880 ogCopy.calculateAliasedParamSet(fc, possibleMd.isStatic(), pflatm);
882 MethodContext mcNew = new MethodContext( possibleMd, aliasedParamIndices );
883 Set contexts = mapDescriptorToAllMethodContexts.get( md );
884 assert contexts != null;
885 contexts.add( mcNew );
887 addDependent( mc, mcNew );
889 OwnershipGraph ogPotentialCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
891 if( ogPotentialCallee == null ) {
892 // if this method context has never been analyzed just schedule it for analysis
893 // and skip over this call site for now
894 if( !methodContextsToVisitSet.contains( mcNew ) ) {
895 methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( md ),
897 methodContextsToVisitSet.add( mcNew );
901 ogCopy.resolveMethodCall(fc, possibleMd.isStatic(), pflatm, ogPotentialCallee, mc);
904 ogMergeOfAllPossibleCalleeResults.merge(ogCopy);
908 og = ogMergeOfAllPossibleCalleeResults;
911 case FKind.FlatReturnNode:
912 FlatReturnNode frn = (FlatReturnNode) fn;
913 rhs = frn.getReturnTemp();
914 if( rhs != null && !rhs.getType().isImmutable() ) {
915 og.assignReturnEqualToTemp(rhs);
917 setRetNodes.add(frn);
925 // this method should generate integers strictly greater than zero!
926 // special "shadow" regions are made from a heap region by negating
928 static public Integer generateUniqueHeapRegionNodeID() {
930 return new Integer(uniqueIDcount);
934 static public FieldDescriptor getArrayField( TypeDescriptor tdElement ) {
935 FieldDescriptor fdElement = mapTypeToArrayField.get( tdElement );
936 if( fdElement == null ) {
937 fdElement = new FieldDescriptor(new Modifiers(Modifiers.PUBLIC),
939 arrayElementFieldName,
942 mapTypeToArrayField.put( tdElement, fdElement );
948 private void setGraphForMethodContext(MethodContext mc, OwnershipGraph og) {
950 mapMethodContextToCompleteOwnershipGraph.put(mc, og);
952 if( writeDOTs && writeAllDOTs ) {
953 if( !mapMethodContextToNumUpdates.containsKey(mc) ) {
954 mapMethodContextToNumUpdates.put(mc, new Integer(0) );
956 Integer n = mapMethodContextToNumUpdates.get(mc);
958 og.writeGraph(mc, n, true, true, true, false, false);
959 } catch( IOException e ) {}
960 mapMethodContextToNumUpdates.put(mc, n + 1);
965 private void addDependent( MethodContext caller, MethodContext callee ) {
966 HashSet<MethodContext> deps = mapMethodContextToDependentContexts.get( callee );
968 deps = new HashSet<MethodContext>();
971 mapMethodContextToDependentContexts.put( callee, deps );
974 private Iterator<MethodContext> iteratorDependents( MethodContext callee ) {
975 HashSet<MethodContext> deps = mapMethodContextToDependentContexts.get( callee );
977 deps = new HashSet<MethodContext>();
978 mapMethodContextToDependentContexts.put( callee, deps );
980 return deps.iterator();
984 private void writeFinalContextGraphs() {
985 // arguments to writeGraph are:
986 // boolean writeLabels,
987 // boolean labelSelect,
988 // boolean pruneGarbage,
989 // boolean writeReferencers
990 // boolean writeParamMappings
992 Set entrySet = mapMethodContextToCompleteOwnershipGraph.entrySet();
993 Iterator itr = entrySet.iterator();
994 while( itr.hasNext() ) {
995 Map.Entry me = (Map.Entry) itr.next();
996 MethodContext mc = (MethodContext) me.getKey();
997 OwnershipGraph og = (OwnershipGraph) me.getValue();
1000 og.writeGraph(mc, true, true, true, false, false);
1001 } catch( IOException e ) {}
1006 // return just the allocation site associated with one FlatNew node
1007 private AllocationSite getAllocationSiteFromFlatNewPRIVATE(FlatNew fn) {
1009 if( !mapFlatNewToAllocationSite.containsKey(fn) ) {
1010 AllocationSite as = new AllocationSite(allocationDepth, fn, fn.getDisjointId());
1012 // the newest nodes are single objects
1013 for( int i = 0; i < allocationDepth; ++i ) {
1014 Integer id = generateUniqueHeapRegionNodeID();
1015 as.setIthOldest(i, id);
1018 // the oldest node is a summary node
1019 Integer idSummary = generateUniqueHeapRegionNodeID();
1020 as.setSummary(idSummary);
1022 mapFlatNewToAllocationSite.put(fn, as);
1025 return mapFlatNewToAllocationSite.get(fn);
1029 // return all allocation sites in the method (there is one allocation
1030 // site per FlatNew node in a method)
1031 private HashSet<AllocationSite> getAllocationSiteSet(Descriptor d) {
1032 if( !mapDescriptorToAllocationSiteSet.containsKey(d) ) {
1033 buildAllocationSiteSet(d);
1036 return mapDescriptorToAllocationSiteSet.get(d);
1040 private void buildAllocationSiteSet(Descriptor d) {
1041 HashSet<AllocationSite> s = new HashSet<AllocationSite>();
1044 if( d instanceof MethodDescriptor ) {
1045 fm = state.getMethodFlat( (MethodDescriptor) d);
1047 assert d instanceof TaskDescriptor;
1048 fm = state.getMethodFlat( (TaskDescriptor) d);
1051 // visit every node in this FlatMethod's IR graph
1052 // and make a set of the allocation sites from the
1053 // FlatNew node's visited
1054 HashSet<FlatNode> visited = new HashSet<FlatNode>();
1055 HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
1058 while( !toVisit.isEmpty() ) {
1059 FlatNode n = toVisit.iterator().next();
1061 if( n instanceof FlatNew ) {
1062 s.add(getAllocationSiteFromFlatNewPRIVATE( (FlatNew) n) );
1068 for( int i = 0; i < n.numNext(); ++i ) {
1069 FlatNode child = n.getNext(i);
1070 if( !visited.contains(child) ) {
1076 mapDescriptorToAllocationSiteSet.put(d, s);
1080 private HashSet<AllocationSite> getFlaggedAllocationSites(Descriptor dIn) {
1082 HashSet<AllocationSite> out = new HashSet<AllocationSite>();
1083 HashSet<Descriptor> toVisit = new HashSet<Descriptor>();
1084 HashSet<Descriptor> visited = new HashSet<Descriptor>();
1088 while( !toVisit.isEmpty() ) {
1089 Descriptor d = toVisit.iterator().next();
1093 HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
1094 Iterator asItr = asSet.iterator();
1095 while( asItr.hasNext() ) {
1096 AllocationSite as = (AllocationSite) asItr.next();
1097 if( as.getDisjointId() != null ) {
1102 // enqueue callees of this method to be searched for
1103 // allocation sites also
1104 Set callees = callGraph.getCalleeSet(d);
1105 if( callees != null ) {
1106 Iterator methItr = callees.iterator();
1107 while( methItr.hasNext() ) {
1108 MethodDescriptor md = (MethodDescriptor) methItr.next();
1110 if( !visited.contains(md) ) {
1121 private HashSet<AllocationSite>
1122 getFlaggedAllocationSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
1124 HashSet<AllocationSite> asSetTotal = new HashSet<AllocationSite>();
1125 HashSet<Descriptor> toVisit = new HashSet<Descriptor>();
1126 HashSet<Descriptor> visited = new HashSet<Descriptor>();
1130 // traverse this task and all methods reachable from this task
1131 while( !toVisit.isEmpty() ) {
1132 Descriptor d = toVisit.iterator().next();
1136 HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
1137 Iterator asItr = asSet.iterator();
1138 while( asItr.hasNext() ) {
1139 AllocationSite as = (AllocationSite) asItr.next();
1140 TypeDescriptor typed = as.getType();
1141 if( typed != null ) {
1142 ClassDescriptor cd = typed.getClassDesc();
1143 if( cd != null && cd.hasFlags() ) {
1149 // enqueue callees of this method to be searched for
1150 // allocation sites also
1151 Set callees = callGraph.getCalleeSet(d);
1152 if( callees != null ) {
1153 Iterator methItr = callees.iterator();
1154 while( methItr.hasNext() ) {
1155 MethodDescriptor md = (MethodDescriptor) methItr.next();
1157 if( !visited.contains(md) ) {
1169 private LinkedList<MethodContext> topologicalSort( HashSet<MethodContext> set ) {
1170 HashSet <MethodContext> discovered = new HashSet <MethodContext>();
1171 LinkedList<MethodContext> sorted = new LinkedList<MethodContext>();
1173 Iterator<MethodContext> itr = set.iterator();
1174 while( itr.hasNext() ) {
1175 MethodContext mc = itr.next();
1177 if( !discovered.contains( mc ) ) {
1178 dfsVisit( set, mc, sorted, discovered );
1185 private void dfsVisit( HashSet<MethodContext> set,
1187 LinkedList<MethodContext> sorted,
1188 HashSet <MethodContext> discovered ) {
1189 discovered.add( mc );
1191 Descriptor d = mc.getDescriptor();
1192 if( d instanceof MethodDescriptor ) {
1193 MethodDescriptor md = (MethodDescriptor) d;
1194 Iterator itr = callGraph.getCallerSet( md ).iterator();
1195 while( itr.hasNext() ) {
1196 Descriptor dCaller = (Descriptor) itr.next();
1198 // only consider the callers in the original set to analyze
1199 Set<MethodContext> callerContexts = mapDescriptorToAllMethodContexts.get( dCaller );
1200 if( callerContexts == null )
1203 // since the analysis hasn't started, there should be exactly one
1204 // context if there are any at all
1205 assert callerContexts.size() == 1;
1206 MethodContext mcCaller = callerContexts.iterator().next();
1207 assert set.contains( mcCaller );
1209 if( !discovered.contains( mcCaller ) ) {
1210 dfsVisit( set, mcCaller, sorted, discovered );
1215 sorted.addFirst( mc );
1220 private String computeAliasContextHistogram() {
1222 Hashtable<Integer, Integer> mapNumContexts2NumDesc =
1223 new Hashtable<Integer, Integer>();
1225 Iterator itr = mapDescriptorToAllMethodContexts.entrySet().iterator();
1226 while( itr.hasNext() ) {
1227 Map.Entry me = (Map.Entry) itr.next();
1228 HashSet<MethodContext> s = (HashSet<MethodContext>) me.getValue();
1230 Integer i = mapNumContexts2NumDesc.get( s.size() );
1232 i = new Integer( 0 );
1234 mapNumContexts2NumDesc.put( s.size(), i + 1 );
1240 itr = mapNumContexts2NumDesc.entrySet().iterator();
1241 while( itr.hasNext() ) {
1242 Map.Entry me = (Map.Entry) itr.next();
1243 Integer c0 = (Integer) me.getKey();
1244 Integer d0 = (Integer) me.getValue();
1246 s += String.format( "%4d methods had %4d unique alias contexts.\n", d0, c0 );
1249 s += String.format( "\n%4d total methods analayzed.\n", total );
1256 // insert a call to debugSnapshot() somewhere in the analysis
1257 // to get successive captures of the analysis state
1258 boolean takeDebugSnapshots = false;
1259 String mcDescSymbolDebug = "main";
1260 boolean stopAfterCapture = true;
1262 // increments every visit to debugSnapshot, don't fiddle with it
1263 int debugCounter = 0;
1265 // the value of debugCounter to start reporting the debugCounter
1266 // to the screen to let user know what debug iteration we're at
1267 int numStartCountReport = 100;
1269 // the frequency of debugCounter values to print out, 0 no report
1270 int freqCountReport = 0;
1272 // the debugCounter value at which to start taking snapshots
1273 int iterStartCapture = 134;
1275 // the number of snapshots to take
1276 int numIterToCapture = 50;
1278 void debugSnapshot(OwnershipGraph og, FlatNode fn) {
1279 if( debugCounter > iterStartCapture + numIterToCapture ) {
1284 if( debugCounter > numStartCountReport &&
1285 freqCountReport > 0 &&
1286 debugCounter % freqCountReport == 0 ) {
1287 System.out.println(" @@@ debug counter = "+debugCounter);
1289 if( debugCounter > iterStartCapture ) {
1290 System.out.println(" @@@ capturing debug "+(debugCounter-iterStartCapture)+" @@@");
1291 String graphName = String.format("snap%04d",debugCounter-iterStartCapture);
1293 graphName = graphName+fn;
1296 og.writeGraph(graphName, true, true, true, false, false);
1297 } catch( Exception e ) {
1298 System.out.println("Error writing debug capture.");
1303 if( debugCounter == iterStartCapture + numIterToCapture && stopAfterCapture ) {
1304 System.out.println("Stopping analysis after debug captures.");