Skip to content

Commit 01477b7

Browse files
AZero13tomtor
authored andcommitted
[AArch64] Signed comparison using CMN is safe when the subtraction is nsw (llvm#141993)
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 9d627b6 commit 01477b7

File tree

2 files changed

+61
-4
lines changed

2 files changed

+61
-4
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: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,3 +602,49 @@ define i1 @almost_immediate_neg_ugt_64(i64 %x) {
602602
%cmp = icmp ugt i64 %x, -16773121
603603
ret i1 %cmp
604604
}
605+
606+
define i1 @cmn_nsw(i32 %a, i32 %b) {
607+
; CHECK-LABEL: cmn_nsw:
608+
; CHECK: // %bb.0:
609+
; CHECK-NEXT: cmn w0, w1
610+
; CHECK-NEXT: cset w0, gt
611+
; CHECK-NEXT: ret
612+
%sub = sub nsw i32 0, %b
613+
%cmp = icmp sgt i32 %a, %sub
614+
ret i1 %cmp
615+
}
616+
617+
define i1 @cmn_nsw_64(i64 %a, i64 %b) {
618+
; CHECK-LABEL: cmn_nsw_64:
619+
; CHECK: // %bb.0:
620+
; CHECK-NEXT: cmn x0, x1
621+
; CHECK-NEXT: cset w0, gt
622+
; CHECK-NEXT: ret
623+
%sub = sub nsw i64 0, %b
624+
%cmp = icmp sgt i64 %a, %sub
625+
ret i1 %cmp
626+
}
627+
628+
define i1 @cmn_nsw_neg(i32 %a, i32 %b) {
629+
; CHECK-LABEL: cmn_nsw_neg:
630+
; CHECK: // %bb.0:
631+
; CHECK-NEXT: neg w8, w1
632+
; CHECK-NEXT: cmp w0, w8
633+
; CHECK-NEXT: cset w0, gt
634+
; CHECK-NEXT: ret
635+
%sub = sub i32 0, %b
636+
%cmp = icmp sgt i32 %a, %sub
637+
ret i1 %cmp
638+
}
639+
640+
define i1 @cmn_nsw_neg_64(i64 %a, i64 %b) {
641+
; CHECK-LABEL: cmn_nsw_neg_64:
642+
; CHECK: // %bb.0:
643+
; CHECK-NEXT: neg x8, x1
644+
; CHECK-NEXT: cmp x0, x8
645+
; CHECK-NEXT: cset w0, gt
646+
; CHECK-NEXT: ret
647+
%sub = sub i64 0, %b
648+
%cmp = icmp sgt i64 %a, %sub
649+
ret i1 %cmp
650+
}

0 commit comments

Comments
 (0)