Skip to content
ARMInstrInfo.td 122 KiB
Newer Older
def SMMLSR : AMul2I <0b0111010, (outs GPR:$dst), (ins GPR:$a, GPR:$b, GPR:$c),
               IIC_iMAC32, "smmlsr", "\t$dst, $a, $b, $c",
               [/* For disassembly only; pattern left blank */]>,
            Requires<[IsARM, HasV6]> {
  let Inst{7-4}   = 0b1111; // R = 1
}

  def BB : AMulxyI<0b0001011, (outs GPR:$dst), (ins GPR:$a, GPR:$b),
              IIC_iMUL32, !strconcat(opc, "bb"), "\t$dst, $a, $b",
              [(set GPR:$dst, (opnode (sext_inreg GPR:$a, i16),
                                      (sext_inreg GPR:$b, i16)))]>,
           Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 0;
             let Inst{6} = 0;
           }
  def BT : AMulxyI<0b0001011, (outs GPR:$dst), (ins GPR:$a, GPR:$b),
              IIC_iMUL32, !strconcat(opc, "bt"), "\t$dst, $a, $b",
              [(set GPR:$dst, (opnode (sext_inreg GPR:$a, i16),
           Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 0;
             let Inst{6} = 1;
           }
  def TB : AMulxyI<0b0001011, (outs GPR:$dst), (ins GPR:$a, GPR:$b),
              IIC_iMUL32, !strconcat(opc, "tb"), "\t$dst, $a, $b",
              [(set GPR:$dst, (opnode (sra GPR:$a, (i32 16)),
                                      (sext_inreg GPR:$b, i16)))]>,
           Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 1;
             let Inst{6} = 0;
           }
  def TT : AMulxyI<0b0001011, (outs GPR:$dst), (ins GPR:$a, GPR:$b),
              IIC_iMUL32, !strconcat(opc, "tt"), "\t$dst, $a, $b",
              [(set GPR:$dst, (opnode (sra GPR:$a, (i32 16)),
                                      (sra GPR:$b, (i32 16))))]>,
            Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 1;
             let Inst{6} = 1;
           }
  def WB : AMulxyI<0b0001001, (outs GPR:$dst), (ins GPR:$a, GPR:$b),
              IIC_iMUL16, !strconcat(opc, "wb"), "\t$dst, $a, $b",
              [(set GPR:$dst, (sra (opnode GPR:$a,
                                    (sext_inreg GPR:$b, i16)), (i32 16)))]>,
           Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 1;
             let Inst{6} = 0;
           }
  def WT : AMulxyI<0b0001001, (outs GPR:$dst), (ins GPR:$a, GPR:$b),
              IIC_iMUL16, !strconcat(opc, "wt"), "\t$dst, $a, $b",
              [(set GPR:$dst, (sra (opnode GPR:$a,
                                    (sra GPR:$b, (i32 16))), (i32 16)))]>,
            Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 1;
             let Inst{6} = 1;
           }
  def BB : AMulxyI<0b0001000, (outs GPR:$dst), (ins GPR:$a, GPR:$b, GPR:$acc),
              IIC_iMAC16, !strconcat(opc, "bb"), "\t$dst, $a, $b, $acc",
              [(set GPR:$dst, (add GPR:$acc,
                               (opnode (sext_inreg GPR:$a, i16),
                                       (sext_inreg GPR:$b, i16))))]>,
           Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 0;
             let Inst{6} = 0;
           }
  def BT : AMulxyI<0b0001000, (outs GPR:$dst), (ins GPR:$a, GPR:$b, GPR:$acc),
              IIC_iMAC16, !strconcat(opc, "bt"), "\t$dst, $a, $b, $acc",
              [(set GPR:$dst, (add GPR:$acc, (opnode (sext_inreg GPR:$a, i16),
Jim Grosbach's avatar
Jim Grosbach committed
                                                    (sra GPR:$b, (i32 16)))))]>,
           Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 0;
             let Inst{6} = 1;
           }
  def TB : AMulxyI<0b0001000, (outs GPR:$dst), (ins GPR:$a, GPR:$b, GPR:$acc),
              IIC_iMAC16, !strconcat(opc, "tb"), "\t$dst, $a, $b, $acc",
              [(set GPR:$dst, (add GPR:$acc, (opnode (sra GPR:$a, (i32 16)),
                                                 (sext_inreg GPR:$b, i16))))]>,
           Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 1;
             let Inst{6} = 0;
           }
  def TT : AMulxyI<0b0001000, (outs GPR:$dst), (ins GPR:$a, GPR:$b, GPR:$acc),
              IIC_iMAC16, !strconcat(opc, "tt"), "\t$dst, $a, $b, $acc",
             [(set GPR:$dst, (add GPR:$acc, (opnode (sra GPR:$a, (i32 16)),
                                                    (sra GPR:$b, (i32 16)))))]>,
            Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 1;
             let Inst{6} = 1;
           }
  def WB : AMulxyI<0b0001001, (outs GPR:$dst), (ins GPR:$a, GPR:$b, GPR:$acc),
              IIC_iMAC16, !strconcat(opc, "wb"), "\t$dst, $a, $b, $acc",
              [(set GPR:$dst, (add GPR:$acc, (sra (opnode GPR:$a,
                                       (sext_inreg GPR:$b, i16)), (i32 16))))]>,
           Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 0;
             let Inst{6} = 0;
           }
  def WT : AMulxyI<0b0001001, (outs GPR:$dst), (ins GPR:$a, GPR:$b, GPR:$acc),
              IIC_iMAC16, !strconcat(opc, "wt"), "\t$dst, $a, $b, $acc",
              [(set GPR:$dst, (add GPR:$acc, (sra (opnode GPR:$a,
                                         (sra GPR:$b, (i32 16))), (i32 16))))]>,
            Requires<[IsARM, HasV5TE]> {
             let Inst{5} = 0;
             let Inst{6} = 1;
           }
defm SMUL : AI_smul<"smul", BinOpFrag<(mul node:$LHS, node:$RHS)>>;
defm SMLA : AI_smla<"smla", BinOpFrag<(mul node:$LHS, node:$RHS)>>;
// Halfword multiply accumulate long: SMLAL<x><y> -- for disassembly only
def SMLALBB : AMulxyI<0b0001010,(outs GPR:$ldst,GPR:$hdst),(ins GPR:$a,GPR:$b),
                      IIC_iMAC64, "smlalbb", "\t$ldst, $hdst, $a, $b",
                      [/* For disassembly only; pattern left blank */]>,
              Requires<[IsARM, HasV5TE]> {
  let Inst{5} = 0;
  let Inst{6} = 0;
}

def SMLALBT : AMulxyI<0b0001010,(outs GPR:$ldst,GPR:$hdst),(ins GPR:$a,GPR:$b),
                      IIC_iMAC64, "smlalbt", "\t$ldst, $hdst, $a, $b",
                      [/* For disassembly only; pattern left blank */]>,
              Requires<[IsARM, HasV5TE]> {
  let Inst{5} = 0;
  let Inst{6} = 1;
}

def SMLALTB : AMulxyI<0b0001010,(outs GPR:$ldst,GPR:$hdst),(ins GPR:$a,GPR:$b),
                      IIC_iMAC64, "smlaltb", "\t$ldst, $hdst, $a, $b",
                      [/* For disassembly only; pattern left blank */]>,
              Requires<[IsARM, HasV5TE]> {
  let Inst{5} = 1;
  let Inst{6} = 0;
}

def SMLALTT : AMulxyI<0b0001010,(outs GPR:$ldst,GPR:$hdst),(ins GPR:$a,GPR:$b),
                      IIC_iMAC64, "smlaltt", "\t$ldst, $hdst, $a, $b",
                      [/* For disassembly only; pattern left blank */]>,
              Requires<[IsARM, HasV5TE]> {
  let Inst{5} = 1;
  let Inst{6} = 1;
}

// Helper class for AI_smld -- for disassembly only
class AMulDualI<bit long, bit sub, bit swap, dag oops, dag iops,
                InstrItinClass itin, string opc, string asm>
  : AI<oops, iops, MulFrm, itin, opc, asm, []>, Requires<[IsARM, HasV6]> {
  let Inst{4}     = 1;
  let Inst{5}     = swap;
  let Inst{6}     = sub;
  let Inst{7}     = 0;
  let Inst{21-20} = 0b00;
  let Inst{22}    = long;
  let Inst{27-23} = 0b01110;
}

multiclass AI_smld<bit sub, string opc> {

  def D : AMulDualI<0, sub, 0, (outs GPR:$dst), (ins GPR:$a, GPR:$b, GPR:$acc),
                  NoItinerary, !strconcat(opc, "d"), "\t$dst, $a, $b, $acc">;

  def DX : AMulDualI<0, sub, 1, (outs GPR:$dst), (ins GPR:$a, GPR:$b, GPR:$acc),
                  NoItinerary, !strconcat(opc, "dx"), "\t$dst, $a, $b, $acc">;

  def LD : AMulDualI<1, sub, 0, (outs GPR:$ldst,GPR:$hdst), (ins GPR:$a,GPR:$b),
                  NoItinerary, !strconcat(opc, "ld"), "\t$ldst, $hdst, $a, $b">;

  def LDX : AMulDualI<1, sub, 1, (outs GPR:$ldst,GPR:$hdst),(ins GPR:$a,GPR:$b),
                  NoItinerary, !strconcat(opc, "ldx"),"\t$ldst, $hdst, $a, $b">;

}

defm SMLA : AI_smld<0, "smla">;
defm SMLS : AI_smld<1, "smls">;

multiclass AI_sdml<bit sub, string opc> {

  def D : AMulDualI<0, sub, 0, (outs GPR:$dst), (ins GPR:$a, GPR:$b),
                    NoItinerary, !strconcat(opc, "d"), "\t$dst, $a, $b"> {
    let Inst{15-12} = 0b1111;
  }

  def DX : AMulDualI<0, sub, 1, (outs GPR:$dst), (ins GPR:$a, GPR:$b),
                    NoItinerary, !strconcat(opc, "dx"), "\t$dst, $a, $b"> {
    let Inst{15-12} = 0b1111;
  }

}

defm SMUA : AI_sdml<0, "smua">;
defm SMUS : AI_sdml<1, "smus">;
//===----------------------------------------------------------------------===//
//  Misc. Arithmetic Instructions.
//
def CLZ  : AMiscA1I<0b000010110, (outs GPR:$dst), (ins GPR:$src), IIC_iUNAr,
              [(set GPR:$dst, (ctlz GPR:$src))]>, Requires<[IsARM, HasV5T]> {
  let Inst{7-4}   = 0b0001;
  let Inst{11-8}  = 0b1111;
  let Inst{19-16} = 0b1111;
}
Jim Grosbach's avatar
Jim Grosbach committed
def RBIT : AMiscA1I<0b01101111, (outs GPR:$dst), (ins GPR:$src), IIC_iUNAr,
              "rbit", "\t$dst, $src",
              [(set GPR:$dst, (ARMrbit GPR:$src))]>,
           Requires<[IsARM, HasV6T2]> {
Jim Grosbach's avatar
Jim Grosbach committed
  let Inst{7-4}   = 0b0011;
  let Inst{11-8}  = 0b1111;
  let Inst{19-16} = 0b1111;
}

def REV  : AMiscA1I<0b01101011, (outs GPR:$dst), (ins GPR:$src), IIC_iUNAr,
              [(set GPR:$dst, (bswap GPR:$src))]>, Requires<[IsARM, HasV6]> {
  let Inst{7-4}   = 0b0011;
  let Inst{11-8}  = 0b1111;
  let Inst{19-16} = 0b1111;
}
def REV16 : AMiscA1I<0b01101011, (outs GPR:$dst), (ins GPR:$src), IIC_iUNAr,
               [(set GPR:$dst,
                   (or (and (srl GPR:$src, (i32 8)), 0xFF),
                       (or (and (shl GPR:$src, (i32 8)), 0xFF00),
                           (or (and (srl GPR:$src, (i32 8)), 0xFF0000),
                               (and (shl GPR:$src, (i32 8)), 0xFF000000)))))]>,
               Requires<[IsARM, HasV6]> {
  let Inst{7-4}   = 0b1011;
  let Inst{11-8}  = 0b1111;
  let Inst{19-16} = 0b1111;
}
def REVSH : AMiscA1I<0b01101111, (outs GPR:$dst), (ins GPR:$src), IIC_iUNAr,
               [(set GPR:$dst,
                  (sext_inreg
                    (or (srl (and GPR:$src, 0xFF00), (i32 8)),
                        (shl GPR:$src, (i32 8))), i16))]>,
               Requires<[IsARM, HasV6]> {
  let Inst{7-4}   = 0b1011;
  let Inst{11-8}  = 0b1111;
  let Inst{19-16} = 0b1111;
}
def lsl_shift_imm : SDNodeXForm<imm, [{
  unsigned Sh = ARM_AM::getSORegOpc(ARM_AM::lsl, N->getZExtValue());
  return CurDAG->getTargetConstant(Sh, MVT::i32);
}]>;

def lsl_amt : PatLeaf<(i32 imm), [{
  return (N->getZExtValue() < 32);
}], lsl_shift_imm>;

def PKHBT : AMiscA1I<0b01101000, (outs GPR:$dst),
                                 (ins GPR:$src1, GPR:$src2, shift_imm:$sh),
               IIC_iALUsi, "pkhbt", "\t$dst, $src1, $src2$sh",
               [(set GPR:$dst, (or (and GPR:$src1, 0xFFFF),
                                        0xFFFF0000)))]>,
               Requires<[IsARM, HasV6]> {
  let Inst{6-4} = 0b001;
}

// Alternate cases for PKHBT where identities eliminate some nodes.
def : ARMV6Pat<(or (and GPR:$src1, 0xFFFF), (and GPR:$src2, 0xFFFF0000)),
               (PKHBT GPR:$src1, GPR:$src2, 0)>;
def : ARMV6Pat<(or (and GPR:$src1, 0xFFFF), (shl GPR:$src2, imm16_31:$sh)),
               (PKHBT GPR:$src1, GPR:$src2, (lsl_shift_imm imm16_31:$sh))>;
def asr_shift_imm : SDNodeXForm<imm, [{
  unsigned Sh = ARM_AM::getSORegOpc(ARM_AM::asr, N->getZExtValue());
  return CurDAG->getTargetConstant(Sh, MVT::i32);
}]>;

def asr_amt : PatLeaf<(i32 imm), [{
  return (N->getZExtValue() <= 32);
}], asr_shift_imm>;
// Note: Shifts of 1-15 bits will be transformed to srl instead of sra and
// will match the pattern below.
def PKHTB : AMiscA1I<0b01101000, (outs GPR:$dst),
                                 (ins GPR:$src1, GPR:$src2, shift_imm:$sh),
               IIC_iBITsi, "pkhtb", "\t$dst, $src1, $src2$sh",
               [(set GPR:$dst, (or (and GPR:$src1, 0xFFFF0000),
                                   (and (sra GPR:$src2, asr_amt:$sh),
                                        0xFFFF)))]>,
               Requires<[IsARM, HasV6]> {
  let Inst{6-4} = 0b101;
}

// Alternate cases for PKHTB where identities eliminate some nodes.  Note that
// a shift amount of 0 is *not legal* here, it is PKHBT instead.
def : ARMV6Pat<(or (and GPR:$src1, 0xFFFF0000), (srl GPR:$src2, imm16_31:$sh)),
               (PKHTB GPR:$src1, GPR:$src2, (asr_shift_imm imm16_31:$sh))>;
def : ARMV6Pat<(or (and GPR:$src1, 0xFFFF0000),
                   (and (srl GPR:$src2, imm1_15:$sh), 0xFFFF)),
               (PKHTB GPR:$src1, GPR:$src2, (asr_shift_imm imm1_15:$sh))>;
//===----------------------------------------------------------------------===//
//  Comparison Instructions...
//

                        BinOpFrag<(ARMcmp node:$LHS, node:$RHS)>>;
// FIXME: We have to be careful when using the CMN instruction and comparison
// with 0. One would expect these two pieces of code should give identical
// results:
//
//   rsbs r1, r1, 0
//   cmp  r0, r1
//   mov  r0, #0
//   it   ls
//   mov  r0, #1
//
// and:
// 
//   cmn  r0, r1
//   mov  r0, #0
//   it   ls
//   mov  r0, #1
//
// However, the CMN gives the *opposite* result when r1 is 0. This is because
// the carry flag is set in the CMP case but not in the CMN case. In short, the
// CMP instruction doesn't perform a truncate of the (logical) NOT of 0 plus the
// value of r0 and the carry bit (because the "carry bit" parameter to
// AddWithCarry is defined as 1 in this case, the carry flag will always be set
// when r0 >= 0). The CMN instruction doesn't perform a NOT of 0 so there is
// never a "carry" when this AddWithCarry is performed (because the "carry bit"
// parameter to AddWithCarry is defined as 0).
//
// When x is 0 and unsigned:
//
//    x = 0
//   ~x = 0xFFFF FFFF
//   ~x + 1 = 0x1 0000 0000
//   (-x = 0) != (0x1 0000 0000 = ~x + 1)
//
// Therefore, we should disable CMN when comparing against zero, until we can
// limit when the CMN instruction is used (when we know that the RHS is not 0 or
// when it's a comparison which doesn't look at the 'carry' flag).
//
// (See the ARM docs for the "AddWithCarry" pseudo-code.)
//
// This is related to <rdar://problem/7569620>.
//
//defm CMN  : AI1_cmp_irs<0b1011, "cmn",
//                        BinOpFrag<(ARMcmp node:$LHS,(ineg node:$RHS))>>;

// Note that TST/TEQ don't set all the same flags that CMP does!
                        BinOpFrag<(ARMcmpZ (and node:$LHS, node:$RHS), 0)>, 1>;
                        BinOpFrag<(ARMcmpZ (xor node:$LHS, node:$RHS), 0)>, 1>;
                         BinOpFrag<(ARMcmpZ node:$LHS, node:$RHS)>>;
defm CMNz  : AI1_cmp_irs<0b1011, "cmn",
                         BinOpFrag<(ARMcmpZ node:$LHS,(ineg node:$RHS))>>;
//def : ARMPat<(ARMcmp GPR:$src, so_imm_neg:$imm),
//             (CMNri  GPR:$src, so_imm_neg:$imm)>;
def : ARMPat<(ARMcmpZ GPR:$src, so_imm_neg:$imm),
// Pseudo i64 compares for some floating point compares.
let usesCustomInserter = 1, isBranch = 1, isTerminator = 1,
    Defs = [CPSR] in {
def BCCi64 : PseudoInst<(outs),
Jim Grosbach's avatar
Jim Grosbach committed
    (ins i32imm:$cc, GPR:$lhs1, GPR:$lhs2, GPR:$rhs1, GPR:$rhs2, brtarget:$dst),
     IIC_Br,
     "${:comment} B\t$dst GPR:$lhs1, GPR:$lhs2, GPR:$rhs1, GPR:$rhs2, imm:$cc",
    [(ARMBcci64 imm:$cc, GPR:$lhs1, GPR:$lhs2, GPR:$rhs1, GPR:$rhs2, bb:$dst)]>;

def BCCZi64 : PseudoInst<(outs),
     (ins i32imm:$cc, GPR:$lhs1, GPR:$lhs2, brtarget:$dst),
      IIC_Br,
     "${:comment} B\t$dst GPR:$lhs1, GPR:$lhs2, 0, 0, imm:$cc",
    [(ARMBcci64 imm:$cc, GPR:$lhs1, GPR:$lhs2, 0, 0, bb:$dst)]>;
} // usesCustomInserter


// Conditional moves
// FIXME: should be able to write a pattern for ARMcmov, but can't use
Jim Grosbach's avatar
Jim Grosbach committed
// a two-value operand where a dag node expects two operands. :(
def MOVCCr : AI1<0b1101, (outs GPR:$dst), (ins GPR:$false, GPR:$true), DPFrm,
      [/*(set GPR:$dst, (ARMcmov GPR:$false, GPR:$true, imm:$cc, CCR:$ccr))*/]>,
                RegConstraint<"$false = $dst">, UnaryDP {
                        (ins GPR:$false, so_reg:$true), DPSoRegFrm, IIC_iCMOVsr,
   [/*(set GPR:$dst, (ARMcmov GPR:$false, so_reg:$true, imm:$cc, CCR:$ccr))*/]>,
                RegConstraint<"$false = $dst">, UnaryDP {
  let Inst{25} = 0;
}
                        (ins GPR:$false, so_imm:$true), DPFrm, IIC_iCMOVi,
   [/*(set GPR:$dst, (ARMcmov GPR:$false, so_imm:$true, imm:$cc, CCR:$ccr))*/]>,
//===----------------------------------------------------------------------===//
// Atomic operations intrinsics
//

// memory barriers protect the atomic sequences
let hasSideEffects = 1 in {
def DMBsy : AInoP<(outs), (ins), MiscFrm, NoItinerary, "dmb", "",
                  [(ARMMemBarrier)]>, Requires<[IsARM, HasDB]> {
  let Inst{31-4} = 0xf57ff05;
  // FIXME: add support for options other than a full system DMB
  // See DMB disassembly-only variants below.
  let Inst{3-0} = 0b1111;
}
def DSBsy : AInoP<(outs), (ins), MiscFrm, NoItinerary, "dsb", "",
                  [(ARMSyncBarrier)]>, Requires<[IsARM, HasDB]> {
  let Inst{31-4} = 0xf57ff04;
  // FIXME: add support for options other than a full system DSB
  // See DSB disassembly-only variants below.
  let Inst{3-0} = 0b1111;
}
def DMB_MCR : AInoP<(outs), (ins GPR:$zero), MiscFrm, NoItinerary,
                       "mcr", "\tp15, 0, $zero, c7, c10, 5",
                       Requires<[IsARM, HasV6]> {
  // FIXME: add support for options other than a full system DMB
  // FIXME: add encoding
}

def DSB_MCR : AInoP<(outs), (ins GPR:$zero), MiscFrm, NoItinerary,
                        "mcr", "\tp15, 0, $zero, c7, c10, 4",
                        Requires<[IsARM, HasV6]> {
  // FIXME: add support for options other than a full system DSB
  // FIXME: add encoding
}
// Memory Barrier Operations Variants -- for disassembly only
def memb_opt : Operand<i32> {
  let PrintMethod = "printMemBOption";
}
class AMBI<bits<4> op7_4, string opc>
  : AInoP<(outs), (ins memb_opt:$opt), MiscFrm, NoItinerary, opc, "\t$opt",
          [/* For disassembly only; pattern left blank */]>,
    Requires<[IsARM, HasDB]> {
  let Inst{31-8} = 0xf57ff0;
  let Inst{7-4} = op7_4;
}

// These DMB variants are for disassembly only.

// These DSB variants are for disassembly only.

// ISB has only full system option -- for disassembly only
def ISBsy : AInoP<(outs), (ins), MiscFrm, NoItinerary, "isb", "", []>,
            Requires<[IsARM, HasDB]> {
  let Inst{31-4} = 0xf57ff06;
Jim Grosbach's avatar
Jim Grosbach committed
let usesCustomInserter = 1 in {
  let Uses = [CPSR] in {
    def ATOMIC_LOAD_ADD_I8 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_ADD_I8 PSEUDO!",
      [(set GPR:$dst, (atomic_load_add_8 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_SUB_I8 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_SUB_I8 PSEUDO!",
      [(set GPR:$dst, (atomic_load_sub_8 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_AND_I8 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_AND_I8 PSEUDO!",
      [(set GPR:$dst, (atomic_load_and_8 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_OR_I8 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_OR_I8 PSEUDO!",
      [(set GPR:$dst, (atomic_load_or_8 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_XOR_I8 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_XOR_I8 PSEUDO!",
      [(set GPR:$dst, (atomic_load_xor_8 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_NAND_I8 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_NAND_I8 PSEUDO!",
      [(set GPR:$dst, (atomic_load_nand_8 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_ADD_I16 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_ADD_I16 PSEUDO!",
      [(set GPR:$dst, (atomic_load_add_16 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_SUB_I16 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_SUB_I16 PSEUDO!",
      [(set GPR:$dst, (atomic_load_sub_16 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_AND_I16 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_AND_I16 PSEUDO!",
      [(set GPR:$dst, (atomic_load_and_16 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_OR_I16 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_OR_I16 PSEUDO!",
      [(set GPR:$dst, (atomic_load_or_16 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_XOR_I16 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_XOR_I16 PSEUDO!",
      [(set GPR:$dst, (atomic_load_xor_16 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_NAND_I16 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_NAND_I16 PSEUDO!",
      [(set GPR:$dst, (atomic_load_nand_16 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_ADD_I32 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_ADD_I32 PSEUDO!",
      [(set GPR:$dst, (atomic_load_add_32 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_SUB_I32 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_SUB_I32 PSEUDO!",
      [(set GPR:$dst, (atomic_load_sub_32 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_AND_I32 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_AND_I32 PSEUDO!",
      [(set GPR:$dst, (atomic_load_and_32 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_OR_I32 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_OR_I32 PSEUDO!",
      [(set GPR:$dst, (atomic_load_or_32 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_XOR_I32 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_XOR_I32 PSEUDO!",
      [(set GPR:$dst, (atomic_load_xor_32 GPR:$ptr, GPR:$incr))]>;
    def ATOMIC_LOAD_NAND_I32 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$incr), NoItinerary,
      "${:comment} ATOMIC_LOAD_NAND_I32 PSEUDO!",
      [(set GPR:$dst, (atomic_load_nand_32 GPR:$ptr, GPR:$incr))]>;

    def ATOMIC_SWAP_I8 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$new), NoItinerary,
      "${:comment} ATOMIC_SWAP_I8 PSEUDO!",
      [(set GPR:$dst, (atomic_swap_8 GPR:$ptr, GPR:$new))]>;
    def ATOMIC_SWAP_I16 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$new), NoItinerary,
      "${:comment} ATOMIC_SWAP_I16 PSEUDO!",
      [(set GPR:$dst, (atomic_swap_16 GPR:$ptr, GPR:$new))]>;
    def ATOMIC_SWAP_I32 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$new), NoItinerary,
      "${:comment} ATOMIC_SWAP_I32 PSEUDO!",
      [(set GPR:$dst, (atomic_swap_32 GPR:$ptr, GPR:$new))]>;

    def ATOMIC_CMP_SWAP_I8 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$old, GPR:$new), NoItinerary,
      "${:comment} ATOMIC_CMP_SWAP_I8 PSEUDO!",
      [(set GPR:$dst, (atomic_cmp_swap_8 GPR:$ptr, GPR:$old, GPR:$new))]>;
    def ATOMIC_CMP_SWAP_I16 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$old, GPR:$new), NoItinerary,
      "${:comment} ATOMIC_CMP_SWAP_I16 PSEUDO!",
      [(set GPR:$dst, (atomic_cmp_swap_16 GPR:$ptr, GPR:$old, GPR:$new))]>;
    def ATOMIC_CMP_SWAP_I32 : PseudoInst<
      (outs GPR:$dst), (ins GPR:$ptr, GPR:$old, GPR:$new), NoItinerary,
      "${:comment} ATOMIC_CMP_SWAP_I32 PSEUDO!",
      [(set GPR:$dst, (atomic_cmp_swap_32 GPR:$ptr, GPR:$old, GPR:$new))]>;
}
}

let mayLoad = 1 in {
def LDREXB : AIldrex<0b10, (outs GPR:$dest), (ins GPR:$ptr), NoItinerary,
                    "ldrexb", "\t$dest, [$ptr]",
                    []>;
def LDREXH : AIldrex<0b11, (outs GPR:$dest), (ins GPR:$ptr), NoItinerary,
                    "ldrexh", "\t$dest, [$ptr]",
                    []>;
def LDREX  : AIldrex<0b00, (outs GPR:$dest), (ins GPR:$ptr), NoItinerary,
                    "ldrex", "\t$dest, [$ptr]",
                    []>;
def LDREXD : AIldrex<0b01, (outs GPR:$dest, GPR:$dest2), (ins GPR:$ptr),
                    NoItinerary,
                    "ldrexd", "\t$dest, $dest2, [$ptr]",
                    []>;
let mayStore = 1, Constraints = "@earlyclobber $success" in {
def STREXB : AIstrex<0b10, (outs GPR:$success), (ins GPR:$src, GPR:$ptr),
                    NoItinerary,
                    "strexb", "\t$success, $src, [$ptr]",
                    []>;
def STREXH : AIstrex<0b11, (outs GPR:$success), (ins GPR:$src, GPR:$ptr),
                    NoItinerary,
                    "strexh", "\t$success, $src, [$ptr]",
                    []>;
def STREX  : AIstrex<0b00, (outs GPR:$success), (ins GPR:$src, GPR:$ptr),
                    NoItinerary,
def STREXD : AIstrex<0b01, (outs GPR:$success),
                    (ins GPR:$src, GPR:$src2, GPR:$ptr),
                    NoItinerary,
                    "strexd", "\t$success, $src, $src2, [$ptr]",
                    []>;
// Clear-Exclusive is for disassembly only.
def CLREX : AXI<(outs), (ins), MiscFrm, NoItinerary, "clrex",
                [/* For disassembly only; pattern left blank */]>,
            Requires<[IsARM, HasV7]>  {
  let Inst{31-20} = 0xf57;
  let Inst{7-4} = 0b0001;
}

// SWP/SWPB are deprecated in V6/V7 and for disassembly only.
let mayLoad = 1 in {
def SWP : AI<(outs GPR:$dst), (ins GPR:$src, GPR:$ptr), LdStExFrm, NoItinerary,
             "swp", "\t$dst, $src, [$ptr]",
             [/* For disassembly only; pattern left blank */]> {
  let Inst{27-23} = 0b00010;
  let Inst{22} = 0; // B = 0
  let Inst{21-20} = 0b00;
  let Inst{7-4} = 0b1001;
}

def SWPB : AI<(outs GPR:$dst), (ins GPR:$src, GPR:$ptr), LdStExFrm, NoItinerary,
             "swpb", "\t$dst, $src, [$ptr]",
             [/* For disassembly only; pattern left blank */]> {
  let Inst{27-23} = 0b00010;
  let Inst{22} = 1; // B = 1
  let Inst{21-20} = 0b00;
  let Inst{7-4} = 0b1001;
}
}

//===----------------------------------------------------------------------===//
// TLS Instructions
//

// __aeabi_read_tp preserves the registers r1-r3.
  def TPsoft : ABXI<0b1011, (outs), (ins), IIC_Br,
//===----------------------------------------------------------------------===//
// SJLJ Exception handling intrinsics
Jim Grosbach's avatar
Jim Grosbach committed
//   eh_sjlj_setjmp() is an instruction sequence to store the return
//   address and save #0 in R0 for the non-longjmp case.
//   Since by its nature we may be coming from some other function to get
//   here, and we're using the stack frame for the containing function to
//   save/restore registers, we can't keep anything live in regs across
//   the eh_sjlj_setjmp(), else it will almost certainly have been tromped upon
//   when we get here from a longjmp(). We force everthing out of registers
//   except for our own input by listing the relevant registers in Defs. By
//   doing so, we also cause the prologue/epilogue code to actively preserve
//   all of the callee-saved resgisters, which is exactly what we want.
//   A constant value is passed in $val, and we use the location as a scratch.
let Defs =
  [ R0,  R1,  R2,  R3,  R4,  R5,  R6,  R7,  R8,  R9,  R10, R11, R12, LR,  D0,
    D1,  D2,  D3,  D4,  D5,  D6,  D7,  D8,  D9,  D10, D11, D12, D13, D14, D15,
    D16, D17, D18, D19, D20, D21, D22, D23, D24, D25, D26, D27, D28, D29, D30,
    D31 ], hasSideEffects = 1, isBarrier = 1 in {
  def Int_eh_sjlj_setjmp : XI<(outs), (ins GPR:$src, GPR:$val),
                               AddrModeNone, SizeSpecial, IndexModeNone,
                               Pseudo, NoItinerary,
                           "add\t$val, pc, #8\t${:comment} eh_setjmp begin\n\t"
                           "str\t$val, [$src, #4]\n\t"
                           "mov\tr0, #0\n\t"
                           "add\tpc, pc, #0\n\t"
                           "mov\tr0, #1 ${:comment} eh_setjmp end", "",
                         [(set R0, (ARMeh_sjlj_setjmp GPR:$src, GPR:$val))]>,
                           Requires<[IsARM, HasVFP2]>;
}

let Defs =
  [ R0,  R1,  R2,  R3,  R4,  R5,  R6,  R7,  R8,  R9,  R10, R11, R12, LR ],
  hasSideEffects = 1, isBarrier = 1 in {
  def Int_eh_sjlj_setjmp_nofp : XI<(outs), (ins GPR:$src, GPR:$val),
                                   AddrModeNone, SizeSpecial, IndexModeNone,
                                   Pseudo, NoItinerary,
                           "add\t$val, pc, #8\t${:comment} eh_setjmp begin\n\t"
                           "str\t$val, [$src, #4]\n\t"
                           "mov\tr0, #0\n\t"
                           "add\tpc, pc, #0\n\t"
                           "mov\tr0, #1 ${:comment} eh_setjmp end", "",
                         [(set R0, (ARMeh_sjlj_setjmp GPR:$src, GPR:$val))]>,
                                Requires<[IsARM, NoVFP]>;
// FIXME: Non-Darwin version(s)
let isBarrier = 1, hasSideEffects = 1, isTerminator = 1,
    Defs = [ R7, LR, SP ] in {
def Int_eh_sjlj_longjmp : XI<(outs), (ins GPR:$src, GPR:$scratch),
                             AddrModeNone, SizeSpecial, IndexModeNone,
                             Pseudo, NoItinerary,
                             "ldr\tsp, [$src, #8]\n\t"
                             "ldr\t$scratch, [$src, #4]\n\t"
                             "ldr\tr7, [$src]\n\t"
                             "bx\t$scratch", "",
                         [(ARMeh_sjlj_longjmp GPR:$src, GPR:$scratch)]>,
                                Requires<[IsARM, IsDarwin]>;
}

//===----------------------------------------------------------------------===//
// Non-Instruction Patterns
//
// Large immediate handling.
Rafael Espindola's avatar
Rafael Espindola committed

// Two piece so_imms.
// FIXME: Expand this in ARMExpandPseudoInsts.
// FIXME: Remove this when we can do generalized remat.
Jim Grosbach's avatar
Jim Grosbach committed
def MOVi2pieces : AI1x2<(outs GPR:$dst), (ins so_imm2part:$src),
                         [(set GPR:$dst, so_imm2part:$src)]>,
                  Requires<[IsARM, NoV6T2]>;
def : ARMPat<(or GPR:$LHS, so_imm2part:$RHS),
             (ORRri (ORRri GPR:$LHS, (so_imm2part_1 imm:$RHS)),
                    (so_imm2part_2 imm:$RHS))>;
def : ARMPat<(xor GPR:$LHS, so_imm2part:$RHS),
             (EORri (EORri GPR:$LHS, (so_imm2part_1 imm:$RHS)),
                    (so_imm2part_2 imm:$RHS))>;
def : ARMPat<(add GPR:$LHS, so_imm2part:$RHS),
             (ADDri (ADDri GPR:$LHS, (so_imm2part_1 imm:$RHS)),
                    (so_imm2part_2 imm:$RHS))>;
def : ARMPat<(add GPR:$LHS, so_neg_imm2part:$RHS),
             (SUBri (SUBri GPR:$LHS, (so_neg_imm2part_1 imm:$RHS)),
                    (so_neg_imm2part_2 imm:$RHS))>;
Rafael Espindola's avatar
Rafael Espindola committed

// This is a single pseudo instruction, the benefit is that it can be remat'd
// as a single unit instead of having to handle reg inputs.
// FIXME: Remove this when we can do generalized remat.
def MOVi32imm : AI1x2<(outs GPR:$dst), (ins i32imm:$src), Pseudo, IIC_iMOVix2,
Jim Grosbach's avatar
Jim Grosbach committed
                   "movw", "\t$dst, ${src:lo16}\n\tmovt${p}\t$dst, ${src:hi16}",
                     [(set GPR:$dst, (i32 imm:$src))]>,
               Requires<[IsARM, HasV6T2]>;
// ConstantPool, GlobalAddress, and JumpTable
def : ARMPat<(ARMWrapper  tglobaladdr :$dst), (LEApcrel tglobaladdr :$dst)>,
            Requires<[IsARM, DontUseMovt]>;
def : ARMPat<(ARMWrapper  tconstpool  :$dst), (LEApcrel tconstpool  :$dst)>;
def : ARMPat<(ARMWrapper  tglobaladdr :$dst), (MOVi32imm tglobaladdr :$dst)>,
            Requires<[IsARM, UseMovt]>;
def : ARMPat<(ARMWrapperJT tjumptable:$dst, imm:$id),
             (LEApcrelJT tjumptable:$dst, imm:$id)>;

// TODO: add,sub,and, 3-instr forms?
Rafael Espindola's avatar
Rafael Espindola committed

def : ARMPat<(ARMtcret tcGPR:$dst),
          (TCRETURNri tcGPR:$dst)>, Requires<[IsDarwin]>;

def : ARMPat<(ARMtcret (i32 tglobaladdr:$dst)),
          (TCRETURNdi texternalsym:$dst)>, Requires<[IsDarwin]>;

def : ARMPat<(ARMtcret (i32 texternalsym:$dst)),
          (TCRETURNdi texternalsym:$dst)>, Requires<[IsDarwin]>;

def : ARMPat<(ARMtcret tcGPR:$dst),
          (TCRETURNriND tcGPR:$dst)>, Requires<[IsNotDarwin]>;

def : ARMPat<(ARMtcret (i32 tglobaladdr:$dst)),
          (TCRETURNdiND texternalsym:$dst)>, Requires<[IsNotDarwin]>;

def : ARMPat<(ARMtcret (i32 texternalsym:$dst)),
          (TCRETURNdiND texternalsym:$dst)>, Requires<[IsNotDarwin]>;
// Direct calls
def : ARMPat<(ARMcall texternalsym:$func), (BL texternalsym:$func)>,
      Requires<[IsARM, IsNotDarwin]>;
def : ARMPat<(ARMcall texternalsym:$func), (BLr9 texternalsym:$func)>,
      Requires<[IsARM, IsDarwin]>;
// zextload i1 -> zextload i8
def : ARMPat<(zextloadi1 addrmode2:$addr),  (LDRB addrmode2:$addr)>;
// extload -> zextload
def : ARMPat<(extloadi1  addrmode2:$addr),  (LDRB addrmode2:$addr)>;
def : ARMPat<(extloadi8  addrmode2:$addr),  (LDRB addrmode2:$addr)>;
def : ARMPat<(extloadi16 addrmode3:$addr),  (LDRH addrmode3:$addr)>;
def : ARMPat<(extloadi8  addrmodepc:$addr), (PICLDRB addrmodepc:$addr)>;
def : ARMPat<(extloadi16 addrmodepc:$addr), (PICLDRH addrmodepc:$addr)>;

def : ARMV5TEPat<(mul (sra (shl GPR:$a, (i32 16)), (i32 16)),
                      (sra (shl GPR:$b, (i32 16)), (i32 16))),
                 (SMULBB GPR:$a, GPR:$b)>;
def : ARMV5TEPat<(mul sext_16_node:$a, sext_16_node:$b),
                 (SMULBB GPR:$a, GPR:$b)>;
def : ARMV5TEPat<(mul (sra (shl GPR:$a, (i32 16)), (i32 16)),
                      (sra GPR:$b, (i32 16))),
def : ARMV5TEPat<(mul sext_16_node:$a, (sra GPR:$b, (i32 16))),
def : ARMV5TEPat<(mul (sra GPR:$a, (i32 16)),
                      (sra (shl GPR:$b, (i32 16)), (i32 16))),
def : ARMV5TEPat<(mul (sra GPR:$a, (i32 16)), sext_16_node:$b),
def : ARMV5TEPat<(sra (mul GPR:$a, (sra (shl GPR:$b, (i32 16)), (i32 16))),
                      (i32 16)),
def : ARMV5TEPat<(sra (mul GPR:$a, sext_16_node:$b), (i32 16)),
                 (SMULWB GPR:$a, GPR:$b)>;

def : ARMV5TEPat<(add GPR:$acc,
                      (mul (sra (shl GPR:$a, (i32 16)), (i32 16)),
                           (sra (shl GPR:$b, (i32 16)), (i32 16)))),
                 (SMLABB GPR:$a, GPR:$b, GPR:$acc)>;
def : ARMV5TEPat<(add GPR:$acc,
                      (mul sext_16_node:$a, sext_16_node:$b)),
                 (SMLABB GPR:$a, GPR:$b, GPR:$acc)>;
def : ARMV5TEPat<(add GPR:$acc,
                      (mul (sra (shl GPR:$a, (i32 16)), (i32 16)),
                           (sra GPR:$b, (i32 16)))),
                 (SMLABT GPR:$a, GPR:$b, GPR:$acc)>;
def : ARMV5TEPat<(add GPR:$acc,
                      (mul sext_16_node:$a, (sra GPR:$b, (i32 16)))),
                 (SMLABT GPR:$a, GPR:$b, GPR:$acc)>;
def : ARMV5TEPat<(add GPR:$acc,
                      (mul (sra GPR:$a, (i32 16)),
                           (sra (shl GPR:$b, (i32 16)), (i32 16)))),
                 (SMLATB GPR:$a, GPR:$b, GPR:$acc)>;
def : ARMV5TEPat<(add GPR:$acc,
                      (mul (sra GPR:$a, (i32 16)), sext_16_node:$b)),
                 (SMLATB GPR:$a, GPR:$b, GPR:$acc)>;
def : ARMV5TEPat<(add GPR:$acc,
                      (sra (mul GPR:$a, (sra (shl GPR:$b, (i32 16)), (i32 16))),
                           (i32 16))),
                 (SMLAWB GPR:$a, GPR:$b, GPR:$acc)>;
def : ARMV5TEPat<(add GPR:$acc,
                      (sra (mul GPR:$a, sext_16_node:$b), (i32 16))),
                 (SMLAWB GPR:$a, GPR:$b, GPR:$acc)>;

//===----------------------------------------------------------------------===//
// Thumb Support
//
include "ARMInstrThumb.td"
//===----------------------------------------------------------------------===//
// Thumb2 Support
//

include "ARMInstrThumb2.td"

//===----------------------------------------------------------------------===//
// Floating Point Support
//
include "ARMInstrVFP.td"

//===----------------------------------------------------------------------===//
// Advanced SIMD (NEON) Support
//

include "ARMInstrNEON.td"

//===----------------------------------------------------------------------===//
// Coprocessor Instructions.  For disassembly only.
//

def CDP : ABI<0b1110, (outs), (ins nohash_imm:$cop, i32imm:$opc1,
            nohash_imm:$CRd, nohash_imm:$CRn, nohash_imm:$CRm, i32imm:$opc2),
            NoItinerary, "cdp", "\tp$cop, $opc1, cr$CRd, cr$CRn, cr$CRm, $opc2",
              [/* For disassembly only; pattern left blank */]> {
  let Inst{4} = 0;
}

def CDP2 : ABXI<0b1110, (outs), (ins nohash_imm:$cop, i32imm:$opc1,
               nohash_imm:$CRd, nohash_imm:$CRn, nohash_imm:$CRm, i32imm:$opc2),
               NoItinerary, "cdp2\tp$cop, $opc1, cr$CRd, cr$CRn, cr$CRm, $opc2",
               [/* For disassembly only; pattern left blank */]> {
  let Inst{31-28} = 0b1111;
  let Inst{4} = 0;
}

class ACI<dag oops, dag iops, string opc, string asm>
  : I<oops, iops, AddrModeNone, Size4Bytes, IndexModeNone, BrFrm, NoItinerary,
      opc, asm, "", [/* For disassembly only; pattern left blank */]> {
  let Inst{27-25} = 0b110;
}

multiclass LdStCop<bits<4> op31_28, bit load, string opc> {

  def _OFFSET : ACI<(outs),
      (ins nohash_imm:$cop, nohash_imm:$CRd, addrmode2:$addr),
      opc, "\tp$cop, cr$CRd, $addr"> {
    let Inst{31-28} = op31_28;
    let Inst{24} = 1; // P = 1
    let Inst{21} = 0; // W = 0
    let Inst{22} = 0; // D = 0
    let Inst{20} = load;
  }

  def _PRE : ACI<(outs),
      (ins nohash_imm:$cop, nohash_imm:$CRd, addrmode2:$addr),
      opc, "\tp$cop, cr$CRd, $addr!"> {
    let Inst{31-28} = op31_28;
    let Inst{24} = 1; // P = 1
    let Inst{21} = 1; // W = 1
    let Inst{22} = 0; // D = 0
    let Inst{20} = load;
  }

  def _POST : ACI<(outs),
      (ins nohash_imm:$cop, nohash_imm:$CRd, GPR:$base, am2offset:$offset),
      opc, "\tp$cop, cr$CRd, [$base], $offset"> {
    let Inst{31-28} = op31_28;
    let Inst{24} = 0; // P = 0
    let Inst{21} = 1; // W = 1
    let Inst{22} = 0; // D = 0
    let Inst{20} = load;
  }

  def _OPTION : ACI<(outs),
      (ins nohash_imm:$cop, nohash_imm:$CRd, GPR:$base, i32imm:$option),
      opc, "\tp$cop, cr$CRd, [$base], $option"> {
    let Inst{31-28} = op31_28;
    let Inst{24} = 0; // P = 0
    let Inst{23} = 1; // U = 1
    let Inst{21} = 0; // W = 0
    let Inst{22} = 0; // D = 0
    let Inst{20} = load;
  }

  def L_OFFSET : ACI<(outs),
      (ins nohash_imm:$cop, nohash_imm:$CRd, addrmode2:$addr),
      !strconcat(opc, "l"), "\tp$cop, cr$CRd, $addr"> {
    let Inst{31-28} = op31_28;
    let Inst{24} = 1; // P = 1
    let Inst{21} = 0; // W = 0
    let Inst{22} = 1; // D = 1
    let Inst{20} = load;
  }

  def L_PRE : ACI<(outs),
      (ins nohash_imm:$cop, nohash_imm:$CRd, addrmode2:$addr),
      !strconcat(opc, "l"), "\tp$cop, cr$CRd, $addr!"> {
    let Inst{31-28} = op31_28;
    let Inst{24} = 1; // P = 1
    let Inst{21} = 1; // W = 1
    let Inst{22} = 1; // D = 1
    let Inst{20} = load;
  }

  def L_POST : ACI<(outs),
      (ins nohash_imm:$cop, nohash_imm:$CRd, GPR:$base, am2offset:$offset),
      !strconcat(opc, "l"), "\tp$cop, cr$CRd, [$base], $offset"> {
    let Inst{31-28} = op31_28;
    let Inst{24} = 0; // P = 0
    let Inst{21} = 1; // W = 1
    let Inst{22} = 1; // D = 1
    let Inst{20} = load;
  }