diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp index e7399fe6f00d..49547d319ebd 100644 --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -1622,6 +1622,28 @@ SDValue SystemZTargetLowering::lowerBR_CC(SDValue Op, SelectionDAG &DAG) const { DAG.getConstant(C.CCMask, MVT::i32), Dest, Glue); } +// Return true if Pos is CmpOp and Neg is the negative of CmpOp, +// allowing Pos and Neg to be wider than CmpOp. +static bool isAbsolute(SDValue CmpOp, SDValue Pos, SDValue Neg) { + return (Neg.getOpcode() == ISD::SUB && + Neg.getOperand(0).getOpcode() == ISD::Constant && + cast(Neg.getOperand(0))->getZExtValue() == 0 && + Neg.getOperand(1) == Pos && + (Pos == CmpOp || + (Pos.getOpcode() == ISD::SIGN_EXTEND && + Pos.getOperand(0) == CmpOp))); +} + +// Return the absolute or negative absolute of Op; IsNegative decides which. +static SDValue getAbsolute(SelectionDAG &DAG, SDLoc DL, SDValue Op, + bool IsNegative) { + Op = DAG.getNode(SystemZISD::IABS, DL, Op.getValueType(), Op); + if (IsNegative) + Op = DAG.getNode(ISD::SUB, DL, Op.getValueType(), + DAG.getConstant(0, Op.getValueType()), Op); + return Op; +} + SDValue SystemZTargetLowering::lowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { SDValue CmpOp0 = Op.getOperand(0); @@ -1632,6 +1654,21 @@ SDValue SystemZTargetLowering::lowerSELECT_CC(SDValue Op, SDLoc DL(Op); Comparison C(getCmp(DAG, CmpOp0, CmpOp1, CC)); + + // Check for absolute and negative-absolute selections, including those + // where the comparison value is sign-extended (for LPGFR and LNGFR). + // This check supplements the one in DAGCombiner. + if (C.Opcode == SystemZISD::ICMP && + C.CCMask != SystemZ::CCMASK_CMP_EQ && + C.CCMask != SystemZ::CCMASK_CMP_NE && + C.Op1.getOpcode() == ISD::Constant && + cast(C.Op1)->getZExtValue() == 0) { + if (isAbsolute(C.Op0, TrueOp, FalseOp)) + return getAbsolute(DAG, DL, TrueOp, C.CCMask & SystemZ::CCMASK_CMP_LT); + if (isAbsolute(C.Op0, FalseOp, TrueOp)) + return getAbsolute(DAG, DL, FalseOp, C.CCMask & SystemZ::CCMASK_CMP_GT); + } + SDValue Glue = emitCmp(DAG, DL, C); // Special case for handling -1/0 results. The shifts we use here @@ -2324,6 +2361,7 @@ const char *SystemZTargetLowering::getTargetNodeName(unsigned Opcode) const { OPCODE(SIBCALL); OPCODE(PCREL_WRAPPER); OPCODE(PCREL_OFFSET); + OPCODE(IABS); OPCODE(ICMP); OPCODE(FCMP); OPCODE(TM); diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h b/llvm/lib/Target/SystemZ/SystemZISelLowering.h index 4cbb30da8b57..01f09c37372c 100644 --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h +++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h @@ -45,6 +45,9 @@ namespace SystemZISD { // as a register base. PCREL_OFFSET, + // Integer absolute. + IABS, + // Integer comparisons. There are three operands: the two values // to compare, and an integer of type SystemZICMP. ICMP, diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td index 00cb7d2c362a..587b170b5ac7 100644 --- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td +++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td @@ -595,22 +595,28 @@ let neverHasSideEffects = 1, isAsCheapAsAMove = 1, isMoveImm = 1, let Defs = [CC] in { let CCValues = 0xF, CompareZeroCCMask = 0x8 in { - def LPR : UnaryRR <"lp", 0x10, z_iabs32, GR32, GR32>; - def LPGR : UnaryRRE<"lpg", 0xB900, z_iabs64, GR64, GR64>; + def LPR : UnaryRR <"lp", 0x10, z_iabs, GR32, GR32>; + def LPGR : UnaryRRE<"lpg", 0xB900, z_iabs, GR64, GR64>; } let CCValues = 0xE, CompareZeroCCMask = 0xE in def LPGFR : UnaryRRE<"lpgf", 0xB910, null_frag, GR64, GR32>; } +def : Pat<(z_iabs32 GR32:$src), (LPR GR32:$src)>; +def : Pat<(z_iabs64 GR64:$src), (LPGR GR64:$src)>; +defm : SXU; defm : SXU; let Defs = [CC] in { let CCValues = 0xF, CompareZeroCCMask = 0x8 in { - def LNR : UnaryRR <"ln", 0x11, z_inegabs32, GR32, GR32>; - def LNGR : UnaryRRE<"lng", 0xB901, z_inegabs64, GR64, GR64>; + def LNR : UnaryRR <"ln", 0x11, z_inegabs, GR32, GR32>; + def LNGR : UnaryRRE<"lng", 0xB901, z_inegabs, GR64, GR64>; } let CCValues = 0xE, CompareZeroCCMask = 0xE in def LNGFR : UnaryRRE<"lngf", 0xB911, null_frag, GR64, GR32>; } +def : Pat<(z_inegabs32 GR32:$src), (LNR GR32:$src)>; +def : Pat<(z_inegabs64 GR64:$src), (LNGR GR64:$src)>; +defm : SXU; defm : SXU; let Defs = [CC] in { diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td index 771fbc32f3cb..bffe1a561876 100644 --- a/llvm/lib/Target/SystemZ/SystemZOperators.td +++ b/llvm/lib/Target/SystemZ/SystemZOperators.td @@ -103,6 +103,7 @@ def z_sibcall : SDNode<"SystemZISD::SIBCALL", SDT_ZCall, def z_pcrel_wrapper : SDNode<"SystemZISD::PCREL_WRAPPER", SDT_ZWrapPtr, []>; def z_pcrel_offset : SDNode<"SystemZISD::PCREL_OFFSET", SDT_ZWrapOffset, []>; +def z_iabs : SDNode<"SystemZISD::IABS", SDTIntUnaryOp, []>; def z_icmp : SDNode<"SystemZISD::ICMP", SDT_ZICmp, [SDNPOutGlue]>; def z_fcmp : SDNode<"SystemZISD::FCMP", SDT_ZCmp, [SDNPOutGlue]>; def z_tm : SDNode<"SystemZISD::TM", SDT_ZICmp, [SDNPOutGlue]>; @@ -349,6 +350,9 @@ def or_as_revinserti8 : PatFrag<(ops node:$src1, node:$src2), APInt::getLowBitsSet(BitWidth, 8)); }]>; +// Negative integer absolute. +def z_inegabs : PatFrag<(ops node:$src), (ineg (z_iabs node:$src))>; + // Integer absolute, matching the canonical form generated by DAGCombiner. def z_iabs32 : PatFrag<(ops node:$src), (xor (add node:$src, (sra node:$src, (i32 31))), diff --git a/llvm/test/CodeGen/SystemZ/int-abs-01.ll b/llvm/test/CodeGen/SystemZ/int-abs-01.ll index 40fb61192c6e..053c347c0b75 100644 --- a/llvm/test/CodeGen/SystemZ/int-abs-01.ll +++ b/llvm/test/CodeGen/SystemZ/int-abs-01.ll @@ -81,3 +81,67 @@ define i64 @f7(i64 %val) { %res = select i1 %cmp, i64 %neg, i64 %val ret i64 %res } + +; Test another form of f6, which is that produced by InstCombine. +define i64 @f8(i64 %val) { +; CHECK-LABEL: f8: +; CHECK: lpgfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp slt i64 %shl, 0 + %abs = select i1 %cmp, i64 %neg, i64 %ashr + ret i64 %abs +} + +; Try again with sle rather than slt. +define i64 @f9(i64 %val) { +; CHECK-LABEL: f9: +; CHECK: lpgfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp sle i64 %shl, 0 + %abs = select i1 %cmp, i64 %neg, i64 %ashr + ret i64 %abs +} + +; Repeat f8 with the operands reversed. +define i64 @f10(i64 %val) { +; CHECK-LABEL: f10: +; CHECK: lpgfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp sgt i64 %shl, 0 + %abs = select i1 %cmp, i64 %ashr, i64 %neg + ret i64 %abs +} + +; Try again with sge rather than sgt. +define i64 @f11(i64 %val) { +; CHECK-LABEL: f11: +; CHECK: lpgfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp sge i64 %shl, 0 + %abs = select i1 %cmp, i64 %ashr, i64 %neg + ret i64 %abs +} + +; Repeat f5 with the comparison on the unextended value. +define i64 @f12(i32 %val) { +; CHECK-LABEL: f12: +; CHECK: lpgfr %r2, %r2 +; CHECK: br %r14 + %ext = sext i32 %val to i64 + %cmp = icmp slt i32 %val, 0 + %neg = sub i64 0, %ext + %abs = select i1 %cmp, i64 %neg, i64 %ext + ret i64 %abs +} diff --git a/llvm/test/CodeGen/SystemZ/int-neg-02.ll b/llvm/test/CodeGen/SystemZ/int-neg-02.ll index e26194c162d4..7f3f6375129a 100644 --- a/llvm/test/CodeGen/SystemZ/int-neg-02.ll +++ b/llvm/test/CodeGen/SystemZ/int-neg-02.ll @@ -89,3 +89,136 @@ define i64 @f7(i64 %val) { %res = sub i64 0, %abs ret i64 %res } + +; Test another form of f6, which is that produced by InstCombine. +define i64 @f8(i64 %val) { +; CHECK-LABEL: f8: +; CHECK: lngfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp slt i64 %shl, 0 + %abs = select i1 %cmp, i64 %neg, i64 %ashr + %res = sub i64 0, %abs + ret i64 %res +} + +; Try again with sle rather than slt. +define i64 @f9(i64 %val) { +; CHECK-LABEL: f9: +; CHECK: lngfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp sle i64 %shl, 0 + %abs = select i1 %cmp, i64 %neg, i64 %ashr + %res = sub i64 0, %abs + ret i64 %res +} + +; Repeat f8 with the operands reversed. +define i64 @f10(i64 %val) { +; CHECK-LABEL: f10: +; CHECK: lngfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp sgt i64 %shl, 0 + %abs = select i1 %cmp, i64 %ashr, i64 %neg + %res = sub i64 0, %abs + ret i64 %res +} + +; Try again with sge rather than sgt. +define i64 @f11(i64 %val) { +; CHECK-LABEL: f11: +; CHECK: lngfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp sge i64 %shl, 0 + %abs = select i1 %cmp, i64 %ashr, i64 %neg + %res = sub i64 0, %abs + ret i64 %res +} + +; Repeat f8 with the negation coming from swapped operands. +define i64 @f12(i64 %val) { +; CHECK-LABEL: f12: +; CHECK: lngfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp slt i64 %shl, 0 + %negabs = select i1 %cmp, i64 %ashr, i64 %neg + ret i64 %negabs +} + +; Likewise f9. +define i64 @f13(i64 %val) { +; CHECK-LABEL: f13: +; CHECK: lngfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp sle i64 %shl, 0 + %negabs = select i1 %cmp, i64 %ashr, i64 %neg + ret i64 %negabs +} + +; Likewise f10. +define i64 @f14(i64 %val) { +; CHECK-LABEL: f14: +; CHECK: lngfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp sgt i64 %shl, 0 + %negabs = select i1 %cmp, i64 %neg, i64 %ashr + ret i64 %negabs +} + +; Likewise f11. +define i64 @f15(i64 %val) { +; CHECK-LABEL: f15: +; CHECK: lngfr %r2, %r2 +; CHECK: br %r14 + %shl = shl i64 %val, 32 + %ashr = ashr i64 %shl, 32 + %neg = sub i64 0, %ashr + %cmp = icmp sge i64 %shl, 0 + %negabs = select i1 %cmp, i64 %neg, i64 %ashr + ret i64 %negabs +} + +; Repeat f5 with the comparison on the unextended value. +define i64 @f16(i32 %val) { +; CHECK-LABEL: f16: +; CHECK: lngfr %r2, %r2 +; CHECK: br %r14 + %ext = sext i32 %val to i64 + %cmp = icmp slt i32 %val, 0 + %neg = sub i64 0, %ext + %abs = select i1 %cmp, i64 %neg, i64 %ext + %res = sub i64 0, %abs + ret i64 %res +} + +; And again with the negation coming from swapped operands. +define i64 @f17(i32 %val) { +; CHECK-LABEL: f17: +; CHECK: lngfr %r2, %r2 +; CHECK: br %r14 + %ext = sext i32 %val to i64 + %cmp = icmp slt i32 %val, 0 + %neg = sub i64 0, %ext + %abs = select i1 %cmp, i64 %ext, i64 %neg + ret i64 %abs +}