View Javadoc

1   // MethodInline.java, created Wed Mar 13  1:39:18 2002 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package joeq.Compiler.Quad;
5   
6   import java.util.Collection;
7   import java.util.HashMap;
8   import java.io.BufferedReader;
9   import java.io.FileNotFoundException;
10  import java.io.FileReader;
11  import java.io.IOException;
12  import joeq.Class.jq_Class;
13  import joeq.Class.jq_Method;
14  import joeq.Class.jq_Primitive;
15  import joeq.Class.jq_Type;
16  import joeq.Compiler.Analysis.IPA.ProgramLocation;
17  import joeq.Compiler.Analysis.IPA.ProgramLocation.QuadProgramLocation;
18  import joeq.Compiler.BytecodeAnalysis.BytecodeVisitor;
19  import joeq.Compiler.Quad.Operand.ConditionOperand;
20  import joeq.Compiler.Quad.Operand.IConstOperand;
21  import joeq.Compiler.Quad.Operand.ParamListOperand;
22  import joeq.Compiler.Quad.Operand.RegisterOperand;
23  import joeq.Compiler.Quad.Operand.TargetOperand;
24  import joeq.Compiler.Quad.Operand.TypeOperand;
25  import joeq.Compiler.Quad.Operator.Goto;
26  import joeq.Compiler.Quad.Operator.InstanceOf;
27  import joeq.Compiler.Quad.Operator.IntIfCmp;
28  import joeq.Compiler.Quad.Operator.Invoke;
29  import joeq.Compiler.Quad.Operator.Move;
30  import joeq.Compiler.Quad.Operator.Return;
31  import joeq.Compiler.Quad.RegisterFactory.Register;
32  import joeq.Util.NameMunger;
33  import joeq.Util.Templates.ListIterator;
34  import jwutil.util.Assert;
35  
36  /***
37   * @author John Whaley <jwhaley@alum.mit.edu>
38   * @version $Id: MethodInline.java 2465 2006-06-07 23:03:17Z joewhaley $
39   */
40  public class MethodInline implements ControlFlowGraphVisitor {
41  
42      public static final boolean TRACE = false;
43      public static final boolean TRACE_ORACLE = false;
44      public static final boolean TRACE_DECISIONS = false;
45      public static final java.io.PrintStream out = System.out;
46  
47      Oracle oracle;
48      CallGraph cg;
49      //private static Map fakeMethodOperand = new HashMap();
50  
51      public MethodInline(Oracle o) {
52          this.oracle = o;
53      }
54      public MethodInline(CallGraph cg) {
55  //        this(new InlineSmallSingleTargetCalls(cg));\
56          this(new InlineSelectedCalls(cg));
57          this.cg = cg;
58      }
59      public MethodInline() {
60  //        this(new InlineSmallSingleTargetCalls(CHACallGraph.INSTANCE));
61          this(new InlineSelectedCalls(CHACallGraph.INSTANCE));
62          this.cg = CHACallGraph.INSTANCE;
63      }
64  
65      public static interface Oracle {
66          InliningDecision shouldInline(ControlFlowGraph caller, BasicBlock bb, Quad callSite);
67      }
68      
69      public static abstract class InliningDecision {
70          public abstract void inlineCall(ControlFlowGraph caller, BasicBlock bb, Quad q);
71      }
72      
73      public static final class DontInline extends InliningDecision {
74          private DontInline() {}
75          public void inlineCall(ControlFlowGraph caller, BasicBlock bb, Quad q) {
76          }
77          public static final DontInline INSTANCE = new DontInline();
78      }
79      
80      public static class NoCheckInliningDecision extends InliningDecision {
81          ControlFlowGraph callee;
82          public NoCheckInliningDecision(jq_Method target) {
83              this(CodeCache.getCode(target));
84          }
85          public NoCheckInliningDecision(ControlFlowGraph target) {
86              this.callee = target;
87          }
88          public void inlineCall(ControlFlowGraph caller, BasicBlock bb, Quad q) {
89              inlineNonVirtualCallSite(caller, bb, q, callee);
90          }
91          public String toString() { return callee.getMethod().toString(); }
92      }
93      
94      public static class TypeCheckInliningDecision extends InliningDecision {
95          jq_Class expectedType;
96          ControlFlowGraph callee;
97          public TypeCheckInliningDecision(jq_Method target) {
98              this.expectedType = target.getDeclaringClass();
99              this.callee = CodeCache.getCode(target);
100         }
101         public void inlineCall(ControlFlowGraph caller, BasicBlock bb, Quad q) {
102             inlineVirtualCallSiteWithTypeCheck(caller, bb, q, callee, expectedType);
103         }
104         public String toString() { return callee.getMethod().toString(); }
105     }
106     
107     public static class InlineSmallSingleTargetCalls implements Oracle {
108         
109         protected CallGraph cg;
110         int bcThreshold;
111         public static final int DEFAULT_THRESHOLD = 25;
112         
113         public InlineSmallSingleTargetCalls(CallGraph cg) {
114             this(cg, DEFAULT_THRESHOLD);
115         }
116         public InlineSmallSingleTargetCalls(CallGraph cg, int bcThreshold) {
117             this.cg = cg;
118             this.bcThreshold = bcThreshold;
119         }
120     
121         public InliningDecision shouldInline(ControlFlowGraph caller, BasicBlock bb, Quad callSite) {
122             if (TRACE_ORACLE) out.println("Oracle evaluating "+callSite);
123             if (Invoke.getMethod(callSite).getMethod().needsDynamicLink(caller.getMethod())) {
124                 if (TRACE_ORACLE) out.println("Skipping because call site needs dynamic link.");
125                 return null;
126             }
127             Invoke i = (Invoke) callSite.getOperator();
128             jq_Method target;
129             if (i.isVirtual()) {
130                 ProgramLocation pl = new QuadProgramLocation(caller.getMethod(), callSite);
131                 Collection c = cg.getTargetMethods(pl);
132                 if (c.size() != 1) {
133                     if (TRACE_ORACLE) out.println("Skipping because call site has multiple targets.");
134                     return null;
135                 }
136                 target = (jq_Method) c.iterator().next();
137             } else {
138                 target = Invoke.getMethod(callSite).getMethod();
139             }
140             target.getDeclaringClass().prepare();
141             if (target.getBytecode() == null) {
142                 if (TRACE_ORACLE) out.println("Skipping because target method is native.");
143                 return null;
144             }
145             if (target.getBytecode().length > bcThreshold) {
146                 if (TRACE_ORACLE) out.println("Skipping because target method is too large ("+target.getBytecode().length+").");
147                 return null;
148             }
149             if (target == caller.getMethod()) {
150                 if (TRACE_ORACLE) out.println("Skipping because method is recursive.");
151                 return null;
152             }
153             // HACK: for interpreter.
154             if (!joeq.Interpreter.QuadInterpreter.interpret_filter.isElement(target)) {
155                 if (TRACE_ORACLE) out.println("Skipping because the interpreter cannot handle the target method.");
156                 return null;
157             }
158             
159             if (TRACE_ORACLE) out.println("Oracle says to inline!");
160             return new NoCheckInliningDecision(target);
161         }
162 
163     }
164     
165     /***
166      *     Inline methods whose munged names are read from file methodNameFile (methods.txt). 
167      * */
168     public static class InlineSelectedCalls implements Oracle {        
169         protected CallGraph cg;
170         protected HashMap/*<String,null>*/ knownMethods = new HashMap(10);
171         public static final String methodNameFile = "methods.txt";
172           
173         public InlineSelectedCalls(CallGraph cg) {
174             this.cg = cg;
175             initializeNames();
176         }
177         
178         private void initializeNames() {            
179             BufferedReader r = null;
180             try {
181                 r = new BufferedReader(new FileReader(methodNameFile));
182                 
183                 String s;
184                 while ((s = r.readLine()) != null) {
185                     if(s.startsWith("#")) continue;
186                     if(s.trim().length() == 0) continue;
187                     
188                     if(s.charAt(s.length()-1) == '\n'){
189                         s = s.substring(s.length()-1);
190                     }
191                     
192                     knownMethods.put(s, null);
193                 }
194             } catch (FileNotFoundException e) {
195                 e.printStackTrace();
196             } catch (IOException e) {
197                 e.printStackTrace();
198             } finally {
199                 if (r != null)
200                     try {
201                         r.close();
202                     } catch (IOException e1) {
203                         e1.printStackTrace();
204                     }
205             }
206         }
207 
208         public InliningDecision shouldInline(ControlFlowGraph caller, BasicBlock bb, Quad callSite) {
209             if (TRACE_ORACLE) out.println("Oracle evaluating "+callSite);
210             Invoke i = (Invoke) callSite.getOperator();
211             jq_Method target;
212             if (i.isVirtual()) {
213                 ProgramLocation pl = new QuadProgramLocation(caller.getMethod(), callSite);
214                 Collection c = cg.getTargetMethods(pl);
215                 if (c.size() != 1) {
216                     if (TRACE_ORACLE) out.println("Skipping because call site has multiple targets.");
217                     return null;
218                 }
219                 target = (jq_Method) c.iterator().next();
220             } else {
221                 target = Invoke.getMethod(callSite).getMethod();
222             }
223             target.getDeclaringClass().prepare();
224             if (target.getBytecode() == null) {
225                 if (TRACE_ORACLE) out.println("Skipping because target method is native.");
226                 return null;
227             }
228 
229             if (target == caller.getMethod()) {
230                 if (TRACE_ORACLE) out.println("Skipping because method is recursive.");
231                 return null;
232             }
233             // HACK: for interpreter.
234             if (!joeq.Interpreter.QuadInterpreter.interpret_filter.isElement(target)) {
235                 if (TRACE_ORACLE) out.println("Skipping because the interpreter cannot handle the target method.");
236                 return null;
237             }
238             
239             String mungedName = NameMunger.mungeMethodName(target);
240             if (!knownMethods.containsKey(mungedName)){
241                 return null;
242             }
243             
244             if (Invoke.getMethod(callSite).getMethod().needsDynamicLink(caller.getMethod())) {
245               if (TRACE_ORACLE) out.println("Skipping because call site needs dynamic link.");
246               return null;
247             }
248             
249             if (TRACE_ORACLE) out.println("Oracle says to inline " + target);
250             //return new NoCheckInliningDecision(target);
251             return new TypeCheckInliningDecision(target);
252         }
253     }
254     
255     public void visitCFG(ControlFlowGraph cfg) {
256         QuadIterator qi = new QuadIterator(cfg);
257         java.util.LinkedList inline_blocks = new java.util.LinkedList();
258         java.util.LinkedList inline_quads = new java.util.LinkedList();
259         java.util.LinkedList inline_decisions = new java.util.LinkedList();
260         while (qi.hasNext()) {
261             Quad q = qi.nextQuad();
262             if (q.getOperator() instanceof Invoke) {
263                 BasicBlock bb = qi.getCurrentBasicBlock();
264                 Invoke.getMethod(q).getMethod().getDeclaringClass().load();
265                 InliningDecision d = oracle.shouldInline(cfg, bb, q);
266                 if (d != null) {
267                     if (TRACE_DECISIONS) out.println("Decided to inline "+d);
268                     inline_blocks.add(bb);
269                     inline_quads.add(q);
270                     inline_decisions.add(d);
271                 }
272             }
273         }
274         // do the inlining backwards, so that basic blocks don't change.
275         java.util.ListIterator li1 = inline_blocks.listIterator();
276         java.util.ListIterator li2 = inline_quads.listIterator();
277         java.util.ListIterator li3 = inline_decisions.listIterator();
278         while (li1.hasNext()) { li1.next(); li2.next(); li3.next(); }
279         while (li1.hasPrevious()) {
280             BasicBlock bb = (BasicBlock)li1.previous();
281             Quad q = (Quad)li2.previous();
282             InliningDecision d = (InliningDecision)li3.previous();
283             if (TRACE_DECISIONS) out.println("Performing inlining "+d + " using " + d.getClass());
284             d.inlineCall(cfg, bb, q);
285             if (cg instanceof CachedCallGraph && d instanceof NoCheckInliningDecision) {
286                 CachedCallGraph ccg = (CachedCallGraph) cg;
287                 jq_Method caller = cfg.getMethod();
288                 ProgramLocation pl = new ProgramLocation.QuadProgramLocation(caller, q);
289                 jq_Method callee = ((NoCheckInliningDecision) d).callee.getMethod();
290                 ccg.inlineEdge(caller, pl, callee);
291             }
292         }
293     }
294     
295     public static void inlineNonVirtualCallSite(ControlFlowGraph caller, BasicBlock bb, Quad q, ControlFlowGraph callee) {
296         if (TRACE) out.println("Inlining "+q+" target "+callee.getMethod()+" in "+bb);
297 
298         int invokeLocation = bb.getQuadIndex(q);
299         Assert._assert(invokeLocation != -1);
300 
301         if (TRACE) out.println("Code to inline:");
302         if (TRACE) out.println(callee.fullDump());
303         if (TRACE) out.println(callee.getRegisterFactory().fullDump());
304 
305         // copy the callee's control flow graph, renumbering the basic blocks,
306         // registers and quads, and add the exception handlers.
307         callee = caller.merge(callee);
308         if (TRACE) out.println("After renumbering:");
309         if (TRACE) out.println(callee.fullDump());
310         if (TRACE) out.println(callee.getRegisterFactory().fullDump());
311         callee.appendExceptionHandlers(bb.getExceptionHandlers());
312 
313         if (TRACE) out.println("Original basic block containing invoke:");
314         if (TRACE) out.println(bb.fullDump());
315 
316         // split the basic block containing the invoke, and start splicing
317         // in the callee's control flow graph.
318         BasicBlock successor_bb = caller.createBasicBlock(callee.exit().getNumberOfPredecessors(), bb.getNumberOfSuccessors(), bb.size() - invokeLocation, bb.getExceptionHandlers());
319         int bb_size = bb.size();
320         for (int i=invokeLocation+1; i<bb_size; ++i) {
321             successor_bb.appendQuad(bb.removeQuad(invokeLocation+1));
322         }
323         Quad invokeQuad = bb.removeQuad(invokeLocation);
324         Assert._assert(invokeQuad == q);
325         Assert._assert(bb.size() == invokeLocation);
326         if (TRACE) out.println("Result of splitting:");
327         if (TRACE) out.println(bb.fullDump());
328         if (TRACE) out.println(successor_bb.fullDump());
329 
330         // add instructions to set parameters.
331         ParamListOperand plo = Invoke.getParamList(q);
332         jq_Type[] params = callee.getMethod().getParamTypes();
333         for (int i=0, j=0; i<params.length; ++i, ++j) {
334             Move op = Move.getMoveOp(params[i]);
335             Register dest_r = callee.getRegisterFactory().getOrCreateLocal(j, params[i]);
336             RegisterOperand dest = new RegisterOperand(dest_r, params[i]);
337             Register src_r = plo.get(i).getRegister();
338             RegisterOperand src = new RegisterOperand(src_r, params[i]);
339             Quad q2 = Move.create(caller.getNewQuadID(), op, dest, src);
340             bb.appendQuad(q2);
341             if (params[i].getReferenceSize() == 8) ++j;
342         }
343         if (TRACE) out.println("Result after adding parameter moves:");
344         if (TRACE) out.println(bb.fullDump());
345 
346         // replace return instructions with moves and gotos, and
347         // finish splicing in the control flow graph.
348         for (ListIterator.BasicBlock it = bb.getSuccessors().basicBlockIterator();
349              it.hasNext(); ) {
350             BasicBlock next_bb = it.nextBasicBlock();
351             next_bb.removePredecessor(bb);
352             next_bb.addPredecessor(successor_bb);
353             successor_bb.addSuccessor(next_bb);
354         }
355         bb.removeAllSuccessors();
356         bb.addSuccessor(callee.entry().getFallthroughSuccessor());
357         callee.entry().getFallthroughSuccessor().removeAllPredecessors();
358         callee.entry().getFallthroughSuccessor().addPredecessor(bb);
359         RegisterOperand dest = Invoke.getDest(q);
360         Register dest_r = null;
361         Move op = null;
362         if (dest != null) {
363             dest_r = dest.getRegister();
364             op = Move.getMoveOp(callee.getMethod().getReturnType());
365         }
366 outer:
367         for (ListIterator.BasicBlock it = callee.exit().getPredecessors().basicBlockIterator();
368              it.hasNext(); ) {
369             BasicBlock pred_bb = it.nextBasicBlock();
370             while (pred_bb.size() == 0) {
371                 if (TRACE) System.out.println("Predecessor of exit has no quads? "+pred_bb);
372                 if (pred_bb.getNumberOfPredecessors() == 0) continue outer;
373                 pred_bb = pred_bb.getFallthroughPredecessor();
374             }
375             Quad lq = pred_bb.getLastQuad();
376             if (lq.getOperator() instanceof Return.THROW_A) {
377                 pred_bb.removeAllSuccessors(); pred_bb.addSuccessor(caller.exit());
378                 caller.exit().addPredecessor(pred_bb);
379                 continue;
380             }
381             Assert._assert(lq.getOperator() instanceof Return);
382             pred_bb.removeQuad(lq);
383             if (dest_r != null) {
384                 RegisterOperand ldest = new RegisterOperand(dest_r, callee.getMethod().getReturnType());
385                 Operand src = Return.getSrc(lq).copy();
386                 Quad q3 = Move.create(caller.getNewQuadID(), op, ldest, src);
387                 pred_bb.appendQuad(q3);
388             }
389             Quad q2 = Goto.create(caller.getNewQuadID(),
390                                   Goto.GOTO.INSTANCE,
391                                   new TargetOperand(successor_bb));
392             pred_bb.appendQuad(q2);
393             pred_bb.removeAllSuccessors(); pred_bb.addSuccessor(successor_bb);
394             successor_bb.addPredecessor(pred_bb);
395         }
396         
397         if (TRACE) out.println("Final result:");
398         if (TRACE) out.println(caller.fullDump());
399         if (TRACE) out.println(caller.getRegisterFactory().fullDump());
400 
401         if (TRACE) out.println("Original code:");
402         if (TRACE) out.println(CodeCache.getCode(callee.getMethod()).fullDump());
403         if (TRACE) out.println(CodeCache.getCode(callee.getMethod()).getRegisterFactory().fullDump());
404     }
405 
406     public static void inlineVirtualCallSiteWithTypeCheck(ControlFlowGraph caller, BasicBlock bb, Quad q, ControlFlowGraph callee, jq_Class type) {
407         if (TRACE) out.println("Inlining "+q+" in "+bb+" for target "+callee.getMethod());
408 
409         int invokeLocation = bb.getQuadIndex(q);
410         Assert._assert(invokeLocation != -1);
411 
412         if (TRACE) out.println("Code to inline:");
413         if (TRACE) out.println(callee.fullDump());
414         if (TRACE) out.println(callee.getRegisterFactory().fullDump());
415 
416         // copy the callee's control flow graph, renumbering the basic blocks,
417         // registers and quads, and add the exception handlers.
418         callee = caller.merge(callee);
419         if (TRACE) out.println("After renumbering:");
420         if (TRACE) out.println(callee.fullDump());
421         if (TRACE) out.println(callee.getRegisterFactory().fullDump());
422         callee.appendExceptionHandlers(bb.getExceptionHandlers());
423 
424         if (TRACE) out.println("Original basic block containing invoke:");
425         if (TRACE) out.println(bb.fullDump());
426 
427         // split the basic block containing the invoke, and start splicing
428         // in the callee's control flow graph.
429         BasicBlock successor_bb = caller.createBasicBlock(callee.exit().getNumberOfPredecessors() + 1, bb.getNumberOfSuccessors(), bb.size() - invokeLocation, bb.getExceptionHandlers());
430         
431 //        /* Insert a fake replacement method call */
432 //        MethodOperand fakeOperand = getFakeMethodOperand(Invoke.getMethod(q).getMethod());
433 //        if(fakeOperand != null){
434 //            int len = fakeOperand.getMethod().getParamTypes().length;
435 //            Quad fakeCallQuad = Invoke.InvokeStatic.create(
436 //                caller.getNewQuadID(), Invoke.INVOKESTATIC_A.INSTANCE, 
437 //                (RegisterOperand) q.getOp1().copy(), (MethodOperand) fakeOperand.copy(), 
438 //                len);
439 //            
440 //            for(int i = 0; i < len; i++){
441 //                Invoke.setParam(fakeCallQuad, i, (RegisterOperand) Invoke.getParam(q, i).copy());
442 //            }
443 //            successor_bb.appendQuad(fakeCallQuad);
444 ////            System.out.println(
445 ////                "Replaced a call to " + Invoke.getMethod(q) + 
446 ////                " with a call to " + fakeOperand.getMethod());
447 //        }
448         
449         int bb_size = bb.size();
450         for (int i=invokeLocation+1; i<bb_size; ++i) {
451             successor_bb.appendQuad(bb.removeQuad(invokeLocation+1));
452         }
453         Quad invokeQuad = bb.removeQuad(invokeLocation);
454         Assert._assert(invokeQuad == q);
455         Assert._assert(bb.size() == invokeLocation);
456         if (TRACE) out.println("Result of splitting:");
457         if (TRACE) out.println(bb.fullDump());
458         if (TRACE) out.println(successor_bb.fullDump());
459 
460         // create failsafe case block
461         BasicBlock bb_fail = caller.createBasicBlock(1, 1, 2, bb.getExceptionHandlers());
462         bb_fail.appendQuad(invokeQuad);
463         Quad q2 = Goto.create(caller.getNewQuadID(),
464                               Goto.GOTO.INSTANCE,
465                               new TargetOperand(successor_bb));
466         bb_fail.appendQuad(q2);
467         bb_fail.addSuccessor(successor_bb);
468         successor_bb.addPredecessor(bb_fail);
469         if (TRACE) out.println("Fail-safe block:");
470         if (TRACE) out.println(bb_fail.fullDump());
471         
472         // create success case block
473         BasicBlock bb_success = caller.createBasicBlock(1, 1, Invoke.getParamList(q).length(), bb.getExceptionHandlers());
474         
475         // add test.
476         jq_Type dis_t = Invoke.getParam(q, 0).getType();
477         RegisterOperand dis_op = new RegisterOperand(Invoke.getParam(q, 0).getRegister(), dis_t);
478         Register res = caller.getRegisterFactory().getOrCreateStack(0, jq_Primitive.BOOLEAN);
479         RegisterOperand res_op = new RegisterOperand(res, jq_Primitive.BOOLEAN);
480         TypeOperand type_op = new TypeOperand(type);
481         q2 = InstanceOf.create(caller.getNewQuadID(), InstanceOf.INSTANCEOF.INSTANCE, res_op, dis_op, type_op);
482         bb.appendQuad(q2);
483         res_op = new RegisterOperand(res, jq_Primitive.BOOLEAN);
484         IConstOperand zero_op = new IConstOperand(0);
485         ConditionOperand ne_op = new ConditionOperand(BytecodeVisitor.CMP_NE);
486         TargetOperand success_op = new TargetOperand(bb_success);
487         q2 = IntIfCmp.create(caller.getNewQuadID(), IntIfCmp.IFCMP_I.INSTANCE, res_op, zero_op, ne_op, success_op);
488         bb.appendQuad(q2);
489         
490         // add instructions to set parameters.
491         ParamListOperand plo = Invoke.getParamList(q);
492         jq_Type[] params = callee.getMethod().getParamTypes();
493         for (int i=0, j=0; i<params.length; ++i, ++j) {
494             Move op = Move.getMoveOp(params[i]);
495             Register dest_r = callee.getRegisterFactory().getOrCreateLocal(j, params[i]);
496             RegisterOperand dest = new RegisterOperand(dest_r, params[i]);
497             Register src_r = plo.get(i).getRegister();
498             RegisterOperand src = new RegisterOperand(src_r, params[i]);
499             q2 = Move.create(caller.getNewQuadID(), op, dest, src);
500             bb_success.appendQuad(q2);
501             if (params[i].getReferenceSize() == 8) ++j;
502         }
503         if (TRACE) out.println("Success block after adding parameter moves:");
504         if (TRACE) out.println(bb_success.fullDump());
505 
506         // replace return instructions with moves and gotos, and
507         // finish splicing in the control flow graph.
508         for (ListIterator.BasicBlock it = bb.getSuccessors().basicBlockIterator();
509              it.hasNext(); ) {
510             BasicBlock next_bb = it.nextBasicBlock();
511             next_bb.removePredecessor(bb);
512             next_bb.addPredecessor(successor_bb);
513             successor_bb.addSuccessor(next_bb);
514         }
515         bb_success.addSuccessor(callee.entry().getFallthroughSuccessor());
516         callee.entry().getFallthroughSuccessor().removeAllPredecessors();
517         callee.entry().getFallthroughSuccessor().addPredecessor(bb_success);
518         bb.removeAllSuccessors();
519         bb.addSuccessor(bb_fail);
520         bb_fail.addPredecessor(bb);
521         bb.addSuccessor(bb_success);
522         bb_success.addPredecessor(bb);
523         RegisterOperand dest = Invoke.getDest(q);
524         Register dest_r = null;
525         Move op = null;
526         if (dest != null) {
527             dest_r = dest.getRegister();
528             op = Move.getMoveOp(callee.getMethod().getReturnType());
529         }
530 outer:
531         for (ListIterator.BasicBlock it = callee.exit().getPredecessors().basicBlockIterator();
532              it.hasNext(); ) {
533             BasicBlock pred_bb = it.nextBasicBlock();
534             while (pred_bb.size() == 0) { 
535                 if (TRACE) System.out.println("Predecessor of exit has no quads? "+pred_bb);
536                 if (pred_bb.getNumberOfPredecessors() == 0) continue outer;
537                 pred_bb = pred_bb.getFallthroughPredecessor();
538             }
539             Quad lq = pred_bb.getLastQuad();
540             if (lq.getOperator() instanceof Return.THROW_A) {
541                 pred_bb.removeAllSuccessors(); pred_bb.addSuccessor(caller.exit());
542                 caller.exit().addPredecessor(pred_bb);
543                 continue;
544             }
545             Assert._assert(lq.getOperator() instanceof Return);
546             pred_bb.removeQuad(lq);
547             if (dest_r != null) {
548                 RegisterOperand ldest = new RegisterOperand(dest_r, callee.getMethod().getReturnType());
549                 Operand src = Return.getSrc(lq).copy();
550                 Quad q3 = Move.create(caller.getNewQuadID(), op, ldest, src);
551                 pred_bb.appendQuad(q3);
552             }
553             q2 = Goto.create(caller.getNewQuadID(),
554                              Goto.GOTO.INSTANCE,
555                              new TargetOperand(successor_bb));
556             pred_bb.appendQuad(q2);
557             pred_bb.removeAllSuccessors(); pred_bb.addSuccessor(successor_bb);
558             successor_bb.addPredecessor(pred_bb);
559         }
560         
561         if (TRACE) out.println("Final result:");
562         if (TRACE) out.println(caller.fullDump());
563         if (TRACE) out.println(caller.getRegisterFactory().fullDump());
564 
565         if (TRACE) out.println("Original code:");
566         if (TRACE) out.println(CodeCache.getCode(callee.getMethod()).fullDump());
567         if (TRACE) out.println(CodeCache.getCode(callee.getMethod()).getRegisterFactory().fullDump());
568     }
569         
570 }