[llvm] 1b5b734 - [AArch64][GlobalISel] Combine trunc_nsw(smin) to truncsat (#218605)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 26 22:07:28 PDT 2026


Author: David Green
Date: 2026-08-27T06:07:23+01:00
New Revision: 1b5b7342ab5203899d824686b9fc38c46e37b457

URL: https://github.com/llvm/llvm-project/commit/1b5b7342ab5203899d824686b9fc38c46e37b457
DIFF: https://github.com/llvm/llvm-project/commit/1b5b7342ab5203899d824686b9fc38c46e37b457.diff

LOG: [AArch64][GlobalISel] Combine trunc_nsw(smin) to truncsat (#218605)

The midend of LLVM, in CVP, will often transform the canonical pattern
for a saturate trunc(smin(smax)) into trunc nsw (smin), as the range
analysis proves that the smax is outside of range. This adds a fold
back, converting the trunc with nsw + smin into G_TRUNC_SSAT_S.
https://alive2.llvm.org/ce/z/xfPEXE

Support for widening is added for truncsat nodes too, to prevent
fallbacks. They can be widened in the same way as a standard trunc using
buildPadVectorWithUndefElements.

Added: 
    

Modified: 
    llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
    llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
    llvm/test/CodeGen/AArch64/qmovn.ll
    llvm/test/CodeGen/AArch64/saturating-vec-smull.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
index 5804be63fad48..3f5d9ecfa26fb 100644
--- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
@@ -6290,14 +6290,25 @@ bool CombinerHelper::matchTruncSSatS(MachineInstr &MI,
 
   APInt SignedMax = APInt::getSignedMaxValue(NumDstBits).sext(NumSrcBits);
   APInt SignedMin = APInt::getSignedMinValue(NumDstBits).sext(NumSrcBits);
-  return mi_match(Src, MRI,
-                  m_GSMin(m_GSMax(m_Reg(MatchInfo),
-                                  m_SpecificICstOrSplat(SignedMin)),
-                          m_SpecificICstOrSplat(SignedMax))) ||
-         mi_match(Src, MRI,
-                  m_GSMax(m_GSMin(m_Reg(MatchInfo),
-                                  m_SpecificICstOrSplat(SignedMax)),
-                          m_SpecificICstOrSplat(SignedMin)));
+  if (mi_match(
+          Src, MRI,
+          m_GSMin(m_GSMax(m_Reg(MatchInfo), m_SpecificICstOrSplat(SignedMin)),
+                  m_SpecificICstOrSplat(SignedMax))))
+    return true;
+  if (mi_match(
+          Src, MRI,
+          m_GSMax(m_GSMin(m_Reg(MatchInfo), m_SpecificICstOrSplat(SignedMax)),
+                  m_SpecificICstOrSplat(SignedMin))))
+    return true;
+
+  // CVP in the midend will often transform trunc(smin(smax(..)) into
+  // trunc nsw(smin(..)) as the smax against INT_MIN never saturates.
+  if (MI.getFlag(MachineInstr::MIFlag::NoSWrap) &&
+      mi_match(Src, MRI,
+               m_GSMin(m_Reg(MatchInfo), m_SpecificICstOrSplat(SignedMax))))
+    return true;
+
+  return false;
 }
 
 void CombinerHelper::applyTruncSSatS(MachineInstr &MI,

diff  --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index bdb27927882a7..aac320cd2cc7f 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -7074,7 +7074,10 @@ LegalizerHelper::moreElementsVector(MachineInstr &MI, unsigned TypeIdx,
   case TargetOpcode::G_FPTOSI_SAT:
   case TargetOpcode::G_FPTOUI_SAT:
   case TargetOpcode::G_SITOFP:
-  case TargetOpcode::G_UITOFP: {
+  case TargetOpcode::G_UITOFP:
+  case TargetOpcode::G_TRUNC_SSAT_S:
+  case TargetOpcode::G_TRUNC_SSAT_U:
+  case TargetOpcode::G_TRUNC_USAT_U: {
     Observer.changingInstr(MI);
     LLT SrcExtTy;
     LLT DstExtTy;

diff  --git a/llvm/test/CodeGen/AArch64/qmovn.ll b/llvm/test/CodeGen/AArch64/qmovn.ll
index 30bff916f1f02..c1b8c1c221614 100644
--- a/llvm/test/CodeGen/AArch64/qmovn.ll
+++ b/llvm/test/CodeGen/AArch64/qmovn.ll
@@ -640,10 +640,8 @@ define <16 x i8> @signed_minnsw_v16i16_to_v16i8(<16 x i16> %y) {
 ;
 ; CHECK-GI-LABEL: signed_minnsw_v16i16_to_v16i8:
 ; CHECK-GI:       // %bb.0: // %entry
-; CHECK-GI-NEXT:    movi v2.8h, #127
-; CHECK-GI-NEXT:    smin v0.8h, v0.8h, v2.8h
-; CHECK-GI-NEXT:    smin v1.8h, v1.8h, v2.8h
-; CHECK-GI-NEXT:    uzp1 v0.16b, v0.16b, v1.16b
+; CHECK-GI-NEXT:    sqxtn v0.8b, v0.8h
+; CHECK-GI-NEXT:    sqxtn2 v0.16b, v1.8h
 ; CHECK-GI-NEXT:    ret
 entry:
   %min = call <16 x i16> @llvm.smin.v16i16(<16 x i16> %y, <16 x i16> <i16 127, i16 127, i16 127, i16 127, i16 127, i16 127, i16 127, i16 127, i16 127, i16 127, i16 127, i16 127, i16 127, i16 127, i16 127, i16 127>)
@@ -662,10 +660,8 @@ define <8 x i16> @signed_minnsw_v8i32_to_v8i16(<8 x i32> %y) {
 ;
 ; CHECK-GI-LABEL: signed_minnsw_v8i32_to_v8i16:
 ; CHECK-GI:       // %bb.0: // %entry
-; CHECK-GI-NEXT:    movi v2.4s, #127, msl #8
-; CHECK-GI-NEXT:    smin v0.4s, v0.4s, v2.4s
-; CHECK-GI-NEXT:    smin v1.4s, v1.4s, v2.4s
-; CHECK-GI-NEXT:    uzp1 v0.8h, v0.8h, v1.8h
+; CHECK-GI-NEXT:    sqxtn v0.4h, v0.4s
+; CHECK-GI-NEXT:    sqxtn2 v0.8h, v1.4s
 ; CHECK-GI-NEXT:    ret
 entry:
   %min = call <8 x i32> @llvm.smin.v8i32(<8 x i32> %y, <8 x i32> <i32 32767, i32 32767, i32 32767, i32 32767, i32 32767, i32 32767, i32 32767, i32 32767>)
@@ -687,13 +683,8 @@ define <4 x i32> @signed_minnsw_v4i64_to_v4i32(<4 x i64> %y) {
 ;
 ; CHECK-GI-LABEL: signed_minnsw_v4i64_to_v4i32:
 ; CHECK-GI:       // %bb.0: // %entry
-; CHECK-GI-NEXT:    adrp x8, .LCPI47_0
-; CHECK-GI-NEXT:    ldr q2, [x8, :lo12:.LCPI47_0]
-; CHECK-GI-NEXT:    cmgt v3.2d, v2.2d, v0.2d
-; CHECK-GI-NEXT:    cmgt v4.2d, v2.2d, v1.2d
-; CHECK-GI-NEXT:    bif v0.16b, v2.16b, v3.16b
-; CHECK-GI-NEXT:    bif v1.16b, v2.16b, v4.16b
-; CHECK-GI-NEXT:    uzp1 v0.4s, v0.4s, v1.4s
+; CHECK-GI-NEXT:    sqxtn v0.2s, v0.2d
+; CHECK-GI-NEXT:    sqxtn2 v0.4s, v1.2d
 ; CHECK-GI-NEXT:    ret
 entry:
   %min = call <4 x i64> @llvm.smin.v4i64(<4 x i64> %y, <4 x i64> <i64 2147483647, i64 2147483647, i64 2147483647, i64 2147483647>)

diff  --git a/llvm/test/CodeGen/AArch64/saturating-vec-smull.ll b/llvm/test/CodeGen/AArch64/saturating-vec-smull.ll
index fe9b060be6ae2..a16eb65b32a71 100644
--- a/llvm/test/CodeGen/AArch64/saturating-vec-smull.ll
+++ b/llvm/test/CodeGen/AArch64/saturating-vec-smull.ll
@@ -43,10 +43,7 @@ define <4 x i16> @saturating_4xi16(<4 x i16> %a, <4 x i16> %b) {
 ; CHECK-GI-LABEL: saturating_4xi16:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    smull v0.4s, v1.4h, v0.4h
-; CHECK-GI-NEXT:    movi v1.4s, #127, msl #8
-; CHECK-GI-NEXT:    sshr v0.4s, v0.4s, #15
-; CHECK-GI-NEXT:    smin v0.4s, v0.4s, v1.4s
-; CHECK-GI-NEXT:    xtn v0.4h, v0.4s
+; CHECK-GI-NEXT:    sqshrn v0.4h, v0.4s, #15
 ; CHECK-GI-NEXT:    ret
   %as = sext <4 x i16> %a to <4 x i32>
   %bs = sext <4 x i16> %b to <4 x i32>
@@ -65,14 +62,10 @@ define <8 x i16> @saturating_8xi16(<8 x i16> %a, <8 x i16> %b) {
 ;
 ; CHECK-GI-LABEL: saturating_8xi16:
 ; CHECK-GI:       // %bb.0:
-; CHECK-GI-NEXT:    smull v3.4s, v1.4h, v0.4h
-; CHECK-GI-NEXT:    smull2 v0.4s, v1.8h, v0.8h
-; CHECK-GI-NEXT:    movi v2.4s, #127, msl #8
-; CHECK-GI-NEXT:    sshr v1.4s, v3.4s, #15
-; CHECK-GI-NEXT:    sshr v0.4s, v0.4s, #15
-; CHECK-GI-NEXT:    smin v1.4s, v1.4s, v2.4s
-; CHECK-GI-NEXT:    smin v0.4s, v0.4s, v2.4s
-; CHECK-GI-NEXT:    uzp1 v0.8h, v1.8h, v0.8h
+; CHECK-GI-NEXT:    smull v2.4s, v1.4h, v0.4h
+; CHECK-GI-NEXT:    smull2 v1.4s, v1.8h, v0.8h
+; CHECK-GI-NEXT:    sqshrn v0.4h, v2.4s, #15
+; CHECK-GI-NEXT:    sqshrn2 v0.8h, v1.4s, #15
 ; CHECK-GI-NEXT:    ret
   %as = sext <8 x i16> %a to <8 x i32>
   %bs = sext <8 x i16> %b to <8 x i32>
@@ -92,12 +85,7 @@ define <2 x i32> @saturating_2xi32(<2 x i32> %a, <2 x i32> %b) {
 ; CHECK-GI-LABEL: saturating_2xi32:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    smull v0.2d, v1.2s, v0.2s
-; CHECK-GI-NEXT:    adrp x8, .LCPI3_0
-; CHECK-GI-NEXT:    ldr q1, [x8, :lo12:.LCPI3_0]
-; CHECK-GI-NEXT:    sshr v0.2d, v0.2d, #31
-; CHECK-GI-NEXT:    cmgt v2.2d, v1.2d, v0.2d
-; CHECK-GI-NEXT:    bif v0.16b, v1.16b, v2.16b
-; CHECK-GI-NEXT:    xtn v0.2s, v0.2d
+; CHECK-GI-NEXT:    sqshrn v0.2s, v0.2d, #31
 ; CHECK-GI-NEXT:    ret
   %as = sext <2 x i32> %a to <2 x i64>
   %bs = sext <2 x i32> %b to <2 x i64>
@@ -117,16 +105,9 @@ define <4 x i32> @saturating_4xi32(<4 x i32> %a, <4 x i32> %b) {
 ; CHECK-GI-LABEL: saturating_4xi32:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    smull v2.2d, v1.2s, v0.2s
-; CHECK-GI-NEXT:    smull2 v0.2d, v1.4s, v0.4s
-; CHECK-GI-NEXT:    adrp x8, .LCPI4_0
-; CHECK-GI-NEXT:    sshr v1.2d, v2.2d, #31
-; CHECK-GI-NEXT:    sshr v0.2d, v0.2d, #31
-; CHECK-GI-NEXT:    ldr q2, [x8, :lo12:.LCPI4_0]
-; CHECK-GI-NEXT:    cmgt v3.2d, v2.2d, v1.2d
-; CHECK-GI-NEXT:    cmgt v4.2d, v2.2d, v0.2d
-; CHECK-GI-NEXT:    bif v1.16b, v2.16b, v3.16b
-; CHECK-GI-NEXT:    bif v0.16b, v2.16b, v4.16b
-; CHECK-GI-NEXT:    uzp1 v0.4s, v1.4s, v0.4s
+; CHECK-GI-NEXT:    smull2 v1.2d, v1.4s, v0.4s
+; CHECK-GI-NEXT:    sqshrn v0.2s, v2.2d, #31
+; CHECK-GI-NEXT:    sqshrn2 v0.4s, v1.2d, #31
 ; CHECK-GI-NEXT:    ret
   %as = sext <4 x i32> %a to <4 x i64>
   %bs = sext <4 x i32> %b to <4 x i64>
@@ -147,25 +128,13 @@ define <8 x i32> @saturating_8xi32(<8 x i32> %a, <8 x i32> %b) {
 ; CHECK-GI-LABEL: saturating_8xi32:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    smull v4.2d, v2.2s, v0.2s
-; CHECK-GI-NEXT:    smull2 v0.2d, v2.4s, v0.4s
-; CHECK-GI-NEXT:    adrp x8, .LCPI5_0
-; CHECK-GI-NEXT:    smull v2.2d, v3.2s, v1.2s
-; CHECK-GI-NEXT:    smull2 v1.2d, v3.4s, v1.4s
-; CHECK-GI-NEXT:    ldr q3, [x8, :lo12:.LCPI5_0]
-; CHECK-GI-NEXT:    sshr v4.2d, v4.2d, #31
-; CHECK-GI-NEXT:    sshr v0.2d, v0.2d, #31
-; CHECK-GI-NEXT:    sshr v2.2d, v2.2d, #31
-; CHECK-GI-NEXT:    sshr v1.2d, v1.2d, #31
-; CHECK-GI-NEXT:    cmgt v5.2d, v3.2d, v4.2d
-; CHECK-GI-NEXT:    cmgt v6.2d, v3.2d, v0.2d
-; CHECK-GI-NEXT:    cmgt v7.2d, v3.2d, v2.2d
-; CHECK-GI-NEXT:    cmgt v16.2d, v3.2d, v1.2d
-; CHECK-GI-NEXT:    bif v4.16b, v3.16b, v5.16b
-; CHECK-GI-NEXT:    bif v0.16b, v3.16b, v6.16b
-; CHECK-GI-NEXT:    bif v2.16b, v3.16b, v7.16b
-; CHECK-GI-NEXT:    bif v1.16b, v3.16b, v16.16b
-; CHECK-GI-NEXT:    uzp1 v0.4s, v4.4s, v0.4s
-; CHECK-GI-NEXT:    uzp1 v1.4s, v2.4s, v1.4s
+; CHECK-GI-NEXT:    smull v5.2d, v3.2s, v1.2s
+; CHECK-GI-NEXT:    smull2 v2.2d, v2.4s, v0.4s
+; CHECK-GI-NEXT:    smull2 v3.2d, v3.4s, v1.4s
+; CHECK-GI-NEXT:    sqshrn v0.2s, v4.2d, #31
+; CHECK-GI-NEXT:    sqshrn v1.2s, v5.2d, #31
+; CHECK-GI-NEXT:    sqshrn2 v0.4s, v2.2d, #31
+; CHECK-GI-NEXT:    sqshrn2 v1.4s, v3.2d, #31
 ; CHECK-GI-NEXT:    ret
   %as = sext <8 x i32> %a to <8 x i64>
   %bs = sext <8 x i32> %b to <8 x i64>
@@ -217,38 +186,31 @@ define <6 x i16> @saturating_6xi16(<6 x i16> %a, <6 x i16> %b) {
 ; CHECK-GI-NEXT:    smov w9, v0.h[0]
 ; CHECK-GI-NEXT:    smov w10, v1.h[1]
 ; CHECK-GI-NEXT:    smov w11, v0.h[1]
-; CHECK-GI-NEXT:    smov w12, v1.h[2]
-; CHECK-GI-NEXT:    movi v2.4s, #127, msl #8
-; CHECK-GI-NEXT:    fmov s3, w8
-; CHECK-GI-NEXT:    fmov s4, w9
-; CHECK-GI-NEXT:    smov w8, v0.h[2]
-; CHECK-GI-NEXT:    smov w9, v1.h[3]
+; CHECK-GI-NEXT:    fmov s2, w8
+; CHECK-GI-NEXT:    fmov s3, w9
+; CHECK-GI-NEXT:    smov w8, v1.h[2]
+; CHECK-GI-NEXT:    smov w9, v0.h[2]
+; CHECK-GI-NEXT:    mov v2.s[1], w10
+; CHECK-GI-NEXT:    mov v3.s[1], w11
+; CHECK-GI-NEXT:    smov w10, v1.h[3]
 ; CHECK-GI-NEXT:    sshll2 v1.4s, v1.8h, #0
-; CHECK-GI-NEXT:    mov v3.s[1], w10
-; CHECK-GI-NEXT:    mov v4.s[1], w11
-; CHECK-GI-NEXT:    smov w10, v0.h[3]
+; CHECK-GI-NEXT:    mov v2.s[2], w8
+; CHECK-GI-NEXT:    smov w8, v0.h[3]
+; CHECK-GI-NEXT:    mov v3.s[2], w9
 ; CHECK-GI-NEXT:    sshll2 v0.4s, v0.8h, #0
-; CHECK-GI-NEXT:    mov v3.s[2], w12
-; CHECK-GI-NEXT:    mov v4.s[2], w8
+; CHECK-GI-NEXT:    mov v2.s[3], w10
+; CHECK-GI-NEXT:    mov v3.s[3], w8
 ; CHECK-GI-NEXT:    mul v0.2s, v1.2s, v0.2s
-; CHECK-GI-NEXT:    movi v1.2s, #127, msl #8
-; CHECK-GI-NEXT:    mov v3.s[3], w9
-; CHECK-GI-NEXT:    mov v4.s[3], w10
+; CHECK-GI-NEXT:    mul v2.4s, v2.4s, v3.4s
 ; CHECK-GI-NEXT:    sshr v0.2s, v0.2s, #15
-; CHECK-GI-NEXT:    smin v0.2s, v0.2s, v1.2s
-; CHECK-GI-NEXT:    mul v3.4s, v3.4s, v4.4s
-; CHECK-GI-NEXT:    sshr v3.4s, v3.4s, #15
-; CHECK-GI-NEXT:    smin v2.4s, v3.4s, v2.4s
-; CHECK-GI-NEXT:    mov w8, v2.s[1]
-; CHECK-GI-NEXT:    mov w9, v2.s[2]
-; CHECK-GI-NEXT:    mov w10, v2.s[3]
-; CHECK-GI-NEXT:    mov v2.h[1], w8
-; CHECK-GI-NEXT:    fmov w8, s0
-; CHECK-GI-NEXT:    mov v2.h[2], w9
-; CHECK-GI-NEXT:    mov w9, v0.s[1]
-; CHECK-GI-NEXT:    mov v2.h[3], w10
-; CHECK-GI-NEXT:    mov v2.h[4], w8
-; CHECK-GI-NEXT:    mov v2.h[5], w9
+; CHECK-GI-NEXT:    sqxtn v0.4h, v0.4s
+; CHECK-GI-NEXT:    sqshrn v3.4h, v2.4s, #15
+; CHECK-GI-NEXT:    mov v2.h[0], v3.h[0]
+; CHECK-GI-NEXT:    mov v2.h[1], v3.h[1]
+; CHECK-GI-NEXT:    mov v2.h[2], v3.h[2]
+; CHECK-GI-NEXT:    mov v2.h[3], v3.h[3]
+; CHECK-GI-NEXT:    mov v2.h[4], v0.h[0]
+; CHECK-GI-NEXT:    mov v2.h[5], v0.h[1]
 ; CHECK-GI-NEXT:    mov v0.16b, v2.16b
 ; CHECK-GI-NEXT:    ret
   %as = sext <6 x i16> %a to <6 x i32>


        


More information about the llvm-commits mailing list