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 ///////////////////////////////////////////
18 public HashSet<AllocationSite>
19 getFlaggedAllocationSitesReachableFromTask( TaskDescriptor td ) {
21 return getFlaggedAllocationSitesReachableFromTaskPRIVATE( td );
24 public AllocationSite getAllocationSiteFromFlatNew( FlatNew fn ) {
25 return getAllocationSiteFromFlatNewPRIVATE( fn );
28 public boolean createsPotentialAliases( Descriptor taskOrMethod,
32 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get( taskOrMethod );
35 return createsPotentialAliases( og,
36 getHeapRegionIDset( og, paramIndex1 ),
37 getHeapRegionIDset( og, paramIndex2 ) );
40 public boolean createsPotentialAliases( Descriptor taskOrMethod,
42 AllocationSite alloc ) {
44 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get( taskOrMethod );
47 return createsPotentialAliases( og,
48 getHeapRegionIDset( og, paramIndex ),
49 getHeapRegionIDset( alloc ) );
52 public boolean createsPotentialAliases( Descriptor taskOrMethod,
56 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get( taskOrMethod );
59 return createsPotentialAliases( og,
60 getHeapRegionIDset( og, paramIndex ),
61 getHeapRegionIDset( alloc ) );
64 public boolean createsPotentialAliases( Descriptor taskOrMethod,
65 AllocationSite alloc1,
66 AllocationSite alloc2 ) {
68 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get( taskOrMethod );
71 return createsPotentialAliases( og,
72 getHeapRegionIDset( alloc1 ),
73 getHeapRegionIDset( alloc2 ) );
76 public boolean createsPotentialAliases( Descriptor taskOrMethod,
78 HashSet<AllocationSite> allocSet ) {
80 OwnershipGraph og = mapDescriptorToCompleteOwnershipGraph.get( taskOrMethod );
83 return createsPotentialAliases( og,
84 getHeapRegionIDset( alloc ),
85 getHeapRegionIDset( allocSet ) );
88 // use the methods given above to check every possible alias
89 // between task parameters and flagged allocation sites reachable
91 public void writeAllAliases( String outputFile ) throws java.io.IOException {
93 BufferedWriter bw = new BufferedWriter( new FileWriter( outputFile ) );
95 // look through every task for potential aliases
96 Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
97 while( taskItr.hasNext() ) {
98 TaskDescriptor td = (TaskDescriptor) taskItr.next();
100 HashSet<AllocationSite> allocSites = getFlaggedAllocationSitesReachableFromTask( td );
102 // for each task parameter, check for aliases with
103 // other task parameters and every allocation site
104 // reachable from this task
105 FlatMethod fm = state.getMethodFlat( td );
106 for( int i = 0; i < fm.numParameters(); ++i ) {
108 // for the ith parameter check for aliases to all
109 // higher numbered parameters
110 for( int j = i + 1; j < fm.numParameters(); ++j ) {
111 if( createsPotentialAliases( td, i, j ) ) {
112 bw.write( "Task "+td+" potentially aliases parameters "+i+" and "+j+".\n" );
116 // for the ith parameter, check for aliases against
117 // the set of allocation sites reachable from this
119 Iterator allocItr = allocSites.iterator();
120 while( allocItr.hasNext() ) {
121 AllocationSite as = (AllocationSite) allocItr.next();
122 if( createsPotentialAliases( td, i, as ) ) {
123 bw.write( "Task "+td+" potentially aliases parameter "+i+" and "+as+".\n" );
128 // for each allocation site check for aliases with
129 // other allocation sites in the context of execution
131 Iterator allocItr = allocSites.iterator();
132 while( allocItr.hasNext() ) {
133 AllocationSite as = (AllocationSite) allocItr.next();
134 if( createsPotentialAliases( td, as, allocSites ) ) {
135 bw.write( "Task "+td+" potentially aliases "+as+" and the rest of the set.\n" );
143 ///////////////////////////////////////////
145 // end public interface
147 ///////////////////////////////////////////
156 // data from the compiler
158 private CallGraph callGraph;
159 private int allocationDepth;
161 // used to identify HeapRegionNode objects
162 // A unique ID equates an object in one
163 // ownership graph with an object in another
164 // graph that logically represents the same
166 static private int uniqueIDcount = 0;
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 HashSet <Descriptor> descriptorsToVisit;
174 private Hashtable<Descriptor, OwnershipGraph> mapDescriptorToCompleteOwnershipGraph;
175 private Hashtable<FlatNew, AllocationSite> mapFlatNewToAllocationSite;
176 private Hashtable<Descriptor, HashSet<AllocationSite> > mapDescriptorToAllocationSiteSet;
178 // Use these data structures to track progress of one pass of
179 // processing the FlatNodes of a particular method
180 private HashSet <FlatNode> flatNodesToVisit;
181 private Hashtable<FlatNode, OwnershipGraph> mapFlatNodeToOwnershipGraph;
182 private HashSet <FlatReturnNode> returnNodesToCombineForCompleteOwnershipGraph;
185 // this analysis generates an ownership graph for every task
187 public OwnershipAnalysis( State state,
189 int allocationDepth ) throws java.io.IOException {
191 this.callGraph = callGraph;
192 this.allocationDepth = allocationDepth;
194 descriptorsToVisit = new HashSet<Descriptor>();
196 mapDescriptorToCompleteOwnershipGraph =
197 new Hashtable<Descriptor, OwnershipGraph>();
199 mapFlatNewToAllocationSite =
200 new Hashtable<FlatNew, AllocationSite>();
202 mapDescriptorToAllocationSiteSet =
203 new Hashtable<Descriptor, HashSet<AllocationSite> >();
205 // use this set to prevent infinite recursion when
206 // traversing the call graph
207 HashSet<Descriptor> calleesScheduled = new HashSet<Descriptor>();
210 // initialize methods to visit as the set of all tasks in the
211 // program and then any method that could be called starting
213 Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
214 while( taskItr.hasNext() ) {
215 Descriptor d = (Descriptor) taskItr.next();
216 descriptorsToVisit.add( d );
218 // recursively find all callees from this task
219 scheduleAllCallees( calleesScheduled, d );
222 // before beginning analysis, initialize every scheduled method
223 // with an ownership graph that has populated parameter index tables
224 // by analyzing the first node which is always a FlatMethod node
225 Iterator<Descriptor> dItr = calleesScheduled.iterator();
226 while( dItr.hasNext() ) {
227 Descriptor d = dItr.next();
228 OwnershipGraph og = new OwnershipGraph( allocationDepth );
231 if( d instanceof MethodDescriptor ) {
232 fm = state.getMethodFlat( (MethodDescriptor) d );
234 assert d instanceof TaskDescriptor;
235 fm = state.getMethodFlat( (TaskDescriptor) d );
238 analyzeFlatNode( d, fm, null, og );
239 mapDescriptorToCompleteOwnershipGraph.put( d, og );
242 // as mentioned above, analyze methods one-by-one, possibly revisiting
243 // a method if the methods that it calls are updated
247 // called from the constructor to help initialize the set
248 // of methods that needs to be analyzed by ownership analysis
249 private void scheduleAllCallees( HashSet<Descriptor> calleesScheduled,
251 if( calleesScheduled.contains( d ) ) {
254 calleesScheduled.add( d );
256 Set callees = callGraph.getCalleeSet( d );
257 if( callees == null ) {
261 Iterator methItr = callees.iterator();
262 while( methItr.hasNext() ) {
263 MethodDescriptor md = (MethodDescriptor) methItr.next();
264 descriptorsToVisit.add( md );
266 // recursively find all callees from this task
267 scheduleAllCallees( calleesScheduled, md );
272 // manage the set of tasks and methods to be analyzed
273 // and be sure to reschedule tasks/methods when the methods
274 // they call are updated
275 private void analyzeMethods() throws java.io.IOException {
277 while( !descriptorsToVisit.isEmpty() ) {
278 Descriptor d = (Descriptor) descriptorsToVisit.iterator().next();
279 descriptorsToVisit.remove( d );
281 // because the task or method descriptor just extracted
282 // was in the "to visit" set it either hasn't been analyzed
283 // yet, or some method that it depends on has been
284 // updated. Recompute a complete ownership graph for
285 // this task/method and compare it to any previous result.
286 // If there is a change detected, add any methods/tasks
287 // that depend on this one to the "to visit" set.
289 System.out.println( "Analyzing " + d );
292 if( d instanceof MethodDescriptor ) {
293 fm = state.getMethodFlat( (MethodDescriptor) d );
295 assert d instanceof TaskDescriptor;
296 fm = state.getMethodFlat( (TaskDescriptor) d );
299 OwnershipGraph og = analyzeFlatMethod( d, fm );
300 OwnershipGraph ogPrev = mapDescriptorToCompleteOwnershipGraph.get( d );
301 if( !og.equals( ogPrev ) ) {
302 mapDescriptorToCompleteOwnershipGraph.put( d, og );
304 og.writeGraph( d, true, true, true, false );
306 // only methods have dependents, tasks cannot
307 // be invoked by any user program calls
308 if( d instanceof MethodDescriptor ) {
309 MethodDescriptor md = (MethodDescriptor) d;
310 Set dependents = callGraph.getCallerSet( md );
311 if( dependents != null ) {
312 descriptorsToVisit.addAll( dependents );
324 // keep passing the Descriptor of the method along for debugging
325 // and dot file writing
326 private OwnershipGraph
327 analyzeFlatMethod( Descriptor mDesc,
328 FlatMethod flatm ) throws java.io.IOException {
330 // initialize flat nodes to visit as the flat method
331 // because all other nodes in this flat method are
332 // decendents of the flat method itself
333 flatNodesToVisit = new HashSet<FlatNode>();
334 flatNodesToVisit.add( flatm );
336 // initilize the mapping of flat nodes in this flat method to
337 // ownership graph results to an empty mapping
338 mapFlatNodeToOwnershipGraph = new Hashtable<FlatNode, OwnershipGraph>();
340 // initialize the set of return nodes that will be combined as
341 // the final ownership graph result to return as an empty set
342 returnNodesToCombineForCompleteOwnershipGraph = new HashSet<FlatReturnNode>();
344 while( !flatNodesToVisit.isEmpty() ) {
345 FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
346 flatNodesToVisit.remove( fn );
348 // perform this node's contributions to the ownership
349 // graph on a new copy, then compare it to the old graph
350 // at this node to see if anything was updated.
351 OwnershipGraph og = new OwnershipGraph( allocationDepth );
353 // start by merging all node's parents' graphs
354 for( int i = 0; i < fn.numPrev(); ++i ) {
355 FlatNode pn = fn.getPrev( i );
356 OwnershipGraph ogParent = getGraphFromFlatNode( pn );
357 og.merge( ogParent );
360 // apply the analysis of the flat node to the
361 // ownership graph made from the merge of the
363 analyzeFlatNode( mDesc,
365 returnNodesToCombineForCompleteOwnershipGraph,
368 // if the results of the new graph are different from
369 // the current graph at this node, replace the graph
370 // with the update and enqueue the children for
372 OwnershipGraph ogPrev = getGraphFromFlatNode( fn );
374 if( !og.equals( ogPrev ) ) {
375 setGraphForFlatNode( fn, og );
381 String s = String.format( "debug%04d", x );
382 //og.writeGraph( s, true, true, true, false );
387 for( int i = 0; i < fn.numNext(); i++ ) {
388 FlatNode nn = fn.getNext( i );
389 flatNodesToVisit.add( nn );
394 // end by merging all return nodes into a complete
395 // ownership graph that represents all possible heap
396 // states after the flat method returns
397 OwnershipGraph completeGraph = new OwnershipGraph( allocationDepth );
398 Iterator retItr = returnNodesToCombineForCompleteOwnershipGraph.iterator();
399 while( retItr.hasNext() ) {
400 FlatReturnNode frn = (FlatReturnNode) retItr.next();
401 OwnershipGraph ogr = getGraphFromFlatNode( frn );
402 completeGraph.merge( ogr );
404 return completeGraph;
409 analyzeFlatNode( Descriptor methodDesc,
411 HashSet<FlatReturnNode> setRetNodes,
412 OwnershipGraph og ) throws java.io.IOException {
418 // use node type to decide what alterations to make
419 // to the ownership graph
420 switch( fn.kind() ) {
422 case FKind.FlatMethod:
423 FlatMethod fm = (FlatMethod) fn;
425 // there should only be one FlatMethod node as the
426 // parent of all other FlatNode objects, so take
427 // the opportunity to construct the initial graph by
428 // adding parameters labels to new heap regions
429 for( int i = 0; i < fm.numParameters(); ++i ) {
430 TempDescriptor tdParam = fm.getParameter( i );
431 og.assignTempToParameterAllocation( methodDesc instanceof TaskDescriptor,
438 case FKind.FlatOpNode:
439 FlatOpNode fon = (FlatOpNode) fn;
440 if( fon.getOp().getOp() == Operation.ASSIGN ) {
443 og.assignTempToTemp( src, dst );
447 case FKind.FlatFieldNode:
448 FlatFieldNode ffn = (FlatFieldNode) fn;
451 fld = ffn.getField();
452 if( !fld.getType().isPrimitive() ) {
453 og.assignTempToField( src, dst, fld );
457 case FKind.FlatSetFieldNode:
458 FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
461 fld = fsfn.getField();
462 og.assignFieldToTemp( src, dst, fld );
466 FlatNew fnn = (FlatNew) fn;
468 AllocationSite as = getAllocationSiteFromFlatNewPRIVATE( fnn );
470 og.assignTempToNewAllocation( dst, as );
474 FlatCall fc = (FlatCall) fn;
475 MethodDescriptor md = fc.getMethod();
476 FlatMethod flatm = state.getMethodFlat( md );
477 //HashSet<AllocationSite> allocSiteSet = getAllocationSiteSet( md );
478 OwnershipGraph ogAllPossibleCallees = new OwnershipGraph( allocationDepth );
480 if( md.isStatic() ) {
481 // a static method is simply always the same, makes life easy
482 OwnershipGraph onlyPossibleCallee = mapDescriptorToCompleteOwnershipGraph.get( md );
483 ogAllPossibleCallees.merge( onlyPossibleCallee );
486 if( onlyPossibleCallee != null ) {
487 onlyPossibleCallee.writeGraph( "only", false, false );
488 System.out.println( "There was only one possible callee, "+md );
493 // if the method descriptor is virtual, then there could be a
494 // set of possible methods that will actually be invoked, so
495 // find all of them and merge all of their graphs together
496 TypeDescriptor typeDesc = fc.getThis().getType();
497 Set possibleCallees = callGraph.getMethods( md, typeDesc );
501 Iterator i = possibleCallees.iterator();
502 while( i.hasNext() ) {
503 MethodDescriptor possibleMd = (MethodDescriptor) i.next();
504 //allocSiteSet.addAll( getAllocationSiteSet( possibleMd ) );
505 OwnershipGraph ogPotentialCallee = mapDescriptorToCompleteOwnershipGraph.get( possibleMd );
508 if( ogPotentialCallee != null ) {
509 ogPotentialCallee.writeGraph( "potential"+j, false, false );
514 ogAllPossibleCallees.merge( ogPotentialCallee );
517 //System.out.println( "There were "+j+" potential callees merged together." );
520 //System.out.println( "AllocationSiteSet has "+allocSiteSet.size()+" items." );
522 // now we should have the following information to resolve this method call:
524 // 1. A FlatCall fc to query for the caller's context (argument labels, etc)
526 // 2. Whether the method is static; if not we need to deal with the "this" pointer
528 // *******************************************************************************************
529 // 3. The original FlatMethod flatm to query for callee's context (paramter labels)
530 // NOTE! I assume FlatMethod before virtual dispatch accurately describes all possible methods!
531 // *******************************************************************************************
533 // 4. The OwnershipGraph ogAllPossibleCallees is a merge of every ownership graph of all the possible
534 // methods to capture any possible references made.
536 // 5. The Set of AllocationSite objects, allocSiteSet that is the set of allocation sites from
537 // every possible method we might have chosen
539 og.resolveMethodCall( fc, md.isStatic(), flatm, ogAllPossibleCallees );
542 case FKind.FlatReturnNode:
543 FlatReturnNode frn = (FlatReturnNode) fn;
544 setRetNodes.add( frn );
550 // this method should generate integers strictly greater than zero!
551 // special "shadow" regions are made from a heap region by negating
553 static public Integer generateUniqueHeapRegionNodeID() {
555 return new Integer( uniqueIDcount );
559 private OwnershipGraph getGraphFromFlatNode( FlatNode fn ) {
560 if( !mapFlatNodeToOwnershipGraph.containsKey( fn ) ) {
561 mapFlatNodeToOwnershipGraph.put( fn, new OwnershipGraph( allocationDepth ) );
564 return mapFlatNodeToOwnershipGraph.get( fn );
567 private void setGraphForFlatNode( FlatNode fn, OwnershipGraph og ) {
568 mapFlatNodeToOwnershipGraph.put( fn, og );
576 // return just the allocation site associated with one FlatNew node
577 private AllocationSite getAllocationSiteFromFlatNewPRIVATE( FlatNew fn ) {
579 if( !mapFlatNewToAllocationSite.containsKey( fn ) ) {
580 AllocationSite as = new AllocationSite( allocationDepth, fn.getType() );
582 // the newest nodes are single objects
583 for( int i = 0; i < allocationDepth; ++i ) {
584 Integer id = generateUniqueHeapRegionNodeID();
585 as.setIthOldest( i, id );
588 // the oldest node is a summary node
589 Integer idSummary = generateUniqueHeapRegionNodeID();
590 as.setSummary( idSummary );
592 mapFlatNewToAllocationSite.put( fn, as );
595 return mapFlatNewToAllocationSite.get( fn );
599 // return all allocation sites in the method (there is one allocation
600 // site per FlatNew node in a method)
601 private HashSet<AllocationSite> getAllocationSiteSet( Descriptor d ) {
602 if( !mapDescriptorToAllocationSiteSet.containsKey( d ) ) {
603 buildAllocationSiteSet( d );
606 return mapDescriptorToAllocationSiteSet.get( d );
610 private void buildAllocationSiteSet( Descriptor d ) {
611 HashSet<AllocationSite> s = new HashSet<AllocationSite>();
614 if( d instanceof MethodDescriptor ) {
615 fm = state.getMethodFlat( (MethodDescriptor) d );
617 assert d instanceof TaskDescriptor;
618 fm = state.getMethodFlat( (TaskDescriptor) d );
621 // visit every node in this FlatMethod's IR graph
622 // and make a set of the allocation sites from the
623 // FlatNew node's visited
624 HashSet<FlatNode> visited = new HashSet<FlatNode>();
625 HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
628 while( !toVisit.isEmpty() ) {
629 FlatNode n = toVisit.iterator().next();
631 if( n instanceof FlatNew ) {
632 s.add( getAllocationSiteFromFlatNewPRIVATE( (FlatNew) n ) );
638 for( int i = 0; i < n.numNext(); ++i ) {
639 FlatNode child = n.getNext( i );
640 if( !visited.contains( child ) ) {
641 toVisit.add( child );
646 mapDescriptorToAllocationSiteSet.put( d, s );
650 private HashSet<AllocationSite>
651 getFlaggedAllocationSitesReachableFromTaskPRIVATE( TaskDescriptor td ) {
653 HashSet<AllocationSite> asSetTotal = new HashSet<AllocationSite>();
654 HashSet<Descriptor> toVisit = new HashSet<Descriptor>();
655 HashSet<Descriptor> visited = new HashSet<Descriptor>();
659 // traverse this task and all methods reachable from this task
660 while( !toVisit.isEmpty() ) {
661 Descriptor d = toVisit.iterator().next();
665 HashSet<AllocationSite> asSet = getAllocationSiteSet( d );
666 Iterator asItr = asSet.iterator();
667 while( asItr.hasNext() ) {
668 AllocationSite as = (AllocationSite) asItr.next();
669 if( as.getType().getClassDesc().hasFlags() ) {
670 asSetTotal.add( as );
674 // enqueue callees of this method to be searched for
675 // allocation sites also
676 Set callees = callGraph.getCalleeSet( d );
677 if( callees != null ) {
678 Iterator methItr = callees.iterator();
679 while( methItr.hasNext() ) {
680 MethodDescriptor md = (MethodDescriptor) methItr.next();
682 if( !visited.contains( md ) ) {
695 private HashSet<Integer> getHeapRegionIDset( OwnershipGraph og,
698 assert og.paramIndex2id.containsKey( paramIndex );
699 Integer idParam = og.paramIndex2id.get( paramIndex );
701 HashSet<Integer> idSet = new HashSet<Integer>();
702 idSet.add( idParam );
708 private HashSet<Integer> getHeapRegionIDset( AllocationSite alloc ) {
710 HashSet<Integer> idSet = new HashSet<Integer>();
712 for( int i = 0; i < alloc.getAllocationDepth(); ++i ) {
713 Integer id = alloc.getIthOldest( i );
717 Integer idSummary = alloc.getSummary();
718 idSet.add( idSummary );
723 private HashSet<Integer> getHeapRegionIDset( HashSet<AllocationSite> allocSet ) {
725 HashSet<Integer> idSet = new HashSet<Integer>();
727 Iterator allocItr = allocSet.iterator();
728 while( allocItr.hasNext() ) {
729 AllocationSite alloc = (AllocationSite) allocItr.next();
731 for( int i = 0; i < alloc.getAllocationDepth(); ++i ) {
732 Integer id = alloc.getIthOldest( i );
736 Integer idSummary = alloc.getSummary();
737 idSet.add( idSummary );
743 private boolean createsPotentialAliases( OwnershipGraph og,
744 HashSet<Integer> idSetA,
745 HashSet<Integer> idSetB ) {
746 boolean potentialAlias = false;
748 // first expand set B into the set of all heap region node ID's
749 // reachable from the nodes in set B
750 HashSet<Integer> idSetReachableFromB = og.getReachableSet( idSetB );
752 // then see if anything in A can reach a node in the set reachable
753 // from B. If so, there is a potential alias.
754 Iterator i = idSetA.iterator();
755 while( i.hasNext() ) {
756 Integer id = (Integer) i.next();
757 if( og.canIdReachSet( id, idSetB ) ) {