Skip to content

Commit c9cb9f8

Browse files
committed
[AArch64] Signed comparison using CMN is safe when the subtraction is nsw
nsw means no signed wrap, and 0 - INT_MIN is a signed wrap. Now, this is going to be a point I need to get out of the way: So is it okay to always transform a > -b into cmn if it is a signed comparison, even if b is INT_MIN because -INT_MIN is undefined, at least in C, because unless fwrapv is specified, opt puts nsw on signed integer operations, allowing for more folds anyway.
1 parent c496f19 commit c9cb9f8

File tree

2 files changed

+29
-20
lines changed

2 files changed

+29
-20
lines changed

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3392,8 +3392,19 @@ bool isLegalCmpImmed(APInt C) {
33923392
return isLegalArithImmed(C.abs().getZExtValue());
33933393
}
33943394

3395-
static bool cannotBeIntMin(SDValue CheckedVal, SelectionDAG &DAG) {
3396-
KnownBits KnownSrc = DAG.computeKnownBits(CheckedVal);
3395+
static bool isSafeSignedCMN(SDValue Op, SelectionDAG &DAG) {
3396+
// 0 - INT_MIN sign wraps, so no signed wrap means cmn is safe.
3397+
if (Op->getFlags().hasNoSignedWrap())
3398+
return true;
3399+
3400+
// We can still figure out if the second operand is safe to use
3401+
// in a CMN instruction by checking if it is known to be not the minimum
3402+
// signed value. If it is not, then we can safely use CMN.
3403+
// Note: We can eventually remove this check and simply rely on
3404+
// Op->getFlags().hasNoSignedWrap() once SelectionDAG/ISelLowering
3405+
// consistently sets them appropriately when making said nodes.
3406+
3407+
KnownBits KnownSrc = DAG.computeKnownBits(Op.getOperand(1));
33973408
return !KnownSrc.getSignedMinValue().isMinSignedValue();
33983409
}
33993410

@@ -3402,7 +3413,7 @@ static bool cannotBeIntMin(SDValue CheckedVal, SelectionDAG &DAG) {
34023413
// can be set differently by this operation. It comes down to whether
34033414
// "SInt(~op2)+1 == SInt(~op2+1)" (and the same for UInt). If they are then
34043415
// everything is fine. If not then the optimization is wrong. Thus general
3405-
// comparisons are only valid if op2 != 0.
3416+
// comparisons are only valid if op2 != 0 and op2 != INT_MIN.
34063417
//
34073418
// So, finally, the only LLVM-native comparisons that don't mention C or V
34083419
// are the ones that aren't unsigned comparisons. They're the only ones we can
@@ -3411,7 +3422,7 @@ static bool isCMN(SDValue Op, ISD::CondCode CC, SelectionDAG &DAG) {
34113422
return Op.getOpcode() == ISD::SUB && isNullConstant(Op.getOperand(0)) &&
34123423
(isIntEqualitySetCC(CC) ||
34133424
(isUnsignedIntSetCC(CC) && DAG.isKnownNeverZero(Op.getOperand(1))) ||
3414-
(isSignedIntSetCC(CC) && cannotBeIntMin(Op.getOperand(1), DAG)));
3425+
(isSignedIntSetCC(CC) && isSafeSignedCMN(Op, DAG)));
34153426
}
34163427

34173428
static SDValue emitStrictFPComparison(SDValue LHS, SDValue RHS, const SDLoc &dl,

llvm/test/CodeGen/AArch64/cmp-to-cmn.ll

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -606,25 +606,23 @@ define i1 @almost_immediate_neg_ugt_64(i64 %x) {
606606
define i1 @cmn_nsw(i32 %a, i32 %b) {
607607
; CHECK-LABEL: cmn_nsw:
608608
; CHECK: // %bb.0:
609-
; CHECK-NEXT: neg w8, w1
610-
; CHECK-NEXT: cmp w0, w8
609+
; CHECK-NEXT: cmn w0, w1
611610
; CHECK-NEXT: cset w0, gt
612611
; CHECK-NEXT: ret
613-
%sub = sub nsw i32 0, %b
614-
%cmp = icmp sgt i32 %a, %sub
615-
ret i1 %cmp
612+
%sub = sub nsw i32 0, %b
613+
%cmp = icmp sgt i32 %a, %sub
614+
ret i1 %cmp
616615
}
617616

618617
define i1 @cmn_nsw_64(i64 %a, i64 %b) {
619618
; CHECK-LABEL: cmn_nsw_64:
620619
; CHECK: // %bb.0:
621-
; CHECK-NEXT: neg x8, x1
622-
; CHECK-NEXT: cmp x0, x8
620+
; CHECK-NEXT: cmn x0, x1
623621
; CHECK-NEXT: cset w0, gt
624622
; CHECK-NEXT: ret
625-
%sub = sub nsw i64 0, %b
626-
%cmp = icmp sgt i64 %a, %sub
627-
ret i1 %cmp
623+
%sub = sub nsw i64 0, %b
624+
%cmp = icmp sgt i64 %a, %sub
625+
ret i1 %cmp
628626
}
629627

630628
define i1 @cmn_nsw_neg(i32 %a, i32 %b) {
@@ -634,9 +632,9 @@ define i1 @cmn_nsw_neg(i32 %a, i32 %b) {
634632
; CHECK-NEXT: cmp w0, w8
635633
; CHECK-NEXT: cset w0, gt
636634
; CHECK-NEXT: ret
637-
%sub = sub i32 0, %b
638-
%cmp = icmp sgt i32 %a, %sub
639-
ret i1 %cmp
635+
%sub = sub i32 0, %b
636+
%cmp = icmp sgt i32 %a, %sub
637+
ret i1 %cmp
640638
}
641639

642640
define i1 @cmn_nsw_neg_64(i64 %a, i64 %b) {
@@ -646,7 +644,7 @@ define i1 @cmn_nsw_neg_64(i64 %a, i64 %b) {
646644
; CHECK-NEXT: cmp x0, x8
647645
; CHECK-NEXT: cset w0, gt
648646
; CHECK-NEXT: ret
649-
%sub = sub i64 0, %b
650-
%cmp = icmp sgt i64 %a, %sub
651-
ret i1 %cmp
647+
%sub = sub i64 0, %b
648+
%cmp = icmp sgt i64 %a, %sub
649+
ret i1 %cmp
652650
}

0 commit comments

Comments
 (0)