1 package Analysis.OoOJava;
5 import IR.MethodDescriptor;
6 import IR.TypeDescriptor;
8 import Analysis.CallGraph.CallGraph;
12 // This analysis finds all reachable rblocks in the
13 // program and computes parent/child relations
14 // between those rblocks
17 // There is a distict between parent/child and
18 // local parent/local child! "Local" means defined
19 // and nested within a single method context.
20 // Otherwise, SESE/rblocks/tasks may have many
21 // parents and many non-method-context-local
22 // children considering the call graph
24 // Also this analysis should identify "critical regions"
25 // in the context of interprocedural sese/rblock/task relations
26 // where a statement may conflict with some previously executing
27 // child task, even if it is in another method context.
41 // void doSomething( Foo f ) {
42 // f.z++; <-------- These two statements are in the critical
43 // f.z--; <-------- region of 'a' after 'c1' and before 'c2'
50 public class RBlockRelationAnalysis {
57 // an implicit SESE is automatically spliced into
58 // the IR graph around the C main before this analysis--it
59 // is nothing special except that we can make assumptions
60 // about it, such as the whole program ends when it ends
61 protected FlatSESEEnterNode mainSESE;
63 // this is a special task object, it is not in any IR graph
64 // and it does not appear to have any children or parents.
65 // It is a stand-in for whichever task is running when a
66 // method context starts such that intraprocedural task
67 // analyses have one static name for "the task who invoked
68 // this method" to attach facts to. It GREATLY simplifies
69 // the OoOJava variable analysis, for instance
70 protected FlatSESEEnterNode callerProxySESE;
72 // simply the set of every reachable SESE in the program
73 protected Set<FlatSESEEnterNode> allSESEs;
75 // to support calculation of leaf SESEs (no children even
76 // through method calls) for optimization during code gen
77 protected Set<MethodDescriptor> methodsContainingSESEs;
79 // maps method descriptor to SESE defined inside of it
80 // only contains local root SESE definitions in corresponding method
81 // (has no parent in the local method context)
82 protected Hashtable< MethodDescriptor, Set<FlatSESEEnterNode> > md2localRootSESEs;
84 // the set of every local root SESE in the program (SESE that
85 // has no parent in the local method context)
86 protected Set<FlatSESEEnterNode> allLocalRootSESEs;
88 // if you want to know which rblocks might be executing a given flat
89 // node it will be in this set
90 protected Hashtable< FlatNode, Set<FlatSESEEnterNode> > fn2currentSESEs;
92 // if you want to know which rblocks might be executing a given flat
93 // node it will be in this set
94 protected Hashtable< FlatNode, Set<FlatSESEEnterNode> > fn2allSESEs;
96 // if you want to know the method-local, inner-most nested task that
97 // is executing a flat node, it is either here or null.
102 // bar(); <-- here 'a' is the localInnerSESE
105 // baz(); <-- here there is no locally-defined SESE, would be null
107 protected Hashtable<FlatNode, FlatSESEEnterNode> fn2localInnerSESE;
109 // indicates whether this statement might occur in a task and
110 // after some child task definition such that, without looking at
111 // the flat node itself, the parent might have to stall for child
112 protected Hashtable<FlatNode, Boolean> fn2isPotentialStallSite;
115 ////////////////////////
117 ////////////////////////
118 public FlatSESEEnterNode getMainSESE() {
122 public Set<FlatSESEEnterNode> getAllSESEs() {
126 public Set<FlatSESEEnterNode> getLocalRootSESEs() {
127 return allLocalRootSESEs;
130 public Set<FlatSESEEnterNode> getLocalRootSESEs( FlatMethod fm ) {
131 Set<FlatSESEEnterNode> out = md2localRootSESEs.get( fm );
133 out = new HashSet<FlatSESEEnterNode>();
138 /* Returns all SESE's that this fn can be a member of
141 public Set<FlatSESEEnterNode> getTransitiveExecutingRBlocks(FlatNode fn) {
142 if (fn2allSESEs.containsKey(fn))
143 return fn2allSESEs.get(fn);
145 //Compute and memoize result
146 HashSet<FlatSESEEnterNode> seseSet=new HashSet<FlatSESEEnterNode>();
147 fn2allSESEs.put(fn, seseSet);
148 Stack<FlatNode> toprocess=new Stack<FlatNode>();
150 while(!toprocess.isEmpty()) {
151 FlatNode curr=toprocess.pop();
152 Set<FlatSESEEnterNode> callers=fn2currentSESEs.get(curr);
154 for(FlatSESEEnterNode sese:callers) {
155 if (!seseSet.contains(sese)) {
165 public Set<FlatSESEEnterNode> getPossibleExecutingRBlocks( FlatNode fn ) {
166 Set<FlatSESEEnterNode> out = fn2currentSESEs.get( fn );
168 out = new HashSet<FlatSESEEnterNode>();
173 public FlatSESEEnterNode getLocalInnerRBlock( FlatNode fn ) {
174 return fn2localInnerSESE.get( fn );
177 // the "caller proxy" is a static name for whichever
178 // task invoked the current method context. It is very
179 // convenient to know this is ALWAYS a different instance
180 // of any task defined within the current method context,
181 // and so using its name simplifies many intraprocedural
183 public FlatSESEEnterNode getCallerProxySESE() {
184 return callerProxySESE;
187 public boolean isPotentialStallSite( FlatNode fn ) {
188 Boolean ipss = fn2isPotentialStallSite.get( fn );
196 public RBlockRelationAnalysis( State state,
198 CallGraph callGraph ) {
200 this.typeUtil = typeUtil;
201 this.callGraph = callGraph;
203 callerProxySESE = new FlatSESEEnterNode( null );
204 callerProxySESE.setIsCallerProxySESE();
206 allSESEs = new HashSet<FlatSESEEnterNode>();
207 allLocalRootSESEs = new HashSet<FlatSESEEnterNode>();
208 methodsContainingSESEs = new HashSet<MethodDescriptor>();
209 md2localRootSESEs = new Hashtable<MethodDescriptor, Set<FlatSESEEnterNode>>();
210 fn2currentSESEs = new Hashtable<FlatNode, Set<FlatSESEEnterNode>>();
211 fn2localInnerSESE = new Hashtable<FlatNode, FlatSESEEnterNode>();
212 fn2isPotentialStallSite = new Hashtable<FlatNode, Boolean>();
213 fn2allSESEs = new Hashtable< FlatNode, Set<FlatSESEEnterNode>>();
216 MethodDescriptor mdSourceEntry = typeUtil.getMain();
217 FlatMethod fmMain = state.getMethodFlat( mdSourceEntry );
219 mainSESE = (FlatSESEEnterNode) fmMain.getNext( 0 );
220 mainSESE.setfmEnclosing( fmMain );
221 mainSESE.setmdEnclosing( fmMain.getMethod() );
222 mainSESE.setcdEnclosing( fmMain.getMethod().getClassDesc() );
225 // add all methods transitively reachable from the
226 // source's main to set to find rblocks
227 Set<MethodDescriptor> descriptorsToAnalyze =
228 callGraph.getAllMethods( mdSourceEntry );
230 descriptorsToAnalyze.add( mdSourceEntry );
232 findRblocksAndLocalParentChildRelations( descriptorsToAnalyze );
234 findTransitiveParentChildRelations();
236 findPossibleExecutingRBlocksAndStallSites();
239 // Uncomment this phase to debug the marking of potential
240 // stall sites for parents between/after children tasks.
241 // After this debug thing runs in calls System.exit()
242 //debugPrintPotentialStallSites( descriptorsToAnalyze );
248 protected void findRblocksAndLocalParentChildRelations( Set<MethodDescriptor> descriptorsToAnalyze ) {
250 Iterator<MethodDescriptor> mdItr = descriptorsToAnalyze.iterator();
251 while( mdItr.hasNext() ) {
252 FlatMethod fm = state.getMethodFlat( mdItr.next() );
254 // start from flat method top, visit every node in
255 // method exactly once, find SESE stack on every
256 // control path: this will discover every reachable
257 // SESE in the program, and define the local parent
258 // and local children relations
259 Hashtable< FlatNode, Stack<FlatSESEEnterNode> > seseStacks =
260 new Hashtable< FlatNode, Stack<FlatSESEEnterNode> >();
262 Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
263 flatNodesToVisit.add( fm );
265 Set<FlatNode> visited = new HashSet<FlatNode>();
267 Stack<FlatSESEEnterNode> seseStackFirst = new Stack<FlatSESEEnterNode>();
268 seseStacks.put( fm, seseStackFirst );
270 while( !flatNodesToVisit.isEmpty() ) {
271 Iterator<FlatNode> fnItr = flatNodesToVisit.iterator();
272 FlatNode fn = fnItr.next();
274 Stack<FlatSESEEnterNode> seseStack = seseStacks.get( fn );
275 assert seseStack != null;
277 flatNodesToVisit.remove( fn );
280 if( !seseStack.isEmpty() ) {
281 fn2localInnerSESE.put( fn, seseStack.peek() );
284 nodeActions( fn, seseStack, fm );
286 for( int i = 0; i < fn.numNext(); i++ ) {
287 FlatNode nn = fn.getNext( i );
289 if( !visited.contains( nn ) ) {
290 flatNodesToVisit.add( nn );
292 // clone stack and send along each control path
293 seseStacks.put( nn, (Stack<FlatSESEEnterNode>)seseStack.clone() );
300 protected void nodeActions( FlatNode fn,
301 Stack<FlatSESEEnterNode> seseStack,
303 switch( fn.kind() ) {
305 case FKind.FlatSESEEnterNode: {
306 FlatSESEEnterNode fsen = (FlatSESEEnterNode) fn;
308 allSESEs.add( fsen );
309 methodsContainingSESEs.add( fm.getMethod() );
311 fsen.setfmEnclosing( fm );
312 fsen.setmdEnclosing( fm.getMethod() );
313 fsen.setcdEnclosing( fm.getMethod().getClassDesc() );
315 if( seseStack.empty() ) {
317 fsen.setLocalParent( null );
319 allLocalRootSESEs.add( fsen );
321 Set<FlatSESEEnterNode> seseSet = md2localRootSESEs.get( fm.getMethod() );
322 if( seseSet == null ) {
323 seseSet = new HashSet<FlatSESEEnterNode>();
326 md2localRootSESEs.put( fm.getMethod(), seseSet );
329 // otherwise a local parent/child relation
330 // which is also the broader parent/child
332 seseStack.peek().addLocalChild( fsen );
333 fsen.setLocalParent( seseStack.peek() );
335 seseStack.peek().addChild( fsen );
336 fsen.addParent( seseStack.peek() );
339 seseStack.push( fsen );
342 case FKind.FlatSESEExitNode: {
343 FlatSESEExitNode fsexn = (FlatSESEExitNode) fn;
344 assert !seseStack.empty();
345 FlatSESEEnterNode fsen = seseStack.pop();
348 case FKind.FlatReturnNode: {
349 FlatReturnNode frn = (FlatReturnNode) fn;
350 if( !seseStack.empty() ) {
351 throw new Error( "Error: return statement enclosed within SESE "+
352 seseStack.peek().getPrettyIdentifier() );
361 protected void findTransitiveParentChildRelations() {
363 for (Iterator<FlatSESEEnterNode> itr = allSESEs.iterator(); itr.hasNext();) {
364 FlatSESEEnterNode fsen = itr.next();
366 boolean hasNoNestedChildren = fsen.getLocalChildren().isEmpty();
367 boolean hasNoChildrenByCall = !hasChildrenByCall( fsen );
369 fsen.setIsLeafSESE( hasNoNestedChildren && hasNoChildrenByCall );
373 protected boolean hasChildrenByCall( FlatSESEEnterNode fsen ) {
375 boolean hasChildrenByCall = false;
377 // visit every flat node in SESE body, find method calls that
378 // may transitively call methods with SESEs enclosed
379 Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
380 flatNodesToVisit.add( fsen );
382 Set<FlatNode> visited = new HashSet<FlatNode>();
384 while( !flatNodesToVisit.isEmpty() ) {
385 Iterator<FlatNode> fnItr = flatNodesToVisit.iterator();
386 FlatNode fn = fnItr.next();
388 flatNodesToVisit.remove( fn );
391 if( fn.kind() == FKind.FlatCall ) {
392 FlatCall fc = (FlatCall) fn;
393 MethodDescriptor mdCallee = fc.getMethod();
394 Set reachable = new HashSet();
396 reachable.add( mdCallee );
397 reachable.addAll( callGraph.getAllMethods( mdCallee ) );
398 reachable.retainAll( methodsContainingSESEs );
400 if( !reachable.isEmpty() ) {
401 hasChildrenByCall = true;
403 Set reachableSESEMethodSet =
404 callGraph.getFirstReachableMethodContainingSESE( mdCallee, methodsContainingSESEs );
406 reachableSESEMethodSet.add( mdCallee );
407 reachableSESEMethodSet.retainAll( methodsContainingSESEs );
409 for( Iterator iterator = reachableSESEMethodSet.iterator(); iterator.hasNext(); ) {
410 MethodDescriptor md = (MethodDescriptor) iterator.next();
411 Set<FlatSESEEnterNode> seseSet = md2localRootSESEs.get( md );
412 if( seseSet != null ) {
413 fsen.addChildren( seseSet );
414 for( Iterator iterator2 = seseSet.iterator(); iterator2.hasNext(); ) {
415 FlatSESEEnterNode child = (FlatSESEEnterNode) iterator2.next();
416 child.addParent( fsen );
423 if( fn == fsen.getFlatExit() ) {
424 // don't enqueue any futher nodes
428 for( int i = 0; i < fn.numNext(); i++ ) {
429 FlatNode nn = fn.getNext( i );
431 if( !visited.contains( nn ) ) {
432 flatNodesToVisit.add( nn );
437 return hasChildrenByCall;
440 protected void findPossibleExecutingRBlocksAndStallSites() {
441 for( Iterator<FlatSESEEnterNode> fsenItr = allSESEs.iterator(); fsenItr.hasNext(); ) {
442 FlatSESEEnterNode fsen = fsenItr.next();
444 // walk the program points, including across method calls, reachable within
445 // this sese/rblock/task and mark that this rblock might be executing.
446 // Important: skip the body of child rblocks, BUT DO mark the child ENTER
447 // and EXIT flat nodes as the parent being the current executing rblock!
448 Hashtable<FlatNode, FlatMethod> flatNodesToVisit =
449 new Hashtable<FlatNode, FlatMethod>();
451 for( int i = 0; i < fsen.numNext(); i++ ) {
452 FlatNode nn = fsen.getNext( i );
453 flatNodesToVisit.put( nn, fsen.getfmEnclosing() );
454 mergeIsPotentialStallSite( nn, false );
457 Set<FlatNode> visited = new HashSet<FlatNode>();
459 while( !flatNodesToVisit.isEmpty() ) {
460 Map.Entry me = (Map.Entry) flatNodesToVisit.entrySet().iterator().next();
461 FlatNode fn = (FlatNode) me.getKey();
462 FlatMethod fm = (FlatMethod) me.getValue();
464 flatNodesToVisit.remove( fn );
468 // the "is potential stall site" strategy is to propagate
469 // "false" from the beginning of a task until you hit a
470 // child, then from the child's exit propagate "true" for
471 // the parent statements after children. When you pull a node
472 // out of the bag for traversal and it happens to be an
473 // enter or an exit node, fix the dumb propagation that
474 // your IR predecessor pushed on you
475 Boolean isPotentialStallSite = isPotentialStallSite( fn );
477 if( fn instanceof FlatSESEEnterNode ||
478 fn instanceof FlatSESEExitNode ) {
479 // fix it so this is never a potential stall site, but from
480 // a child definition onward propagate 'true'
481 setIsPotentialStallSite( fn, false );
482 isPotentialStallSite = true;
486 if( fn == fsen.getFlatExit() ) {
487 // don't enqueue any futher nodes when you find your exit,
488 // NOR mark your own flat as a statement you are currently
489 // executing, your parent(s) will mark it
494 // the purpose of this traversal is to find program
495 // points where rblock 'fsen' might be executing
496 addPossibleExecutingRBlock( fn, fsen );
499 if( fn instanceof FlatSESEEnterNode ) {
500 // don't visit internal nodes of child,
501 // just enqueue the exit node
502 FlatSESEEnterNode child = (FlatSESEEnterNode) fn;
503 assert fsen.getChildren().contains( child );
504 assert child.getParents().contains( fsen );
505 flatNodesToVisit.put( child.getFlatExit(), fm );
507 // explicitly do this to handle the case that you
508 // should mark yourself as possibly executing at
509 // your own exit, because one instance can
510 // recursively invoke another
511 addPossibleExecutingRBlock( child.getFlatExit(), fsen );
516 if( fn instanceof FlatCall ) {
517 // start visiting nodes in other contexts
518 FlatCall fc = (FlatCall) fn;
519 MethodDescriptor mdCallee = fc.getMethod();
521 Set<MethodDescriptor> implementations = new HashSet<MethodDescriptor>();
523 if( mdCallee.isStatic() ) {
524 implementations.add( mdCallee );
526 TypeDescriptor typeDesc = fc.getThis().getType();
527 implementations.addAll( callGraph.getMethods( mdCallee, typeDesc ) );
530 for( Iterator imps = implementations.iterator(); imps.hasNext(); ) {
531 MethodDescriptor mdImp = (MethodDescriptor) imps.next();
532 FlatMethod fmImp = state.getMethodFlat( mdImp );
533 flatNodesToVisit.put( fmImp, fmImp );
535 // propagate your IR graph predecessor's stall site potential
536 mergeIsPotentialStallSite( fmImp, isPotentialStallSite );
538 // don't 'continue' out of this loop, also enqueue
539 // flat nodes that flow in the current method context
542 // otherwise keep visiting nodes in same context
543 for( int i = 0; i < fn.numNext(); i++ ) {
544 FlatNode nn = fn.getNext( i );
546 if( !visited.contains( nn ) ) {
547 flatNodesToVisit.put( nn, fm );
549 // propagate your IR graph predecessor's stall site potential
550 mergeIsPotentialStallSite( nn, isPotentialStallSite );
559 protected void addPossibleExecutingRBlock( FlatNode fn,
560 FlatSESEEnterNode fsen ) {
562 Set<FlatSESEEnterNode> currentSESEs = fn2currentSESEs.get( fn );
563 if( currentSESEs == null ) {
564 currentSESEs = new HashSet<FlatSESEEnterNode>();
567 currentSESEs.add( fsen );
568 fn2currentSESEs.put( fn, currentSESEs );
572 // definitively set whether a statement is a potential stall site
573 // such as a task exit is FALSE and the statement following an exit
575 protected void setIsPotentialStallSite( FlatNode fn,
577 fn2isPotentialStallSite.put( fn, ipss );
581 // Use this to OR the previous result with a new result
582 protected void mergeIsPotentialStallSite( FlatNode fn,
584 Boolean ipssPrev = isPotentialStallSite( fn );
585 setIsPotentialStallSite( fn, ipssPrev || ipss );
592 /////////////////////////////////////////////////
594 /////////////////////////////////////////////////
595 protected void debugPrintPotentialStallSites(Set<MethodDescriptor> descriptorsToAnalyze) {
596 Iterator<MethodDescriptor> mdItr = descriptorsToAnalyze.iterator();
597 while (mdItr.hasNext()) {
598 FlatMethod fm = state.getMethodFlat(mdItr.next());
604 protected void printStatusMap(FlatMethod fm) {
606 System.out.println("\n\n=== "+fm+" ===");
608 Set<FlatNode> flatNodesToVisit = new HashSet<FlatNode>();
609 flatNodesToVisit.add(fm);
611 Set<FlatNode> visited = new HashSet<FlatNode>();
613 while (!flatNodesToVisit.isEmpty()) {
614 Iterator<FlatNode> fnItr = flatNodesToVisit.iterator();
615 FlatNode fn = fnItr.next();
617 flatNodesToVisit.remove(fn);
620 System.out.println(fn+"[["+isPotentialStallSite(fn)+"]]");
622 for (int i = 0; i < fn.numNext(); i++) {
623 FlatNode nn = fn.getNext(i);
625 if (!visited.contains(nn)) {
626 flatNodesToVisit.add(nn);