1 package Analysis.OwnershipAnalysis;
3 import Analysis.CallGraph.*;
10 public class OwnershipAnalysis {
12 ///////////////////////////////////////////
14 // Public interface to discover possible
15 // aliases in the program under analysis
17 ///////////////////////////////////////////
19 public HashSet<AllocationSite>
20 getFlaggedAllocationSitesReachableFromTask( TaskDescriptor td ) {
22 return getFlaggedAllocationSitesReachableFromTaskPRIVATE( td );
25 public AllocationSite getAllocationSiteFromFlatNew( FlatNew fn ) {
26 return getAllocationSiteFromFlatNewPRIVATE( fn );
29 public boolean createsPotentialAliases( Descriptor taskOrMethod,
33 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get( taskOrMethod );
36 return createsPotentialAliases( og,
37 getHeapRegionIDset( og, paramIndex1 ),
38 getHeapRegionIDset( og, paramIndex2 ) );
41 public boolean createsPotentialAliases( Descriptor taskOrMethod,
43 AllocationSite alloc ) {
45 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get( taskOrMethod );
48 return createsPotentialAliases( og,
49 getHeapRegionIDset( og, paramIndex ),
50 getHeapRegionIDset( alloc ) );
53 public boolean createsPotentialAliases( Descriptor taskOrMethod,
57 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get( taskOrMethod );
60 return createsPotentialAliases( og,
61 getHeapRegionIDset( og, paramIndex ),
62 getHeapRegionIDset( alloc ) );
65 public boolean createsPotentialAliases( Descriptor taskOrMethod,
66 AllocationSite alloc1,
67 AllocationSite alloc2 ) {
69 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get( taskOrMethod );
72 return createsPotentialAliases( og,
73 getHeapRegionIDset( alloc1 ),
74 getHeapRegionIDset( alloc2 ) );
77 public boolean createsPotentialAliases( Descriptor taskOrMethod,
79 HashSet<AllocationSite> allocSet ) {
81 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get( taskOrMethod );
84 return createsPotentialAliases( og,
85 getHeapRegionIDset( alloc ),
86 getHeapRegionIDset( allocSet ) );
90 // use the methods given above to check every possible alias
91 // between task parameters and flagged allocation sites reachable
93 public void writeAllAliases(String outputFile) throws java.io.IOException {
95 BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
97 // look through every task for potential aliases
98 Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
99 while( taskItr.hasNext() ) {
100 TaskDescriptor td = (TaskDescriptor) taskItr.next();
102 HashSet<AllocationSite> allocSites = getFlaggedAllocationSitesReachableFromTask( td );
104 // for each task parameter, check for aliases with
105 // other task parameters and every allocation site
106 // reachable from this task
107 FlatMethod fm = state.getMethodFlat( td );
108 for( int i = 0; i < fm.numParameters(); ++i ) {
110 // for the ith parameter check for aliases to all
111 // higher numbered parameters
112 for( int j = i + 1; j < fm.numParameters(); ++j ) {
113 if( createsPotentialAliases( td, i, j ) ) {
114 bw.write( "Task "+td+" potentially aliases parameters "+i+" and "+j+".\n" );
118 // for the ith parameter, check for aliases against
119 // the set of allocation sites reachable from this
121 Iterator allocItr = allocSites.iterator();
122 while( allocItr.hasNext() ) {
123 AllocationSite as = (AllocationSite) allocItr.next();
124 if( createsPotentialAliases( td, i, as ) ) {
125 bw.write( "Task "+td+" potentially aliases parameter "+i+" and "+as+".\n" );
130 // for each allocation site check for aliases with
131 // other allocation sites in the context of execution
133 Iterator allocItr = allocSites.iterator();
134 while( allocItr.hasNext() ) {
135 AllocationSite as = (AllocationSite) allocItr.next();
136 if( createsPotentialAliases( td, as, allocSites ) ) {
137 bw.write( "Task "+td+" potentially aliases "+as+" and the rest of the set.\n" );
146 ///////////////////////////////////////////
148 // end public interface
150 ///////////////////////////////////////////
159 // data from the compiler
161 private CallGraph callGraph;
162 private int allocationDepth;
164 // used to identify HeapRegionNode objects
165 // A unique ID equates an object in one
166 // ownership graph with an object in another
167 // graph that logically represents the same
169 // start at 10 and incerement to leave some
170 // reserved IDs for special purposes
171 static private int uniqueIDcount = 10;
174 // Use these data structures to track progress of
175 // processing all methods in the program, and by methods
176 // TaskDescriptor and MethodDescriptor are combined
177 // together, with a common parent class Descriptor
178 private Hashtable<Descriptor, OwnershipGraph> mapDescriptorToCompleteOwnershipGraph;
179 private Hashtable<FlatNew, AllocationSite> mapFlatNewToAllocationSite;
180 private Hashtable<Descriptor, HashSet<AllocationSite> > mapDescriptorToAllocationSiteSet;
182 // Use these data structures to track progress of one pass of
183 // processing the FlatNodes of a particular method
184 private HashSet <FlatNode> flatNodesToVisit;
185 private Hashtable<FlatNode, OwnershipGraph> mapFlatNodeToOwnershipGraph;
186 private HashSet <FlatReturnNode> returnNodesToCombineForCompleteOwnershipGraph;
188 // descriptorsToAnalyze identifies the set of tasks and methods
189 // that are reachable from the program tasks, this set is initialized
190 // and then remains static
191 private HashSet<Descriptor> descriptorsToAnalyze;
193 // descriptorsToVisit is initialized to descriptorsToAnalyze and is
194 // reduced by visiting a descriptor during analysis. When dependents
195 // must be scheduled, only those contained in descriptorsToAnalyze
196 // should be re-added to this set
197 private HashSet<Descriptor> descriptorsToVisit;
201 // this analysis generates an ownership graph for every task
203 public OwnershipAnalysis(State state,
205 int allocationDepth) throws java.io.IOException {
207 this.callGraph = callGraph;
208 this.allocationDepth = allocationDepth;
211 descriptorsToAnalyze = new HashSet<Descriptor>();
213 mapDescriptorToCompleteOwnershipGraph =
214 new Hashtable<Descriptor, OwnershipGraph>();
216 mapFlatNewToAllocationSite =
217 new Hashtable<FlatNew, AllocationSite>();
219 mapDescriptorToAllocationSiteSet =
220 new Hashtable<Descriptor, HashSet<AllocationSite> >();
223 // initialize methods to visit as the set of all tasks in the
224 // program and then any method that could be called starting
226 Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
227 while( taskItr.hasNext() ) {
228 Descriptor d = (Descriptor) taskItr.next();
229 scheduleAllCallees(d);
232 // before beginning analysis, initialize every scheduled method
233 // with an ownership graph that has populated parameter index tables
234 // by analyzing the first node which is always a FlatMethod node
235 Iterator<Descriptor> dItr = descriptorsToAnalyze.iterator();
236 while( dItr.hasNext() ) {
237 Descriptor d = dItr.next();
238 OwnershipGraph og = new OwnershipGraph(allocationDepth);
241 if( d instanceof MethodDescriptor ) {
242 fm = state.getMethodFlat( (MethodDescriptor) d);
244 assert d instanceof TaskDescriptor;
245 fm = state.getMethodFlat( (TaskDescriptor) d);
248 analyzeFlatNode(d, fm, null, og);
249 mapDescriptorToCompleteOwnershipGraph.put(d, og);
252 // as mentioned above, analyze methods one-by-one, possibly revisiting
253 // a method if the methods that it calls are updated
257 // called from the constructor to help initialize the set
258 // of methods that needs to be analyzed by ownership analysis
259 private void scheduleAllCallees(Descriptor d) {
260 if( descriptorsToAnalyze.contains(d) ) {
263 descriptorsToAnalyze.add(d);
265 // start with all method calls to further schedule
266 Set moreMethodsToCheck = moreMethodsToCheck = callGraph.getMethodCalls( d );
268 if( d instanceof MethodDescriptor ) {
269 // see if this method has virtual dispatch
270 Set virtualMethods = callGraph.getMethods( (MethodDescriptor)d );
271 moreMethodsToCheck.addAll( virtualMethods );
274 // keep following any further methods identified in
276 Iterator methItr = moreMethodsToCheck.iterator();
277 while( methItr.hasNext() ) {
278 Descriptor m = (Descriptor) methItr.next();
279 scheduleAllCallees(m);
284 // manage the set of tasks and methods to be analyzed
285 // and be sure to reschedule tasks/methods when the methods
286 // they call are updated
287 private void analyzeMethods() throws java.io.IOException {
289 descriptorsToVisit = (HashSet<Descriptor>)descriptorsToAnalyze.clone();
291 while( !descriptorsToVisit.isEmpty() ) {
292 Descriptor d = (Descriptor) descriptorsToVisit.iterator().next();
293 descriptorsToVisit.remove(d);
295 // because the task or method descriptor just extracted
296 // was in the "to visit" set it either hasn't been analyzed
297 // yet, or some method that it depends on has been
298 // updated. Recompute a complete ownership graph for
299 // this task/method and compare it to any previous result.
300 // If there is a change detected, add any methods/tasks
301 // that depend on this one to the "to visit" set.
303 System.out.println("Analyzing " + d);
306 if( d instanceof MethodDescriptor ) {
307 fm = state.getMethodFlat( (MethodDescriptor) d);
309 assert d instanceof TaskDescriptor;
310 fm = state.getMethodFlat( (TaskDescriptor) d);
313 OwnershipGraph og = analyzeFlatMethod(d, fm);
314 OwnershipGraph ogPrev = mapDescriptorToCompleteOwnershipGraph.get(d);
315 if( !og.equals(ogPrev) ) {
316 mapDescriptorToCompleteOwnershipGraph.put(d, og);
321 boolean pruneGarbage,
322 boolean writeReferencers
324 og.writeGraph(d, true, true, false, false);
326 // only methods have dependents, tasks cannot
327 // be invoked by any user program calls
328 if( d instanceof MethodDescriptor ) {
329 MethodDescriptor md = (MethodDescriptor) d;
330 Set dependents = callGraph.getCallerSet(md);
331 if( dependents != null ) {
332 Iterator depItr = dependents.iterator();
333 while( depItr.hasNext() ) {
334 Descriptor dependent = (Descriptor) depItr.next();
335 if( descriptorsToAnalyze.contains( dependent ) ) {
336 descriptorsToVisit.add(dependent);
347 // keep passing the Descriptor of the method along for debugging
348 // and dot file writing
349 private OwnershipGraph
350 analyzeFlatMethod(Descriptor mDesc,
351 FlatMethod flatm) throws java.io.IOException {
353 // initialize flat nodes to visit as the flat method
354 // because all other nodes in this flat method are
355 // decendents of the flat method itself
356 flatNodesToVisit = new HashSet<FlatNode>();
357 flatNodesToVisit.add(flatm);
359 // initilize the mapping of flat nodes in this flat method to
360 // ownership graph results to an empty mapping
361 mapFlatNodeToOwnershipGraph = new Hashtable<FlatNode, OwnershipGraph>();
363 // initialize the set of return nodes that will be combined as
364 // the final ownership graph result to return as an empty set
365 returnNodesToCombineForCompleteOwnershipGraph = new HashSet<FlatReturnNode>();
367 while( !flatNodesToVisit.isEmpty() ) {
368 FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
369 flatNodesToVisit.remove(fn);
371 // perform this node's contributions to the ownership
372 // graph on a new copy, then compare it to the old graph
373 // at this node to see if anything was updated.
374 OwnershipGraph og = new OwnershipGraph(allocationDepth);
376 // start by merging all node's parents' graphs
377 for( int i = 0; i < fn.numPrev(); ++i ) {
378 FlatNode pn = fn.getPrev(i);
379 OwnershipGraph ogParent = getGraphFromFlatNode(pn);
383 // apply the analysis of the flat node to the
384 // ownership graph made from the merge of the
386 analyzeFlatNode(mDesc,
388 returnNodesToCombineForCompleteOwnershipGraph,
391 // if the results of the new graph are different from
392 // the current graph at this node, replace the graph
393 // with the update and enqueue the children for
395 OwnershipGraph ogPrev = getGraphFromFlatNode(fn);
397 if( !og.equals(ogPrev) ) {
398 setGraphForFlatNode(fn, og);
400 for( int i = 0; i < fn.numNext(); i++ ) {
401 FlatNode nn = fn.getNext(i);
402 flatNodesToVisit.add(nn);
407 // end by merging all return nodes into a complete
408 // ownership graph that represents all possible heap
409 // states after the flat method returns
410 OwnershipGraph completeGraph = new OwnershipGraph(allocationDepth);
411 Iterator retItr = returnNodesToCombineForCompleteOwnershipGraph.iterator();
412 while( retItr.hasNext() ) {
413 FlatReturnNode frn = (FlatReturnNode) retItr.next();
414 OwnershipGraph ogr = getGraphFromFlatNode(frn);
415 completeGraph.merge(ogr);
417 return completeGraph;
422 analyzeFlatNode(Descriptor methodDesc,
424 HashSet<FlatReturnNode> setRetNodes,
425 OwnershipGraph og) throws java.io.IOException {
431 // use node type to decide what alterations to make
432 // to the ownership graph
433 switch( fn.kind() ) {
435 case FKind.FlatMethod:
436 FlatMethod fm = (FlatMethod) fn;
438 // there should only be one FlatMethod node as the
439 // parent of all other FlatNode objects, so take
440 // the opportunity to construct the initial graph by
441 // adding parameters labels to new heap regions
442 for( int i = 0; i < fm.numParameters(); ++i ) {
443 TempDescriptor tdParam = fm.getParameter(i);
444 og.assignParameterAllocationToTemp(methodDesc instanceof TaskDescriptor,
451 case FKind.FlatOpNode:
452 FlatOpNode fon = (FlatOpNode) fn;
453 if( fon.getOp().getOp() == Operation.ASSIGN ) {
456 og.assignTempYToTempX(src, dst);
460 case FKind.FlatFieldNode:
461 FlatFieldNode ffn = (FlatFieldNode) fn;
464 fld = ffn.getField();
465 if( !fld.getType().isPrimitive() ) {
466 og.assignTempYFieldFToTempX(src, fld, dst);
470 case FKind.FlatSetFieldNode:
471 FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
474 fld = fsfn.getField();
475 og.assignTempYToTempXFieldF(src, dst, fld);
479 FlatNew fnn = (FlatNew) fn;
481 AllocationSite as = getAllocationSiteFromFlatNewPRIVATE(fnn);
483 og.assignNewAllocationToTempX(dst, as);
487 FlatCall fc = (FlatCall) fn;
488 MethodDescriptor md = fc.getMethod();
489 FlatMethod flatm = state.getMethodFlat(md);
490 OwnershipGraph ogAllPossibleCallees = new OwnershipGraph(allocationDepth);
492 if( md.isStatic() ) {
493 // a static method is simply always the same, makes life easy
494 OwnershipGraph onlyPossibleCallee = mapDescriptorToCompleteOwnershipGraph.get(md);
495 ogAllPossibleCallees.merge(onlyPossibleCallee);
498 // if the method descriptor is virtual, then there could be a
499 // set of possible methods that will actually be invoked, so
500 // find all of them and merge all of their graphs together
501 TypeDescriptor typeDesc = fc.getThis().getType();
502 Set possibleCallees = callGraph.getMethods(md, typeDesc);
504 Iterator i = possibleCallees.iterator();
505 while( i.hasNext() ) {
506 MethodDescriptor possibleMd = (MethodDescriptor) i.next();
507 OwnershipGraph ogPotentialCallee = mapDescriptorToCompleteOwnershipGraph.get(possibleMd);
508 ogAllPossibleCallees.merge(ogPotentialCallee);
512 // Now we should have the following information to resolve this method call:
514 // 1. A FlatCall fc to query for the caller's context (argument labels, etc)
516 // 2. Whether the method is static; if not we need to deal with the "this" pointer
518 // *******************************************************************************************
519 // 3. The original FlatMethod flatm to query for callee's context (paramter labels)
520 // NOTE! I assume FlatMethod before virtual dispatch accurately describes all possible methods!
521 // *******************************************************************************************
523 // 4. The OwnershipGraph ogAllPossibleCallees is a merge of every ownership graph of all the possible
524 // methods to capture any possible references made.
526 og.resolveMethodCall(fc, md.isStatic(), flatm, ogAllPossibleCallees);
529 case FKind.FlatReturnNode:
530 FlatReturnNode frn = (FlatReturnNode) fn;
531 setRetNodes.add(frn);
537 // this method should generate integers strictly greater than zero!
538 // special "shadow" regions are made from a heap region by negating
540 static public Integer generateUniqueHeapRegionNodeID() {
542 return new Integer(uniqueIDcount);
546 private OwnershipGraph getGraphFromFlatNode(FlatNode fn) {
547 if( !mapFlatNodeToOwnershipGraph.containsKey(fn) ) {
548 mapFlatNodeToOwnershipGraph.put(fn, new OwnershipGraph(allocationDepth) );
551 return mapFlatNodeToOwnershipGraph.get(fn);
554 private void setGraphForFlatNode(FlatNode fn, OwnershipGraph og) {
555 mapFlatNodeToOwnershipGraph.put(fn, og);
563 // return just the allocation site associated with one FlatNew node
564 private AllocationSite getAllocationSiteFromFlatNewPRIVATE(FlatNew fn) {
566 if( !mapFlatNewToAllocationSite.containsKey(fn) ) {
567 AllocationSite as = new AllocationSite(allocationDepth, fn.getType() );
569 // the newest nodes are single objects
570 for( int i = 0; i < allocationDepth; ++i ) {
571 Integer id = generateUniqueHeapRegionNodeID();
572 as.setIthOldest(i, id);
575 // the oldest node is a summary node
576 Integer idSummary = generateUniqueHeapRegionNodeID();
577 as.setSummary(idSummary);
579 mapFlatNewToAllocationSite.put(fn, as);
582 return mapFlatNewToAllocationSite.get(fn);
586 // return all allocation sites in the method (there is one allocation
587 // site per FlatNew node in a method)
588 private HashSet<AllocationSite> getAllocationSiteSet(Descriptor d) {
589 if( !mapDescriptorToAllocationSiteSet.containsKey(d) ) {
590 buildAllocationSiteSet(d);
593 return mapDescriptorToAllocationSiteSet.get(d);
597 private void buildAllocationSiteSet(Descriptor d) {
598 HashSet<AllocationSite> s = new HashSet<AllocationSite>();
601 if( d instanceof MethodDescriptor ) {
602 fm = state.getMethodFlat( (MethodDescriptor) d);
604 assert d instanceof TaskDescriptor;
605 fm = state.getMethodFlat( (TaskDescriptor) d);
608 // visit every node in this FlatMethod's IR graph
609 // and make a set of the allocation sites from the
610 // FlatNew node's visited
611 HashSet<FlatNode> visited = new HashSet<FlatNode>();
612 HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
615 while( !toVisit.isEmpty() ) {
616 FlatNode n = toVisit.iterator().next();
618 if( n instanceof FlatNew ) {
619 s.add(getAllocationSiteFromFlatNewPRIVATE( (FlatNew) n) );
625 for( int i = 0; i < n.numNext(); ++i ) {
626 FlatNode child = n.getNext(i);
627 if( !visited.contains(child) ) {
633 mapDescriptorToAllocationSiteSet.put(d, s);
637 private HashSet<AllocationSite>
638 getFlaggedAllocationSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
640 HashSet<AllocationSite> asSetTotal = new HashSet<AllocationSite>();
641 HashSet<Descriptor> toVisit = new HashSet<Descriptor>();
642 HashSet<Descriptor> visited = new HashSet<Descriptor>();
646 // traverse this task and all methods reachable from this task
647 while( !toVisit.isEmpty() ) {
648 Descriptor d = toVisit.iterator().next();
652 HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
653 Iterator asItr = asSet.iterator();
654 while( asItr.hasNext() ) {
655 AllocationSite as = (AllocationSite) asItr.next();
656 if( as.getType().getClassDesc().hasFlags() ) {
661 // enqueue callees of this method to be searched for
662 // allocation sites also
663 Set callees = callGraph.getCalleeSet(d);
664 if( callees != null ) {
665 Iterator methItr = callees.iterator();
666 while( methItr.hasNext() ) {
667 MethodDescriptor md = (MethodDescriptor) methItr.next();
669 if( !visited.contains(md) ) {
682 private HashSet<Integer> getHeapRegionIDset(OwnershipGraph og,
685 assert og.paramIndex2id.containsKey(paramIndex);
686 Integer idParam = og.paramIndex2id.get(paramIndex);
688 HashSet<Integer> idSet = new HashSet<Integer>();
695 private HashSet<Integer> getHeapRegionIDset(AllocationSite alloc) {
697 HashSet<Integer> idSet = new HashSet<Integer>();
699 for( int i = 0; i < alloc.getAllocationDepth(); ++i ) {
700 Integer id = alloc.getIthOldest(i);
704 Integer idSummary = alloc.getSummary();
705 idSet.add(idSummary);
710 private HashSet<Integer> getHeapRegionIDset(HashSet<AllocationSite> allocSet) {
712 HashSet<Integer> idSet = new HashSet<Integer>();
714 Iterator allocItr = allocSet.iterator();
715 while( allocItr.hasNext() ) {
716 AllocationSite alloc = (AllocationSite) allocItr.next();
718 for( int i = 0; i < alloc.getAllocationDepth(); ++i ) {
719 Integer id = alloc.getIthOldest(i);
723 Integer idSummary = alloc.getSummary();
724 idSet.add(idSummary);
730 private boolean createsPotentialAliases(OwnershipGraph og,
731 HashSet<Integer> idSetA,
732 HashSet<Integer> idSetB) {
733 boolean potentialAlias = false;
736 // first expand set B into the set of all heap region node ID's
737 // reachable from the nodes in set B
738 HashSet<Integer> idSetReachableFromB = og.getReachableSet( idSetB );
740 // then see if anything in A can reach a node in the set reachable
741 // from B. If so, there is a potential alias.
742 Iterator i = idSetA.iterator();
743 while( i.hasNext() ) {
744 Integer id = (Integer) i.next();
745 if( og.canIdReachSet( id, idSetB ) ) {