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 checkAnalysisComplete();
24 return getFlaggedAllocationSitesReachableFromTaskPRIVATE(td);
27 public AllocationSite getAllocationSiteFromFlatNew(FlatNew fn) {
28 checkAnalysisComplete();
29 return getAllocationSiteFromFlatNewPRIVATE(fn);
32 public AllocationSite getAllocationSiteFromHeapRegionNodeID(Integer id) {
33 checkAnalysisComplete();
34 return mapHrnIdToAllocationSite.get(id);
37 public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
40 checkAnalysisComplete();
41 OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
43 return og.hasPotentialAlias(paramIndex1, paramIndex2);
46 public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
48 AllocationSite alloc) {
49 checkAnalysisComplete();
50 OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
52 return og.hasPotentialAlias(paramIndex, alloc);
55 public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
58 checkAnalysisComplete();
59 OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
61 return og.hasPotentialAlias(paramIndex, alloc);
64 public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
65 AllocationSite alloc1,
66 AllocationSite alloc2) {
67 checkAnalysisComplete();
68 OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
70 return og.hasPotentialAlias(alloc1, alloc2);
74 protected OwnershipGraph getGraphOfAllContextsFromDescriptor(Descriptor d) {
75 checkAnalysisComplete();
79 OwnershipGraph og = new OwnershipGraph();
81 assert mapDescriptorToAllMethodContexts.containsKey( d );
82 HashSet<MethodContext> contexts = mapDescriptorToAllMethodContexts.get( d );
83 Iterator<MethodContext> mcItr = contexts.iterator();
84 while( mcItr.hasNext() ) {
85 MethodContext mc = mcItr.next();
87 OwnershipGraph ogContext = mapMethodContextToCompleteOwnershipGraph.get(mc);
88 assert ogContext != null;
90 og.merge( ogContext );
97 public String prettyPrintNodeSet( Set<HeapRegionNode> s ) {
98 checkAnalysisComplete();
102 Iterator<HeapRegionNode> i = s.iterator();
103 while( i.hasNext() ) {
104 HeapRegionNode n = i.next();
106 AllocationSite as = n.getAllocationSite();
108 out += " "+n.toString()+",\n";
110 out += " "+n.toString()+": "+as.toStringVerbose()+",\n";
119 // use the methods given above to check every possible alias
120 // between task parameters and flagged allocation sites reachable
122 public void writeAllAliases(String outputFile,
125 boolean tabularOutput,
127 ) throws java.io.IOException {
128 checkAnalysisComplete();
130 BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
132 if( !tabularOutput ) {
133 bw.write("Conducting ownership analysis with allocation depth = "+allocationDepth+"\n");
134 bw.write(timeReport+"\n");
139 // look through every task for potential aliases
140 Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
141 while( taskItr.hasNext() ) {
142 TaskDescriptor td = (TaskDescriptor) taskItr.next();
144 if( !tabularOutput ) {
145 bw.write("\n---------"+td+"--------\n");
148 HashSet<AllocationSite> allocSites = getFlaggedAllocationSitesReachableFromTask(td);
150 Set<HeapRegionNode> common;
152 // for each task parameter, check for aliases with
153 // other task parameters and every allocation site
154 // reachable from this task
155 boolean foundSomeAlias = false;
157 FlatMethod fm = state.getMethodFlat(td);
158 for( int i = 0; i < fm.numParameters(); ++i ) {
160 // for the ith parameter check for aliases to all
161 // higher numbered parameters
162 for( int j = i + 1; j < fm.numParameters(); ++j ) {
163 common = createsPotentialAliases(td, i, j);
164 if( !common.isEmpty() ) {
165 foundSomeAlias = true;
166 if( !tabularOutput ) {
167 bw.write("Potential alias between parameters "+i+" and "+j+".\n");
168 bw.write(prettyPrintNodeSet( common )+"\n" );
175 // for the ith parameter, check for aliases against
176 // the set of allocation sites reachable from this
178 Iterator allocItr = allocSites.iterator();
179 while( allocItr.hasNext() ) {
180 AllocationSite as = (AllocationSite) allocItr.next();
181 common = createsPotentialAliases(td, i, as);
182 if( !common.isEmpty() ) {
183 foundSomeAlias = true;
184 if( !tabularOutput ) {
185 bw.write("Potential alias between parameter "+i+" and "+as.getFlatNew()+".\n");
186 bw.write(prettyPrintNodeSet( common )+"\n" );
194 // for each allocation site check for aliases with
195 // other allocation sites in the context of execution
197 HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
198 Iterator allocItr1 = allocSites.iterator();
199 while( allocItr1.hasNext() ) {
200 AllocationSite as1 = (AllocationSite) allocItr1.next();
202 Iterator allocItr2 = allocSites.iterator();
203 while( allocItr2.hasNext() ) {
204 AllocationSite as2 = (AllocationSite) allocItr2.next();
206 if( !outerChecked.contains(as2) ) {
207 common = createsPotentialAliases(td, as1, as2);
209 if( !common.isEmpty() ) {
210 foundSomeAlias = true;
211 if( !tabularOutput ) {
212 bw.write("Potential alias between "+as1.getFlatNew()+" and "+as2.getFlatNew()+".\n");
213 bw.write(prettyPrintNodeSet( common )+"\n" );
221 outerChecked.add(as1);
224 if( !foundSomeAlias ) {
225 if( !tabularOutput ) {
226 bw.write("No aliases between flagged objects in Task "+td+".\n");
231 if( !tabularOutput ) {
232 bw.write( "\n"+computeAliasContextHistogram() );
234 bw.write( " & "+numAlias+
237 " & "+numMethodsAnalyzed()+
245 // this version of writeAllAliases is for Java programs that have no tasks
246 public void writeAllAliasesJava(String outputFile,
249 boolean tabularOutput,
251 ) throws java.io.IOException {
252 checkAnalysisComplete();
256 BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
258 bw.write("Conducting ownership analysis with allocation depth = "+allocationDepth+"\n");
259 bw.write(timeReport+"\n\n");
261 boolean foundSomeAlias = false;
263 Descriptor d = typeUtil.getMain();
264 HashSet<AllocationSite> allocSites = getFlaggedAllocationSites(d);
266 // for each allocation site check for aliases with
267 // other allocation sites in the context of execution
269 HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
270 Iterator allocItr1 = allocSites.iterator();
271 while( allocItr1.hasNext() ) {
272 AllocationSite as1 = (AllocationSite) allocItr1.next();
274 Iterator allocItr2 = allocSites.iterator();
275 while( allocItr2.hasNext() ) {
276 AllocationSite as2 = (AllocationSite) allocItr2.next();
278 if( !outerChecked.contains(as2) ) {
279 Set<HeapRegionNode> common = createsPotentialAliases(d, as1, as2);
281 if( !common.isEmpty() ) {
282 foundSomeAlias = true;
283 bw.write("Potential alias between "+as1.getDisjointId()+" and "+as2.getDisjointId()+".\n");
284 bw.write( prettyPrintNodeSet( common )+"\n" );
289 outerChecked.add(as1);
292 if( !foundSomeAlias ) {
293 bw.write("No aliases between flagged objects found.\n");
296 bw.write( "\n"+computeAliasContextHistogram() );
299 ///////////////////////////////////////////
301 // end public interface
303 ///////////////////////////////////////////
305 protected void checkAnalysisComplete() {
306 if( !analysisComplete ) {
307 throw new Error("Warning: public interface method called while analysis is running.");
315 // data from the compiler
317 public CallGraph callGraph;
318 public TypeUtil typeUtil;
319 public int allocationDepth;
321 // for public interface methods to warn that they
322 // are grabbing results during analysis
323 private boolean analysisComplete;
325 // used to identify HeapRegionNode objects
326 // A unique ID equates an object in one
327 // ownership graph with an object in another
328 // graph that logically represents the same
330 // start at 10 and increment to leave some
331 // reserved IDs for special purposes
332 static private int uniqueIDcount = 10;
334 // Use these data structures to track progress of
335 // processing all methods in the program, and by methods
336 // TaskDescriptor and MethodDescriptor are combined
337 // together, with a common parent class Descriptor
338 private Hashtable<MethodContext, OwnershipGraph> mapMethodContextToInitialParamAllocGraph;
339 private Hashtable<MethodContext, OwnershipGraph> mapMethodContextToCompleteOwnershipGraph;
340 private Hashtable<FlatNew, AllocationSite> mapFlatNewToAllocationSite;
341 private Hashtable<Descriptor, HashSet<AllocationSite> > mapDescriptorToAllocationSiteSet;
342 private Hashtable<MethodContext, Integer> mapMethodContextToNumUpdates;
343 private Hashtable<Descriptor, HashSet<MethodContext> > mapDescriptorToAllMethodContexts;
344 private Hashtable<MethodContext, HashSet<MethodContext> > mapMethodContextToDependentContexts;
345 private Hashtable<Integer, AllocationSite> mapHrnIdToAllocationSite;
347 // Use these data structures to track progress of one pass of
348 // processing the FlatNodes of a particular method
349 private HashSet <FlatNode> flatNodesToVisit;
350 private Hashtable<FlatNode, OwnershipGraph> mapFlatNodeToOwnershipGraph;
351 private HashSet <FlatReturnNode> returnNodesToCombineForCompleteOwnershipGraph;
353 // descriptorsToAnalyze identifies the set of tasks and methods
354 // that are reachable from the program tasks, this set is initialized
355 // and then remains static
356 public HashSet<Descriptor> descriptorsToAnalyze;
358 // descriptorsToVisit is initialized to descriptorsToAnalyze and is
359 // reduced by visiting a descriptor during analysis. When dependents
360 // must be scheduled, only those contained in descriptorsToAnalyze
361 // should be re-added to this queue
362 private PriorityQueue<MethodContextQWrapper> methodContextsToVisitQ;
363 private Set <MethodContext> methodContextsToVisitSet;
364 private Hashtable<Descriptor, Integer> mapDescriptorToPriority;
367 // special field descriptors for array elements
368 public static final String arrayElementFieldName = "___element_";
369 private static Hashtable<TypeDescriptor, FieldDescriptor> mapTypeToArrayField =
370 new Hashtable<TypeDescriptor, FieldDescriptor>();
373 // for controlling DOT file output
374 private boolean writeDOTs;
375 private boolean writeAllDOTs;
377 // for controlling method effects
378 private boolean methodEffects;
380 //map each FlatNode to its own internal ownership graph
381 private MethodEffectsAnalysis meAnalysis;
383 //keep internal ownership graph by method context and flat node
384 private Hashtable<MethodContext, Hashtable<FlatNode, OwnershipGraph>> mapMethodContextToFlatNodeOwnershipGraph;
386 //map method context to a set of allocation sites of live-in vars
387 private Hashtable<MethodContext, HashSet<AllocationSite>> mapMethodContextToLiveInAllocationSiteSet;
391 // this analysis generates an ownership graph for every task
393 public OwnershipAnalysis(State state,
398 boolean writeAllDOTs,
399 String aliasFile) throws java.io.IOException {
401 this.methodEffects = false;
402 init(state,tu,callGraph,allocationDepth,writeDOTs,writeAllDOTs,aliasFile);
406 public OwnershipAnalysis(State state,
411 boolean writeAllDOTs,
413 boolean methodEffects) throws java.io.IOException {
415 this.methodEffects = methodEffects;
416 init(state,tu,callGraph,allocationDepth,writeDOTs,writeAllDOTs,aliasFile);
420 // new constructor for on-demand disjoint analysis
421 public OwnershipAnalysis(
427 boolean writeAllDOTs,
429 boolean methodEffects,
430 Hashtable<MethodContext, HashSet<AllocationSite>> mapMethodContextToLiveInAllocationSiteSet)
431 throws java.io.IOException {
433 this.methodEffects = methodEffects;
434 this.mapMethodContextToLiveInAllocationSiteSet=mapMethodContextToLiveInAllocationSiteSet;
435 init(state, tu, callGraph, allocationDepth, writeDOTs, writeAllDOTs,
440 private void init(State state,
445 boolean writeAllDOTs,
446 String aliasFile) throws java.io.IOException {
448 analysisComplete = false;
452 this.callGraph = callGraph;
453 this.allocationDepth = allocationDepth;
454 this.writeDOTs = writeDOTs;
455 this.writeAllDOTs = writeAllDOTs;
457 // set some static configuration for OwnershipGraphs
458 OwnershipGraph.allocationDepth = allocationDepth;
459 OwnershipGraph.typeUtil = typeUtil;
460 OwnershipGraph.debugCallMapCount = state.OWNERSHIPDEBUGCALLCOUNT;
461 OwnershipGraph.debugCallee = state.OWNERSHIPDEBUGCALLEE;
462 OwnershipGraph.debugCaller = state.OWNERSHIPDEBUGCALLER;
463 if( OwnershipGraph.debugCallee != null &&
464 OwnershipGraph.debugCaller != null ) {
465 OwnershipGraph.debugCallMap = true;
468 descriptorsToAnalyze = new HashSet<Descriptor>();
470 mapMethodContextToInitialParamAllocGraph =
471 new Hashtable<MethodContext, OwnershipGraph>();
473 mapMethodContextToCompleteOwnershipGraph =
474 new Hashtable<MethodContext, OwnershipGraph>();
476 mapFlatNewToAllocationSite =
477 new Hashtable<FlatNew, AllocationSite>();
479 mapDescriptorToAllocationSiteSet =
480 new Hashtable<Descriptor, HashSet<AllocationSite> >();
482 mapDescriptorToAllMethodContexts =
483 new Hashtable<Descriptor, HashSet<MethodContext> >();
485 mapMethodContextToDependentContexts =
486 new Hashtable<MethodContext, HashSet<MethodContext> >();
488 mapDescriptorToPriority =
489 new Hashtable<Descriptor, Integer>();
491 mapHrnIdToAllocationSite =
492 new Hashtable<Integer, AllocationSite>();
494 mapMethodContextToFlatNodeOwnershipGraph=new Hashtable<MethodContext, Hashtable<FlatNode, OwnershipGraph>>();
496 meAnalysis=new MethodEffectsAnalysis(methodEffects);
500 mapMethodContextToNumUpdates = new Hashtable<MethodContext, Integer>();
504 double timeStartAnalysis = (double) System.nanoTime();
508 // initialize methods to visit as the set of all tasks in the
509 // program and then any method that could be called starting
511 Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
512 while( taskItr.hasNext() ) {
513 Descriptor d = (Descriptor) taskItr.next();
514 scheduleAllCallees(d);
518 // we are not in task mode, just normal Java, so start with
520 Descriptor d = typeUtil.getMain();
521 scheduleAllCallees(d);
525 // before beginning analysis, initialize every scheduled method
526 // with an ownership graph that has populated parameter index tables
527 // by analyzing the first node which is always a FlatMethod node
528 Iterator<Descriptor> dItr = descriptorsToAnalyze.iterator();
529 while( dItr.hasNext() ) {
530 Descriptor d = dItr.next();
531 OwnershipGraph og = new OwnershipGraph();
534 if( d instanceof MethodDescriptor ) {
535 fm = state.getMethodFlat( (MethodDescriptor) d);
537 assert d instanceof TaskDescriptor;
538 fm = state.getMethodFlat( (TaskDescriptor) d);
541 MethodContext mc = new MethodContext( d );
542 assert !mapDescriptorToAllMethodContexts.containsKey( d );
543 HashSet<MethodContext> s = new HashSet<MethodContext>();
545 mapDescriptorToAllMethodContexts.put( d, s );
547 //System.out.println("Previsiting " + mc);
549 meAnalysis.createNewMapping(mc);
551 og = analyzeFlatNode(mc, fm, null, og);
552 setGraphForMethodContext(mc, og);
555 // as mentioned above, analyze methods one-by-one, possibly revisiting
556 // a method if the methods that it calls are updated
558 analysisComplete = true;
561 double timeEndAnalysis = (double) System.nanoTime();
562 double dt = (timeEndAnalysis - timeStartAnalysis)/(Math.pow( 10.0, 9.0 ) );
563 String treport = String.format( "The reachability analysis took %.3f sec.", dt );
564 String justtime = String.format( "%.2f", dt );
565 System.out.println( treport );
567 if( writeDOTs && !writeAllDOTs ) {
568 writeFinalContextGraphs();
572 meAnalysis.writeMethodEffectsResult();
575 if( aliasFile != null ) {
577 writeAllAliases(aliasFile, treport, justtime, state.OWNERSHIPALIASTAB, state.lines);
579 writeAllAliasesJava(aliasFile, treport, justtime, state.OWNERSHIPALIASTAB, state.lines);
585 // called from the constructor to help initialize the set
586 // of methods that needs to be analyzed by ownership analysis
587 private void scheduleAllCallees(Descriptor d) {
588 if( descriptorsToAnalyze.contains(d) ) {
591 descriptorsToAnalyze.add(d);
593 // start with all method calls to further schedule
594 Set moreMethodsToCheck = moreMethodsToCheck = callGraph.getMethodCalls(d);
596 if( d instanceof MethodDescriptor ) {
597 // see if this method has virtual dispatch
598 Set virtualMethods = callGraph.getMethods( (MethodDescriptor)d);
599 moreMethodsToCheck.addAll(virtualMethods);
602 // keep following any further methods identified in
604 Iterator methItr = moreMethodsToCheck.iterator();
605 while( methItr.hasNext() ) {
606 Descriptor m = (Descriptor) methItr.next();
607 scheduleAllCallees(m);
612 // manage the set of tasks and methods to be analyzed
613 // and be sure to reschedule tasks/methods when the methods
614 // they call are updated
615 private void analyzeMethods() throws java.io.IOException {
617 // first gather all of the method contexts to analyze
618 HashSet<MethodContext> allContexts = new HashSet<MethodContext>();
619 Iterator<Descriptor> itrd2a = descriptorsToAnalyze.iterator();
620 while( itrd2a.hasNext() ) {
621 HashSet<MethodContext> mcs = mapDescriptorToAllMethodContexts.get( itrd2a.next() );
624 Iterator<MethodContext> itrmc = mcs.iterator();
625 while( itrmc.hasNext() ) {
626 allContexts.add( itrmc.next() );
630 // topologically sort them according to the caller graph so leaf calls are
631 // ordered first; use that ordering to give method contexts priorities
632 LinkedList<MethodContext> sortedMethodContexts = topologicalSort( allContexts );
634 methodContextsToVisitQ = new PriorityQueue<MethodContextQWrapper>();
635 methodContextsToVisitSet = new HashSet<MethodContext>();
638 Iterator<MethodContext> mcItr = sortedMethodContexts.iterator();
639 while( mcItr.hasNext() ) {
640 MethodContext mc = mcItr.next();
641 mapDescriptorToPriority.put( mc.getDescriptor(), new Integer( p ) );
642 methodContextsToVisitQ.add( new MethodContextQWrapper( p, mc ) );
643 methodContextsToVisitSet.add( mc );
647 // analyze methods from the priority queue until it is empty
648 while( !methodContextsToVisitQ.isEmpty() ) {
649 MethodContext mc = methodContextsToVisitQ.poll().getMethodContext();
650 assert methodContextsToVisitSet.contains( mc );
651 methodContextsToVisitSet.remove( mc );
653 // because the task or method descriptor just extracted
654 // was in the "to visit" set it either hasn't been analyzed
655 // yet, or some method that it depends on has been
656 // updated. Recompute a complete ownership graph for
657 // this task/method and compare it to any previous result.
658 // If there is a change detected, add any methods/tasks
659 // that depend on this one to the "to visit" set.
661 System.out.println("Analyzing " + mc);
663 Descriptor d = mc.getDescriptor();
665 if( d instanceof MethodDescriptor ) {
666 fm = state.getMethodFlat( (MethodDescriptor) d);
668 assert d instanceof TaskDescriptor;
669 fm = state.getMethodFlat( (TaskDescriptor) d);
672 OwnershipGraph og = analyzeFlatMethod(mc, fm);
673 OwnershipGraph ogPrev = mapMethodContextToCompleteOwnershipGraph.get(mc);
674 if( !og.equals(ogPrev) ) {
675 setGraphForMethodContext(mc, og);
677 Iterator<MethodContext> depsItr = iteratorDependents( mc );
678 while( depsItr.hasNext() ) {
679 MethodContext mcNext = depsItr.next();
681 if( !methodContextsToVisitSet.contains( mcNext ) ) {
682 methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( mcNext.getDescriptor() ),
684 methodContextsToVisitSet.add( mcNext );
693 // keep passing the Descriptor of the method along for debugging
694 // and dot file writing
695 private OwnershipGraph
696 analyzeFlatMethod(MethodContext mc,
697 FlatMethod flatm) throws java.io.IOException {
699 // initialize flat nodes to visit as the flat method
700 // because it is the entry point
702 flatNodesToVisit = new HashSet<FlatNode>();
703 flatNodesToVisit.add(flatm);
705 // initilize the mapping of flat nodes in this flat method to
706 // ownership graph results to an empty mapping
707 mapFlatNodeToOwnershipGraph = new Hashtable<FlatNode, OwnershipGraph>();
709 // initialize the set of return nodes that will be combined as
710 // the final ownership graph result to return as an empty set
711 returnNodesToCombineForCompleteOwnershipGraph = new HashSet<FlatReturnNode>();
714 while( !flatNodesToVisit.isEmpty() ) {
715 FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
716 flatNodesToVisit.remove(fn);
718 //System.out.println( " "+fn );
720 // perform this node's contributions to the ownership
721 // graph on a new copy, then compare it to the old graph
722 // at this node to see if anything was updated.
723 OwnershipGraph og = new OwnershipGraph();
725 // start by merging all node's parents' graphs
726 for( int i = 0; i < fn.numPrev(); ++i ) {
727 FlatNode pn = fn.getPrev(i);
728 if( mapFlatNodeToOwnershipGraph.containsKey(pn) ) {
729 OwnershipGraph ogParent = mapFlatNodeToOwnershipGraph.get(pn);
734 // apply the analysis of the flat node to the
735 // ownership graph made from the merge of the
737 og = analyzeFlatNode(mc,
739 returnNodesToCombineForCompleteOwnershipGraph,
745 if( takeDebugSnapshots &&
746 mc.getDescriptor().getSymbol().equals( mcDescSymbolDebug ) ) {
747 debugSnapshot(og,fn);
751 // if the results of the new graph are different from
752 // the current graph at this node, replace the graph
753 // with the update and enqueue the children for
755 OwnershipGraph ogPrev = mapFlatNodeToOwnershipGraph.get(fn);
756 if( !og.equals(ogPrev) ) {
757 mapFlatNodeToOwnershipGraph.put(fn, og);
759 for( int i = 0; i < fn.numNext(); i++ ) {
760 FlatNode nn = fn.getNext(i);
761 flatNodesToVisit.add(nn);
766 // end by merging all return nodes into a complete
767 // ownership graph that represents all possible heap
768 // states after the flat method returns
769 OwnershipGraph completeGraph = new OwnershipGraph();
770 Iterator retItr = returnNodesToCombineForCompleteOwnershipGraph.iterator();
771 while( retItr.hasNext() ) {
772 FlatReturnNode frn = (FlatReturnNode) retItr.next();
773 assert mapFlatNodeToOwnershipGraph.containsKey(frn);
774 OwnershipGraph ogr = mapFlatNodeToOwnershipGraph.get(frn);
775 completeGraph.merge(ogr);
778 return completeGraph;
782 private OwnershipGraph
783 analyzeFlatNode(MethodContext mc,
785 HashSet<FlatReturnNode> setRetNodes,
786 OwnershipGraph og) throws java.io.IOException {
792 // use node type to decide what alterations to make
793 // to the ownership graph
794 switch( fn.kind() ) {
796 case FKind.FlatMethod:
797 FlatMethod fm = (FlatMethod) fn;
799 // there should only be one FlatMethod node as the
800 // parent of all other FlatNode objects, so take
801 // the opportunity to construct the initial graph by
802 // adding parameters labels to new heap regions
803 // AND this should be done once globally so that the
804 // parameter IDs are consistent between analysis
805 // iterations, so if this step has been done already
806 // just merge in the cached version
807 OwnershipGraph ogInitParamAlloc = mapMethodContextToInitialParamAllocGraph.get(mc);
808 if( ogInitParamAlloc == null ) {
810 // if the method context has aliased parameters, make sure
811 // there is a blob region for all those param to reference
812 Set<Integer> aliasedParamIndices = mc.getAliasedParamIndices();
814 if( !aliasedParamIndices.isEmpty() ) {
815 og.makeAliasedParamHeapRegionNode();
818 // set up each parameter
819 for( int i = 0; i < fm.numParameters(); ++i ) {
820 TempDescriptor tdParam = fm.getParameter( i );
821 TypeDescriptor typeParam = tdParam.getType();
822 Integer paramIndex = new Integer( i );
824 if( typeParam.isImmutable() && !typeParam.isArray() ) {
825 // don't bother with this primitive parameter, it
826 // cannot affect reachability
830 if( aliasedParamIndices.contains( paramIndex ) ) {
831 // use the alias blob but give parameters their
832 // own primary obj region
833 og.assignTempEqualToAliasedParam( tdParam,
836 // this parameter is not aliased to others, give it
837 // a fresh primary obj and secondary object
838 og.assignTempEqualToParamAlloc( tdParam,
839 mc.getDescriptor() instanceof TaskDescriptor,
844 // add additional edges for aliased regions if necessary
845 if( !aliasedParamIndices.isEmpty() ) {
846 og.addParam2ParamAliasEdges( fm, aliasedParamIndices );
849 // clean up reachability on initial parameter shapes
852 // this maps tokens to parameter indices and vice versa
853 // for when this method is a callee
854 og.prepareParamTokenMaps( fm );
857 OwnershipGraph ogResult = new OwnershipGraph();
859 mapMethodContextToInitialParamAllocGraph.put(mc, ogResult);
862 // or just leverage the cached copy
863 og.merge(ogInitParamAlloc);
867 case FKind.FlatOpNode:
868 FlatOpNode fon = (FlatOpNode) fn;
869 if( fon.getOp().getOp() == Operation.ASSIGN ) {
872 og.assignTempXEqualToTempY(lhs, rhs);
876 case FKind.FlatCastNode:
877 FlatCastNode fcn = (FlatCastNode) fn;
881 TypeDescriptor td = fcn.getType();
884 og.assignTypedTempXEqualToTempY(lhs, rhs, td);
887 case FKind.FlatFieldNode:
888 FlatFieldNode ffn = (FlatFieldNode) fn;
891 fld = ffn.getField();
892 if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
893 og.assignTempXEqualToTempYFieldF(lhs, rhs, fld);
896 meAnalysis.analyzeFlatFieldNode(mc, og, rhs, fld);
900 case FKind.FlatSetFieldNode:
901 FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
903 fld = fsfn.getField();
905 if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
906 og.assignTempXFieldFEqualToTempY(lhs, fld, rhs);
909 meAnalysis.analyzeFlatSetFieldNode(mc, og, lhs, fld);
913 case FKind.FlatElementNode:
914 FlatElementNode fen = (FlatElementNode) fn;
917 if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
919 assert rhs.getType() != null;
920 assert rhs.getType().isArray();
922 TypeDescriptor tdElement = rhs.getType().dereference();
923 FieldDescriptor fdElement = getArrayField( tdElement );
925 og.assignTempXEqualToTempYFieldF(lhs, rhs, fdElement);
929 case FKind.FlatSetElementNode:
930 FlatSetElementNode fsen = (FlatSetElementNode) fn;
933 if( !rhs.getType().isImmutable() || rhs.getType().isArray() ) {
935 assert lhs.getType() != null;
936 assert lhs.getType().isArray();
938 TypeDescriptor tdElement = lhs.getType().dereference();
939 FieldDescriptor fdElement = getArrayField( tdElement );
941 og.assignTempXFieldFEqualToTempY(lhs, fdElement, rhs);
946 FlatNew fnn = (FlatNew) fn;
948 if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
949 AllocationSite as = getAllocationSiteFromFlatNewPRIVATE(fnn);
951 if (mapMethodContextToLiveInAllocationSiteSet != null){
952 HashSet<AllocationSite> alllocSet=mapMethodContextToLiveInAllocationSiteSet.get(mc);
954 for (Iterator iterator = alllocSet.iterator(); iterator
956 AllocationSite allocationSite = (AllocationSite) iterator
958 if(allocationSite.flatNew.equals(as.flatNew)){
965 og.assignTempEqualToNewAlloc(lhs, as);
970 FlatCall fc = (FlatCall) fn;
971 MethodDescriptor md = fc.getMethod();
972 FlatMethod flatm = state.getMethodFlat(md);
973 OwnershipGraph ogMergeOfAllPossibleCalleeResults = new OwnershipGraph();
975 if( md.isStatic() ) {
976 // a static method is simply always the same, makes life easy
977 ogMergeOfAllPossibleCalleeResults = og;
979 Set<Integer> aliasedParamIndices =
980 ogMergeOfAllPossibleCalleeResults.calculateAliasedParamSet(fc, md.isStatic(), flatm);
982 MethodContext mcNew = new MethodContext( md, aliasedParamIndices );
983 Set contexts = mapDescriptorToAllMethodContexts.get( md );
984 assert contexts != null;
985 contexts.add( mcNew );
987 addDependent( mc, mcNew );
989 OwnershipGraph onlyPossibleCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
991 if( onlyPossibleCallee == null ) {
992 // if this method context has never been analyzed just schedule it for analysis
993 // and skip over this call site for now
994 if( !methodContextsToVisitSet.contains( mcNew ) ) {
995 methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( md ),
997 methodContextsToVisitSet.add( mcNew );
1001 ogMergeOfAllPossibleCalleeResults.resolveMethodCall(fc, md.isStatic(), flatm, onlyPossibleCallee, mc, null);
1004 meAnalysis.createNewMapping(mcNew);
1005 meAnalysis.analyzeFlatCall(ogMergeOfAllPossibleCalleeResults, mcNew, mc, fc);
1009 // if the method descriptor is virtual, then there could be a
1010 // set of possible methods that will actually be invoked, so
1011 // find all of them and merge all of their results together
1012 TypeDescriptor typeDesc = fc.getThis().getType();
1013 Set possibleCallees = callGraph.getMethods(md, typeDesc);
1015 Iterator i = possibleCallees.iterator();
1016 while( i.hasNext() ) {
1017 MethodDescriptor possibleMd = (MethodDescriptor) i.next();
1018 FlatMethod pflatm = state.getMethodFlat(possibleMd);
1020 // don't alter the working graph (og) until we compute a result for every
1021 // possible callee, merge them all together, then set og to that
1022 OwnershipGraph ogCopy = new OwnershipGraph();
1025 Set<Integer> aliasedParamIndices =
1026 ogCopy.calculateAliasedParamSet(fc, possibleMd.isStatic(), pflatm);
1028 MethodContext mcNew = new MethodContext( possibleMd, aliasedParamIndices );
1029 Set contexts = mapDescriptorToAllMethodContexts.get( md );
1030 assert contexts != null;
1031 contexts.add( mcNew );
1034 meAnalysis.createNewMapping(mcNew);
1037 addDependent( mc, mcNew );
1039 OwnershipGraph ogPotentialCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
1041 if( ogPotentialCallee == null ) {
1042 // if this method context has never been analyzed just schedule it for analysis
1043 // and skip over this call site for now
1044 if( !methodContextsToVisitSet.contains( mcNew ) ) {
1045 methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( md ),
1047 methodContextsToVisitSet.add( mcNew );
1051 ogCopy.resolveMethodCall(fc, possibleMd.isStatic(), pflatm, ogPotentialCallee, mc, null);
1054 ogMergeOfAllPossibleCalleeResults.merge(ogCopy);
1056 meAnalysis.analyzeFlatCall(ogMergeOfAllPossibleCalleeResults, mcNew, mc, fc);
1061 og = ogMergeOfAllPossibleCalleeResults;
1064 case FKind.FlatReturnNode:
1065 FlatReturnNode frn = (FlatReturnNode) fn;
1066 rhs = frn.getReturnTemp();
1067 if( rhs != null && !rhs.getType().isImmutable() ) {
1068 og.assignReturnEqualToTemp(rhs);
1070 setRetNodes.add(frn);
1074 Hashtable<FlatNode, OwnershipGraph> table=mapMethodContextToFlatNodeOwnershipGraph.get(mc);
1076 table=new Hashtable<FlatNode, OwnershipGraph>();
1079 mapMethodContextToFlatNodeOwnershipGraph.put(mc, table);
1085 // this method should generate integers strictly greater than zero!
1086 // special "shadow" regions are made from a heap region by negating
1088 static public Integer generateUniqueHeapRegionNodeID() {
1090 return new Integer(uniqueIDcount);
1094 static public FieldDescriptor getArrayField( TypeDescriptor tdElement ) {
1095 FieldDescriptor fdElement = mapTypeToArrayField.get( tdElement );
1096 if( fdElement == null ) {
1097 fdElement = new FieldDescriptor(new Modifiers(Modifiers.PUBLIC),
1099 arrayElementFieldName,
1102 mapTypeToArrayField.put( tdElement, fdElement );
1108 private void setGraphForMethodContext(MethodContext mc, OwnershipGraph og) {
1110 mapMethodContextToCompleteOwnershipGraph.put(mc, og);
1112 if( writeDOTs && writeAllDOTs ) {
1113 if( !mapMethodContextToNumUpdates.containsKey(mc) ) {
1114 mapMethodContextToNumUpdates.put(mc, new Integer(0) );
1116 Integer n = mapMethodContextToNumUpdates.get(mc);
1118 og.writeGraph(mc+"COMPLETE"+String.format("%05d", n),
1119 true, // write labels (variables)
1120 true, // selectively hide intermediate temp vars
1121 true, // prune unreachable heap regions
1122 false, // show back edges to confirm graph validity
1123 false, // show parameter indices (unmaintained!)
1124 true, // hide subset reachability states
1125 true); // hide edge taints
1126 } catch( IOException e ) {}
1127 mapMethodContextToNumUpdates.put(mc, n + 1);
1132 private void addDependent( MethodContext caller, MethodContext callee ) {
1133 HashSet<MethodContext> deps = mapMethodContextToDependentContexts.get( callee );
1134 if( deps == null ) {
1135 deps = new HashSet<MethodContext>();
1138 mapMethodContextToDependentContexts.put( callee, deps );
1141 private Iterator<MethodContext> iteratorDependents( MethodContext callee ) {
1142 HashSet<MethodContext> deps = mapMethodContextToDependentContexts.get( callee );
1143 if( deps == null ) {
1144 deps = new HashSet<MethodContext>();
1145 mapMethodContextToDependentContexts.put( callee, deps );
1147 return deps.iterator();
1151 private void writeFinalContextGraphs() {
1152 Set entrySet = mapMethodContextToCompleteOwnershipGraph.entrySet();
1153 Iterator itr = entrySet.iterator();
1154 while( itr.hasNext() ) {
1155 Map.Entry me = (Map.Entry) itr.next();
1156 MethodContext mc = (MethodContext) me.getKey();
1157 OwnershipGraph og = (OwnershipGraph) me.getValue();
1160 og.writeGraph(mc+"COMPLETE",
1161 true, // write labels (variables)
1162 true, // selectively hide intermediate temp vars
1163 true, // prune unreachable heap regions
1164 false, // show back edges to confirm graph validity
1165 false, // show parameter indices (unmaintained!)
1166 true, // hide subset reachability states
1167 true); // hide edge taints
1168 } catch( IOException e ) {}
1174 // return just the allocation site associated with one FlatNew node
1175 private AllocationSite getAllocationSiteFromFlatNewPRIVATE(FlatNew fn) {
1177 if( !mapFlatNewToAllocationSite.containsKey(fn) ) {
1178 AllocationSite as = new AllocationSite(allocationDepth, fn, fn.getDisjointId());
1180 // the newest nodes are single objects
1181 for( int i = 0; i < allocationDepth; ++i ) {
1182 Integer id = generateUniqueHeapRegionNodeID();
1183 as.setIthOldest(i, id);
1184 mapHrnIdToAllocationSite.put( id, as );
1187 // the oldest node is a summary node
1188 Integer idSummary = generateUniqueHeapRegionNodeID();
1189 as.setSummary(idSummary);
1191 mapFlatNewToAllocationSite.put(fn, as);
1194 return mapFlatNewToAllocationSite.get(fn);
1198 // return all allocation sites in the method (there is one allocation
1199 // site per FlatNew node in a method)
1200 private HashSet<AllocationSite> getAllocationSiteSet(Descriptor d) {
1201 if( !mapDescriptorToAllocationSiteSet.containsKey(d) ) {
1202 buildAllocationSiteSet(d);
1205 return mapDescriptorToAllocationSiteSet.get(d);
1209 private void buildAllocationSiteSet(Descriptor d) {
1210 HashSet<AllocationSite> s = new HashSet<AllocationSite>();
1213 if( d instanceof MethodDescriptor ) {
1214 fm = state.getMethodFlat( (MethodDescriptor) d);
1216 assert d instanceof TaskDescriptor;
1217 fm = state.getMethodFlat( (TaskDescriptor) d);
1220 // visit every node in this FlatMethod's IR graph
1221 // and make a set of the allocation sites from the
1222 // FlatNew node's visited
1223 HashSet<FlatNode> visited = new HashSet<FlatNode>();
1224 HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
1227 while( !toVisit.isEmpty() ) {
1228 FlatNode n = toVisit.iterator().next();
1230 if( n instanceof FlatNew ) {
1231 s.add(getAllocationSiteFromFlatNewPRIVATE( (FlatNew) n) );
1237 for( int i = 0; i < n.numNext(); ++i ) {
1238 FlatNode child = n.getNext(i);
1239 if( !visited.contains(child) ) {
1245 mapDescriptorToAllocationSiteSet.put(d, s);
1249 private HashSet<AllocationSite> getFlaggedAllocationSites(Descriptor dIn) {
1251 HashSet<AllocationSite> out = new HashSet<AllocationSite>();
1252 HashSet<Descriptor> toVisit = new HashSet<Descriptor>();
1253 HashSet<Descriptor> visited = new HashSet<Descriptor>();
1257 while( !toVisit.isEmpty() ) {
1258 Descriptor d = toVisit.iterator().next();
1262 HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
1263 Iterator asItr = asSet.iterator();
1264 while( asItr.hasNext() ) {
1265 AllocationSite as = (AllocationSite) asItr.next();
1266 if( as.getDisjointId() != null ) {
1271 // enqueue callees of this method to be searched for
1272 // allocation sites also
1273 Set callees = callGraph.getCalleeSet(d);
1274 if( callees != null ) {
1275 Iterator methItr = callees.iterator();
1276 while( methItr.hasNext() ) {
1277 MethodDescriptor md = (MethodDescriptor) methItr.next();
1279 if( !visited.contains(md) ) {
1290 private HashSet<AllocationSite>
1291 getFlaggedAllocationSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
1293 HashSet<AllocationSite> asSetTotal = new HashSet<AllocationSite>();
1294 HashSet<Descriptor> toVisit = new HashSet<Descriptor>();
1295 HashSet<Descriptor> visited = new HashSet<Descriptor>();
1299 // traverse this task and all methods reachable from this task
1300 while( !toVisit.isEmpty() ) {
1301 Descriptor d = toVisit.iterator().next();
1305 HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
1306 Iterator asItr = asSet.iterator();
1307 while( asItr.hasNext() ) {
1308 AllocationSite as = (AllocationSite) asItr.next();
1309 TypeDescriptor typed = as.getType();
1310 if( typed != null ) {
1311 ClassDescriptor cd = typed.getClassDesc();
1312 if( cd != null && cd.hasFlags() ) {
1318 // enqueue callees of this method to be searched for
1319 // allocation sites also
1320 Set callees = callGraph.getCalleeSet(d);
1321 if( callees != null ) {
1322 Iterator methItr = callees.iterator();
1323 while( methItr.hasNext() ) {
1324 MethodDescriptor md = (MethodDescriptor) methItr.next();
1326 if( !visited.contains(md) ) {
1338 private LinkedList<MethodContext> topologicalSort( HashSet<MethodContext> set ) {
1339 HashSet <MethodContext> discovered = new HashSet <MethodContext>();
1340 LinkedList<MethodContext> sorted = new LinkedList<MethodContext>();
1342 Iterator<MethodContext> itr = set.iterator();
1343 while( itr.hasNext() ) {
1344 MethodContext mc = itr.next();
1346 if( !discovered.contains( mc ) ) {
1347 dfsVisit( set, mc, sorted, discovered );
1354 private void dfsVisit( HashSet<MethodContext> set,
1356 LinkedList<MethodContext> sorted,
1357 HashSet <MethodContext> discovered ) {
1358 discovered.add( mc );
1360 Descriptor d = mc.getDescriptor();
1361 if( d instanceof MethodDescriptor ) {
1362 MethodDescriptor md = (MethodDescriptor) d;
1363 Iterator itr = callGraph.getCallerSet( md ).iterator();
1364 while( itr.hasNext() ) {
1365 Descriptor dCaller = (Descriptor) itr.next();
1367 // only consider the callers in the original set to analyze
1368 Set<MethodContext> callerContexts = mapDescriptorToAllMethodContexts.get( dCaller );
1369 if( callerContexts == null )
1372 // since the analysis hasn't started, there should be exactly one
1373 // context if there are any at all
1374 assert callerContexts.size() == 1;
1375 MethodContext mcCaller = callerContexts.iterator().next();
1376 assert set.contains( mcCaller );
1378 if( !discovered.contains( mcCaller ) ) {
1379 dfsVisit( set, mcCaller, sorted, discovered );
1384 sorted.addFirst( mc );
1389 private String computeAliasContextHistogram() {
1391 Hashtable<Integer, Integer> mapNumContexts2NumDesc =
1392 new Hashtable<Integer, Integer>();
1394 Iterator itr = mapDescriptorToAllMethodContexts.entrySet().iterator();
1395 while( itr.hasNext() ) {
1396 Map.Entry me = (Map.Entry) itr.next();
1397 HashSet<MethodContext> s = (HashSet<MethodContext>) me.getValue();
1399 Integer i = mapNumContexts2NumDesc.get( s.size() );
1401 i = new Integer( 0 );
1403 mapNumContexts2NumDesc.put( s.size(), i + 1 );
1409 itr = mapNumContexts2NumDesc.entrySet().iterator();
1410 while( itr.hasNext() ) {
1411 Map.Entry me = (Map.Entry) itr.next();
1412 Integer c0 = (Integer) me.getKey();
1413 Integer d0 = (Integer) me.getValue();
1415 s += String.format( "%4d methods had %4d unique alias contexts.\n", d0, c0 );
1418 s += String.format( "\n%4d total methods analayzed.\n", total );
1423 private int numMethodsAnalyzed() {
1424 return descriptorsToAnalyze.size();
1430 // insert a call to debugSnapshot() somewhere in the analysis
1431 // to get successive captures of the analysis state
1432 boolean takeDebugSnapshots = false;
1433 String mcDescSymbolDebug = "addFirst";
1434 boolean stopAfterCapture = true;
1436 // increments every visit to debugSnapshot, don't fiddle with it
1437 int debugCounter = 0;
1439 // the value of debugCounter to start reporting the debugCounter
1440 // to the screen to let user know what debug iteration we're at
1441 int numStartCountReport = 0;
1443 // the frequency of debugCounter values to print out, 0 no report
1444 int freqCountReport = 0;
1446 // the debugCounter value at which to start taking snapshots
1447 int iterStartCapture = 0;
1449 // the number of snapshots to take
1450 int numIterToCapture = 40;
1452 void debugSnapshot(OwnershipGraph og, FlatNode fn) {
1453 if( debugCounter > iterStartCapture + numIterToCapture ) {
1458 if( debugCounter > numStartCountReport &&
1459 freqCountReport > 0 &&
1460 debugCounter % freqCountReport == 0 ) {
1461 System.out.println(" @@@ debug counter = "+debugCounter);
1463 if( debugCounter > iterStartCapture ) {
1464 System.out.println(" @@@ capturing debug "+(debugCounter-iterStartCapture)+" @@@");
1465 String graphName = String.format("snap%04d",debugCounter-iterStartCapture);
1467 graphName = graphName+fn;
1470 og.writeGraph(graphName,
1471 true, // write labels (variables)
1472 true, // selectively hide intermediate temp vars
1473 true, // prune unreachable heap regions
1474 false, // show back edges to confirm graph validity
1475 false, // show parameter indices (unmaintained!)
1476 true, // hide subset reachability states
1477 true); // hide edge taints
1478 } catch( Exception e ) {
1479 System.out.println("Error writing debug capture.");
1484 if( debugCounter == iterStartCapture + numIterToCapture && stopAfterCapture ) {
1485 System.out.println("Stopping analysis after debug captures.");
1490 public MethodEffectsAnalysis getMethodEffectsAnalysis(){
1494 public OwnershipGraph getOwnvershipGraphByMethodContext(MethodContext mc){
1495 return mapMethodContextToCompleteOwnershipGraph.get(mc);
1498 public HashSet<MethodContext> getAllMethodContextSetByDescriptor(Descriptor d){
1499 return mapDescriptorToAllMethodContexts.get(d);
1502 public MethodContext getCalleeMethodContext(MethodContext callerMC, FlatCall fc){
1504 Hashtable<FlatNode, OwnershipGraph> table=mapMethodContextToFlatNodeOwnershipGraph.get(callerMC);
1506 // merge previous ownership graph to calculate corresponding method context
1507 OwnershipGraph mergeOG = new OwnershipGraph();
1509 for(int i=0;i<fc.numPrev();i++){
1510 FlatNode prevNode=fc.getPrev(i);
1512 OwnershipGraph prevOG=table.get(prevNode);
1513 mergeOG.merge(prevOG);
1517 MethodDescriptor md=fc.getMethod();
1518 FlatMethod flatm = state.getMethodFlat(md);
1519 Set<Integer> aliasedParamIndices = mergeOG.calculateAliasedParamSet(fc, md.isStatic(), flatm);
1520 MethodContext calleeMC = new MethodContext( md, aliasedParamIndices );