/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.compilers.opt.lir2mir.ppc;

import org.jikesrvm.VM;
import org.jikesrvm.compilers.opt.DefUse;
import org.jikesrvm.compilers.opt.ir.Attempt;
import org.jikesrvm.compilers.opt.ir.Binary;
import org.jikesrvm.compilers.opt.ir.BooleanCmp;
import org.jikesrvm.compilers.opt.ir.IfCmp;
import org.jikesrvm.compilers.opt.ir.MIR_Binary;
import org.jikesrvm.compilers.opt.ir.MIR_Branch;
import org.jikesrvm.compilers.opt.ir.MIR_CondBranch;
import org.jikesrvm.compilers.opt.ir.MIR_CondBranch2;
import org.jikesrvm.compilers.opt.ir.MIR_Load;
import org.jikesrvm.compilers.opt.ir.MIR_Move;
import org.jikesrvm.compilers.opt.ir.MIR_Store;
import org.jikesrvm.compilers.opt.ir.MIR_Unary;
import org.jikesrvm.compilers.opt.ir.Nullary;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.IRTools;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.Operator;
import static org.jikesrvm.compilers.opt.ir.Operators.ATTEMPT_ADDR_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.ATTEMPT_INT_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.ATTEMPT_LONG_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_ADDR_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_INT_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_2INT_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_2LONG_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_CMPG_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_CMPL_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_2INT_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_2LONG_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_CMPG_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_CMPL_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.GET_TIME_BASE_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_CMP_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_IFCMP_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_SHR_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC64_CMP;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC64_CMPI;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC64_CMPL;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC64_CMPLI;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC64_FCTIDZ;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC64_LD;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_ADDI;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_B;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_BCOND;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_BCOND2;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_CMP;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_CMPI;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_CMPL;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_CMPLI;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_FCMPU;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_FCTIWZ;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_LDI;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_LInt;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_MFTB;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_MFTBU;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_OR;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_SLW;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_SRAW;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_SRW;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_STAddrCXr;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_STFD;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_STWCXr;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_SUBFIC;
import org.jikesrvm.compilers.opt.ir.Register;
import org.jikesrvm.compilers.opt.ir.Unary;
import org.jikesrvm.compilers.opt.ir.operand.BranchProfileOperand;
import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand;
import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.LocationOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
import org.jikesrvm.compilers.opt.ir.operand.ppc.PowerPCConditionOperand;
import org.jikesrvm.compilers.opt.ir.ppc.PhysicalRegisterSet;


/**
 * Handles the conversion from LIR to MIR of operators whose
 * expansion requires the introduction of new control flow (new basic blocks).
 *
 * TODO: Make these methods virtual; spilt into Common superclass with
 *       32/64 subclasses.
 */
public abstract class ComplexLIR2MIRExpansion extends IRTools {

  /**
   * Converts the given IR to low level PowerPC IR.
   *
   * @param ir IR to convert
   */
  public static void convert(IR ir) {
    for (Instruction next = null, s = ir.firstInstructionInCodeOrder(); s != null; s = next) {
      next = s.nextInstructionInCodeOrder();
      switch (s.getOpcode()) {
        case DOUBLE_2INT_opcode:
        case FLOAT_2INT_opcode:
          double_2int(s, ir);
          break;
        case DOUBLE_2LONG_opcode:
        case FLOAT_2LONG_opcode:
          if (VM.BuildFor64Addr) {
            double_2long(s, ir);
          } else {
            if (VM.VerifyAssertions) VM._assert(false);
          }
          break;
        case LONG_SHR_opcode:
          if (VM.BuildFor32Addr) {
            long_shr(s, ir);
          } else {
            if (VM.VerifyAssertions) VM._assert(false);
          }
          break;
        case LONG_IFCMP_opcode:
          if (VM.BuildFor32Addr) {
            long_ifcmp(s, ir);
          } else {
            if (VM.VerifyAssertions) VM._assert(false);
          }
          break;
        case BOOLEAN_CMP_INT_opcode:
          boolean_cmp(s, ir, true);
          break;
        case BOOLEAN_CMP_ADDR_opcode:
          boolean_cmp(s, ir, VM.BuildFor32Addr);
          break;
        case DOUBLE_CMPL_opcode:
        case FLOAT_CMPL_opcode:
        case DOUBLE_CMPG_opcode:
        case FLOAT_CMPG_opcode:
          threeValueCmp(s, ir);
          break;
        case LONG_CMP_opcode:
          threeValueLongCmp(s, ir);
          break;
        case GET_TIME_BASE_opcode:
          get_time_base(s, ir);
          break;
        case ATTEMPT_INT_opcode:
          attempt(s, ir, false, false);
          break;
        case ATTEMPT_LONG_opcode:
          attempt(s, ir, false, true);
          break;
        case ATTEMPT_ADDR_opcode:
          attempt(s, ir, true, false);
          break;
      }
    }
    DefUse.recomputeSpansBasicBlock(ir);
  }

  private static void double_2int(Instruction s, IR ir) {
    Register res = Unary.getResult(s).getRegister();
    Register src = ((RegisterOperand) Unary.getVal(s)).getRegister();
    Register FP = ir.regpool.getPhysicalRegisterSet().getFP();
    int p = ir.stackManager.allocateSpaceForConversion();
    Register temp = ir.regpool.getDouble();
    BasicBlock BB1 = s.getBasicBlock();
    BasicBlock BB3 = BB1.splitNodeAt(s, ir);
    BasicBlock BB2 = BB1.createSubBlock(0, ir);
    RegisterOperand cond = ir.regpool.makeTempCondition();
    BB1.appendInstruction(MIR_Binary.create(PPC_FCMPU, cond, D(src), D(src)));
    BB1.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(0)));
    BB1.appendInstruction(MIR_CondBranch.create(PPC_BCOND,
                                                cond.copyD2U(),
                                                PowerPCConditionOperand.UNORDERED(),
                                                BB3.makeJumpTarget(),
                                                new BranchProfileOperand()));
    BB2.appendInstruction(MIR_Unary.create(PPC_FCTIWZ, D(temp), D(src)));
    BB2.appendInstruction(MIR_Store.create(PPC_STFD, D(temp), A(FP), IC(p)));
    BB2.appendInstruction(MIR_Load.create(PPC_LInt, I(res), A(FP), IC(p + 4)));
    // fix up CFG
    BB1.insertOut(BB2);
    BB1.insertOut(BB3);
    BB2.insertOut(BB3);
    ir.cfg.linkInCodeOrder(BB1, BB2);
    ir.cfg.linkInCodeOrder(BB2, BB3);
    s.remove();
  }

  private static void double_2long(Instruction s, IR ir) {
    if (VM.VerifyAssertions) VM._assert(VM.BuildFor64Addr);
    Register res = Unary.getResult(s).getRegister();
    Register src = ((RegisterOperand) Unary.getVal(s)).getRegister();
    Register FP = ir.regpool.getPhysicalRegisterSet().getFP();
    int p = ir.stackManager.allocateSpaceForConversion();
    Register temp = ir.regpool.getDouble();
    BasicBlock BB1 = s.getBasicBlock();
    BasicBlock BB3 = BB1.splitNodeAt(s, ir);
    BasicBlock BB2 = BB1.createSubBlock(0, ir);
    RegisterOperand cond = ir.regpool.makeTempCondition();
    BB1.appendInstruction(MIR_Binary.create(PPC_FCMPU, cond, D(src), D(src)));
    BB1.appendInstruction(MIR_Unary.create(PPC_LDI, L(res), IC(0)));
    BB1.appendInstruction(MIR_CondBranch.create(PPC_BCOND,
                                                cond.copyD2U(),
                                                PowerPCConditionOperand.UNORDERED(),
                                                BB3.makeJumpTarget(),
                                                new BranchProfileOperand()));
    BB2.appendInstruction(MIR_Unary.create(PPC64_FCTIDZ, D(temp), D(src)));
    BB2.appendInstruction(MIR_Store.create(PPC_STFD, D(temp), A(FP), IC(p)));
    BB2.appendInstruction(MIR_Load.create(PPC64_LD, L(res), A(FP), IC(p)));
    // fix up CFG
    BB1.insertOut(BB2);
    BB1.insertOut(BB3);
    BB2.insertOut(BB3);
    ir.cfg.linkInCodeOrder(BB1, BB2);
    ir.cfg.linkInCodeOrder(BB2, BB3);
    s.remove();
  }

  private static void boolean_cmp(Instruction s, IR ir, boolean cmp32Bit) {
    // undo the optimization because it cannot efficiently be generated
    Register res = BooleanCmp.getClearResult(s).getRegister();
    RegisterOperand one = (RegisterOperand) BooleanCmp.getClearVal1(s);
    Operand two = BooleanCmp.getClearVal2(s);
    ConditionOperand cond = BooleanCmp.getClearCond(s);
    res.setSpansBasicBlock();
    BasicBlock BB1 = s.getBasicBlock();
    BasicBlock BB4 = BB1.splitNodeAt(s, ir);
    s = s.remove();
    BasicBlock BB2 = BB1.createSubBlock(0, ir);
    BasicBlock BB3 = BB1.createSubBlock(0, ir);
    RegisterOperand t = ir.regpool.makeTempInt();
    t.getRegister().setCondition();
    Operator op;
    if (VM.BuildFor64Addr && !cmp32Bit) {
      if (two instanceof IntConstantOperand) {
        op = cond.isUNSIGNED() ? PPC64_CMPLI : PPC64_CMPI;
      } else {
        op = cond.isUNSIGNED() ? PPC64_CMPL : PPC64_CMP;
      }
    } else if (two instanceof IntConstantOperand) {
      op = cond.isUNSIGNED() ? PPC_CMPLI : PPC_CMPI;
    } else {
      op = cond.isUNSIGNED() ? PPC_CMPL : PPC_CMP;
    }
    BB1.appendInstruction(MIR_Binary.create(op, t, one, two));
    BB1.appendInstruction(MIR_CondBranch.create(PPC_BCOND,
                                                t.copyD2U(),
                                                PowerPCConditionOperand.get(cond),
                                                BB3.makeJumpTarget(),
                                                new BranchProfileOperand()));
    BB2.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(0)));
    BB2.appendInstruction(MIR_Branch.create(PPC_B, BB4.makeJumpTarget()));
    BB3.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(1)));
    // fix CFG
    BB1.insertOut(BB2);
    BB1.insertOut(BB3);
    BB2.insertOut(BB4);
    BB3.insertOut(BB4);
    ir.cfg.linkInCodeOrder(BB1, BB2);
    ir.cfg.linkInCodeOrder(BB2, BB3);
    ir.cfg.linkInCodeOrder(BB3, BB4);
  }

  /**
   * compare to values and set result to -1, 0, 1 for <, =, >, respectively
   * @param s the compare instruction
   * @param ir the governing IR
   */
  private static void threeValueCmp(Instruction s, IR ir) {
    PowerPCConditionOperand firstCond = PowerPCConditionOperand.LESS_EQUAL();
    int firstConst = 1;

    switch (s.operator.opcode) {
      case DOUBLE_CMPG_opcode:
      case FLOAT_CMPG_opcode:
        firstCond = PowerPCConditionOperand.GREATER_EQUAL();
        firstConst = -1;
        break;
      case DOUBLE_CMPL_opcode:
      case FLOAT_CMPL_opcode:
        break;
      default:
        if (VM.VerifyAssertions) VM._assert(false);
        break;
    }
    Register res = Binary.getClearResult(s).getRegister();
    RegisterOperand one = (RegisterOperand) Binary.getClearVal1(s);
    RegisterOperand two = (RegisterOperand) Binary.getClearVal2(s);
    res.setSpansBasicBlock();
    BasicBlock BB1 = s.getBasicBlock();
    BasicBlock BB6 = BB1.splitNodeAt(s, ir);
    s = s.remove();
    BasicBlock BB2 = BB1.createSubBlock(0, ir);
    BasicBlock BB3 = BB1.createSubBlock(0, ir);
    BasicBlock BB4 = BB1.createSubBlock(0, ir);
    BasicBlock BB5 = BB1.createSubBlock(0, ir);
    RegisterOperand t = ir.regpool.makeTempInt();
    t.getRegister().setCondition();
    BB1.appendInstruction(MIR_Binary.create(PPC_FCMPU, t, one, two));
    BB1.appendInstruction(MIR_CondBranch.create(PPC_BCOND,
                                                t.copyD2U(),
                                                firstCond,
                                                BB3.makeJumpTarget(),
                                                new BranchProfileOperand(0.5f)));
    BB2.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(firstConst)));
    BB2.appendInstruction(MIR_Branch.create(PPC_B, BB6.makeJumpTarget()));
    BB3.appendInstruction(MIR_CondBranch.create(PPC_BCOND,
                                                t.copyD2U(),
                                                PowerPCConditionOperand.EQUAL(),
                                                BB5.makeJumpTarget(),
                                                BranchProfileOperand.unlikely()));

    BB4.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(-firstConst)));
    BB4.appendInstruction(MIR_Branch.create(PPC_B, BB6.makeJumpTarget()));
    BB5.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(0)));
    // fix CFG
    BB1.insertOut(BB2);
    BB1.insertOut(BB3);
    BB2.insertOut(BB6);
    BB3.insertOut(BB4);
    BB3.insertOut(BB5);
    BB4.insertOut(BB6);
    BB5.insertOut(BB6);
    ir.cfg.linkInCodeOrder(BB1, BB2);
    ir.cfg.linkInCodeOrder(BB2, BB3);
    ir.cfg.linkInCodeOrder(BB3, BB4);
    ir.cfg.linkInCodeOrder(BB4, BB5);
    ir.cfg.linkInCodeOrder(BB5, BB6);
  }

  /**
   * compare to values and set result to -1, 0, 1 for <, =, >, respectively
   * @param s the compare instruction
   * @param ir the governing IR
   */
  private static void threeValueLongCmp(Instruction s, IR ir) {
    if (VM.BuildFor32Addr) {
      threeValueLongCmp_32(s, ir);
    } else {
      threeValueLongCmp_64(s, ir);
    }
  }

  private static void threeValueLongCmp_32(Instruction s, IR ir) {
    Register res = Binary.getClearResult(s).getRegister();
    RegisterOperand one = (RegisterOperand) Binary.getClearVal1(s);
    RegisterOperand two = (RegisterOperand) Binary.getClearVal2(s);
    RegisterOperand lone = L(ir.regpool.getSecondReg(one.getRegister()));
    RegisterOperand ltwo = L(ir.regpool.getSecondReg(two.getRegister()));
    res.setSpansBasicBlock();
    BasicBlock BB1 = s.getBasicBlock();
    BasicBlock BB6 = BB1.splitNodeAt(s, ir);
    s = s.remove();
    BasicBlock BB2 = BB1.createSubBlock(0, ir);
    BasicBlock BB3 = BB1.createSubBlock(0, ir);
    BasicBlock BB4 = BB1.createSubBlock(0, ir);
    BasicBlock BB5 = BB1.createSubBlock(0, ir);
    RegisterOperand t = ir.regpool.makeTempInt();
    t.getRegister().setCondition();
    BB1.appendInstruction(MIR_Binary.create(PPC_CMP, t, one, two));
    BB1.appendInstruction(MIR_CondBranch2.create(PPC_BCOND2,
                                                 t.copyD2U(),
                                                 PowerPCConditionOperand.LESS(),
                                                 BB4.makeJumpTarget(),
                                                 new BranchProfileOperand(0.49f),
                                                 PowerPCConditionOperand.GREATER(),
                                                 BB5.makeJumpTarget(),
                                                 new BranchProfileOperand(0.49f)));
    BB2.appendInstruction(MIR_Binary.create(PPC_CMPL, t.copyD2D(), lone, ltwo));
    BB2.appendInstruction(MIR_CondBranch2.create(PPC_BCOND2,
                                                 t.copyD2U(),
                                                 PowerPCConditionOperand.LESS(),
                                                 BB4.makeJumpTarget(),
                                                 new BranchProfileOperand(0.49f),
                                                 PowerPCConditionOperand.GREATER(),
                                                 BB5.makeJumpTarget(),
                                                 new BranchProfileOperand(0.49f)));
    BB3.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(0)));
    BB3.appendInstruction(MIR_Branch.create(PPC_B, BB6.makeJumpTarget()));
    BB4.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(-1)));
    BB4.appendInstruction(MIR_Branch.create(PPC_B, BB6.makeJumpTarget()));
    BB5.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(1)));
    // fix CFG
    BB1.insertOut(BB2);
    BB1.insertOut(BB4);
    BB1.insertOut(BB5);
    BB2.insertOut(BB3);
    BB2.insertOut(BB4);
    BB2.insertOut(BB5);
    BB3.insertOut(BB6);
    BB4.insertOut(BB6);
    BB5.insertOut(BB6);
    ir.cfg.linkInCodeOrder(BB1, BB2);
    ir.cfg.linkInCodeOrder(BB2, BB3);
    ir.cfg.linkInCodeOrder(BB3, BB4);
    ir.cfg.linkInCodeOrder(BB4, BB5);
    ir.cfg.linkInCodeOrder(BB5, BB6);
  }

  private static void threeValueLongCmp_64(Instruction s, IR ir) {
    Register res = Binary.getClearResult(s).getRegister();
    RegisterOperand one = (RegisterOperand) Binary.getClearVal1(s);
    RegisterOperand two = (RegisterOperand) Binary.getClearVal2(s);
    res.setSpansBasicBlock();
    BasicBlock BB1 = s.getBasicBlock();
    BasicBlock BB5 = BB1.splitNodeAt(s, ir);
    s = s.remove();
    BasicBlock BB2 = BB1.createSubBlock(0, ir);
    BasicBlock BB3 = BB1.createSubBlock(0, ir);
    BasicBlock BB4 = BB1.createSubBlock(0, ir);
    RegisterOperand t = ir.regpool.makeTempInt();
    t.getRegister().setCondition();
    BB1.appendInstruction(MIR_Binary.create(PPC64_CMP, t, one, two));
    BB1.appendInstruction(MIR_CondBranch2.create(PPC_BCOND2,
                                                 t.copyD2U(),
                                                 PowerPCConditionOperand.LESS(),
                                                 BB3.makeJumpTarget(),
                                                 new BranchProfileOperand(0.49f),
                                                 PowerPCConditionOperand.GREATER(),
                                                 BB4.makeJumpTarget(),
                                                 new BranchProfileOperand(0.49f)));
    BB2.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(0)));
    BB2.appendInstruction(MIR_Branch.create(PPC_B, BB5.makeJumpTarget()));
    BB3.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(-1)));
    BB3.appendInstruction(MIR_Branch.create(PPC_B, BB5.makeJumpTarget()));
    BB4.appendInstruction(MIR_Unary.create(PPC_LDI, I(res), IC(1)));
    // fix CFG
    BB1.insertOut(BB2);
    BB1.insertOut(BB3);
    BB1.insertOut(BB4);
    BB2.insertOut(BB5);
    BB3.insertOut(BB5);
    BB4.insertOut(BB5);
    ir.cfg.linkInCodeOrder(BB1, BB2);
    ir.cfg.linkInCodeOrder(BB2, BB3);
    ir.cfg.linkInCodeOrder(BB3, BB4);
    ir.cfg.linkInCodeOrder(BB4, BB5);
  }

  private static void long_shr(Instruction s, IR ir) {
    BasicBlock BB1 = s.getBasicBlock();
    BasicBlock BB2 = BB1.createSubBlock(0, ir);
    BasicBlock BB3 = BB1.splitNodeAt(s, ir);
    Register defHigh = Binary.getResult(s).getRegister();
    Register defLow = ir.regpool.getSecondReg(defHigh);
    RegisterOperand left = (RegisterOperand) Binary.getVal1(s);
    Register leftHigh = left.getRegister();
    Register leftLow = ir.regpool.getSecondReg(leftHigh);
    RegisterOperand shiftOp = (RegisterOperand) Binary.getVal2(s);
    Register shift = shiftOp.getRegister();
    Register t31 = ir.regpool.getInteger();
    Register t0 = ir.regpool.getInteger();
    Register cr = ir.regpool.getCondition();
    defLow.setSpansBasicBlock();
    defHigh.setSpansBasicBlock();
    s.insertBefore(MIR_Binary.create(PPC_SUBFIC, I(t31), I(shift), IC(32)));
    s.insertBefore(MIR_Binary.create(PPC_SRW, I(defLow), I(leftLow), I(shift)));
    s.insertBefore(MIR_Binary.create(PPC_SLW, I(t0), I(leftHigh), I(t31)));
    s.insertBefore(MIR_Binary.create(PPC_OR, I(defLow), I(defLow), I(t0)));
    s.insertBefore(MIR_Binary.create(PPC_ADDI, I(t31), I(shift), IC(-32)));
    s.insertBefore(MIR_Binary.create(PPC_SRAW, I(t0), I(leftHigh), I(t31)));
    s.insertBefore(MIR_Binary.create(PPC_SRAW, I(defHigh), I(leftHigh), I(shift)));
    s.insertBefore(MIR_Binary.create(PPC_CMPI, I(cr), I(t31), IC(0)));
    MIR_CondBranch.mutate(s,
                          PPC_BCOND,
                          I(cr),
                          PowerPCConditionOperand.LESS_EQUAL(),
                          BB3.makeJumpTarget(),
                          new BranchProfileOperand());
    // insert the branch and second compare
    BB2.appendInstruction(MIR_Move.create(PPC_MOVE, I(defLow), I(t0)));
    // fix up CFG
    BB1.insertOut(BB2);
    BB1.insertOut(BB3);
    BB2.insertOut(BB3);
    ir.cfg.linkInCodeOrder(BB1, BB2);
    ir.cfg.linkInCodeOrder(BB2, BB3);
  }

  private static void long_ifcmp(Instruction s, IR ir) {
    if (VM.VerifyAssertions) VM._assert(!IfCmp.getCond(s).isUNSIGNED());
    BasicBlock BB1 = s.getBasicBlock();
    BasicBlock BB3 = BB1.splitNodeAt(s, ir);
    BasicBlock BB2 = BB1.createSubBlock(0, ir);
    BB1.insertOut(BB2);
    BB1.insertOut(BB3);
    BB2.insertOut(BB3);
    ir.cfg.linkInCodeOrder(BB1, BB2);
    ir.cfg.linkInCodeOrder(BB2, BB3);

    s.remove(); // s is in BB1, we'll mutate it and insert in BB3 below.

    RegisterOperand cr = ir.regpool.makeTempCondition();
    RegisterOperand val1 = (RegisterOperand) IfCmp.getClearVal1(s);
    RegisterOperand val2 = (RegisterOperand) IfCmp.getClearVal2(s);
    RegisterOperand lval1 = L(ir.regpool.getSecondReg(val1.getRegister()));
    RegisterOperand lval2 = L(ir.regpool.getSecondReg(val2.getRegister()));
    PowerPCConditionOperand cond = new PowerPCConditionOperand(IfCmp.getCond(s));
    BB1.appendInstruction(MIR_Binary.create(PPC_CMP, cr, val1, val2));
    BB1.appendInstruction(MIR_CondBranch.create(PPC_BCOND,
                                                cr.copyD2U(),
                                                PowerPCConditionOperand.NOT_EQUAL(),
                                                BB3.makeJumpTarget(),
                                                new BranchProfileOperand()));
    BB2.appendInstruction(MIR_Binary.create(PPC_CMPL, cr.copyD2D(), lval1, lval2));
    BB3.prependInstruction(MIR_CondBranch.mutate(s,
                                                 PPC_BCOND,
                                                 cr.copyD2U(),
                                                 cond,
                                                 IfCmp.getTarget(s),
                                                 IfCmp.getBranchProfile(s)));
  }

  private static void get_time_base(Instruction s, IR ir) {
    if (VM.BuildFor32Addr) {
      BasicBlock BB1 = s.getBasicBlock();
      BB1 = BB1.segregateInstruction(s, ir);
      Register defHigh = Nullary.getResult(s).getRegister();
      Register defLow = ir.regpool.getSecondReg(defHigh);
      Register t0 = ir.regpool.getInteger();
      Register cr = ir.regpool.getCondition();
      defLow.setSpansBasicBlock();
      defHigh.setSpansBasicBlock();
      // Try to get the base
      Register TU = ir.regpool.getPhysicalRegisterSet().getTU();
      Register TL = ir.regpool.getPhysicalRegisterSet().getTL();
      s.insertBefore(MIR_Move.create(PPC_MFTBU, I(defHigh), I(TU)));
      s.insertBefore(MIR_Move.create(PPC_MFTB, I(defLow), I(TL)));
      // Try again to see if it changed
      s.insertBefore(MIR_Move.create(PPC_MFTBU, I(t0), I(TU)));
      s.insertBefore(MIR_Binary.create(PPC_CMP, I(cr), I(t0), I(defHigh)));
      MIR_CondBranch.mutate(s,
                            PPC_BCOND,
                            I(cr),
                            PowerPCConditionOperand.NOT_EQUAL(),
                            BB1.makeJumpTarget(),
                            new BranchProfileOperand());
      // fix up CFG
      BB1.insertOut(BB1);
    } else {
      // We read the 64-bit time base register atomically
      Register def = Nullary.getResult(s).getRegister();
      // See PowerPC Architecture, Book II, pp.352-353
      Register TL = ir.regpool.getPhysicalRegisterSet().getTL();
      MIR_Move.mutate(s, PPC_MFTB, L(def), L(TL));
    }
  }

  private static void attempt(Instruction s, IR ir, boolean isAddress, boolean isLong) {
    BasicBlock BB1 = s.getBasicBlock();
    BasicBlock BB4 = BB1.splitNodeAt(s, ir);
    BasicBlock BB2 = BB1.createSubBlock(0, ir);
    BasicBlock BB3 = BB2.createSubBlock(0, ir);
    BB1.insertOut(BB2);
    BB1.insertOut(BB3);
    BB2.insertOut(BB4);
    BB3.insertOut(BB4);
    ir.cfg.linkInCodeOrder(BB1, BB2);
    ir.cfg.linkInCodeOrder(BB2, BB3);
    ir.cfg.linkInCodeOrder(BB3, BB4);

    // mutate ATTEMPT into a STWCX
    RegisterOperand newValue = (RegisterOperand) Attempt.getNewValue(s);
    RegisterOperand address = (RegisterOperand) Attempt.getAddress(s);
    Operand offset = Attempt.getOffset(s);
    LocationOperand location = Attempt.getLocation(s);
    Operand guard = Attempt.getGuard(s);
    RegisterOperand result = Attempt.getResult(s);
    MIR_Store.mutate(s, (isAddress || isLong ? PPC_STAddrCXr : PPC_STWCXr),
        newValue, address, offset, location, guard);

    // Branch to BB3 iff the STWXC succeeds (CR(0) is EQUAL)
    // Else fall through to BB2
    PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
    BB1.appendInstruction(MIR_CondBranch.create(PPC_BCOND,
                                                I(phys.getConditionRegister(0)),
                                                PowerPCConditionOperand.EQUAL(),
                                                BB3.makeJumpTarget(),
                                                new BranchProfileOperand()));
    // BB2 sets result to FALSE and jumps to BB4
    BB2.appendInstruction(MIR_Unary.create(PPC_LDI, result.copyRO(), IC(0)));
    BB2.appendInstruction(MIR_Branch.create(PPC_B, BB4.makeJumpTarget()));

    // BB3 sets result to TRUE and falls through to BB4
    BB3.appendInstruction(MIR_Unary.create(PPC_LDI, result.copyRO(), IC(1)));
  }

}
