Skip to content

Commit 8d57211

Browse files
authored
[LLVM][AArch64] Optimize sign bit tests with TST instruction for SIGN_EXTEND patterns (#158061)
Hi, I recently found out in some cases LLVM doesn't generate optimal code like: ``` sxtb w8, w0 cmp w8, #0 csel w0, w1, w2, lt ``` ``` tst w0, #0x80 csel w0, w1, w2, mi ``` This optimization is only applied when the following conditions are met: 1. The comparison is setlt (signed less than) 2. The right-hand side is zero 3. The left-hand side is a sign extension operation (SIGN_EXTEND or SIGN_EXTEND_INREG) 4. The sign-extended value has only one use (hasOneUse()) 5. The original type is an integer type
1 parent edc76e1 commit 8d57211

File tree

4 files changed

+97
-28
lines changed

4 files changed

+97
-28
lines changed

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11778,6 +11778,28 @@ SDValue AArch64TargetLowering::LowerSELECT_CC(
1177811778
return DAG.getNode(ISD::AND, DL, VT, LHS, Shift);
1177911779
}
1178011780

11781+
// Check for sign bit test patterns that can use TST optimization.
11782+
// (SELECT_CC setlt, sign_extend_inreg, 0, tval, fval)
11783+
// -> TST %operand, sign_bit; CSEL
11784+
// (SELECT_CC setlt, sign_extend, 0, tval, fval)
11785+
// -> TST %operand, sign_bit; CSEL
11786+
if (CC == ISD::SETLT && RHSC && RHSC->isZero() && LHS.hasOneUse() &&
11787+
(LHS.getOpcode() == ISD::SIGN_EXTEND_INREG ||
11788+
LHS.getOpcode() == ISD::SIGN_EXTEND)) {
11789+
11790+
uint64_t SignBitPos;
11791+
std::tie(LHS, SignBitPos) = lookThroughSignExtension(LHS);
11792+
EVT TestVT = LHS.getValueType();
11793+
SDValue SignBitConst = DAG.getConstant(1ULL << SignBitPos, DL, TestVT);
11794+
SDValue TST =
11795+
DAG.getNode(AArch64ISD::ANDS, DL, DAG.getVTList(TestVT, MVT::i32),
11796+
LHS, SignBitConst);
11797+
11798+
SDValue Flags = TST.getValue(1);
11799+
return DAG.getNode(AArch64ISD::CSEL, DL, TVal.getValueType(), TVal, FVal,
11800+
DAG.getConstant(AArch64CC::NE, DL, MVT::i32), Flags);
11801+
}
11802+
1178111803
// Canonicalise absolute difference patterns:
1178211804
// select_cc lhs, rhs, sub(lhs, rhs), sub(rhs, lhs), cc ->
1178311805
// select_cc lhs, rhs, sub(lhs, rhs), neg(sub(lhs, rhs)), cc

llvm/test/CodeGen/AArch64/check-sign-bit-before-extension.ll

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,8 @@ B:
7878
define i32 @g_i8_sign_extend_inreg(i8 %in, i32 %a, i32 %b) nounwind {
7979
; CHECK-LABEL: g_i8_sign_extend_inreg:
8080
; CHECK: // %bb.0: // %entry
81-
; CHECK-NEXT: sxtb w8, w0
82-
; CHECK-NEXT: cmp w8, #0
83-
; CHECK-NEXT: csel w8, w1, w2, mi
81+
; CHECK-NEXT: tst w0, #0x80
82+
; CHECK-NEXT: csel w8, w1, w2, ne
8483
; CHECK-NEXT: add w0, w8, w0, uxtb
8584
; CHECK-NEXT: ret
8685
entry:
@@ -100,9 +99,8 @@ B:
10099
define i32 @g_i16_sign_extend_inreg(i16 %in, i32 %a, i32 %b) nounwind {
101100
; CHECK-LABEL: g_i16_sign_extend_inreg:
102101
; CHECK: // %bb.0: // %entry
103-
; CHECK-NEXT: sxth w8, w0
104-
; CHECK-NEXT: cmp w8, #0
105-
; CHECK-NEXT: csel w8, w1, w2, mi
102+
; CHECK-NEXT: tst w0, #0x8000
103+
; CHECK-NEXT: csel w8, w1, w2, ne
106104
; CHECK-NEXT: add w0, w8, w0, uxth
107105
; CHECK-NEXT: ret
108106
entry:
@@ -167,10 +165,8 @@ B:
167165
define i64 @g_i32_sign_extend_i64(i32 %in, i64 %a, i64 %b) nounwind {
168166
; CHECK-LABEL: g_i32_sign_extend_i64:
169167
; CHECK: // %bb.0: // %entry
170-
; CHECK-NEXT: // kill: def $w0 killed $w0 def $x0
171-
; CHECK-NEXT: sxtw x8, w0
172-
; CHECK-NEXT: cmp x8, #0
173-
; CHECK-NEXT: csel x8, x1, x2, mi
168+
; CHECK-NEXT: tst w0, #0x80000000
169+
; CHECK-NEXT: csel x8, x1, x2, ne
174170
; CHECK-NEXT: add x0, x8, w0, uxtw
175171
; CHECK-NEXT: ret
176172
entry:

llvm/test/CodeGen/AArch64/icmp.ll

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,3 +2093,54 @@ define <2 x i1> @icmp_slt_v2i64_Zero_LHS(<2 x i64> %a) {
20932093
%c = icmp slt <2 x i64> <i64 0, i64 0>, %a
20942094
ret <2 x i1> %c
20952095
}
2096+
2097+
; Test TST optimization for i8 sign bit testing with cross-type select
2098+
; This tests the pattern: icmp slt i8 %val, 0; select i1 %cmp, i32 %a, i32 %b
2099+
; The optimization should convert sxtb+cmp to tst for sign bit testing.
2100+
2101+
define i32 @i8_signbit_tst_constants(i8 %x, i8 %y) {
2102+
; CHECK-SD-LABEL: i8_signbit_tst_constants:
2103+
; CHECK-SD: // %bb.0:
2104+
; CHECK-SD-NEXT: add w9, w0, w1
2105+
; CHECK-SD-NEXT: mov w8, #42 // =0x2a
2106+
; CHECK-SD-NEXT: tst w9, #0x80
2107+
; CHECK-SD-NEXT: mov w9, #20894 // =0x519e
2108+
; CHECK-SD-NEXT: csel w0, w9, w8, ne
2109+
; CHECK-SD-NEXT: ret
2110+
;
2111+
; CHECK-GI-LABEL: i8_signbit_tst_constants:
2112+
; CHECK-GI: // %bb.0:
2113+
; CHECK-GI-NEXT: add w8, w0, w1
2114+
; CHECK-GI-NEXT: mov w9, #42 // =0x2a
2115+
; CHECK-GI-NEXT: mov w10, #20894 // =0x519e
2116+
; CHECK-GI-NEXT: sxtb w8, w8
2117+
; CHECK-GI-NEXT: cmp w8, #0
2118+
; CHECK-GI-NEXT: csel w0, w10, w9, mi
2119+
; CHECK-GI-NEXT: ret
2120+
%add = add i8 %x, %y
2121+
%cmp = icmp slt i8 %add, 0
2122+
%sel = select i1 %cmp, i32 20894, i32 42
2123+
ret i32 %sel
2124+
}
2125+
2126+
; Test i8 sign bit testing with variable select values (problematic case)
2127+
define i32 @i8_signbit_variables(i8 %x, i8 %y, i32 %a, i32 %b) {
2128+
; CHECK-SD-LABEL: i8_signbit_variables:
2129+
; CHECK-SD: // %bb.0:
2130+
; CHECK-SD-NEXT: add w8, w0, w1
2131+
; CHECK-SD-NEXT: tst w8, #0x80
2132+
; CHECK-SD-NEXT: csel w0, w2, w3, ne
2133+
; CHECK-SD-NEXT: ret
2134+
;
2135+
; CHECK-GI-LABEL: i8_signbit_variables:
2136+
; CHECK-GI: // %bb.0:
2137+
; CHECK-GI-NEXT: add w8, w0, w1
2138+
; CHECK-GI-NEXT: sxtb w8, w8
2139+
; CHECK-GI-NEXT: cmp w8, #0
2140+
; CHECK-GI-NEXT: csel w0, w2, w3, mi
2141+
; CHECK-GI-NEXT: ret
2142+
%add = add i8 %x, %y
2143+
%cmp = icmp slt i8 %add, 0
2144+
%sel = select i1 %cmp, i32 %a, i32 %b
2145+
ret i32 %sel
2146+
}

llvm/test/CodeGen/AArch64/vecreduce-bool.ll

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ define i32 @reduce_and_v1i8(<1 x i8> %a0, i32 %a1, i32 %a2) nounwind {
2626
; CHECK-LABEL: reduce_and_v1i8:
2727
; CHECK: // %bb.0:
2828
; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0
29-
; CHECK-NEXT: smov w8, v0.b[0]
30-
; CHECK-NEXT: cmp w8, #0
31-
; CHECK-NEXT: csel w0, w0, w1, mi
29+
; CHECK-NEXT: umov w8, v0.b[0]
30+
; CHECK-NEXT: tst w8, #0x80
31+
; CHECK-NEXT: csel w0, w0, w1, ne
3232
; CHECK-NEXT: ret
3333
%x = icmp slt <1 x i8> %a0, zeroinitializer
3434
%y = call i1 @llvm.vector.reduce.and.v1i1(<1 x i1> %x)
@@ -120,9 +120,9 @@ define i32 @reduce_and_v1i16(<1 x i16> %a0, i32 %a1, i32 %a2) nounwind {
120120
; CHECK-LABEL: reduce_and_v1i16:
121121
; CHECK: // %bb.0:
122122
; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0
123-
; CHECK-NEXT: smov w8, v0.h[0]
124-
; CHECK-NEXT: cmp w8, #0
125-
; CHECK-NEXT: csel w0, w0, w1, mi
123+
; CHECK-NEXT: umov w8, v0.h[0]
124+
; CHECK-NEXT: tst w8, #0x8000
125+
; CHECK-NEXT: csel w0, w0, w1, ne
126126
; CHECK-NEXT: ret
127127
%x = icmp slt <1 x i16> %a0, zeroinitializer
128128
%y = call i1 @llvm.vector.reduce.and.v1i1(<1 x i1> %x)
@@ -305,9 +305,9 @@ define i32 @reduce_or_v1i8(<1 x i8> %a0, i32 %a1, i32 %a2) nounwind {
305305
; CHECK-LABEL: reduce_or_v1i8:
306306
; CHECK: // %bb.0:
307307
; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0
308-
; CHECK-NEXT: smov w8, v0.b[0]
309-
; CHECK-NEXT: cmp w8, #0
310-
; CHECK-NEXT: csel w0, w0, w1, mi
308+
; CHECK-NEXT: umov w8, v0.b[0]
309+
; CHECK-NEXT: tst w8, #0x80
310+
; CHECK-NEXT: csel w0, w0, w1, ne
311311
; CHECK-NEXT: ret
312312
%x = icmp slt <1 x i8> %a0, zeroinitializer
313313
%y = call i1 @llvm.vector.reduce.or.v1i1(<1 x i1> %x)
@@ -399,9 +399,9 @@ define i32 @reduce_or_v1i16(<1 x i16> %a0, i32 %a1, i32 %a2) nounwind {
399399
; CHECK-LABEL: reduce_or_v1i16:
400400
; CHECK: // %bb.0:
401401
; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0
402-
; CHECK-NEXT: smov w8, v0.h[0]
403-
; CHECK-NEXT: cmp w8, #0
404-
; CHECK-NEXT: csel w0, w0, w1, mi
402+
; CHECK-NEXT: umov w8, v0.h[0]
403+
; CHECK-NEXT: tst w8, #0x8000
404+
; CHECK-NEXT: csel w0, w0, w1, ne
405405
; CHECK-NEXT: ret
406406
%x = icmp slt <1 x i16> %a0, zeroinitializer
407407
%y = call i1 @llvm.vector.reduce.or.v1i1(<1 x i1> %x)
@@ -584,9 +584,9 @@ define i32 @reduce_xor_v1i8(<1 x i8> %a0, i32 %a1, i32 %a2) nounwind {
584584
; CHECK-LABEL: reduce_xor_v1i8:
585585
; CHECK: // %bb.0:
586586
; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0
587-
; CHECK-NEXT: smov w8, v0.b[0]
588-
; CHECK-NEXT: cmp w8, #0
589-
; CHECK-NEXT: csel w0, w0, w1, mi
587+
; CHECK-NEXT: umov w8, v0.b[0]
588+
; CHECK-NEXT: tst w8, #0x80
589+
; CHECK-NEXT: csel w0, w0, w1, ne
590590
; CHECK-NEXT: ret
591591
%x = icmp slt <1 x i8> %a0, zeroinitializer
592592
%y = call i1 @llvm.vector.reduce.xor.v1i1(<1 x i1> %x)
@@ -679,9 +679,9 @@ define i32 @reduce_xor_v1i16(<1 x i16> %a0, i32 %a1, i32 %a2) nounwind {
679679
; CHECK-LABEL: reduce_xor_v1i16:
680680
; CHECK: // %bb.0:
681681
; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0
682-
; CHECK-NEXT: smov w8, v0.h[0]
683-
; CHECK-NEXT: cmp w8, #0
684-
; CHECK-NEXT: csel w0, w0, w1, mi
682+
; CHECK-NEXT: umov w8, v0.h[0]
683+
; CHECK-NEXT: tst w8, #0x8000
684+
; CHECK-NEXT: csel w0, w0, w1, ne
685685
; CHECK-NEXT: ret
686686
%x = icmp slt <1 x i16> %a0, zeroinitializer
687687
%y = call i1 @llvm.vector.reduce.xor.v1i1(<1 x i1> %x)

0 commit comments

Comments
 (0)