486645220e470ba89a96b7bd7f74b32ee3aa8ae1
[IRC.git] / Robust / src / Analysis / OwnershipAnalysis / OwnershipAnalysis.java
1 package Analysis.OwnershipAnalysis;
2
3 import Analysis.CallGraph.*;
4 import IR.*;
5 import IR.Flat.*;
6 import java.util.*;
7 import java.io.*;
8
9
10 public class OwnershipAnalysis {
11
12     // from the compiler
13     private State     state;
14     private CallGraph callGraph;
15     private int       allocationDepth;
16
17
18     // used to identify HeapRegionNode objects
19     // A unique ID equates an object in one
20     // ownership graph with an object in another
21     // graph that logically represents the same
22     // heap region
23     static private int uniqueIDcount = 0;
24
25
26     // Use these data structures to track progress of 
27     // processing all methods in the program, and by methods
28     // TaskDescriptor and MethodDescriptor are combined 
29     // together, with a common parent class Descriptor
30     private HashSet  <Descriptor>                           descriptorsToVisit;
31     private Hashtable<Descriptor, OwnershipGraph>           mapDescriptorToCompleteOwnershipGraph;
32     private Hashtable<FlatNew,    AllocationSite>           mapFlatNewToAllocationSite;
33     private Hashtable<Descriptor, HashSet<AllocationSite> > mapDescriptorToAllocationSiteSet;
34
35     // Use these data structures to track progress of one pass of
36     // processing the FlatNodes of a particular method
37     private HashSet  <FlatNode>                 flatNodesToVisit;
38     private Hashtable<FlatNode, OwnershipGraph> mapFlatNodeToOwnershipGraph;    
39     private HashSet  <FlatReturnNode>           returnNodesToCombineForCompleteOwnershipGraph;
40
41
42     // this analysis generates an ownership graph for every task
43     // in the program
44     public OwnershipAnalysis( State     state,
45                               CallGraph callGraph, 
46                               int       allocationDepth ) throws java.io.IOException {
47         this.state           = state;      
48         this.callGraph       = callGraph;
49         this.allocationDepth = allocationDepth;
50
51         descriptorsToVisit = new HashSet<Descriptor>();
52
53         mapDescriptorToCompleteOwnershipGraph =
54             new Hashtable<Descriptor, OwnershipGraph>();
55
56         mapFlatNewToAllocationSite =
57             new Hashtable<FlatNew, AllocationSite>();
58
59         mapDescriptorToAllocationSiteSet =
60             new Hashtable<Descriptor, HashSet<AllocationSite> >();
61
62         // use this set to prevent infinite recursion when
63         // traversing the call graph
64         HashSet<Descriptor> calleesScheduled = new HashSet<Descriptor>();
65
66         // initialize methods to visit as the set of all tasks in the
67         // program and then any method that could be called starting
68         // from those tasks
69         Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
70         while( taskItr.hasNext() ) {
71             Descriptor d = (Descriptor) taskItr.next();
72             descriptorsToVisit.add( d );
73
74             // recursively find all callees from this task
75             scheduleAllCallees( calleesScheduled, d );
76         }
77         
78         // as mentioned above, analyze methods one-by-one, possibly revisiting
79         // a method if the methods that it calls are updated
80         analyzeMethods();
81     }
82
83     // called from the constructor to help initialize the set
84     // of methods that needs to be analyzed by ownership analysis
85     private void scheduleAllCallees( HashSet<Descriptor> calleesScheduled,
86                                      Descriptor d ) {
87         if( calleesScheduled.contains( d ) ) {
88             return;
89         }
90         calleesScheduled.add( d );
91
92         Set callees = callGraph.getCalleeSet( d );
93         if( callees == null ) {
94             return;
95         }
96
97         Iterator methItr = callees.iterator();
98         while( methItr.hasNext() ) {
99             MethodDescriptor md = (MethodDescriptor) methItr.next();
100             descriptorsToVisit.add( md );
101
102             // recursively find all callees from this task
103             scheduleAllCallees( calleesScheduled, md );
104         }
105     }
106
107
108     // manage the set of tasks and methods to be analyzed
109     // and be sure to reschedule tasks/methods when the methods
110     // they call are updated
111     private void analyzeMethods() throws java.io.IOException {
112         
113         while( !descriptorsToVisit.isEmpty() ) {
114             Descriptor d = (Descriptor) descriptorsToVisit.iterator().next();
115             descriptorsToVisit.remove( d );
116
117             // because the task or method descriptor just extracted
118             // was in the "to visit" set it either hasn't been analyzed
119             // yet, or some method that it depends on has been
120             // updated.  Recompute a complete ownership graph for
121             // this task/method and compare it to any previous result.
122             // If there is a change detected, add any methods/tasks
123             // that depend on this one to the "to visit" set.
124
125             System.out.println( "Analyzing " + d );
126
127             FlatMethod fm;
128             if( d instanceof MethodDescriptor ) {
129                 fm = state.getMethodFlat( (MethodDescriptor) d );
130             } else {
131                 assert d instanceof TaskDescriptor;
132                 fm = state.getMethodFlat( (TaskDescriptor) d );
133             }
134             
135             OwnershipGraph og     = analyzeFlatMethod( d, fm );
136             OwnershipGraph ogPrev = mapDescriptorToCompleteOwnershipGraph.get( d );
137
138             if( !og.equals( ogPrev ) ) {
139                 mapDescriptorToCompleteOwnershipGraph.put( d, og );
140
141                 og.writeGraph( d );
142
143                 // only methods have dependents, tasks cannot
144                 // be invoked by any user program calls
145                 if( d instanceof MethodDescriptor ) {
146                     MethodDescriptor md = (MethodDescriptor) d;
147                     Set dependents = callGraph.getCallerSet( md );
148                     if( dependents != null ) {
149                         descriptorsToVisit.addAll( dependents );
150                     }
151                 }
152             }
153         }
154
155     }
156
157
158     // keep passing the Descriptor of the method along for debugging
159     // and dot file writing
160     private OwnershipGraph
161         analyzeFlatMethod( Descriptor mDesc,
162                            FlatMethod flatm ) throws java.io.IOException {
163
164         // initialize flat nodes to visit as the flat method
165         // because all other nodes in this flat method are 
166         // decendents of the flat method itself
167         flatNodesToVisit = new HashSet<FlatNode>();
168         flatNodesToVisit.add( flatm );
169
170         // initilize the mapping of flat nodes in this flat method to
171         // ownership graph results to an empty mapping
172         mapFlatNodeToOwnershipGraph = new Hashtable<FlatNode, OwnershipGraph>();
173
174         // initialize the set of return nodes that will be combined as
175         // the final ownership graph result to return as an empty set
176         returnNodesToCombineForCompleteOwnershipGraph = new HashSet<FlatReturnNode>();
177
178         while( !flatNodesToVisit.isEmpty() ) {
179             FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
180             flatNodesToVisit.remove( fn );
181
182             // perform this node's contributions to the ownership
183             // graph on a new copy, then compare it to the old graph
184             // at this node to see if anything was updated.
185             OwnershipGraph og = new OwnershipGraph( allocationDepth );
186
187             // start by merging all node's parents' graphs
188             for( int i = 0; i < fn.numPrev(); ++i ) {
189                 FlatNode       pn       = fn.getPrev( i );
190                 OwnershipGraph ogParent = getGraphFromFlatNode( pn );
191                 og.merge( ogParent );
192             }
193             
194             // apply the analysis of the flat node to the
195             // ownership graph made from the merge of the
196             // parent graphs
197             analyzeFlatNode( mDesc,
198                              fn, 
199                              returnNodesToCombineForCompleteOwnershipGraph,
200                              og );
201             
202             // if the results of the new graph are different from
203             // the current graph at this node, replace the graph
204             // with the update and enqueue the children for
205             // processing
206             OwnershipGraph ogPrev = getGraphFromFlatNode( fn );
207
208             if( !og.equals( ogPrev ) ) {
209                 setGraphForFlatNode( fn, og );
210
211                 for( int i = 0; i < fn.numNext(); i++ ) {
212                     FlatNode nn = fn.getNext( i );                
213                     flatNodesToVisit.add( nn );
214                 }
215             }
216         }
217
218         // end by merging all return nodes into a complete
219         // ownership graph that represents all possible heap
220         // states after the flat method returns
221         OwnershipGraph completeGraph = new OwnershipGraph( allocationDepth );
222         Iterator retItr = returnNodesToCombineForCompleteOwnershipGraph.iterator();
223         while( retItr.hasNext() ) {
224             FlatReturnNode frn = (FlatReturnNode) retItr.next();
225             OwnershipGraph ogr = getGraphFromFlatNode( frn );
226             completeGraph.merge( ogr );
227         }
228         return completeGraph;
229     }
230
231
232     private void 
233         analyzeFlatNode( Descriptor              methodDesc,
234                          FlatNode                fn,
235                          HashSet<FlatReturnNode> setRetNodes,
236                          OwnershipGraph          og ) throws java.io.IOException {
237
238         TempDescriptor  src;
239         TempDescriptor  dst;
240         FieldDescriptor fld;
241
242         // use node type to decide what alterations to make
243         // to the ownership graph           
244         switch( fn.kind() ) {
245             
246         case FKind.FlatMethod:
247             FlatMethod fm = (FlatMethod) fn;
248
249             // there should only be one FlatMethod node as the
250             // parent of all other FlatNode objects, so take
251             // the opportunity to construct the initial graph by
252             // adding parameters labels to new heap regions
253             for( int i = 0; i < fm.numParameters(); ++i ) {
254                 TempDescriptor tdParam = fm.getParameter( i );
255                 og.assignTempToParameterAllocation( methodDesc instanceof TaskDescriptor,
256                                                     tdParam,
257                                                     new Integer( i ) );
258                 //og.writeGraph( methodDesc, fn );
259             }
260
261             break;
262
263         case FKind.FlatOpNode:
264             FlatOpNode fon = (FlatOpNode) fn;
265             if( fon.getOp().getOp() == Operation.ASSIGN ) {
266                 src = fon.getLeft();
267                 dst = fon.getDest();
268                 og.assignTempToTemp( src, dst );
269                 //og.writeGraph( methodDesc, fn );
270             }
271             break;
272             
273         case FKind.FlatFieldNode:
274             FlatFieldNode ffn = (FlatFieldNode) fn;
275             src = ffn.getSrc();
276             dst = ffn.getDst();
277             fld = ffn.getField();
278             if( !fld.getType().isPrimitive() ) {
279                 og.assignTempToField( src, dst, fld );
280                 //og.writeGraph( methodDesc, fn );
281             }
282             break;
283             
284         case FKind.FlatSetFieldNode:
285             FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
286             src = fsfn.getSrc();
287             dst = fsfn.getDst();
288             fld = fsfn.getField();
289             og.assignFieldToTemp( src, dst, fld );
290             //og.writeGraph( methodDesc, fn );
291             break;
292             
293         case FKind.FlatNew:
294             FlatNew fnn = (FlatNew) fn;
295             dst = fnn.getDst();
296             AllocationSite as = getAllocationSiteFromFlatNew( fnn );
297
298             og.assignTempToNewAllocation( dst, as );
299             break;
300
301         case FKind.FlatCall:
302             FlatCall                fc           = (FlatCall) fn;
303             MethodDescriptor        md           = fc.getMethod();
304             FlatMethod              flatm        = state.getMethodFlat( md );
305             HashSet<AllocationSite> allocSiteSet = getAllocationSiteSet( md );
306             OwnershipGraph ogAllPossibleCallees  = new OwnershipGraph( allocationDepth );
307
308             if( md.isStatic() ) {
309                 // a static method is simply always the same, makes life easy
310                 OwnershipGraph onlyPossibleCallee = mapDescriptorToCompleteOwnershipGraph.get( md );
311                 ogAllPossibleCallees.merge( onlyPossibleCallee );
312
313             } else {
314                 // if the method descriptor is virtual, then there could be a
315                 // set of possible methods that will actually be invoked, so
316                 // find all of them and merge all of their graphs together
317                 TypeDescriptor typeDesc        = fc.getThis().getType();
318                 Set            possibleCallees = callGraph.getMethods( md, typeDesc );
319
320                 Iterator i = possibleCallees.iterator();
321                 while( i.hasNext() ) {
322                     MethodDescriptor possibleMd = (MethodDescriptor) i.next();
323                     allocSiteSet.addAll( getAllocationSiteSet( possibleMd ) );
324                     OwnershipGraph ogPotentialCallee = mapDescriptorToCompleteOwnershipGraph.get( possibleMd );
325                     ogAllPossibleCallees.merge( ogPotentialCallee );
326                 }
327             }
328
329             // now we should have the following information to resolve this method call:
330             // 
331             // 1. A FlatCall fc to query for the caller's context (argument labels, etc)
332             //
333             // 2. Whether the method is static; if not we need to deal with the "this" pointer
334             //
335             // *******************************************************************************************
336             // 3. The original FlatMethod flatm to query for callee's context (paramter labels)
337             //   NOTE!  I assume FlatMethod before virtual dispatch accurately describes all possible methods!
338             // *******************************************************************************************
339             //
340             // 4. The OwnershipGraph ogAllPossibleCallees is a merge of every ownership graph of all the possible
341             // methods to capture any possible references made.
342             //
343             // 5. The Set of AllocationSite objects, allocSiteSet that is the set of allocation sites from
344             // every possible method we might have chosen
345             //
346             og.resolveMethodCall( fc, md.isStatic(), flatm, ogAllPossibleCallees, allocSiteSet );
347
348             //og.writeGraph( methodDesc, fn );
349             break;
350
351         case FKind.FlatReturnNode:
352             FlatReturnNode frn = (FlatReturnNode) fn;
353             setRetNodes.add( frn );
354             //og.writeGraph( methodDesc, fn );
355             break;
356         }
357     }
358
359
360     static public Integer generateUniqueHeapRegionNodeID() {
361         ++uniqueIDcount;
362         return new Integer( uniqueIDcount );
363     }    
364
365
366     private OwnershipGraph getGraphFromFlatNode( FlatNode fn ) {
367         if( !mapFlatNodeToOwnershipGraph.containsKey( fn ) ) {
368             mapFlatNodeToOwnershipGraph.put( fn, new OwnershipGraph( allocationDepth ) );
369         }
370
371         return mapFlatNodeToOwnershipGraph.get( fn );
372     }
373
374     private void setGraphForFlatNode( FlatNode fn, OwnershipGraph og ) {
375         mapFlatNodeToOwnershipGraph.put( fn, og );
376     }
377
378
379     // return just the allocation site associated with one FlatNew node
380     private AllocationSite getAllocationSiteFromFlatNew( FlatNew fn ) {
381         if( !mapFlatNewToAllocationSite.containsKey( fn ) ) {
382             AllocationSite as = new AllocationSite( allocationDepth );
383
384             // the newest nodes are single objects
385             for( int i = 0; i < allocationDepth; ++i ) {
386                 Integer id = generateUniqueHeapRegionNodeID();
387                 as.setIthOldest( i, id );
388             }
389
390             // the oldest node is a summary node
391             Integer idSummary = generateUniqueHeapRegionNodeID();
392             as.setSummary( idSummary );
393
394             mapFlatNewToAllocationSite.put( fn, as );
395         }
396
397         return mapFlatNewToAllocationSite.get( fn );
398     }
399
400
401     // return all allocation sites in the method (there is one allocation
402     // site per FlatNew node in a method)
403     private HashSet<AllocationSite> getAllocationSiteSet( Descriptor d ) {
404         if( !mapDescriptorToAllocationSiteSet.containsKey( d ) ) {
405             buildAllocationSiteSet( d );   
406         }
407
408         return mapDescriptorToAllocationSiteSet.get( d );
409
410     }
411
412     private void buildAllocationSiteSet( Descriptor d ) {
413         HashSet<AllocationSite> s = new HashSet<AllocationSite>();
414
415         FlatMethod fm;
416         if( d instanceof MethodDescriptor ) {
417             fm = state.getMethodFlat( (MethodDescriptor) d );
418         } else {
419             assert d instanceof TaskDescriptor;
420             fm = state.getMethodFlat( (TaskDescriptor) d );
421         }
422
423         // visit every node in this FlatMethod's IR graph
424         // and make a set of the allocation sites from the
425         // FlatNew node's visited
426         HashSet<FlatNode> visited = new HashSet<FlatNode>();
427         HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
428         toVisit.add( fm );
429
430         while( !toVisit.isEmpty() ) {
431             FlatNode n = toVisit.iterator().next();
432
433             if( n instanceof FlatNew ) {
434                 s.add( getAllocationSiteFromFlatNew( (FlatNew) n ) );
435             }
436
437             toVisit.remove( n );
438             visited.add( n );
439
440             for( int i = 0; i < n.numNext(); ++i ) {
441                 FlatNode child = n.getNext( i );
442                 if( !visited.contains( child ) ) {
443                     toVisit.add( child );
444                 }
445             }
446         }
447
448         mapDescriptorToAllocationSiteSet.put( d, s );
449     }
450 }