mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-25 12:49:50 +00:00
Re-enable the CMN instruction.
We turned off the CMN instruction because it had semantics which we weren't getting correct. If we are comparing with an immediate, then it's okay to use the CMN instruction. <rdar://problem/7569620> git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@158302 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
7a99b467df
commit
ad5c880892
@ -1420,12 +1420,12 @@ bool ARMFastISel::ARMEmitCmp(const Value *Src1Value, const Value *Src2Value,
|
||||
if (!UseImm)
|
||||
CmpOpc = ARM::t2CMPrr;
|
||||
else
|
||||
CmpOpc = isNegativeImm ? ARM::t2CMNzri : ARM::t2CMPri;
|
||||
CmpOpc = isNegativeImm ? ARM::t2CMNri : ARM::t2CMPri;
|
||||
} else {
|
||||
if (!UseImm)
|
||||
CmpOpc = ARM::CMPrr;
|
||||
else
|
||||
CmpOpc = isNegativeImm ? ARM::CMNzri : ARM::CMPri;
|
||||
CmpOpc = isNegativeImm ? ARM::CMNri : ARM::CMPri;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -895,6 +895,7 @@ const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
||||
case ARMISD::RET_FLAG: return "ARMISD::RET_FLAG";
|
||||
case ARMISD::PIC_ADD: return "ARMISD::PIC_ADD";
|
||||
case ARMISD::CMP: return "ARMISD::CMP";
|
||||
case ARMISD::CMN: return "ARMISD::CMN";
|
||||
case ARMISD::CMPZ: return "ARMISD::CMPZ";
|
||||
case ARMISD::CMPFP: return "ARMISD::CMPFP";
|
||||
case ARMISD::CMPFPw0: return "ARMISD::CMPFPw0";
|
||||
|
@ -56,6 +56,7 @@ namespace llvm {
|
||||
PIC_ADD, // Add with a PC operand and a PIC label.
|
||||
|
||||
CMP, // ARM compare instructions.
|
||||
CMN, // ARM CMN instructions.
|
||||
CMPZ, // ARM compare that sets only Z flag.
|
||||
CMPFP, // ARM VFP compare instruction, sets FPSCR.
|
||||
CMPFPw0, // ARM VFP compare against zero instruction, sets FPSCR.
|
||||
|
@ -128,6 +128,9 @@ def ARMBcci64 : SDNode<"ARMISD::BCC_i64", SDT_ARMBCC_i64,
|
||||
def ARMcmp : SDNode<"ARMISD::CMP", SDT_ARMCmp,
|
||||
[SDNPOutGlue]>;
|
||||
|
||||
def ARMcmn : SDNode<"ARMISD::CMN", SDT_ARMCmp,
|
||||
[SDNPOutGlue]>;
|
||||
|
||||
def ARMcmpZ : SDNode<"ARMISD::CMPZ", SDT_ARMCmp,
|
||||
[SDNPOutGlue, SDNPCommutative]>;
|
||||
|
||||
@ -3842,49 +3845,85 @@ def : ARMPat<(ARMcmpZ GPR:$src, so_reg_imm:$rhs),
|
||||
def : ARMPat<(ARMcmpZ GPR:$src, so_reg_reg:$rhs),
|
||||
(CMPrsr GPR:$src, so_reg_reg:$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))>>;
|
||||
// CMN register-integer
|
||||
let isCompare = 1, Defs = [CPSR] in {
|
||||
def CMNri : AI1<0b1011, (outs), (ins GPR:$Rn, so_imm:$imm), DPFrm, IIC_iCMPi,
|
||||
"cmn", "\t$Rn, $imm",
|
||||
[(ARMcmn GPR:$Rn, so_imm:$imm)]> {
|
||||
bits<4> Rn;
|
||||
bits<12> imm;
|
||||
let Inst{25} = 1;
|
||||
let Inst{20} = 1;
|
||||
let Inst{19-16} = Rn;
|
||||
let Inst{15-12} = 0b0000;
|
||||
let Inst{11-0} = imm;
|
||||
|
||||
let Unpredictable{15-12} = 0b1111;
|
||||
}
|
||||
|
||||
// CMN register-register/shift
|
||||
def CMNzrr : AI1<0b1011, (outs), (ins GPR:$Rn, GPR:$Rm), DPFrm, IIC_iCMPr,
|
||||
"cmn", "\t$Rn, $Rm",
|
||||
[(BinOpFrag<(ARMcmpZ node:$LHS,(ineg node:$RHS))>
|
||||
GPR:$Rn, GPR:$Rm)]> {
|
||||
bits<4> Rn;
|
||||
bits<4> Rm;
|
||||
let isCommutable = 1;
|
||||
let Inst{25} = 0;
|
||||
let Inst{20} = 1;
|
||||
let Inst{19-16} = Rn;
|
||||
let Inst{15-12} = 0b0000;
|
||||
let Inst{11-4} = 0b00000000;
|
||||
let Inst{3-0} = Rm;
|
||||
|
||||
let Unpredictable{15-12} = 0b1111;
|
||||
}
|
||||
|
||||
def CMNzrsi : AI1<0b1011, (outs),
|
||||
(ins GPR:$Rn, so_reg_imm:$shift), DPSoRegImmFrm, IIC_iCMPsr,
|
||||
"cmn", "\t$Rn, $shift",
|
||||
[(BinOpFrag<(ARMcmpZ node:$LHS,(ineg node:$RHS))>
|
||||
GPR:$Rn, so_reg_imm:$shift)]> {
|
||||
bits<4> Rn;
|
||||
bits<12> shift;
|
||||
let Inst{25} = 0;
|
||||
let Inst{20} = 1;
|
||||
let Inst{19-16} = Rn;
|
||||
let Inst{15-12} = 0b0000;
|
||||
let Inst{11-5} = shift{11-5};
|
||||
let Inst{4} = 0;
|
||||
let Inst{3-0} = shift{3-0};
|
||||
|
||||
let Unpredictable{15-12} = 0b1111;
|
||||
}
|
||||
|
||||
def CMNzrsr : AI1<0b1011, (outs),
|
||||
(ins GPRnopc:$Rn, so_reg_reg:$shift), DPSoRegRegFrm, IIC_iCMPsr,
|
||||
"cmn", "\t$Rn, $shift",
|
||||
[(BinOpFrag<(ARMcmpZ node:$LHS,(ineg node:$RHS))>
|
||||
GPRnopc:$Rn, so_reg_reg:$shift)]> {
|
||||
bits<4> Rn;
|
||||
bits<12> shift;
|
||||
let Inst{25} = 0;
|
||||
let Inst{20} = 1;
|
||||
let Inst{19-16} = Rn;
|
||||
let Inst{15-12} = 0b0000;
|
||||
let Inst{11-8} = shift{11-8};
|
||||
let Inst{7} = 0;
|
||||
let Inst{6-5} = shift{6-5};
|
||||
let Inst{4} = 1;
|
||||
let Inst{3-0} = shift{3-0};
|
||||
|
||||
let Unpredictable{15-12} = 0b1111;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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),
|
||||
(CMNri GPR:$src, so_imm_neg:$imm)>;
|
||||
|
||||
// Note that TST/TEQ don't set all the same flags that CMP does!
|
||||
defm TST : AI1_cmp_irs<0b1000, "tst",
|
||||
@ -3894,16 +3933,6 @@ defm TEQ : AI1_cmp_irs<0b1001, "teq",
|
||||
IIC_iTSTi, IIC_iTSTr, IIC_iTSTsr,
|
||||
BinOpFrag<(ARMcmpZ (xor_su node:$LHS, node:$RHS), 0)>, 1>;
|
||||
|
||||
defm CMNz : AI1_cmp_irs<0b1011, "cmn",
|
||||
IIC_iCMPi, IIC_iCMPr, IIC_iCMPsr,
|
||||
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),
|
||||
(CMNzri GPR:$src, so_imm_neg:$imm)>;
|
||||
|
||||
// Pseudo i64 compares for some floating point compares.
|
||||
let usesCustomInserter = 1, isBranch = 1, isTerminator = 1,
|
||||
Defs = [CPSR] in {
|
||||
@ -5052,7 +5081,7 @@ def : ARMInstAlias<"add${s}${p} $Rd, $imm",
|
||||
(SUBri GPR:$Rd, GPR:$Rd, so_imm_neg:$imm, pred:$p, cc_out:$s)>;
|
||||
// Same for CMP <--> CMN via so_imm_neg
|
||||
def : ARMInstAlias<"cmp${p} $Rd, $imm",
|
||||
(CMNzri rGPR:$Rd, so_imm_neg:$imm, pred:$p)>;
|
||||
(CMNri rGPR:$Rd, so_imm_neg:$imm, pred:$p)>;
|
||||
def : ARMInstAlias<"cmn${p} $Rd, $imm",
|
||||
(CMPri rGPR:$Rd, so_imm_neg:$imm, pred:$p)>;
|
||||
|
||||
|
@ -2849,20 +2849,64 @@ def : T2Pat<(ARMcmpZ GPRnopc:$lhs, rGPR:$rhs),
|
||||
def : T2Pat<(ARMcmpZ GPRnopc:$lhs, t2_so_reg:$rhs),
|
||||
(t2CMPrs GPRnopc:$lhs, t2_so_reg:$rhs)>;
|
||||
|
||||
//FIXME: Disable CMN, as CCodes are backwards from compare expectations
|
||||
// Compare-to-zero still works out, just not the relationals
|
||||
//defm t2CMN : T2I_cmp_irs<0b1000, "cmn",
|
||||
// BinOpFrag<(ARMcmp node:$LHS,(ineg node:$RHS))>>;
|
||||
defm t2CMNz : T2I_cmp_irs<0b1000, "cmn",
|
||||
IIC_iCMPi, IIC_iCMPr, IIC_iCMPsi,
|
||||
BinOpFrag<(ARMcmpZ node:$LHS,(ineg node:$RHS))>,
|
||||
"t2CMNz">;
|
||||
let isCompare = 1, Defs = [CPSR] in {
|
||||
// shifted imm
|
||||
def t2CMNri : T2OneRegCmpImm<
|
||||
(outs), (ins GPRnopc:$Rn, t2_so_imm:$imm), IIC_iCMPi,
|
||||
"cmn", ".w\t$Rn, $imm",
|
||||
[(ARMcmn GPRnopc:$Rn, (ineg t2_so_imm:$imm))]> {
|
||||
let Inst{31-27} = 0b11110;
|
||||
let Inst{25} = 0;
|
||||
let Inst{24-21} = 0b1000;
|
||||
let Inst{20} = 1; // The S bit.
|
||||
let Inst{15} = 0;
|
||||
let Inst{11-8} = 0b1111; // Rd
|
||||
}
|
||||
// register
|
||||
def t2CMNzrr : T2TwoRegCmp<
|
||||
(outs), (ins GPRnopc:$Rn, rGPR:$Rm), IIC_iCMPr,
|
||||
"cmn", ".w\t$Rn, $Rm",
|
||||
[(BinOpFrag<(ARMcmpZ node:$LHS,(ineg node:$RHS))>
|
||||
GPRnopc:$Rn, rGPR:$Rm)]> {
|
||||
let Inst{31-27} = 0b11101;
|
||||
let Inst{26-25} = 0b01;
|
||||
let Inst{24-21} = 0b1000;
|
||||
let Inst{20} = 1; // The S bit.
|
||||
let Inst{14-12} = 0b000; // imm3
|
||||
let Inst{11-8} = 0b1111; // Rd
|
||||
let Inst{7-6} = 0b00; // imm2
|
||||
let Inst{5-4} = 0b00; // type
|
||||
}
|
||||
// shifted register
|
||||
def t2CMNzrs : T2OneRegCmpShiftedReg<
|
||||
(outs), (ins GPRnopc:$Rn, t2_so_reg:$ShiftedRm), IIC_iCMPsi,
|
||||
"cmn", ".w\t$Rn, $ShiftedRm",
|
||||
[(BinOpFrag<(ARMcmpZ node:$LHS,(ineg node:$RHS))>
|
||||
GPRnopc:$Rn, t2_so_reg:$ShiftedRm)]> {
|
||||
let Inst{31-27} = 0b11101;
|
||||
let Inst{26-25} = 0b01;
|
||||
let Inst{24-21} = 0b1000;
|
||||
let Inst{20} = 1; // The S bit.
|
||||
let Inst{11-8} = 0b1111; // Rd
|
||||
}
|
||||
}
|
||||
|
||||
//def : T2Pat<(ARMcmp GPR:$src, t2_so_imm_neg:$imm),
|
||||
// (t2CMNri GPR:$src, t2_so_imm_neg:$imm)>;
|
||||
// Assembler aliases w/o the ".w" suffix.
|
||||
// No alias here for 'rr' version as not all instantiations of this multiclass
|
||||
// want one (CMP in particular, does not).
|
||||
def : t2InstAlias<!strconcat("cmn", "${p}", " $Rn, $imm"),
|
||||
(!cast<Instruction>(!strconcat("t2CMN", "ri")) GPRnopc:$Rn,
|
||||
t2_so_imm:$imm, pred:$p)>;
|
||||
def : t2InstAlias<!strconcat("cmn", "${p}", " $Rn, $shift"),
|
||||
(!cast<Instruction>(!strconcat("t2CMNz", "rs")) GPRnopc:$Rn,
|
||||
t2_so_reg:$shift,
|
||||
pred:$p)>;
|
||||
|
||||
def : T2Pat<(ARMcmp GPR:$src, t2_so_imm_neg:$imm),
|
||||
(t2CMNri GPR:$src, t2_so_imm_neg:$imm)>;
|
||||
|
||||
def : T2Pat<(ARMcmpZ GPRnopc:$src, t2_so_imm_neg:$imm),
|
||||
(t2CMNzri GPRnopc:$src, t2_so_imm_neg:$imm)>;
|
||||
(t2CMNri GPRnopc:$src, t2_so_imm_neg:$imm)>;
|
||||
|
||||
defm t2TST : T2I_cmp_irs<0b0000, "tst",
|
||||
IIC_iTSTi, IIC_iTSTr, IIC_iTSTsi,
|
||||
@ -4224,7 +4268,7 @@ def : t2InstAlias<"add${s}${p} $Rd, $imm",
|
||||
pred:$p, cc_out:$s)>;
|
||||
// Same for CMP <--> CMN via t2_so_imm_neg
|
||||
def : t2InstAlias<"cmp${p} $Rd, $imm",
|
||||
(t2CMNzri rGPR:$Rd, t2_so_imm_neg:$imm, pred:$p)>;
|
||||
(t2CMNri rGPR:$Rd, t2_so_imm_neg:$imm, pred:$p)>;
|
||||
def : t2InstAlias<"cmn${p} $Rd, $imm",
|
||||
(t2CMPri rGPR:$Rd, t2_so_imm_neg:$imm, pred:$p)>;
|
||||
|
||||
|
22
test/CodeGen/ARM/cmn.ll
Normal file
22
test/CodeGen/ARM/cmn.ll
Normal file
@ -0,0 +1,22 @@
|
||||
; RUN: llc < %s -mtriple thumbv7-apple-ios | FileCheck %s
|
||||
; <rdar://problem/7569620>
|
||||
|
||||
define i32 @compare_i_gt(i32 %a) {
|
||||
entry:
|
||||
; CHECK: compare_i_gt
|
||||
; CHECK-NOT: mvn
|
||||
; CHECK: cmn
|
||||
%cmp = icmp sgt i32 %a, -78
|
||||
%. = zext i1 %cmp to i32
|
||||
ret i32 %.
|
||||
}
|
||||
|
||||
define i32 @compare_r_eq(i32 %a, i32 %b) {
|
||||
entry:
|
||||
; CHECK: compare_r_eq
|
||||
; CHECK: cmn
|
||||
%sub = sub nsw i32 0, %b
|
||||
%cmp = icmp eq i32 %a, %sub
|
||||
%. = zext i1 %cmp to i32
|
||||
ret i32 %.
|
||||
}
|
Loading…
Reference in New Issue
Block a user