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);
30 public boolean createsPotentialAliases(Descriptor taskOrMethod,
34 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get(taskOrMethod);
36 return og.hasPotentialAlias(paramIndex1, paramIndex2);
39 public boolean createsPotentialAliases(Descriptor taskOrMethod,
41 AllocationSite alloc) {
43 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get(taskOrMethod);
45 return og.hasPotentialAlias(paramIndex, alloc);
48 public boolean createsPotentialAliases(Descriptor taskOrMethod,
52 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get(taskOrMethod);
54 return og.hasPotentialAlias(paramIndex, alloc);
57 public boolean createsPotentialAliases(Descriptor taskOrMethod,
58 AllocationSite alloc1,
59 AllocationSite alloc2) {
61 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get(taskOrMethod);
63 return og.hasPotentialAlias(alloc1, alloc2);
66 // use the methods given above to check every possible alias
67 // between task parameters and flagged allocation sites reachable
69 public void writeAllAliases(String outputFile) throws java.io.IOException {
71 BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
73 // look through every task for potential aliases
74 Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
75 while( taskItr.hasNext() ) {
76 TaskDescriptor td = (TaskDescriptor) taskItr.next();
78 bw.write("\n---------"+td+"--------\n");
80 HashSet<AllocationSite> allocSites = getFlaggedAllocationSitesReachableFromTask(td);
82 // for each task parameter, check for aliases with
83 // other task parameters and every allocation site
84 // reachable from this task
85 boolean foundSomeAlias = false;
87 FlatMethod fm = state.getMethodFlat(td);
88 for( int i = 0; i < fm.numParameters(); ++i ) {
90 // for the ith parameter check for aliases to all
91 // higher numbered parameters
92 for( int j = i + 1; j < fm.numParameters(); ++j ) {
93 if( createsPotentialAliases(td, i, j) ) {
94 foundSomeAlias = true;
95 bw.write("Potential alias between parameters "+i+" and "+j+".\n");
99 // for the ith parameter, check for aliases against
100 // the set of allocation sites reachable from this
102 Iterator allocItr = allocSites.iterator();
103 while( allocItr.hasNext() ) {
104 AllocationSite as = (AllocationSite) allocItr.next();
105 if( createsPotentialAliases(td, i, as) ) {
106 foundSomeAlias = true;
107 bw.write("Potential alias between parameter "+i+" and "+as+".\n");
112 // for each allocation site check for aliases with
113 // other allocation sites in the context of execution
115 HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
116 Iterator allocItr1 = allocSites.iterator();
117 while( allocItr1.hasNext() ) {
118 AllocationSite as1 = (AllocationSite) allocItr1.next();
120 Iterator allocItr2 = allocSites.iterator();
121 while( allocItr2.hasNext() ) {
122 AllocationSite as2 = (AllocationSite) allocItr2.next();
124 if( !outerChecked.contains(as2) &&
125 createsPotentialAliases(td, as1, as2) ) {
126 bw.write("Potential alias between "+as1+" and "+as2+".\n");
130 outerChecked.add(as1);
133 if( !foundSomeAlias ) {
134 bw.write("Task "+td+" contains no aliases between flagged objects.\n");
141 ///////////////////////////////////////////
143 // end public interface
145 ///////////////////////////////////////////
154 // data from the compiler
156 private CallGraph callGraph;
157 private int allocationDepth;
159 // used to identify HeapRegionNode objects
160 // A unique ID equates an object in one
161 // ownership graph with an object in another
162 // graph that logically represents the same
164 // start at 10 and incerement to leave some
165 // reserved IDs for special purposes
166 static private int uniqueIDcount = 10;
169 // Use these data structures to track progress of
170 // processing all methods in the program, and by methods
171 // TaskDescriptor and MethodDescriptor are combined
172 // together, with a common parent class Descriptor
173 private Hashtable<FlatMethod, OwnershipGraph> mapFlatMethodToInitialParamAllocGraph;
174 private Hashtable<Descriptor, OwnershipGraph> mapDescriptorToCompleteOwnershipGraph;
175 private Hashtable<FlatNew, AllocationSite> mapFlatNewToAllocationSite;
176 private Hashtable<Descriptor, HashSet<AllocationSite> > mapDescriptorToAllocationSiteSet;
177 private Hashtable<Descriptor, Integer> mapDescriptorToNumUpdates;
179 // Use these data structures to track progress of one pass of
180 // processing the FlatNodes of a particular method
181 private HashSet <FlatNode> flatNodesToVisit;
182 private Hashtable<FlatNode, OwnershipGraph> mapFlatNodeToOwnershipGraph;
183 private HashSet <FlatReturnNode> returnNodesToCombineForCompleteOwnershipGraph;
185 // descriptorsToAnalyze identifies the set of tasks and methods
186 // that are reachable from the program tasks, this set is initialized
187 // and then remains static
188 private HashSet<Descriptor> descriptorsToAnalyze;
190 // descriptorsToVisit is initialized to descriptorsToAnalyze and is
191 // reduced by visiting a descriptor during analysis. When dependents
192 // must be scheduled, only those contained in descriptorsToAnalyze
193 // should be re-added to this set
194 private HashSet<Descriptor> descriptorsToVisit;
196 // a special field descriptor for all array elements
197 private static FieldDescriptor fdElement = new FieldDescriptor(new Modifiers(Modifiers.PUBLIC),
198 new TypeDescriptor("Array[]"),
202 // for controlling DOT file output
203 private boolean writeFinalGraphs;
204 private boolean writeAllUpdates;
208 // this analysis generates an ownership graph for every task
210 public OwnershipAnalysis(State state,
213 boolean writeFinalGraphs,
214 boolean writeAllUpdates) throws java.io.IOException {
217 this.callGraph = callGraph;
218 this.allocationDepth = allocationDepth;
219 this.writeFinalGraphs = writeFinalGraphs;
220 this.writeAllUpdates = writeAllUpdates;
222 descriptorsToAnalyze = new HashSet<Descriptor>();
224 mapFlatMethodToInitialParamAllocGraph =
225 new Hashtable<FlatMethod, OwnershipGraph>();
227 mapDescriptorToCompleteOwnershipGraph =
228 new Hashtable<Descriptor, OwnershipGraph>();
230 mapFlatNewToAllocationSite =
231 new Hashtable<FlatNew, AllocationSite>();
233 mapDescriptorToAllocationSiteSet =
234 new Hashtable<Descriptor, HashSet<AllocationSite> >();
236 if( writeAllUpdates ) {
237 mapDescriptorToNumUpdates = new Hashtable<Descriptor, Integer>();
240 // initialize methods to visit as the set of all tasks in the
241 // program and then any method that could be called starting
243 Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
244 while( taskItr.hasNext() ) {
245 Descriptor d = (Descriptor) taskItr.next();
246 scheduleAllCallees(d);
249 // before beginning analysis, initialize every scheduled method
250 // with an ownership graph that has populated parameter index tables
251 // by analyzing the first node which is always a FlatMethod node
252 Iterator<Descriptor> dItr = descriptorsToAnalyze.iterator();
253 while( dItr.hasNext() ) {
254 Descriptor d = dItr.next();
255 OwnershipGraph og = new OwnershipGraph(allocationDepth);
258 if( d instanceof MethodDescriptor ) {
259 fm = state.getMethodFlat( (MethodDescriptor) d);
261 assert d instanceof TaskDescriptor;
262 fm = state.getMethodFlat( (TaskDescriptor) d);
265 System.out.println("Previsiting " + d);
267 analyzeFlatNode(d, fm, null, og);
268 setGraphForDescriptor(d, og);
271 System.out.println("");
273 // as mentioned above, analyze methods one-by-one, possibly revisiting
274 // a method if the methods that it calls are updated
277 writeAllAliases("identifiedAliases.txt");
280 // called from the constructor to help initialize the set
281 // of methods that needs to be analyzed by ownership analysis
282 private void scheduleAllCallees(Descriptor d) {
283 if( descriptorsToAnalyze.contains(d) ) {
286 descriptorsToAnalyze.add(d);
288 // start with all method calls to further schedule
289 Set moreMethodsToCheck = moreMethodsToCheck = callGraph.getMethodCalls(d);
291 if( d instanceof MethodDescriptor ) {
292 // see if this method has virtual dispatch
293 Set virtualMethods = callGraph.getMethods( (MethodDescriptor)d);
294 moreMethodsToCheck.addAll(virtualMethods);
297 // keep following any further methods identified in
299 Iterator methItr = moreMethodsToCheck.iterator();
300 while( methItr.hasNext() ) {
301 Descriptor m = (Descriptor) methItr.next();
302 scheduleAllCallees(m);
307 // manage the set of tasks and methods to be analyzed
308 // and be sure to reschedule tasks/methods when the methods
309 // they call are updated
310 private void analyzeMethods() throws java.io.IOException {
312 descriptorsToVisit = (HashSet<Descriptor>)descriptorsToAnalyze.clone();
314 while( !descriptorsToVisit.isEmpty() ) {
315 Descriptor d = (Descriptor) descriptorsToVisit.iterator().next();
316 descriptorsToVisit.remove(d);
318 // because the task or method descriptor just extracted
319 // was in the "to visit" set it either hasn't been analyzed
320 // yet, or some method that it depends on has been
321 // updated. Recompute a complete ownership graph for
322 // this task/method and compare it to any previous result.
323 // If there is a change detected, add any methods/tasks
324 // that depend on this one to the "to visit" set.
326 System.out.println("Analyzing " + d);
329 if( d instanceof MethodDescriptor ) {
330 fm = state.getMethodFlat( (MethodDescriptor) d);
332 assert d instanceof TaskDescriptor;
333 fm = state.getMethodFlat( (TaskDescriptor) d);
336 OwnershipGraph og = analyzeFlatMethod(d, fm);
337 OwnershipGraph ogPrev = mapDescriptorToCompleteOwnershipGraph.get(d);
338 if( !og.equals(ogPrev) ) {
340 setGraphForDescriptor(d, og);
342 // only methods have dependents, tasks cannot
343 // be invoked by any user program calls
344 if( d instanceof MethodDescriptor ) {
345 MethodDescriptor md = (MethodDescriptor) d;
346 Set dependents = callGraph.getCallerSet(md);
347 if( dependents != null ) {
348 Iterator depItr = dependents.iterator();
349 while( depItr.hasNext() ) {
350 Descriptor dependent = (Descriptor) depItr.next();
351 if( descriptorsToAnalyze.contains(dependent) ) {
352 descriptorsToVisit.add(dependent);
363 // keep passing the Descriptor of the method along for debugging
364 // and dot file writing
365 private OwnershipGraph
366 analyzeFlatMethod(Descriptor mDesc,
367 FlatMethod flatm) throws java.io.IOException {
369 // initialize flat nodes to visit as the flat method
370 // because all other nodes in this flat method are
371 // decendents of the flat method itself
373 flatNodesToVisit = new HashSet<FlatNode>();
374 flatNodesToVisit.add(flatm);
376 // initilize the mapping of flat nodes in this flat method to
377 // ownership graph results to an empty mapping
378 mapFlatNodeToOwnershipGraph = new Hashtable<FlatNode, OwnershipGraph>();
380 // initialize the set of return nodes that will be combined as
381 // the final ownership graph result to return as an empty set
382 returnNodesToCombineForCompleteOwnershipGraph = new HashSet<FlatReturnNode>();
385 while( !flatNodesToVisit.isEmpty() ) {
386 FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
387 flatNodesToVisit.remove(fn);
389 // perform this node's contributions to the ownership
390 // graph on a new copy, then compare it to the old graph
391 // at this node to see if anything was updated.
392 OwnershipGraph og = new OwnershipGraph(allocationDepth);
394 // start by merging all node's parents' graphs
395 for( int i = 0; i < fn.numPrev(); ++i ) {
396 FlatNode pn = fn.getPrev(i);
397 OwnershipGraph ogParent = getGraphFromFlatNode(pn);
401 // apply the analysis of the flat node to the
402 // ownership graph made from the merge of the
404 analyzeFlatNode(mDesc,
406 returnNodesToCombineForCompleteOwnershipGraph,
409 // if the results of the new graph are different from
410 // the current graph at this node, replace the graph
411 // with the update and enqueue the children for
413 OwnershipGraph ogPrev = getGraphFromFlatNode(fn);
415 if( !og.equals(ogPrev) ) {
416 setGraphForFlatNode(fn, og);
418 for( int i = 0; i < fn.numNext(); i++ ) {
419 FlatNode nn = fn.getNext(i);
420 flatNodesToVisit.add(nn);
425 // end by merging all return nodes into a complete
426 // ownership graph that represents all possible heap
427 // states after the flat method returns
428 OwnershipGraph completeGraph = new OwnershipGraph(allocationDepth);
429 Iterator retItr = returnNodesToCombineForCompleteOwnershipGraph.iterator();
430 while( retItr.hasNext() ) {
431 FlatReturnNode frn = (FlatReturnNode) retItr.next();
432 OwnershipGraph ogr = getGraphFromFlatNode(frn);
433 completeGraph.merge(ogr);
435 return completeGraph;
440 analyzeFlatNode(Descriptor methodDesc,
442 HashSet<FlatReturnNode> setRetNodes,
443 OwnershipGraph og) throws java.io.IOException {
449 // use node type to decide what alterations to make
450 // to the ownership graph
451 switch( fn.kind() ) {
453 case FKind.FlatMethod:
454 FlatMethod fm = (FlatMethod) fn;
456 // there should only be one FlatMethod node as the
457 // parent of all other FlatNode objects, so take
458 // the opportunity to construct the initial graph by
459 // adding parameters labels to new heap regions
460 // AND this should be done once globally so that the
461 // parameter IDs are consistent between analysis
462 // iterations, so if this step has been done already
463 // just merge in the cached version
464 OwnershipGraph ogInitParamAlloc = mapFlatMethodToInitialParamAllocGraph.get(fm);
465 if( ogInitParamAlloc == null ) {
467 // analyze this node one time globally
468 for( int i = 0; i < fm.numParameters(); ++i ) {
469 TempDescriptor tdParam = fm.getParameter(i);
470 og.assignTempEqualToParamAlloc(tdParam,
471 methodDesc instanceof TaskDescriptor,
476 OwnershipGraph ogResult = new OwnershipGraph(allocationDepth);
478 mapFlatMethodToInitialParamAllocGraph.put(fm, ogResult);
481 // or just leverage the cached copy
482 og.merge(ogInitParamAlloc);
486 case FKind.FlatOpNode:
487 FlatOpNode fon = (FlatOpNode) fn;
488 if( fon.getOp().getOp() == Operation.ASSIGN ) {
491 og.assignTempXEqualToTempY(lhs, rhs);
495 case FKind.FlatFieldNode:
496 FlatFieldNode ffn = (FlatFieldNode) fn;
499 fld = ffn.getField();
500 if( !fld.getType().isPrimitive() ) {
501 og.assignTempXEqualToTempYFieldF(lhs, rhs, fld);
505 case FKind.FlatSetFieldNode:
506 FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
508 fld = fsfn.getField();
510 og.assignTempXFieldFEqualToTempY(lhs, fld, rhs);
513 case FKind.FlatElementNode:
514 FlatElementNode fen = (FlatElementNode) fn;
517 if( !lhs.getType().isPrimitive() ) {
518 og.assignTempXEqualToTempYFieldF(lhs, rhs, fdElement);
522 case FKind.FlatSetElementNode:
523 FlatSetElementNode fsen = (FlatSetElementNode) fn;
526 if( !rhs.getType().isPrimitive() ) {
527 og.assignTempXFieldFEqualToTempY(lhs, fdElement, rhs);
532 FlatNew fnn = (FlatNew) fn;
534 AllocationSite as = getAllocationSiteFromFlatNewPRIVATE(fnn);
536 og.assignTempEqualToNewAlloc(lhs, as);
540 FlatCall fc = (FlatCall) fn;
541 MethodDescriptor md = fc.getMethod();
542 FlatMethod flatm = state.getMethodFlat(md);
543 OwnershipGraph ogMergeOfAllPossibleCalleeResults = new OwnershipGraph(allocationDepth);
545 if( md.isStatic() ) {
546 // a static method is simply always the same, makes life easy
547 OwnershipGraph onlyPossibleCallee = mapDescriptorToCompleteOwnershipGraph.get(md);
548 ogMergeOfAllPossibleCalleeResults = og;
549 ogMergeOfAllPossibleCalleeResults.resolveMethodCall(fc, md.isStatic(), flatm, onlyPossibleCallee);
551 // if the method descriptor is virtual, then there could be a
552 // set of possible methods that will actually be invoked, so
553 // find all of them and merge all of their results together
554 TypeDescriptor typeDesc = fc.getThis().getType();
555 Set possibleCallees = callGraph.getMethods(md, typeDesc);
557 Iterator i = possibleCallees.iterator();
558 while( i.hasNext() ) {
559 MethodDescriptor possibleMd = (MethodDescriptor) i.next();
561 // don't alter the working graph (og) until we compute a result for every
562 // possible callee, merge them all together, then set og to that
563 OwnershipGraph ogCopy = new OwnershipGraph(allocationDepth);
566 OwnershipGraph ogPotentialCallee = mapDescriptorToCompleteOwnershipGraph.get(possibleMd);
567 ogCopy.resolveMethodCall(fc, md.isStatic(), flatm, ogPotentialCallee);
568 ogMergeOfAllPossibleCalleeResults.merge(ogCopy);
572 og = ogMergeOfAllPossibleCalleeResults;
575 case FKind.FlatReturnNode:
576 FlatReturnNode frn = (FlatReturnNode) fn;
577 rhs = frn.getReturnTemp();
580 og.assignReturnEqualToTemp(rhs);
583 setRetNodes.add(frn);
589 // this method should generate integers strictly greater than zero!
590 // special "shadow" regions are made from a heap region by negating
592 static public Integer generateUniqueHeapRegionNodeID() {
594 return new Integer(uniqueIDcount);
598 private void setGraphForDescriptor(Descriptor d, OwnershipGraph og)
601 mapDescriptorToCompleteOwnershipGraph.put(d, og);
603 // arguments to writeGraph are:
604 // boolean writeLabels,
605 // boolean labelSelect,
606 // boolean pruneGarbage,
607 // boolean writeReferencers
609 if( writeFinalGraphs ) {
611 if( !writeAllUpdates ) {
612 og.writeGraph(d, true, true, true, false);
615 if( !mapDescriptorToNumUpdates.containsKey(d) ) {
616 mapDescriptorToNumUpdates.put(d, new Integer(0) );
618 Integer n = mapDescriptorToNumUpdates.get(d);
619 og.writeGraph(d, n, true, true, true, false);
620 mapDescriptorToNumUpdates.put(d, n + 1);
626 private OwnershipGraph getGraphFromFlatNode(FlatNode fn) {
627 if( !mapFlatNodeToOwnershipGraph.containsKey(fn) ) {
628 mapFlatNodeToOwnershipGraph.put(fn, new OwnershipGraph(allocationDepth) );
631 return mapFlatNodeToOwnershipGraph.get(fn);
634 private void setGraphForFlatNode(FlatNode fn, OwnershipGraph og) {
635 mapFlatNodeToOwnershipGraph.put(fn, og);
639 // return just the allocation site associated with one FlatNew node
640 private AllocationSite getAllocationSiteFromFlatNewPRIVATE(FlatNew fn) {
642 if( !mapFlatNewToAllocationSite.containsKey(fn) ) {
643 AllocationSite as = new AllocationSite(allocationDepth, fn.getType() );
645 // the newest nodes are single objects
646 for( int i = 0; i < allocationDepth; ++i ) {
647 Integer id = generateUniqueHeapRegionNodeID();
648 as.setIthOldest(i, id);
651 // the oldest node is a summary node
652 Integer idSummary = generateUniqueHeapRegionNodeID();
653 as.setSummary(idSummary);
655 mapFlatNewToAllocationSite.put(fn, as);
658 return mapFlatNewToAllocationSite.get(fn);
662 // return all allocation sites in the method (there is one allocation
663 // site per FlatNew node in a method)
664 private HashSet<AllocationSite> getAllocationSiteSet(Descriptor d) {
665 if( !mapDescriptorToAllocationSiteSet.containsKey(d) ) {
666 buildAllocationSiteSet(d);
669 return mapDescriptorToAllocationSiteSet.get(d);
673 private void buildAllocationSiteSet(Descriptor d) {
674 HashSet<AllocationSite> s = new HashSet<AllocationSite>();
677 if( d instanceof MethodDescriptor ) {
678 fm = state.getMethodFlat( (MethodDescriptor) d);
680 assert d instanceof TaskDescriptor;
681 fm = state.getMethodFlat( (TaskDescriptor) d);
684 // visit every node in this FlatMethod's IR graph
685 // and make a set of the allocation sites from the
686 // FlatNew node's visited
687 HashSet<FlatNode> visited = new HashSet<FlatNode>();
688 HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
691 while( !toVisit.isEmpty() ) {
692 FlatNode n = toVisit.iterator().next();
694 if( n instanceof FlatNew ) {
695 s.add(getAllocationSiteFromFlatNewPRIVATE( (FlatNew) n) );
701 for( int i = 0; i < n.numNext(); ++i ) {
702 FlatNode child = n.getNext(i);
703 if( !visited.contains(child) ) {
709 mapDescriptorToAllocationSiteSet.put(d, s);
713 private HashSet<AllocationSite>
714 getFlaggedAllocationSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
716 HashSet<AllocationSite> asSetTotal = new HashSet<AllocationSite>();
717 HashSet<Descriptor> toVisit = new HashSet<Descriptor>();
718 HashSet<Descriptor> visited = new HashSet<Descriptor>();
722 // traverse this task and all methods reachable from this task
723 while( !toVisit.isEmpty() ) {
724 Descriptor d = toVisit.iterator().next();
728 HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
729 Iterator asItr = asSet.iterator();
730 while( asItr.hasNext() ) {
731 AllocationSite as = (AllocationSite) asItr.next();
732 if( as.getType().getClassDesc().hasFlags() ) {
737 // enqueue callees of this method to be searched for
738 // allocation sites also
739 Set callees = callGraph.getCalleeSet(d);
740 if( callees != null ) {
741 Iterator methItr = callees.iterator();
742 while( methItr.hasNext() ) {
743 MethodDescriptor md = (MethodDescriptor) methItr.next();
745 if( !visited.contains(md) ) {