Index: llvm/trunk/include/llvm/Target/TargetLowering.h =================================================================== --- llvm/trunk/include/llvm/Target/TargetLowering.h +++ llvm/trunk/include/llvm/Target/TargetLowering.h @@ -407,6 +407,15 @@ return false; } + /// Return true if the target has a bitwise and-not operation: + /// X = ~A & B + /// This can be used to simplify select or other instructions. + virtual bool hasAndNot(SDValue X) const { + // If the target has the more complex version of this operation, assume that + // it has this operation too. + return hasAndNotCompare(X); + } + /// \brief Return true if the target wants to use the optimization that /// turns ext(promotableInst1(...(promotableInstN(load)))) into /// promotedInst1(...(promotedInstN(ext(load)))). Index: llvm/trunk/lib/CodeGen/SelectionDAG/DAGCombiner.cpp =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ llvm/trunk/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -14879,17 +14879,31 @@ SDValue DAGCombiner::foldSelectCCToShiftAnd(const SDLoc &DL, SDValue N0, SDValue N1, SDValue N2, SDValue N3, ISD::CondCode CC) { - // Check to see if we can perform the "gzip trick", transforming - // (select_cc setlt X, 0, A, 0) -> (and (sra X, size(X)-1), A) + // If this is a select where the false operand is zero and the compare is a + // check of the sign bit, see if we can perform the "gzip trick": + // select_cc setlt X, 0, A, 0 -> and (sra X, size(X)-1), A + // select_cc setgt X, 0, A, 0 -> and (not (sra X, size(X)-1)), A EVT XType = N0.getValueType(); EVT AType = N2.getValueType(); - if (!isNullConstant(N3) || CC != ISD::SETLT || !XType.bitsGE(AType)) + if (!isNullConstant(N3) || !XType.bitsGE(AType)) return SDValue(); - // (a < 0) ? b : 0 - // (a < 1) ? a : 0 - if (!(isNullConstant(N1) || (isOneConstant(N1) && N0 == N2))) + // If the comparison is testing for a positive value, we have to invert + // the sign bit mask, so only do that transform if the target has a bitwise + // 'and not' instruction (the invert is free). + if (CC == ISD::SETGT && TLI.hasAndNot(N2)) { + // (X > -1) ? A : 0 + // (X > 0) ? X : 0 <-- This is canonical signed max. + if (!(isAllOnesConstant(N1) || (isNullConstant(N1) && N0 == N2))) + return SDValue(); + } else if (CC == ISD::SETLT) { + // (X < 0) ? A : 0 + // (X < 1) ? X : 0 <-- This is un-canonicalized signed min. + if (!(isNullConstant(N1) || (isOneConstant(N1) && N0 == N2))) + return SDValue(); + } else { return SDValue(); + } // and (sra X, size(X)-1), A -> "and (srl X, C2), A" iff A is a single-bit // constant. @@ -14906,6 +14920,9 @@ AddToWorklist(Shift.getNode()); } + if (CC == ISD::SETGT) + Shift = DAG.getNOT(DL, Shift, AType); + return DAG.getNode(ISD::AND, DL, AType, Shift, N2); } @@ -14918,6 +14935,9 @@ AddToWorklist(Shift.getNode()); } + if (CC == ISD::SETGT) + Shift = DAG.getNOT(DL, Shift, AType); + return DAG.getNode(ISD::AND, DL, AType, Shift, N2); } Index: llvm/trunk/test/CodeGen/AArch64/arm64-icmp-opt.ll =================================================================== --- llvm/trunk/test/CodeGen/AArch64/arm64-icmp-opt.ll +++ llvm/trunk/test/CodeGen/AArch64/arm64-icmp-opt.ll @@ -8,8 +8,8 @@ define i32 @t1(i64 %a) { ; CHECK-LABEL: t1: ; CHECK: // BB#0: -; CHECK-NEXT: cmp x0, #0 -; CHECK-NEXT: cset w0, ge +; CHECK-NEXT: lsr x8, x0, #63 +; CHECK-NEXT: eor w0, w8, #0x1 ; CHECK-NEXT: ret ; %cmp = icmp sgt i64 %a, -1 Index: llvm/trunk/test/CodeGen/AArch64/selectcc-to-shiftand.ll =================================================================== --- llvm/trunk/test/CodeGen/AArch64/selectcc-to-shiftand.ll +++ llvm/trunk/test/CodeGen/AArch64/selectcc-to-shiftand.ll @@ -54,16 +54,15 @@ ret i32 %min } -; FIXME: Flipping the comparison condition can be handled by getting the bitwise not of the sign mask. +; Flipping the comparison condition can be handled by getting the bitwise not of the sign mask. ; Compare if positive and select of constants where one constant is zero. define i32 @pos_sel_constants(i32 %a) { ; CHECK-LABEL: pos_sel_constants: ; CHECK: // BB#0: -; CHECK-NEXT: cmp w0, #0 ; CHECK-NEXT: mov w8, #5 -; CHECK-NEXT: csel w0, w8, wzr, ge +; CHECK-NEXT: bic w0, w8, w0, asr #31 ; CHECK-NEXT: ret ; %tmp.1 = icmp sgt i32 %a, -1 @@ -76,9 +75,8 @@ define i32 @pos_sel_special_constant(i32 %a) { ; CHECK-LABEL: pos_sel_special_constant: ; CHECK: // BB#0: -; CHECK-NEXT: cmp w0, #0 -; CHECK-NEXT: cset w8, ge -; CHECK-NEXT: lsl w0, w8, #9 +; CHECK-NEXT: orr w8, wzr, #0x200 +; CHECK-NEXT: bic w0, w8, w0, lsr #22 ; CHECK-NEXT: ret ; %tmp.1 = icmp sgt i32 %a, -1 @@ -91,8 +89,7 @@ define i32 @pos_sel_variable_and_zero(i32 %a, i32 %b) { ; CHECK-LABEL: pos_sel_variable_and_zero: ; CHECK: // BB#0: -; CHECK-NEXT: cmp w0, #0 -; CHECK-NEXT: csel w0, w1, wzr, ge +; CHECK-NEXT: bic w0, w1, w0, asr #31 ; CHECK-NEXT: ret ; %tmp.1 = icmp sgt i32 %a, -1 @@ -105,8 +102,7 @@ define i32 @not_neg_sel_same_variable(i32 %a) { ; CHECK-LABEL: not_neg_sel_same_variable: ; CHECK: // BB#0: -; CHECK-NEXT: cmp w0, #0 -; CHECK-NEXT: csel w0, w0, wzr, gt +; CHECK-NEXT: bic w0, w0, w0, asr #31 ; CHECK-NEXT: ret ; %tmp = icmp sgt i32 %a, 0 @@ -121,8 +117,7 @@ ; CHECK-LABEL: PR31175: ; CHECK: // BB#0: ; CHECK-NEXT: sub w8, w0, w1 -; CHECK-NEXT: cmp w8, #0 -; CHECK-NEXT: csel w0, w8, wzr, gt +; CHECK-NEXT: bic w0, w8, w8, asr #31 ; CHECK-NEXT: ret ; %sub = sub nsw i32 %x, %y