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.Operators.GOTO;
016    import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_MOVE;
017    import static org.jikesrvm.compilers.opt.ir.Operators.INT_IFCMP;
018    
019    import java.util.Enumeration;
020    
021    import org.jikesrvm.compilers.opt.DefUse;
022    import org.jikesrvm.compilers.opt.ir.BasicBlock;
023    import org.jikesrvm.compilers.opt.ir.Goto;
024    import org.jikesrvm.compilers.opt.ir.GuardResultCarrier;
025    import org.jikesrvm.compilers.opt.ir.IR;
026    import org.jikesrvm.compilers.opt.ir.IREnumeration;
027    import org.jikesrvm.compilers.opt.ir.IfCmp;
028    import org.jikesrvm.compilers.opt.ir.IfCmp2;
029    import org.jikesrvm.compilers.opt.ir.InlineGuard;
030    import org.jikesrvm.compilers.opt.ir.Instruction;
031    import org.jikesrvm.compilers.opt.ir.LookupSwitch;
032    import org.jikesrvm.compilers.opt.ir.Move;
033    import org.jikesrvm.compilers.opt.ir.TableSwitch;
034    import org.jikesrvm.compilers.opt.ir.operand.BranchOperand;
035    import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand;
036    import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
037    import org.jikesrvm.compilers.opt.ir.operand.Operand;
038    import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
039    import org.jikesrvm.compilers.opt.ir.operand.TrueGuardOperand;
040    
041    /**
042     * Simplify and canonicalize conditional branches with constant operands.
043     *
044     * <p> This module performs no analysis, it simply attempts to
045     * simplify any branching instructions of a basic block that have constant
046     * operands. The intent is that analysis modules can call this
047     * transformation engine, allowing us to share the
048     * simplification code among multiple analysis modules.
049     */
050    public abstract class BranchSimplifier {
051    
052      /**
053       * Given a basic block, attempt to simplify any conditional branch
054       * instructions with constant operands.
055       * The instruction will be mutated in place.
056       * The control flow graph will be updated, but the caller is responsible
057       * for calling BranchOptmizations after simplify has been called on
058       * all basic blocks in the IR to remove unreachable code.
059       *
060       * @param bb the basic block to simplify
061       * @return {@code true} if we do something, {@code false} otherwise.
062       */
063      public static boolean simplify(BasicBlock bb, IR ir) {
064        boolean didSomething = false;
065    
066        for (Enumeration<Instruction> branches = bb.enumerateBranchInstructions(); branches.hasMoreElements();) {
067          Instruction s = branches.nextElement();
068          if (Goto.conforms(s)) {
069            // nothing to do, but a common case so test first
070          } else if (IfCmp.conforms(s)) {
071            if (processIfCmp(ir, bb, s)) {
072              // hack. Just start over since Enumeration has changed.
073              branches = bb.enumerateBranchInstructions();
074              bb.recomputeNormalOut(ir);
075              didSomething = true;
076            }
077          } else if (IfCmp2.conforms(s)) {
078            if (processIfCmp2(ir, bb, s)) {
079              // hack. Just start over since Enumeration has changed.
080              branches = bb.enumerateBranchInstructions();
081              bb.recomputeNormalOut(ir);
082              didSomething = true;
083            }
084          } else if (LookupSwitch.conforms(s)) {
085            if (processLookupSwitch(ir, bb, s)) {
086              // hack. Just start over since Enumeration has changed.
087              branches = bb.enumerateBranchInstructions();
088              bb.recomputeNormalOut(ir);
089              didSomething = true;
090            }
091          } else if (TableSwitch.conforms(s)) {
092            if (processTableSwitch(ir, bb, s)) {
093              // hack. Just start over since Enumeration has changed.
094              branches = bb.enumerateBranchInstructions();
095              bb.recomputeNormalOut(ir);
096              didSomething = true;
097            }
098          } else if (InlineGuard.conforms(s)) {
099            if (processInlineGuard(ir, bb, s)) {
100              // hack. Just start over since Enumeration has changed.
101              branches = bb.enumerateBranchInstructions();
102              bb.recomputeNormalOut(ir);
103              didSomething = true;
104            }
105          }
106        }
107        return didSomething;
108      }
109    
110      /** Process IfCmp branch instruction */
111      static boolean processIfCmp(IR ir, BasicBlock bb, Instruction s) {
112        RegisterOperand guard = IfCmp.getGuardResult(s);
113        Operand val1 = IfCmp.getVal1(s);
114        Operand val2 = IfCmp.getVal2(s);
115        {
116          int cond = IfCmp.getCond(s).evaluate(val1, val2);
117          if (cond != ConditionOperand.UNKNOWN) {
118            // constant fold
119            if (cond == ConditionOperand.TRUE) {  // branch taken
120              insertTrueGuard(s, guard);
121              Goto.mutate(s, GOTO, IfCmp.getTarget(s));
122              removeBranchesAfterGotos(bb);
123            } else {
124              // branch not taken
125              insertTrueGuard(s, guard);
126              s.remove();
127            }
128            return true;
129          }
130        }
131        if (val1.isConstant() && !val2.isConstant()) {
132          // Canonicalize by making second argument the constant
133          IfCmp.setVal1(s, val2);
134          IfCmp.setVal2(s, val1);
135          IfCmp.setCond(s, IfCmp.getCond(s).flipOperands());
136        }
137    
138        if (val2.isIntConstant()) {
139          // Tricks to get compare against zero.
140          int value = ((IntConstantOperand) val2).value;
141          ConditionOperand cond = IfCmp.getCond(s);
142          if (value == 1) {
143            if (cond.isLESS()) {
144              IfCmp.setCond(s, ConditionOperand.LESS_EQUAL());
145              IfCmp.setVal2(s, new IntConstantOperand(0));
146            } else if (cond.isGREATER_EQUAL()) {
147              IfCmp.setCond(s, ConditionOperand.GREATER());
148              IfCmp.setVal2(s, new IntConstantOperand(0));
149            }
150          } else if (value == -1) {
151            if (cond.isGREATER()) {
152              IfCmp.setCond(s, ConditionOperand.GREATER_EQUAL());
153              IfCmp.setVal2(s, new IntConstantOperand(0));
154            } else if (cond.isLESS_EQUAL()) {
155              IfCmp.setCond(s, ConditionOperand.LESS());
156              IfCmp.setVal2(s, new IntConstantOperand(0));
157            }
158          }
159        }
160        return false;
161      }
162    
163      /** Process IfCmp2 branch instruction */
164      static boolean processIfCmp2(IR ir, BasicBlock bb, Instruction s) {
165        RegisterOperand guard = IfCmp2.getGuardResult(s);
166        Operand val1 = IfCmp2.getVal1(s);
167        Operand val2 = IfCmp2.getVal2(s);
168        int cond1 = IfCmp2.getCond1(s).evaluate(val1, val2);
169        int cond2 = IfCmp2.getCond2(s).evaluate(val1, val2);
170        if (cond1 == ConditionOperand.TRUE) {
171          // target 1 taken
172          insertTrueGuard(s, guard);
173          Goto.mutate(s, GOTO, IfCmp2.getTarget1(s));
174          removeBranchesAfterGotos(bb);
175        } else if ((cond1 == ConditionOperand.FALSE) && (cond2 == ConditionOperand.TRUE)) {
176          // target 2 taken
177          insertTrueGuard(s, guard);
178          Goto.mutate(s, GOTO, IfCmp2.getTarget2(s));
179          removeBranchesAfterGotos(bb);
180        } else if ((cond1 == ConditionOperand.FALSE) && (cond2 == ConditionOperand.FALSE)) {
181          // not taken
182          insertTrueGuard(s, guard);
183          s.remove();
184        } else if ((cond1 == ConditionOperand.FALSE) && (cond2 == ConditionOperand.UNKNOWN)) {
185          // target 1 not taken, simplify test to ifcmp
186          IfCmp.mutate(s,
187                       INT_IFCMP,
188                       guard,
189                       val1,
190                       val2,
191                       IfCmp2.getCond2(s),
192                       IfCmp2.getTarget2(s),
193                       IfCmp2.getBranchProfile2(s));
194        } else if ((cond1 == ConditionOperand.UNKNOWN) && (cond2 == ConditionOperand.FALSE)) {
195          // target 1 taken possibly, simplify test to ifcmp
196          IfCmp.mutate(s,
197                       INT_IFCMP,
198                       guard,
199                       val1,
200                       val2,
201                       IfCmp2.getCond1(s),
202                       IfCmp2.getTarget1(s),
203                       IfCmp2.getBranchProfile1(s));
204        } else if ((cond1 == ConditionOperand.UNKNOWN) && (cond2 == ConditionOperand.TRUE)) {
205          // target 1 taken possibly, simplify first test to ifcmp and
206          // insert goto after
207          s.insertAfter(Goto.create(GOTO, IfCmp2.getTarget2(s)));
208          IfCmp.mutate(s,
209                       INT_IFCMP,
210                       guard,
211                       val1,
212                       val2,
213                       IfCmp2.getCond1(s),
214                       IfCmp2.getTarget1(s),
215                       IfCmp2.getBranchProfile1(s));
216          removeBranchesAfterGotos(bb);
217        } else {
218          if (val1.isConstant() && !val2.isConstant()) {
219            // Canonicalize by making second argument the constant
220            IfCmp2.setVal1(s, val2);
221            IfCmp2.setVal2(s, val1);
222            IfCmp2.setCond1(s, IfCmp2.getCond1(s).flipOperands());
223            IfCmp2.setCond2(s, IfCmp2.getCond2(s).flipOperands());
224          }
225          // we did no optimisation, return false
226          return false;
227        }
228        return true;
229      }
230    
231      /** Process LookupSwitch branch instruction */
232      static boolean processLookupSwitch(IR ir, BasicBlock bb, Instruction s) {
233        Operand val = LookupSwitch.getValue(s);
234        int numMatches = LookupSwitch.getNumberOfMatches(s);
235        if (numMatches == 0) {
236          // Can only goto default
237          Goto.mutate(s, GOTO, LookupSwitch.getDefault(s));
238        } else if (val.isConstant()) {
239          // lookup value is constant
240          int value = ((IntConstantOperand) val).value;
241          BranchOperand target = LookupSwitch.getDefault(s);
242          for (int i = 0; i < numMatches; i++) {
243            if (value == LookupSwitch.getMatch(s, i).value) {
244              target = LookupSwitch.getTarget(s, i);
245              break;
246            }
247          }
248          Goto.mutate(s, GOTO, target);
249        } else if (numMatches == 1) {
250          // only 1 match, simplify to ifcmp
251          BranchOperand defaultTarget = LookupSwitch.getDefault(s);
252          IfCmp.mutate(s,
253                       INT_IFCMP,
254                       ir.regpool.makeTempValidation(),
255                       val,
256                       LookupSwitch.getMatch(s, 0),
257                       ConditionOperand.EQUAL(),
258                       LookupSwitch.getTarget(s, 0),
259                       LookupSwitch.getBranchProfile(s, 0));
260          s.insertAfter(Goto.create(GOTO, defaultTarget));
261        } else {
262          // no optimisation, just continue
263          return false;
264        }
265        return true;
266      }
267    
268      /** Process TableSwitch branch instruction */
269      static boolean processTableSwitch(IR ir, BasicBlock bb, Instruction s) {
270        Operand val = TableSwitch.getValue(s);
271        int low = TableSwitch.getLow(s).value;
272        int high = TableSwitch.getHigh(s).value;
273        if (val.isConstant()) {
274          int value = ((IntConstantOperand) val).value;
275          BranchOperand target = TableSwitch.getDefault(s);
276          if (value >= low && value <= high) {
277            target = TableSwitch.getTarget(s, value - low);
278          }
279          Goto.mutate(s, GOTO, target);
280        } else if (low == high) {
281          // only 1 match, simplify to ifcmp
282          BranchOperand defaultTarget = TableSwitch.getDefault(s);
283          IfCmp.mutate(s,
284                       INT_IFCMP,
285                       ir.regpool.makeTempValidation(),
286                       val,
287                       new IntConstantOperand(low),
288                       ConditionOperand.EQUAL(),
289                       TableSwitch.getTarget(s, 0),
290                       TableSwitch.getBranchProfile(s, 0));
291          s.insertAfter(Goto.create(GOTO, defaultTarget));
292        } else {
293          // no optimisation, just continue
294          return false;
295        }
296        return true;
297      }
298    
299      /** Process InlineGuard branch instruction */
300      static boolean processInlineGuard(IR ir, BasicBlock bb, Instruction s) {
301        Operand val = InlineGuard.getValue(s);
302        if (val.isNullConstant()) {
303          // branch not taken
304          s.remove();
305          return true;
306        } else if (val.isObjectConstant()) {
307          // TODO:
308          // VM.sysWrite("TODO: should constant fold MethodIfCmp on ObjectConstant");
309        }
310        return false;
311      }
312    
313      /**
314       * To maintain IR integrity, remove any branches that are after the
315       * first GOTO in the basic block.
316       */
317      private static void removeBranchesAfterGotos(BasicBlock bb) {
318        // identify the first GOTO instruction in the basic block
319        Instruction firstGoto = null;
320        Instruction end = bb.lastRealInstruction();
321        for (Enumeration<Instruction> branches = bb.enumerateBranchInstructions(); branches.hasMoreElements();) {
322          Instruction s = branches.nextElement();
323          if (Goto.conforms(s)) {
324            firstGoto = s;
325            break;
326          }
327        }
328        // remove all instructions after the first GOTO instruction
329        if (firstGoto != null) {
330          Enumeration<Instruction> ie = IREnumeration.forwardIntraBlockIE(firstGoto, end);
331          ie.nextElement();
332          while (ie.hasMoreElements()) {
333            Instruction s = ie.nextElement();
334            if (GuardResultCarrier.conforms(s)) {
335              insertTrueGuard(s, GuardResultCarrier.getGuardResult(s));
336            }
337            s.remove();
338          }
339        }
340      }
341    
342      private static void insertTrueGuard(Instruction inst, RegisterOperand guard) {
343        if (guard == null) return;  // Probably bad style but correct IR
344        Instruction trueGuard = Move.create(GUARD_MOVE, guard.copyD2D(), new TrueGuardOperand());
345        trueGuard.position = inst.position;
346        trueGuard.bcIndex = inst.bcIndex;
347        inst.insertBefore(trueGuard);
348        DefUse.updateDUForNewInstruction(trueGuard);
349      }
350    }