1
2
3
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
50
51 public MethodInline(Oracle o) {
52 this.oracle = o;
53 }
54 public MethodInline(CallGraph cg) {
55
56 this(new InlineSelectedCalls(cg));
57 this.cg = cg;
58 }
59 public MethodInline() {
60
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
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
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
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
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
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
306
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
317
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
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
347
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
417
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
428
429 BasicBlock successor_bb = caller.createBasicBlock(callee.exit().getNumberOfPredecessors() + 1, bb.getNumberOfSuccessors(), bb.size() - invokeLocation, bb.getExceptionHandlers());
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
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
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
473 BasicBlock bb_success = caller.createBasicBlock(1, 1, Invoke.getParamList(q).length(), bb.getExceptionHandlers());
474
475
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
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
507
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 }