Index: include/llvm/Target/TargetSelectionDAG.td =================================================================== --- include/llvm/Target/TargetSelectionDAG.td +++ include/llvm/Target/TargetSelectionDAG.td @@ -116,6 +116,9 @@ def SDTIntShiftOp : SDTypeProfile<1, 2, [ // shl, sra, srl SDTCisSameAs<0, 1>, SDTCisInt<0>, SDTCisInt<2> ]>; +def SDTIntSatNoShOp : SDTypeProfile<1, 2, [ // ssat with no shift + SDTCisSameAs<0, 1>, SDTCisInt<2> +]>; def SDTIntBinHiLoOp : SDTypeProfile<2, 2, [ // mulhi, mullo, sdivrem, udivrem SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisSameAs<0, 3>,SDTCisInt<0> ]>; Index: lib/Target/ARM/ARMISelLowering.h =================================================================== --- lib/Target/ARM/ARMISelLowering.h +++ lib/Target/ARM/ARMISelLowering.h @@ -60,6 +60,8 @@ CMOV, // ARM conditional move instructions. + SSAT, // Signed saturation + BCC_i64, SRL_FLAG, // V,Flag = srl_flag X -> srl X, 1 + save carry out. Index: lib/Target/ARM/ARMISelLowering.cpp =================================================================== --- lib/Target/ARM/ARMISelLowering.cpp +++ lib/Target/ARM/ARMISelLowering.cpp @@ -1136,6 +1136,8 @@ case ARMISD::CMOV: return "ARMISD::CMOV"; + case ARMISD::SSAT: return "ARMISD::SSAT"; + case ARMISD::SRL_FLAG: return "ARMISD::SRL_FLAG"; case ARMISD::SRA_FLAG: return "ARMISD::SRA_FLAG"; case ARMISD::RRX: return "ARMISD::RRX"; @@ -3735,6 +3737,37 @@ } } +bool isGTorGE(ISD::CondCode CC) { + return CC == ISD::SETGT || CC == ISD::SETGE; +} + +bool isLTorLE(ISD::CondCode CC) { + return CC == ISD::SETLT || CC == ISD::SETLE; +} + +// See if a conditional (LHS CC RHS ? TrueVal : FalseVal) is lower-saturating. +// All of these conditions (and their <= and >= counterparts) will do: +// x < k ? k : x +// x > k ? x : k +// k < x ? x : k +// k > x ? k : x +bool isLowerSaturate(SDValue LHS, SDValue RHS, SDValue TrueVal, SDValue FalseVal, + ISD::CondCode CC, SDValue K) { + return + (isGTorGE(CC) && ((K == LHS && K == TrueVal) || (K == RHS && K == FalseVal))) + || + (isLTorLE(CC) && ((K == RHS && K == TrueVal) || (K == LHS && K == FalseVal))); +} + +// Similar to isLowerSaturate(), but checks for upper-saturating conditions. +bool isUpperSaturate(SDValue LHS, SDValue RHS, SDValue TrueVal, SDValue FalseVal, + ISD::CondCode CC, SDValue K) { + return + (isGTorGE(CC) && ((K == RHS && K == TrueVal) || (K == LHS && K == FalseVal))) + || + (isLTorLE(CC) && ((K == LHS && K == TrueVal) || (K == RHS && K == FalseVal))); +} + SDValue ARMTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { EVT VT = Op.getValueType(); SDValue LHS = Op.getOperand(0); @@ -3756,6 +3789,69 @@ } } + // Try to lower (select_cc k k (select_cc ~k ~k x)) into (SSAT e x), + // where 2^e = k + SDValue Op2 = isa(TrueVal) ? FalseVal : TrueVal; + if (Op2.getOpcode() == ISD::SELECT_CC) { + + SDValue LHS2 = Op2.getOperand(0); + SDValue RHS2 = Op2.getOperand(1); + ISD::CondCode CC2 = cast(Op2.getOperand(4))->get(); + SDValue TrueVal2 = Op2.getOperand(2); + SDValue FalseVal2 = Op2.getOperand(3); + + // Find out which are the constants and which are the registers + // in each conditional + SDValue *K1 = isa(LHS) + ? &LHS + : isa(RHS) ? &RHS : NULL; + SDValue *K2 = isa(LHS2) + ? &LHS2 + : isa(RHS2) ? &RHS2 : NULL; + SDValue K2Tmp = isa(TrueVal2) ? TrueVal2 : FalseVal2; + SDValue R1 = (K1 && *K1 == LHS) ? RHS : LHS; + SDValue R2 = (K2 && *K2 == LHS2) ? RHS2 : LHS2; + SDValue R2Tmp = (K2Tmp == TrueVal2) ? FalseVal2 : TrueVal2; + + // Check that the registers and the constants have the correct values + // in both conditionals + if (K1 && K2 && *K1 != Op2 && *K2 == K2Tmp && R1 == R2 && R2 == R2Tmp) { + + // Figure out which conditional is saturating the lower/upper bound. + SDValue *LowerCheckOp = + isLowerSaturate(LHS, RHS, TrueVal, FalseVal, CC, *K1) + ? &Op + : isLowerSaturate(LHS2, RHS2, TrueVal2, FalseVal2, CC2, *K2) + ? &Op2 + : NULL; + SDValue *UpperCheckOp = + isUpperSaturate(LHS, RHS, TrueVal, FalseVal, CC, *K1) + ? &Op + : isUpperSaturate(LHS2, RHS2, TrueVal2, FalseVal2, CC2, *K2) + ? &Op2 + : NULL; + + if (UpperCheckOp && LowerCheckOp && LowerCheckOp != UpperCheckOp) { + + // Check that the constant in the lower-bound check is + // the opposite of the constant in the upper-bound check + // in 1's complement. + uint64_t Val1 = cast(*K1)->getSExtValue(); + uint64_t Val2 = cast(*K2)->getSExtValue(); + uint64_t PosVal = Val1 < Val2 ? Val1 : Val2; + + if (((Val1 < Val2 && UpperCheckOp == &Op) || + (Val1 > Val2 && (UpperCheckOp == &Op2))) && + Val1 == ~Val2 && isPowerOf2_64(PosVal + 1)) { + + SDValue SatImmValue = + DAG.getConstant(countTrailingOnes(PosVal), dl, VT); + return DAG.getNode(ARMISD::SSAT, dl, VT, R1, SatImmValue); + } + } + } + } + if (LHS.getValueType() == MVT::i32) { // Try to generate VSEL on ARMv8. // The VSEL instruction can't use all the usual ARM condition Index: lib/Target/ARM/ARMInstrInfo.td =================================================================== --- lib/Target/ARM/ARMInstrInfo.td +++ lib/Target/ARM/ARMInstrInfo.td @@ -128,6 +128,8 @@ def ARMcmov : SDNode<"ARMISD::CMOV", SDT_ARMCMov, [SDNPInGlue]>; +def ARMssatnoshift : SDNode<"ARMISD::SSAT", SDTIntSatNoShOp, []>; + def ARMbrcond : SDNode<"ARMISD::BRCOND", SDT_ARMBrcond, [SDNPHasChain, SDNPInGlue, SDNPOutGlue]>; @@ -3715,6 +3717,8 @@ (SSAT imm1_32:$pos, GPRnopc:$a, 0)>; def : ARMV6Pat<(int_arm_usat GPRnopc:$a, imm0_31:$pos), (USAT imm0_31:$pos, GPRnopc:$a, 0)>; +def : ARMPat<(ARMssatnoshift GPRnopc:$Rn, imm0_31:$imm), + (SSAT imm0_31:$imm, GPRnopc:$Rn, 0)>; //===----------------------------------------------------------------------===// // Bitwise Instructions. Index: lib/Target/ARM/ARMInstrThumb2.td =================================================================== --- lib/Target/ARM/ARMInstrThumb2.td +++ lib/Target/ARM/ARMInstrThumb2.td @@ -2287,6 +2287,8 @@ def : T2Pat<(int_arm_ssat GPR:$a, imm1_32:$pos), (t2SSAT imm1_32:$pos, GPR:$a, 0)>; def : T2Pat<(int_arm_usat GPR:$a, imm0_31:$pos), (t2USAT imm0_31:$pos, GPR:$a, 0)>; +def : T2Pat<(ARMssatnoshift GPRnopc:$Rn, imm0_31:$imm), + (t2SSAT imm0_31:$imm, GPRnopc:$Rn, 0)>; //===----------------------------------------------------------------------===// // Shift and rotate Instructions. Index: test/CodeGen/ARM/ssat.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/ssat.ll @@ -0,0 +1,241 @@ + +; RUN: llc -mtriple=arm-eabi %s -o - | FileCheck %s + +; Check for several conditions that should result in SSAT. +; For example, sat24_c1_1 is equivalent to +; x < -k ? -k : (x > k ? k : x) in C. All patterns that bound x +; to the interval [-k, k] where k is a power of 2 can be +; transformed into SSAT. At the end there are some tests +; checking that conditionals are not transformed if they don't +; match the correct pattern. + +define i32 @sat24_c1_1(i32 %x) #0 { +; CHECK-LABEL: sat24_c1_1: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp slt i32 %x, -8388608 + %cmp1 = icmp sgt i32 %x, 8388607 + %cond = select i1 %cmp1, i32 8388607, i32 %x + %cond5 = select i1 %cmp, i32 -8388608, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c1_2(i32 %x) #0 { +; CHECK-LABEL: sat24_c1_2: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, -8388608 + %cmp1 = icmp sgt i32 %x, 8388607 + %cond = select i1 %cmp1, i32 8388607, i32 %x + %cond5 = select i1 %cmp, i32 %cond, i32 -8388608 + ret i32 %cond5 +} + +define i32 @sat24_c1_3(i32 %x) #0 { +; CHECK-LABEL: sat24_c1_3: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, -8388608 + %cmp1 = icmp sgt i32 %x, 8388607 + %cond = select i1 %cmp1, i32 8388607, i32 %x + %cond5 = select i1 %cmp, i32 %cond, i32 -8388608 + ret i32 %cond5 +} + +define i32 @sat24_c1_4(i32 %x) #0 { +; CHECK-LABEL: sat24_c1_4: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp slt i32 %x, -8388608 + %cmp1 = icmp sgt i32 %x, 8388607 + %cond = select i1 %cmp1, i32 8388607, i32 %x + %cond5 = select i1 %cmp, i32 -8388608, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c1_5(i32 %x) #0 { +; CHECK-LABEL: sat24_c1_5: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, 8388607 + %cmp1 = icmp slt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 -8388608, i32 %x + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c1_6(i32 %x) #0 { +; CHECK-LABEL: sat24_c1_6: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp slt i32 %x, 8388607 + %cmp1 = icmp slt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 -8388608, i32 %x + %cond5 = select i1 %cmp, i32 %cond, i32 8388607 + ret i32 %cond5 +} + +define i32 @sat24_c1_7(i32 %x) #0 { +; CHECK-LABEL: sat24_c1_7: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp slt i32 %x, 8388607 + %cmp1 = icmp slt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 -8388608, i32 %x + %cond5 = select i1 %cmp, i32 %cond, i32 8388607 + ret i32 %cond5 +} + +define i32 @sat24_c1_8(i32 %x) #0 { +; CHECK-LABEL: sat24_c1_8: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, 8388607 + %cmp1 = icmp slt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 -8388608, i32 %x + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c2_1(i32 %x) #0 { +; CHECK-LABEL: sat24_c2_1: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp slt i32 %x, -8388608 + %cmp1 = icmp sgt i32 %x, 8388607 + %cond = select i1 %cmp1, i32 8388607, i32 %x + %cond5 = select i1 %cmp, i32 -8388608, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c2_2(i32 %x) #0 { +; CHECK-LABEL: sat24_c2_2: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp slt i32 %x, -8388608 + %cmp1 = icmp slt i32 %x, 8388607 + %cond = select i1 %cmp1, i32 %x, i32 8388607 + %cond5 = select i1 %cmp, i32 -8388608, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c2_3(i32 %x) #0 { +; CHECK-LABEL: sat24_c2_3: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp slt i32 %x, -8388608 + %cmp1 = icmp slt i32 %x, 8388607 + %cond = select i1 %cmp1, i32 %x, i32 8388607 + %cond5 = select i1 %cmp, i32 -8388608, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c2_4(i32 %x) #0 { +; CHECK-LABEL: sat24_c2_4: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp slt i32 %x, -8388608 + %cmp1 = icmp sgt i32 %x, 8388607 + %cond = select i1 %cmp1, i32 8388607, i32 %x + %cond5 = select i1 %cmp, i32 -8388608, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c2_5(i32 %x) #0 { +; CHECK-LABEL: sat24_c2_5: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, 8388607 + %cmp1 = icmp slt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 -8388608, i32 %x + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c2_6(i32 %x) #0 { +; CHECK-LABEL: sat24_c2_6: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, 8388607 + %cmp1 = icmp sgt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 %x, i32 -8388608 + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c2_7(i32 %x) #0 { +; CHECK-LABEL: sat24_c2_7: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, 8388607 + %cmp1 = icmp sgt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 %x, i32 -8388608 + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c2_8(i32 %x) #0 { +; CHECK-LABEL: sat24_c2_8: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, 8388607 + %cmp1 = icmp slt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 -8388608, i32 %x + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +} + +define i32 @sat24_c2_9(i32 %x) #0 { +; CHECK-LABEL: sat24_c2_9: +; CHECK: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, 8388607 + %cmp1 = icmp sgt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 %x, i32 -8388608 + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +} + +define i32 @no_sat24_1(i32 %x, i32 %y) #0 { +; CHECK-LABEL: no_sat24_1: +; CHECK-NOT: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, 8388607 + %cmp1 = icmp slt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 -8388608, i32 %y + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +} + +define i32 @no_sat24_2(i32 %x, i32 %y) #0 { +; CHECK-LABEL: no_sat24_2: +; CHECK-NOT: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %y, 8388607 + %cmp1 = icmp slt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 -8388608, i32 %x + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +} + +define i32 @no_sat24_3(i32 %x, i32 %y) #0 { +; CHECK-LABEL: no_sat24_3: +; CHECK-NOT: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, 8388607 + %cmp1 = icmp slt i32 %x, -8388608 + %cond = select i1 %cmp1, i32 -8388607, i32 %x + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +} + +define i32 @no_sat24_4(i32 %x) #0 { +; CHECK-LABEL: no_sat24_4: +; CHECK-NOT: ssat r0, #24, r0 +entry: + %cmp = icmp sgt i32 %x, 8388607 + %cmp1 = icmp slt i32 %x, -19088744 + %cond = select i1 %cmp1, i32 -19088744, i32 %x + %cond5 = select i1 %cmp, i32 8388607, i32 %cond + ret i32 %cond5 +}