001 /*
002 * This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 * This file is licensed to You under the Eclipse Public License (EPL);
005 * You may not use this file except in compliance with the License. You
006 * may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 * See the COPYRIGHT.txt file distributed with this work for information
011 * regarding copyright ownership.
012 */
013 package org.jikesrvm.compilers.opt.controlflow;
014
015 import static org.jikesrvm.compilers.opt.ir.IRTools.CPOS;
016 import static org.jikesrvm.compilers.opt.ir.Operators.BBEND;
017 import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_ADDR;
018 import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_INT;
019 import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_NOT;
020 import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_2FLOAT_opcode;
021 import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_ADD_opcode;
022 import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_MOVE_opcode;
023 import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_MUL_opcode;
024 import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_NEG_opcode;
025 import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_SUB_opcode;
026 import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_2DOUBLE_opcode;
027 import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_ADD_opcode;
028 import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_MOVE_opcode;
029 import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_MUL_opcode;
030 import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_NEG_opcode;
031 import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_SUB_opcode;
032 import static org.jikesrvm.compilers.opt.ir.Operators.GOTO;
033 import static org.jikesrvm.compilers.opt.ir.Operators.INT_2BYTE_opcode;
034 import static org.jikesrvm.compilers.opt.ir.Operators.INT_2SHORT_opcode;
035 import static org.jikesrvm.compilers.opt.ir.Operators.INT_2USHORT_opcode;
036 import static org.jikesrvm.compilers.opt.ir.Operators.INT_ADD_opcode;
037 import static org.jikesrvm.compilers.opt.ir.Operators.INT_AND_opcode;
038 import static org.jikesrvm.compilers.opt.ir.Operators.INT_IFCMP;
039 import static org.jikesrvm.compilers.opt.ir.Operators.INT_IFCMP2;
040 import static org.jikesrvm.compilers.opt.ir.Operators.INT_MOVE;
041 import static org.jikesrvm.compilers.opt.ir.Operators.INT_MOVE_opcode;
042 import static org.jikesrvm.compilers.opt.ir.Operators.INT_MUL_opcode;
043 import static org.jikesrvm.compilers.opt.ir.Operators.INT_NEG_opcode;
044 import static org.jikesrvm.compilers.opt.ir.Operators.INT_NOT_opcode;
045 import static org.jikesrvm.compilers.opt.ir.Operators.INT_OR_opcode;
046 import static org.jikesrvm.compilers.opt.ir.Operators.INT_SHL_opcode;
047 import static org.jikesrvm.compilers.opt.ir.Operators.INT_SHR_opcode;
048 import static org.jikesrvm.compilers.opt.ir.Operators.INT_SUB_opcode;
049 import static org.jikesrvm.compilers.opt.ir.Operators.INT_USHR_opcode;
050 import static org.jikesrvm.compilers.opt.ir.Operators.INT_XOR_opcode;
051 import static org.jikesrvm.compilers.opt.ir.Operators.REF_ADD_opcode;
052 import static org.jikesrvm.compilers.opt.ir.Operators.REF_AND_opcode;
053 import static org.jikesrvm.compilers.opt.ir.Operators.REF_IFCMP;
054 import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE_opcode;
055 import static org.jikesrvm.compilers.opt.ir.Operators.REF_NOT_opcode;
056 import static org.jikesrvm.compilers.opt.ir.Operators.REF_OR_opcode;
057 import static org.jikesrvm.compilers.opt.ir.Operators.REF_SHL_opcode;
058 import static org.jikesrvm.compilers.opt.ir.Operators.REF_SHR_opcode;
059 import static org.jikesrvm.compilers.opt.ir.Operators.REF_SUB_opcode;
060 import static org.jikesrvm.compilers.opt.ir.Operators.REF_USHR_opcode;
061 import static org.jikesrvm.compilers.opt.ir.Operators.REF_XOR_opcode;
062 import static org.jikesrvm.compilers.opt.ir.Operators.RETURN;
063
064 import java.util.HashMap;
065 import java.util.HashSet;
066
067 import org.jikesrvm.VM;
068 import org.jikesrvm.classloader.Atom;
069 import org.jikesrvm.classloader.TypeReference;
070 import org.jikesrvm.compilers.opt.OptimizingCompilerException;
071 import org.jikesrvm.compilers.opt.ir.BasicBlock;
072 import org.jikesrvm.compilers.opt.ir.BooleanCmp;
073 import org.jikesrvm.compilers.opt.ir.CondMove;
074 import org.jikesrvm.compilers.opt.ir.Goto;
075 import org.jikesrvm.compilers.opt.ir.IR;
076 import org.jikesrvm.compilers.opt.ir.IRTools;
077 import org.jikesrvm.compilers.opt.ir.IfCmp;
078 import org.jikesrvm.compilers.opt.ir.IfCmp2;
079 import org.jikesrvm.compilers.opt.ir.InlineGuard;
080 import org.jikesrvm.compilers.opt.ir.Instruction;
081 import org.jikesrvm.compilers.opt.ir.InstructionEnumeration;
082 import org.jikesrvm.compilers.opt.ir.Move;
083 import org.jikesrvm.compilers.opt.ir.OperandEnumeration;
084 import org.jikesrvm.compilers.opt.ir.Operator;
085 import org.jikesrvm.compilers.opt.ir.Register;
086 import org.jikesrvm.compilers.opt.ir.Return;
087 import org.jikesrvm.compilers.opt.ir.Unary;
088 import org.jikesrvm.compilers.opt.ir.operand.BranchOperand;
089 import org.jikesrvm.compilers.opt.ir.operand.BranchProfileOperand;
090 import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand;
091 import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
092 import org.jikesrvm.compilers.opt.ir.operand.Operand;
093 import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
094
095 /**
096 * Perform simple peephole optimizations for branches.
097 */
098 public final class BranchOptimizations extends BranchOptimizationDriver {
099
100 /**
101 * Name of abs method used as a special case in conditional moves
102 */
103 private static final Atom ABS = Atom.findOrCreateAsciiAtom("abs");
104
105 /**
106 * Is branch optimizations allowed to change the code order to
107 * create fallthrough edges (and thus merge basic blocks)?
108 * After we run code reordering, we disallow this transformation to avoid
109 * destroying the desired code order.
110 */
111 private final boolean mayReorderCode;
112
113 /**
114 * Are we allowed to duplication conditional branches?
115 * Restricted until backedge yieldpoints are inserted to
116 * avoid creating irreducible control flow by duplicating
117 * a conditional branch in a loop header into a block outside the
118 * loop, thus creating two loop entry blocks.
119 */
120 private final boolean mayDuplicateCondBranches;
121
122 /**
123 * @param level the minimum optimization level at which the branch
124 * optimizations should be performed.
125 * @param mayReorderCode are we allowed to change the code order?
126 * @param mayDuplicateCondBranches are we allowed to duplicate conditional branches?
127 */
128 public BranchOptimizations(int level, boolean mayReorderCode, boolean mayDuplicateCondBranches) {
129 super(level, true);
130 this.mayReorderCode = mayReorderCode;
131 this.mayDuplicateCondBranches = mayDuplicateCondBranches;
132 }
133
134 /**
135 * @param level the minimum optimization level at which the branch
136 * optimizations should be performed.
137 * @param mayReorderCode are we allowed to change the code order?
138 * @param mayDuplicateCondBranches are we allowed to duplicate conditional branches?
139 * @param simplify simplify prior to optimizing?
140 */
141 public BranchOptimizations(int level, boolean mayReorderCode, boolean mayDuplicateCondBranches,
142 boolean simplify) {
143 super(level, simplify);
144 this.mayReorderCode = mayReorderCode;
145 this.mayDuplicateCondBranches = mayDuplicateCondBranches;
146 }
147
148 /**
149 * This method actually does the work of attempting to
150 * peephole optimize a branch instruction.
151 * See Muchnick ~p.590
152 * @param ir the containing IR
153 * @param s the branch instruction to optimize
154 * @param bb the containing basic block
155 * @return true if an optimization was applied, false otherwise
156 */
157 protected boolean optimizeBranchInstruction(IR ir, Instruction s, BasicBlock bb) {
158 if (Goto.conforms(s)) {
159 return processGoto(ir, s, bb);
160 } else if (IfCmp.conforms(s)) {
161 return processConditionalBranch(ir, s, bb);
162 } else if (InlineGuard.conforms(s)) {
163 return processInlineGuard(ir, s, bb);
164 } else if (IfCmp2.conforms(s)) {
165 return processTwoTargetConditionalBranch(ir, s, bb);
166 } else {
167 return false;
168 }
169 }
170
171 /**
172 * Perform optimizations for a Goto.
173 *
174 * <p> Patterns:
175 * <pre>
176 * 1) GOTO A replaced by GOTO B
177 * A: GOTO B
178 *
179 * 2) GOTO A replaced by IF .. GOTO B
180 * A: IF .. GOTO B GOTO C
181 * C: ...
182 * 3) GOTO next instruction eliminated
183 * 4) GOTO A replaced by GOTO B
184 * A: LABEL
185 * BBEND
186 * B:
187 * 5) GOTO BBn where BBn has exactly one in edge
188 * - move BBn immediately after the GOTO in the code order,
189 * so that pattern 3) will create a fallthrough
190 * <pre>
191 *
192 * <p> Precondition: Goto.conforms(g)
193 *
194 * @param ir governing IR
195 * @param g the instruction to optimize
196 * @param bb the basic block holding g
197 * @return true if made a transformation
198 */
199 private boolean processGoto(IR ir, Instruction g, BasicBlock bb) {
200 BasicBlock targetBlock = g.getBranchTarget();
201
202 // don't optimize jumps to a code motion landing pad
203 if (targetBlock.getLandingPad()) return false;
204
205 Instruction targetLabel = targetBlock.firstInstruction();
206 // get the first real instruction at the g target
207 // NOTE: this instruction is not necessarily in targetBlock,
208 // iff targetBlock has no real instructions
209 Instruction targetInst = firstRealInstructionFollowing(targetLabel);
210 if (targetInst == null || targetInst == g) {
211 return false;
212 }
213 Instruction nextLabel = firstLabelFollowing(g);
214 if (targetLabel == nextLabel) {
215 // found a GOTO to the next instruction. just remove it.
216 g.remove();
217 return true;
218 }
219 if (Goto.conforms(targetInst)) {
220 // unconditional branch to unconditional branch.
221 // replace g with goto to targetInst's target
222 Instruction target2 = firstRealInstructionFollowing(targetInst.getBranchTarget().firstInstruction());
223 if (target2 == targetInst) {
224 // Avoid an infinite recursion in the following bizarre scenario:
225 // g: goto L
226 // ...
227 // L: goto L
228 // This happens in jByteMark.EmFloatPnt.denormalize() due to a while(true) {}
229 return false;
230 }
231 Goto.setTarget(g, (BranchOperand) Goto.getTarget(targetInst).copy());
232 bb.recomputeNormalOut(ir); // fix the CFG
233 return true;
234 }
235 if (targetBlock.isEmpty()) {
236 // GOTO an empty basic block. Change target to the
237 // next block.
238 BasicBlock nextBlock = targetBlock.getFallThroughBlock();
239 Goto.setTarget(g, nextBlock.makeJumpTarget());
240 bb.recomputeNormalOut(ir); // fix the CFG
241 return true;
242 }
243 if (mayDuplicateCondBranches && IfCmp.conforms(targetInst)) {
244 // unconditional branch to a conditional branch.
245 // If the Goto is the only branch instruction in its basic block
246 // and the IfCmp is the only non-GOTO branch instruction
247 // in its basic block then replace the goto with a copy of
248 // targetInst and append another GOTO to the not-taken
249 // target of targetInst's block.
250 // We impose these additional restrictions to avoid getting
251 // multiple conditional branches in a single basic block.
252 if (!g.prevInstructionInCodeOrder().isBranch() &&
253 (targetInst.nextInstructionInCodeOrder().operator == BBEND ||
254 targetInst.nextInstructionInCodeOrder().operator == GOTO)) {
255 Instruction copy = targetInst.copyWithoutLinks();
256 g.replace(copy);
257 Instruction newGoto = targetInst.getBasicBlock().getNotTakenNextBlock().makeGOTO();
258 copy.insertAfter(newGoto);
259 bb.recomputeNormalOut(ir); // fix the CFG
260 return true;
261 }
262 }
263
264 // try to create a fallthrough
265 if (mayReorderCode && targetBlock.getNumberOfIn() == 1) {
266 BasicBlock ftBlock = targetBlock.getFallThroughBlock();
267 if (ftBlock != null) {
268 BranchOperand ftTarget = ftBlock.makeJumpTarget();
269 targetBlock.appendInstruction(CPOS(g,Goto.create(GOTO, ftTarget)));
270 }
271
272 ir.cfg.removeFromCodeOrder(targetBlock);
273 ir.cfg.insertAfterInCodeOrder(bb, targetBlock);
274 targetBlock.recomputeNormalOut(ir); // fix the CFG
275 return true;
276 }
277 return false;
278 }
279
280 /**
281 * Perform optimizations for a conditional branch.
282 *
283 * <pre>
284 * 1) IF .. GOTO A replaced by IF .. GOTO B
285 * ...
286 * A: GOTO B
287 * 2) conditional branch to next instruction eliminated
288 * 3) IF (condition) GOTO A replaced by IF (!condition) GOTO B
289 * GOTO B A: ...
290 * A: ...
291 * 4) special case to generate Boolean compare opcode
292 * 5) special case to generate conditional move sequence
293 * 6) IF .. GOTO A replaced by IF .. GOTO B
294 * A: LABEL
295 * BBEND
296 * B:
297 * 7) fallthrough to a goto: replicate goto to enable other optimizations.
298 * </pre>
299 *
300 * <p> Precondition: IfCmp.conforms(cb)
301 *
302 * @param ir the governing IR
303 * @param cb the instruction to optimize
304 * @param bb the basic block holding if
305 * @return true iff made a transformation
306 */
307 private boolean processConditionalBranch(IR ir, Instruction cb, BasicBlock bb) {
308 BasicBlock targetBlock = cb.getBranchTarget();
309
310 // don't optimize jumps to a code motion landing pad
311 if (targetBlock.getLandingPad()) return false;
312
313 Instruction targetLabel = targetBlock.firstInstruction();
314 // get the first real instruction at the branch target
315 // NOTE: this instruction is not necessarily in targetBlock,
316 // iff targetBlock has no real instructions
317 Instruction targetInst = firstRealInstructionFollowing(targetLabel);
318 if (targetInst == null || targetInst == cb) {
319 return false;
320 }
321 boolean endsBlock = cb.nextInstructionInCodeOrder().operator() == BBEND;
322 if (endsBlock) {
323 Instruction nextLabel = firstLabelFollowing(cb);
324
325 if (targetLabel == nextLabel) {
326 // found a conditional branch to the next instruction. just remove it.
327 cb.remove();
328 return true;
329 }
330 Instruction nextI = firstRealInstructionFollowing(nextLabel);
331 if (nextI != null && Goto.conforms(nextI)) {
332 // Check that the target is not the fall through (the goto itself).
333 // If we add a goto to the next block, it will be removed by
334 // processGoto and we will loop indefinitely.
335 // This can be tripped by (strange) code such as:
336 // if (condition) while (true);
337 BasicBlock gotoTarget = nextI.getBranchTarget();
338 Instruction gotoLabel = gotoTarget.firstInstruction();
339 Instruction gotoInst = firstRealInstructionFollowing(gotoLabel);
340
341 if (gotoInst != nextI) {
342 // replicate Goto
343 cb.insertAfter(nextI.copyWithoutLinks());
344 bb.recomputeNormalOut(ir); // fix the CFG
345 return true;
346 }
347 }
348 }
349 // attempt to generate boolean compare.
350 if (generateBooleanCompare(ir, bb, cb, targetBlock)) {
351 // generateBooleanCompare does all necessary CFG fixup.
352 return true;
353 }
354 // attempt to generate a sequence using conditional moves
355 if (generateCondMove(ir, bb, cb)) {
356 // generateCondMove does all necessary CFG fixup.
357 return true;
358 }
359
360 // do we fall through to a block that has only a goto?
361 BasicBlock fallThrough = bb.getFallThroughBlock();
362 if (fallThrough != null) {
363 Instruction fallThroughInstruction = fallThrough.firstRealInstruction();
364 if ((fallThroughInstruction != null) && Goto.conforms(fallThroughInstruction)) {
365 // copy goto to bb
366 bb.appendInstruction(fallThroughInstruction.copyWithoutLinks());
367 bb.recomputeNormalOut(ir);
368 }
369 }
370
371 if (Goto.conforms(targetInst)) {
372 // conditional branch to unconditional branch.
373 // change conditional branch target to latter's target
374 Instruction target2 = firstRealInstructionFollowing(targetInst.getBranchTarget().firstInstruction());
375 if (target2 == targetInst) {
376 // Avoid an infinite recursion in the following scenario:
377 // g: if (...) goto L
378 // ...
379 // L: goto L
380 // This happens in GCUtil in some systems due to a while(true) {}
381 return false;
382 }
383 IfCmp.setTarget(cb, (BranchOperand) Goto.getTarget(targetInst).copy());
384 bb.recomputeNormalOut(ir); // fix the CFG
385 return true;
386 }
387 if (targetBlock.isEmpty()) {
388 // branch to an empty block. Change target to the next block.
389 BasicBlock nextBlock = targetBlock.getFallThroughBlock();
390 IfCmp.setTarget(cb, nextBlock.makeJumpTarget());
391 bb.recomputeNormalOut(ir); // fix the CFG
392 return true;
393 }
394 if (isFlipCandidate(cb, targetInst)) {
395 flipConditionalBranch(cb);
396 bb.recomputeNormalOut(ir); // fix the CFG
397 return true;
398 }
399 return false;
400 }
401
402 /**
403 * Perform optimizations for an inline guard.
404 *
405 * <p> Precondition: InlineGuard.conforms(cb)
406 *
407 * @param ir the governing IR
408 * @param cb the instruction to optimize
409 * @param bb the basic block holding if
410 * @return true iff made a transformation
411 */
412 private boolean processInlineGuard(IR ir, Instruction cb, BasicBlock bb) {
413 BasicBlock targetBlock = cb.getBranchTarget();
414 Instruction targetLabel = targetBlock.firstInstruction();
415 // get the first real instruction at the branch target
416 // NOTE: this instruction is not necessarily in targetBlock,
417 // iff targetBlock has no real instructions
418 Instruction targetInst = firstRealInstructionFollowing(targetLabel);
419 if (targetInst == null || targetInst == cb) {
420 return false;
421 }
422 boolean endsBlock = cb.nextInstructionInCodeOrder().operator() == BBEND;
423 if (endsBlock) {
424 Instruction nextLabel = firstLabelFollowing(cb);
425 if (targetLabel == nextLabel) {
426 // found a conditional branch to the next instruction. just remove it.
427 cb.remove();
428 return true;
429 }
430 Instruction nextI = firstRealInstructionFollowing(nextLabel);
431 if (nextI != null && Goto.conforms(nextI)) {
432 // replicate Goto
433 cb.insertAfter(nextI.copyWithoutLinks());
434 bb.recomputeNormalOut(ir); // fix the CFG
435 return true;
436 }
437 }
438 // do we fall through to a block that has only a goto?
439 BasicBlock fallThrough = bb.getFallThroughBlock();
440 if (fallThrough != null) {
441 Instruction fallThroughInstruction = fallThrough.firstRealInstruction();
442 if ((fallThroughInstruction != null) && Goto.conforms(fallThroughInstruction)) {
443 // copy goto to bb
444 bb.appendInstruction(fallThroughInstruction.copyWithoutLinks());
445 bb.recomputeNormalOut(ir);
446 }
447 }
448
449 if (Goto.conforms(targetInst)) {
450 // conditional branch to unconditional branch.
451 // change conditional branch target to latter's target
452 InlineGuard.setTarget(cb, (BranchOperand) Goto.getTarget(targetInst).copy());
453 bb.recomputeNormalOut(ir); // fix the CFG
454 return true;
455 }
456 if (targetBlock.isEmpty()) {
457 // branch to an empty block. Change target to the next block.
458 BasicBlock nextBlock = targetBlock.getFallThroughBlock();
459 InlineGuard.setTarget(cb, nextBlock.makeJumpTarget());
460 bb.recomputeNormalOut(ir); // fix the CFG
461 return true;
462 }
463 return false;
464 }
465
466 /**
467 * Perform optimizations for a two way conditional branch.
468 *
469 * <p> Precondition: IfCmp2.conforms(cb)
470 *
471 * @param ir the governing IR
472 * @param cb the instruction to optimize
473 * @param bb the basic block holding if
474 * @return true iff made a transformation
475 */
476 private boolean processTwoTargetConditionalBranch(IR ir, Instruction cb, BasicBlock bb) {
477 // First condition/target
478 Instruction target1Label = IfCmp2.getTarget1(cb).target;
479 Instruction target1Inst = firstRealInstructionFollowing(target1Label);
480 Instruction nextLabel = firstLabelFollowing(cb);
481 boolean endsBlock = cb.nextInstructionInCodeOrder().operator() == BBEND;
482 if (target1Inst != null && target1Inst != cb) {
483 if (Goto.conforms(target1Inst)) {
484 // conditional branch to unconditional branch.
485 // change conditional branch target to latter's target
486 IfCmp2.setTarget1(cb, (BranchOperand) Goto.getTarget(target1Inst).copy());
487 bb.recomputeNormalOut(ir); // fix CFG
488 return true;
489 }
490 BasicBlock target1Block = target1Label.getBasicBlock();
491 if (target1Block.isEmpty()) {
492 // branch to an empty block. Change target to the next block.
493 BasicBlock nextBlock = target1Block.getFallThroughBlock();
494 IfCmp2.setTarget1(cb, nextBlock.makeJumpTarget());
495 bb.recomputeNormalOut(ir); // fix the CFG
496 return true;
497 }
498 }
499
500 // Second condition/target
501 Instruction target2Label = IfCmp2.getTarget2(cb).target;
502 Instruction target2Inst = firstRealInstructionFollowing(target2Label);
503 if (target2Inst != null && target2Inst != cb) {
504 if (Goto.conforms(target2Inst)) {
505 // conditional branch to unconditional branch.
506 // change conditional branch target to latter's target
507 IfCmp2.setTarget2(cb, (BranchOperand) Goto.getTarget(target2Inst).copy());
508 bb.recomputeNormalOut(ir); // fix CFG
509 return true;
510 }
511 if ((target2Label == nextLabel) && endsBlock) {
512 // found a conditional branch to the next instruction. Reduce to IfCmp
513 if (VM.VerifyAssertions) VM._assert(cb.operator() == INT_IFCMP2);
514 IfCmp.mutate(cb,
515 INT_IFCMP,
516 IfCmp2.getGuardResult(cb),
517 IfCmp2.getVal1(cb),
518 IfCmp2.getVal2(cb),
519 IfCmp2.getCond1(cb),
520 IfCmp2.getTarget1(cb),
521 IfCmp2.getBranchProfile1(cb));
522 return true;
523 }
524 BasicBlock target2Block = target2Label.getBasicBlock();
525 if (target2Block.isEmpty()) {
526 // branch to an empty block. Change target to the next block.
527 BasicBlock nextBlock = target2Block.getFallThroughBlock();
528 IfCmp2.setTarget2(cb, nextBlock.makeJumpTarget());
529 bb.recomputeNormalOut(ir); // fix the CFG
530 return true;
531 }
532 }
533
534 // if fall through to a goto; replicate the goto
535 if (endsBlock) {
536 Instruction nextI = firstRealInstructionFollowing(nextLabel);
537 if (nextI != null && Goto.conforms(nextI)) {
538 // replicate Goto
539 cb.insertAfter(nextI.copyWithoutLinks());
540 bb.recomputeNormalOut(ir); // fix the CFG
541 return true;
542 }
543 }
544
545 return false;
546 }
547
548 /**
549 * Is a conditional branch a candidate to be flipped?
550 * See comment 3) of processConditionalBranch
551 *
552 * <p> Precondition: IfCmp.conforms(cb)
553 *
554 * @param cb the conditional branch instruction
555 * @param target the target instruction (real instruction) of the conditional
556 * branch
557 * @return boolean result
558 */
559 private boolean isFlipCandidate(Instruction cb, Instruction target) {
560 // condition 1: is next instruction a GOTO?
561 Instruction next = cb.nextInstructionInCodeOrder();
562 if (!Goto.conforms(next)) {
563 return false;
564 }
565 // condition 2: is the target of the conditional branch the
566 // next instruction after the GOTO?
567 next = firstRealInstructionFollowing(next);
568 if (next != target) {
569 return false;
570 }
571 // got this far. It's a candidate.
572 return true;
573 }
574
575 /**
576 * Flip a conditional branch and remove the trailing goto.
577 * See comment 3) of processConditionalBranch
578 *
579 * <p> Precondition isFlipCandidate(cb)
580 * @param cb the conditional branch instruction
581 */
582 private void flipConditionalBranch(Instruction cb) {
583 // get the trailing GOTO instruction
584 Instruction g = cb.nextInstructionInCodeOrder();
585 BranchOperand gTarget = (BranchOperand) (Goto.getTarget(g).copy());
586 // now flip the test and set the new target
587 IfCmp.setCond(cb, IfCmp.getCond(cb).flipCode());
588 IfCmp.setTarget(cb, gTarget);
589
590 // Update the branch probability. It is now the opposite
591 cb.flipBranchProbability();
592 // finally, remove the trailing GOTO instruction
593 g.remove();
594 }
595
596 /**
597 * Generate a boolean operation opcode
598 *
599 * <pre>
600 * 1) IF br != 0 THEN x=1 ELSE x=0 replaced by INT_MOVE x=br
601 * IF br == 0 THEN x=0 ELSE x=1
602 * 2) IF br == 0 THEN x=1 ELSE x=0 replaced by BOOLEAN_NOT x=br
603 * IF br != 0 THEN x=0 ELSE x=1
604 * 3) IF v1 ~ v2 THEN x=1 ELSE x=0 replaced by BOOLEAN_CMP x=v1,v2,~
605 * </pre>
606 *
607 *
608 * @param cb conditional branch instruction
609 * @param res the operand for result
610 * @param val1 value being compared
611 * @param val2 value being compared with
612 * @param cond comparison condition
613 */
614 private void booleanCompareHelper(Instruction cb, RegisterOperand res, Operand val1, Operand val2,
615 ConditionOperand cond) {
616 if ((val1 instanceof RegisterOperand) &&
617 ((RegisterOperand) val1).getType().isBooleanType() &&
618 (val2 instanceof IntConstantOperand)) {
619 int value = ((IntConstantOperand) val2).value;
620 if (VM.VerifyAssertions && (value != 0) && (value != 1)) {
621 throw new OptimizingCompilerException("Invalid boolean value");
622 }
623 int c = cond.evaluate(value, 0);
624 if (c == ConditionOperand.TRUE) {
625 Unary.mutate(cb, BOOLEAN_NOT, res, val1);
626 return;
627 } else if (c == ConditionOperand.FALSE) {
628 Move.mutate(cb, INT_MOVE, res, val1);
629 return;
630 }
631 }
632 BooleanCmp.mutate(cb,
633 (cb.operator() == REF_IFCMP) ? BOOLEAN_CMP_ADDR : BOOLEAN_CMP_INT,
634 res,
635 val1,
636 val2,
637 cond,
638 new BranchProfileOperand());
639 }
640
641 /**
642 * Attempt to generate a straight-line sequence using conditional move
643 * instructions, to replace a diamond control flow structure.
644 *
645 * <p>Suppose we have the following code, where e{n} is an expression:
646 * <pre>
647 * if (a op b) {
648 * x = e2;
649 * y = e3;
650 * } else {
651 * z = e4;
652 * x = e5;
653 * }
654 * </pre>
655 * We would transform this to:
656 * <pre>
657 * t1 = a;
658 * t2 = b;
659 * t3 = e2;
660 * t4 = e3;
661 * t5 = e4;
662 * t6 = e5;
663 * COND MOVE [if (t1 op t2) x := t3 else x := t6 ];
664 * COND MOVE [if (t1 op t2) y := t4 else y := y];
665 * COND MOVE [if (t1 op t2) z := z else z := t5];
666 * </pre>
667 *
668 * <p>Note that we rely on other optimizations (eg. copy propagation) to
669 * clean up some of this unnecessary mess.
670 *
671 * <p>Note that in this example, we've increased the shortest path by 2
672 * expression evaluations, 2 moves, and 3 cond moves, but eliminated one
673 * conditional branch.
674 *
675 * <p>We apply a cost heuristic to guide this transformation:
676 * We will eliminate a conditional branch iff it increases the shortest
677 * path by no more than 'k' operations. Currently, we count each
678 * instruction (alu, move, or cond move) as 1 evaluation.
679 * The parameter k is specified by OPT\_Options.COND_MOVE_CUTOFF.
680 *
681 * <p> In the example above, since we've increased the shortest path by
682 * 6 instructions, we will only perform the transformation if k >= 7.
683 *
684 * <p> TODO items
685 * <ul>
686 * <li> consider smarter cost heuristics
687 * <li> enhance downstream code generation to avoid redundant evaluation
688 * of condition codes.
689 * </ul>
690 *
691 * @param ir governing IR
692 * @param bb basic block of cb
693 * @param cb conditional branch instruction
694 * @return true if the transformation succeeds, false otherwise
695 */
696 private boolean generateCondMove(IR ir, BasicBlock bb, Instruction cb) {
697 final boolean VERBOSE=false;
698 if (!VM.BuildForIA32) return false;
699 if (!IfCmp.conforms(cb)) return false;
700
701 if (VERBOSE) System.out.println("CondMove: Looking to optimize "+cb);
702 // Don't generate CMOVs for branches that can be folded.
703 if (IfCmp.getVal1(cb).isConstant() && IfCmp.getVal2(cb).isConstant()) {
704 if (VERBOSE) System.out.println("CondMove: fail - could be folded");
705 return false;
706 }
707
708 // see if bb is the root of an if-then-else.
709 Diamond diamond = Diamond.buildDiamond(bb);
710 if (diamond == null) {
711 if (VERBOSE) System.out.println("CondMove: fail - no diamond");
712 return false;
713 }
714 BasicBlock taken = diamond.getTaken();
715 BasicBlock notTaken = diamond.getNotTaken();
716
717 // do not perform the transformation if either branch of the diamond
718 // has a taboo instruction (eg., a PEI, store or divide).
719 if (taken != null && hasCMTaboo(taken)) {
720 if (VERBOSE) System.out.println("CondMove: fail - taken branch has taboo instruction");
721 return false;
722 }
723 if (notTaken != null && hasCMTaboo(notTaken)) {
724 if (VERBOSE) System.out.println("CondMove: fail - not taken branch has taboo instruction");
725 return false;
726 }
727
728 ConditionOperand cond = IfCmp.getCond(cb);
729
730 // Do not generate when we don't know the branch probability or
731 // when branch probability is high. CMOVs reduce performance of
732 // the out-of-order engine (Intel Optimization Guide -
733 // Assembly/Compiler Coding Rule 2).
734 // Ignore in the case of an abs() method as we can create tighter
735 // instructions.
736 BranchProfileOperand profile = IfCmp.getBranchProfile(cb);
737 if ((Math.abs(profile.takenProbability - 0.5) >= ir.options.CONTROL_WELL_PREDICTED_CUTOFF) &&
738 !(cb.position != null && cb.position.method.getName() == ABS && cond.isFLOATINGPOINT())) {
739 if (VERBOSE)
740 System.out.println("CondMove: fail - branch could be well predicted by branch predictor: "+
741 profile.takenProbability);
742 return false;
743 }
744
745 // if we must generate FCMP, make sure the condition code is OK
746 if (cond.isFLOATINGPOINT()) {
747 if (!fpConditionOK(cond)) {
748 // Condition not OK, but maybe if we flip the operands
749 if (!fpConditionOK(cond.flipOperands())) {
750 // still not ok so flip operands back
751 cond.flipOperands();
752 // give up or for SSE2 check if this is a floating point compare
753 // controlling just floating point moves
754 if (!VM.BuildForSSE2Full || hasFloatingPointDef(taken, true) || hasFloatingPointDef(notTaken, true)) {
755 if (VERBOSE) System.out.println("CondMove: fail - fp condition not OK: "+cond);
756 return false;
757 }
758 } else {
759 // flip operands
760 Operand val1 = IfCmp.getVal1(cb);
761 Operand val2 = IfCmp.getVal2(cb);
762 IfCmp.setVal1(cb, val2);
763 IfCmp.setVal2(cb, val1);
764 }
765 }
766 }
767
768 if (!cond.isFLOATINGPOINT()) {
769 // Can only generate moves of floating point values for floating point
770 // compares or for unsigned compares in x87
771 if (VM.BuildForSSE2Full || !cond.isUNSIGNED()) {
772 if (hasFloatingPointDef(taken, false) || hasFloatingPointDef(notTaken, false)) {
773 if (VERBOSE)
774 System.out.println("CondMove: fail - not allowed integer condition controlling floating conditional move");
775 return false;
776 }
777 }
778 }
779
780 // For now, do not generate CMOVs for longs.
781 if (hasLongDef(taken) || hasLongDef(notTaken)) {
782 return false;
783 }
784
785 // count the number of expression evaluations in each side of the
786 // diamond
787 int takenCost = 0;
788 int notTakenCost = 0;
789 if (taken != null) takenCost = evaluateCost(taken);
790 if (notTaken != null) notTakenCost = evaluateCost(notTaken);
791
792 // evaluate whether it's profitable.
793 int shortestCost = Math.min(takenCost, notTakenCost);
794 int xformCost = 2 * (takenCost + notTakenCost);
795 int k = ir.options.CONTROL_COND_MOVE_CUTOFF;
796 if (xformCost - shortestCost > k) {
797 if (VERBOSE) System.out.println("CondMove: fail - cost too high");
798 return false;
799 }
800
801 // Perform the transformation!
802 doCondMove(ir, diamond, cb);
803 return true;
804 }
805
806 /**
807 * Is a specified condition operand 'safe' to transfer into an FCMP
808 * instruction?
809 */
810 private boolean fpConditionOK(ConditionOperand c) {
811 // FCOMI sets ZF, PF, and CF as follows:
812 // Compare Results ZF PF CF
813 // left > right 0 0 0
814 // left < right 0 0 1
815 // left == right 1 0 0
816 // UNORDERED 1 1 1
817 switch (c.value) {
818 case ConditionOperand.CMPL_EQUAL:
819 return false; // (ZF == 1) but ordered
820 case ConditionOperand.CMPL_NOT_EQUAL:
821 return false; // (ZF == 0) but unordered
822 case ConditionOperand.CMPG_LESS:
823 return false; // (CF == 1) but ordered
824 case ConditionOperand.CMPG_GREATER_EQUAL:
825 return false; // (CF == 0) but unordered
826 case ConditionOperand.CMPG_LESS_EQUAL:
827 return false; // (CF == 1 || ZF == 1) but ordered
828 case ConditionOperand.CMPG_GREATER:
829 return false; // (CF == 0 && ZF == 0) but unordered
830
831 case ConditionOperand.CMPL_GREATER:
832 return true; // (CF == 0 && ZF == 0) and ordered
833 case ConditionOperand.CMPL_LESS_EQUAL:
834 return true; // (CF == 1 || ZF == 1) and unordered
835 case ConditionOperand.CMPL_GREATER_EQUAL:
836 return true; // (CF == 0) and ordered
837 case ConditionOperand.CMPL_LESS:
838 return true; // (CF == 1) and unordered
839 default:
840 OptimizingCompilerException.UNREACHABLE();
841 return false; // keep jikes happy
842 }
843 }
844
845 /**
846 * Do any of the instructions in a basic block define a floating-point
847 * register?
848 *
849 * @param bb basic block to search
850 * @param invert invert the sense of the search
851 */
852 private static boolean hasFloatingPointDef(BasicBlock bb, boolean invert) {
853 if (bb == null) return false;
854 for (InstructionEnumeration e = bb.forwardRealInstrEnumerator(); e.hasMoreElements();) {
855 Instruction s = e.nextElement();
856 for (OperandEnumeration d = s.getDefs(); d.hasMoreElements();) {
857 Operand def = d.nextElement();
858 if (def.isRegister()) {
859 if (def.asRegister().getRegister().isFloatingPoint() != invert) return true;
860 }
861 }
862 }
863 return false;
864 }
865
866 /**
867 * Do any of the instructions in a basic block define a long
868 * register?
869 */
870 private boolean hasLongDef(BasicBlock bb) {
871 if (bb == null) return false;
872 for (InstructionEnumeration e = bb.forwardRealInstrEnumerator(); e.hasMoreElements();) {
873 Instruction s = e.nextElement();
874 for (OperandEnumeration d = s.getDefs(); d.hasMoreElements();) {
875 Operand def = d.nextElement();
876 if (def.isRegister()) {
877 if (def.asRegister().getRegister().isLong()) return true;
878 }
879 }
880 }
881 return false;
882 }
883
884 /**
885 * Do any of the instructions in a basic block preclude eliminating the
886 * basic block with conditional moves?
887 */
888 private boolean hasCMTaboo(BasicBlock bb) {
889
890 if (bb == null) return false;
891
892 // Note: it is taboo to assign more than once to any register in the
893 // block.
894 HashSet<Register> defined = new HashSet<Register>();
895
896 for (InstructionEnumeration e = bb.forwardRealInstrEnumerator(); e.hasMoreElements();) {
897 Instruction s = e.nextElement();
898 if (s.isBranch()) continue;
899 // for now, only the following opcodes are legal.
900 switch (s.operator.opcode) {
901 case INT_MOVE_opcode:
902 case REF_MOVE_opcode:
903 case DOUBLE_MOVE_opcode:
904 case FLOAT_MOVE_opcode:
905 case INT_ADD_opcode:
906 case REF_ADD_opcode:
907 case FLOAT_ADD_opcode:
908 case DOUBLE_ADD_opcode:
909 case INT_SUB_opcode:
910 case REF_SUB_opcode:
911 case FLOAT_SUB_opcode:
912 case DOUBLE_SUB_opcode:
913 case INT_MUL_opcode:
914 case FLOAT_MUL_opcode:
915 case DOUBLE_MUL_opcode:
916 case INT_NEG_opcode:
917 case FLOAT_NEG_opcode:
918 case DOUBLE_NEG_opcode:
919 case REF_SHL_opcode:
920 case INT_SHL_opcode:
921 case REF_SHR_opcode:
922 case INT_SHR_opcode:
923 case REF_USHR_opcode:
924 case INT_USHR_opcode:
925 case REF_AND_opcode:
926 case INT_AND_opcode:
927 case REF_OR_opcode:
928 case INT_OR_opcode:
929 case REF_XOR_opcode:
930 case INT_XOR_opcode:
931 case REF_NOT_opcode:
932 case INT_NOT_opcode:
933 case INT_2BYTE_opcode:
934 case INT_2USHORT_opcode:
935 case INT_2SHORT_opcode:
936 case FLOAT_2DOUBLE_opcode:
937 case DOUBLE_2FLOAT_opcode:
938 // these are OK.
939 break;
940 default:
941 return true;
942 }
943
944 // make sure no register is defined more than once in this block.
945 for (OperandEnumeration defs = s.getDefs(); defs.hasMoreElements();) {
946 Operand def = defs.nextElement();
947 if (VM.VerifyAssertions) VM._assert(def.isRegister());
948 Register r = def.asRegister().getRegister();
949 if (defined.contains(r)) return true;
950 defined.add(r);
951 }
952 }
953
954 return false;
955 }
956
957 /**
958 * Evaluate the cost of a basic block, in number of real instructions.
959 */
960 private int evaluateCost(BasicBlock bb) {
961 int result = 0;
962 for (InstructionEnumeration e = bb.forwardRealInstrEnumerator(); e.hasMoreElements();) {
963 Instruction s = e.nextElement();
964 if (!s.isBranch()) result++;
965 }
966 return result;
967 }
968
969 /**
970 * For each real non-branch instruction s in bb,
971 * <ul>
972 * <li> Copy s to s', and store s' in the returned array
973 * <li> Insert the function s->s' in the map
974 * </ul>
975 */
976 private Instruction[] copyAndMapInstructions(BasicBlock bb, HashMap<Instruction, Instruction> map) {
977 if (bb == null) return new Instruction[0];
978
979 int count = 0;
980 // first count the number of instructions
981 for (InstructionEnumeration e = bb.forwardRealInstrEnumerator(); e.hasMoreElements();) {
982 Instruction s = e.nextElement();
983 if (s.isBranch()) continue;
984 count++;
985 }
986 // now copy.
987 Instruction[] result = new Instruction[count];
988 int i = 0;
989 for (InstructionEnumeration e = bb.forwardRealInstrEnumerator(); e.hasMoreElements();) {
990 Instruction s = e.nextElement();
991 if (s.isBranch()) continue;
992 Instruction sprime = s.copyWithoutLinks();
993 result[i++] = sprime;
994 map.put(s, sprime);
995 }
996 return result;
997 }
998
999 /**
1000 * For each in a set of instructions, rewrite every def to use a new
1001 * temporary register. If a rewritten def is subsequently used, then
1002 * use the new temporary register instead.
1003 */
1004 private void rewriteWithTemporaries(Instruction[] set, IR ir) {
1005
1006 // Maintain a mapping holding the new name for each register
1007 HashMap<Register, Register> map = new HashMap<Register, Register>();
1008 for (Instruction s : set) {
1009 // rewrite the uses to use the new names
1010 for (OperandEnumeration e = s.getUses(); e.hasMoreElements();) {
1011 Operand use = e.nextElement();
1012 if (use != null && use.isRegister()) {
1013 Register r = use.asRegister().getRegister();
1014 Register temp = map.get(r);
1015 if (temp != null) {
1016 use.asRegister().setRegister(temp);
1017 }
1018 }
1019 }
1020
1021 if (VM.VerifyAssertions) VM._assert(s.getNumberOfDefs() == 1);
1022
1023 Operand def = s.getDefs().nextElement();
1024 RegisterOperand rDef = def.asRegister();
1025 RegisterOperand temp = ir.regpool.makeTemp(rDef);
1026 map.put(rDef.getRegister(), temp.getRegister());
1027 s.replaceOperand(def, temp);
1028 }
1029 }
1030
1031 /**
1032 * Insert each instruction in a list before instruction s
1033 */
1034 private void insertBefore(Instruction[] list, Instruction s) {
1035 for (Instruction x : list) {
1036 s.insertBefore(x);
1037 }
1038 }
1039
1040 /**
1041 * Perform the transformation to replace conditional branch with a
1042 * sequence using conditional moves.
1043 *
1044 * @param ir governing IR
1045 * @param diamond the IR diamond structure to replace
1046 * @param cb conditional branch instruction at the head of the diamond
1047 */
1048 private void doCondMove(IR ir, Diamond diamond, Instruction cb) {
1049 BasicBlock taken = diamond.getTaken();
1050 BasicBlock notTaken = diamond.getNotTaken();
1051
1052 // for each non-branch instruction s in the diamond,
1053 // copy s to a new instruction s'
1054 // and store a mapping from s to s'
1055 HashMap<Instruction, Instruction> takenInstructions = new HashMap<Instruction, Instruction>();
1056 Instruction[] takenInstructionList = copyAndMapInstructions(taken, takenInstructions);
1057
1058 HashMap<Instruction, Instruction> notTakenInstructions = new HashMap<Instruction, Instruction>();
1059 Instruction[] notTakenInstructionList = copyAndMapInstructions(notTaken, notTakenInstructions);
1060
1061 // Extract the values and condition from the conditional branch.
1062 Operand val1 = IfCmp.getVal1(cb);
1063 Operand val2 = IfCmp.getVal2(cb);
1064 ConditionOperand cond = IfCmp.getCond(cb);
1065
1066 // Copy val1 and val2 to temporaries, just in case they're defined in
1067 // the diamond. If they're not defined in the diamond, copy prop
1068 // should clean these moves up.
1069 RegisterOperand tempVal1 = ir.regpool.makeTemp(val1);
1070 Operator op = IRTools.getMoveOp(tempVal1.getType());
1071 cb.insertBefore(Move.create(op, tempVal1.copyRO(), val1.copy()));
1072 RegisterOperand tempVal2 = ir.regpool.makeTemp(val2);
1073 op = IRTools.getMoveOp(tempVal2.getType());
1074 cb.insertBefore(Move.create(op, tempVal2.copyRO(), val2.copy()));
1075
1076 // For each instruction in each temporary set, rewrite it to def a new
1077 // temporary, and insert it before the branch.
1078 rewriteWithTemporaries(takenInstructionList, ir);
1079 rewriteWithTemporaries(notTakenInstructionList, ir);
1080 insertBefore(takenInstructionList, cb);
1081 insertBefore(notTakenInstructionList, cb);
1082
1083 // For each register defined in the TAKEN branch, save a mapping to
1084 // the corresponding conditional move.
1085 HashMap<Register, Instruction> takenMap = new HashMap<Register, Instruction>();
1086
1087 // Now insert conditional moves to replace each instruction in the diamond.
1088 // First handle the taken branch.
1089 if (taken != null) {
1090 for (InstructionEnumeration e = taken.forwardRealInstrEnumerator(); e.hasMoreElements();) {
1091 Instruction s = e.nextElement();
1092 if (s.isBranch()) continue;
1093 Operand def = s.getDefs().nextElement();
1094 // if the register does not span a basic block, it is a temporary
1095 // that will now be dead
1096 if (def.asRegister().getRegister().spansBasicBlock()) {
1097 Instruction tempS = takenInstructions.get(s);
1098 RegisterOperand temp = (RegisterOperand) tempS.getDefs().nextElement();
1099 op = IRTools.getCondMoveOp(def.asRegister().getType());
1100 Instruction cmov =
1101 CondMove.create(op,
1102 def.asRegister(),
1103 tempVal1.copy(),
1104 tempVal2.copy(),
1105 cond.copy().asCondition(),
1106 temp.copy(),
1107 def.copy());
1108 takenMap.put(def.asRegister().getRegister(), cmov);
1109 cb.insertBefore(cmov);
1110 }
1111 s.remove();
1112 }
1113 }
1114 // For each register defined in the NOT-TAKEN branch, save a mapping to
1115 // the corresponding conditional move.
1116 HashMap<Register, Instruction> notTakenMap = new HashMap<Register, Instruction>();
1117 // Next handle the not taken branch.
1118 if (notTaken != null) {
1119 for (InstructionEnumeration e = notTaken.forwardRealInstrEnumerator(); e.hasMoreElements();) {
1120 Instruction s = e.nextElement();
1121 if (s.isBranch()) continue;
1122 Operand def = s.getDefs().nextElement();
1123 // if the register does not span a basic block, it is a temporary
1124 // that will now be dead
1125 if (def.asRegister().getRegister().spansBasicBlock()) {
1126 Instruction tempS = notTakenInstructions.get(s);
1127 RegisterOperand temp = (RegisterOperand) tempS.getDefs().nextElement();
1128
1129 Instruction prevCmov = takenMap.get(def.asRegister().getRegister());
1130 if (prevCmov != null) {
1131 // if this register was also defined in the taken branch, change
1132 // the previous cmov with a different 'False' Value
1133 CondMove.setFalseValue(prevCmov, temp.copy());
1134 notTakenMap.put(def.asRegister().getRegister(), prevCmov);
1135 } else {
1136 // create a new cmov instruction
1137 op = IRTools.getCondMoveOp(def.asRegister().getType());
1138 Instruction cmov =
1139 CondMove.create(op,
1140 def.asRegister(),
1141 tempVal1.copy(),
1142 tempVal2.copy(),
1143 cond.copy().asCondition(),
1144 def.copy(),
1145 temp.copy());
1146 cb.insertBefore(cmov);
1147 notTakenMap.put(def.asRegister().getRegister(), cmov);
1148 }
1149 }
1150 s.remove();
1151 }
1152 }
1153
1154 // Mutate the conditional branch into a GOTO.
1155 BranchOperand target = diamond.getBottom().makeJumpTarget();
1156 Goto.mutate(cb, GOTO, target);
1157
1158 // Delete a potential GOTO after cb.
1159 Instruction next = cb.nextInstructionInCodeOrder();
1160 if (next.operator != BBEND) {
1161 next.remove();
1162 }
1163
1164 // Recompute the CFG.
1165 diamond.getTop().recomputeNormalOut(ir); // fix the CFG
1166 }
1167
1168 /**
1169 * Attempt to generate a boolean compare opcode from a conditional branch.
1170 *
1171 * <pre>
1172 * 1) IF .. GOTO A replaced by BOOLEAN_CMP x=..
1173 * x = 0
1174 * GOTO B
1175 * A: x = 1
1176 * B: ...
1177 * </pre>
1178 *
1179 * <p> Precondition: <code>IfCmp.conforms(<i>cb</i>)</code>
1180 *
1181 *
1182 * @param ir governing IR
1183 * @param bb basic block of cb
1184 * @param cb conditional branch instruction
1185 * @return true if the transformation succeeds, false otherwise
1186 */
1187 private boolean generateBooleanCompare(IR ir, BasicBlock bb, Instruction cb, BasicBlock tb) {
1188
1189 if ((cb.operator() != INT_IFCMP) && (cb.operator() != REF_IFCMP)) {
1190 return false;
1191 }
1192 // make sure this is the last branch in the block
1193 if (cb.nextInstructionInCodeOrder().operator() != BBEND) {
1194 return false;
1195 }
1196 Operand val1 = IfCmp.getVal1(cb);
1197 Operand val2 = IfCmp.getVal2(cb);
1198 ConditionOperand condition = IfCmp.getCond(cb);
1199 // "not taken" path
1200 BasicBlock fb = cb.getBasicBlock().getNotTakenNextBlock();
1201 // make sure it's a diamond
1202 if (tb.getNumberOfNormalOut() != 1) {
1203 return false;
1204 }
1205 if (fb.getNumberOfNormalOut() != 1) {
1206 return false;
1207 }
1208 BasicBlock jb = fb.getNormalOut().next(); // join block
1209 // make sure it's a diamond
1210 if (!tb.pointsOut(jb)) {
1211 return false;
1212 }
1213 Instruction ti = tb.firstRealInstruction();
1214 Instruction fi = fb.firstRealInstruction();
1215 // make sure the instructions in target blocks are either both moves
1216 // or both returns
1217 if (ti == null || fi == null) {
1218 return false;
1219 }
1220 if (ti.operator() != fi.operator()) {
1221 return false;
1222 }
1223 if (ti.operator != RETURN && ti.operator() != INT_MOVE) {
1224 return false;
1225 }
1226 //
1227 // WARNING: This code is currently NOT exercised!
1228 //
1229 if (ti.operator() == RETURN) {
1230 // make sure each of the target blocks contains only one instruction
1231 if (ti != tb.lastRealInstruction()) {
1232 return false;
1233 }
1234 if (fi != fb.lastRealInstruction()) {
1235 return false;
1236 }
1237 Operand tr = Return.getVal(ti);
1238 Operand fr = Return.getVal(fi);
1239 // make sure we're returning constants
1240 if (!(tr instanceof IntConstantOperand) || !(fr instanceof IntConstantOperand)) {
1241 return false;
1242 }
1243 int tv = ((IntConstantOperand) tr).value;
1244 int fv = ((IntConstantOperand) fr).value;
1245 if (!((tv == 1 && fv == 0) || (tv == 1 && fv == 0))) {
1246 return false;
1247 }
1248 RegisterOperand t = ir.regpool.makeTemp(TypeReference.Boolean);
1249 // Cases 1) and 2)
1250 if (tv == 0) {
1251 condition = condition.flipCode();
1252 }
1253 booleanCompareHelper(cb, t, val1.copy(), val2.copy(), condition);
1254 cb.insertAfter(Return.create(RETURN, t.copyD2U()));
1255 } else { // (ti.operator() == INT_MOVE)
1256 // make sure each of the target blocks only does the move
1257 if (ti != tb.lastRealInstruction() && ti.nextInstructionInCodeOrder().operator() != GOTO) {
1258 return false;
1259 }
1260 if (fi != fb.lastRealInstruction() && fi.nextInstructionInCodeOrder().operator() != GOTO) {
1261 return false;
1262 }
1263 RegisterOperand t = Move.getResult(ti);
1264 // make sure both moves are to the same register
1265 if (t.getRegister() != Move.getResult(fi).getRegister()) {
1266 return false;
1267 }
1268 Operand tr = Move.getVal(ti);
1269 Operand fr = Move.getVal(fi);
1270 // make sure we're assigning constants
1271 if (!(tr instanceof IntConstantOperand) || !(fr instanceof IntConstantOperand)) {
1272 return false;
1273 }
1274 int tv = ((IntConstantOperand) tr).value;
1275 int fv = ((IntConstantOperand) fr).value;
1276 if (!((tv == 1 && fv == 0) || (tv == 0 && fv == 1))) {
1277 return false;
1278 }
1279 // Cases 3) and 4)
1280 if (tv == 0) {
1281 condition = condition.flipCode();
1282 }
1283 booleanCompareHelper(cb, t.copyRO(), val1.copy(), val2.copy(), condition);
1284 Instruction next = cb.nextInstructionInCodeOrder();
1285 if (next.operator() == GOTO) {
1286 Goto.setTarget(next, jb.makeJumpTarget());
1287 } else {
1288 cb.insertAfter(jb.makeGOTO());
1289 }
1290 }
1291 // fixup CFG
1292 bb.deleteOut(tb);
1293 bb.deleteOut(fb);
1294 bb.insertOut(jb); // Note: if we processed returns,
1295 // jb is the exit node.
1296 return true;
1297 }
1298 }