[llvm] [AMDGPU][GlobalISel] Fix i64-to-i16 clamp combine on inverted bounds (PR #216306)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 27 07:47:24 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/216306
>From a05b29e4326acfac7ea7378db8fb05cac3887d73 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 14 Aug 2026 13:20:54 +0200
Subject: [PATCH 1/2] [AMDGPU][GlobalISel] Fix i64-to-i16 clamp combine on
inverted bounds
`abs(Cmp2 - Cmp1)` check couldn't tell inverted bounds `(Lo > Hi)` from valid ones, letting the combine build a med3 that clamped to the wrong range instead of the constant smin/smax result
---
.../AMDGPU/AMDGPUPreLegalizerCombiner.cpp | 26 ++++---
.../combine-short-clamp-inverted-bounds.mir | 71 +++++++++++++++++++
2 files changed, 83 insertions(+), 14 deletions(-)
create mode 100644 llvm/test/CodeGen/AMDGPU/GlobalISel/combine-short-clamp-inverted-bounds.mir
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUPreLegalizerCombiner.cpp b/llvm/lib/Target/AMDGPU/AMDGPUPreLegalizerCombiner.cpp
index 29a1adbb06602..82df0ac70b7bd 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUPreLegalizerCombiner.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUPreLegalizerCombiner.cpp
@@ -122,22 +122,20 @@ bool AMDGPUPreLegalizerCombinerImpl::matchClampI64ToI16(
Register Base;
- auto IsApplicableForCombine = [&MatchInfo]() -> bool {
- const auto Cmp1 = MatchInfo.Cmp1;
- const auto Cmp2 = MatchInfo.Cmp2;
- const auto Diff = std::abs(Cmp2 - Cmp1);
-
- // If the difference between both comparison values is 0 or 1, there is no
- // need to clamp.
- if (Diff == 0 || Diff == 1)
- return false;
+ // Lo must not exceed Hi: with inverted bounds smin(smax(X, Lo), Hi) is
+ // constant, but the med3 built below would still clamp X to [Hi, Lo].
+ auto IsApplicableForCombine = [&MatchInfo](bool OuterIsMin) -> bool {
+ const int64_t Lo = OuterIsMin ? MatchInfo.Cmp2 : MatchInfo.Cmp1;
+ const int64_t Hi = OuterIsMin ? MatchInfo.Cmp1 : MatchInfo.Cmp2;
+ // Range-check first so Hi - Lo below can't overflow.
const int64_t Min = std::numeric_limits<int16_t>::min();
const int64_t Max = std::numeric_limits<int16_t>::max();
+ if (Lo < Min || Lo > Max || Hi < Min || Hi > Max)
+ return false;
- // Check if the comparison values are between SHORT_MIN and SHORT_MAX.
- return ((Cmp2 >= Cmp1 && Cmp1 >= Min && Cmp2 <= Max) ||
- (Cmp1 >= Cmp2 && Cmp1 <= Max && Cmp2 >= Min));
+ // Reject inverted bounds, and bounds so close there is no need to clamp.
+ return Hi - Lo > 1;
};
// Try to match a combination of min / max MIR opcodes.
@@ -145,7 +143,7 @@ bool AMDGPUPreLegalizerCombinerImpl::matchClampI64ToI16(
m_GSMin(m_Reg(Base), m_ICst(MatchInfo.Cmp1)))) {
if (mi_match(Base, MRI,
m_GSMax(m_Reg(MatchInfo.Origin), m_ICst(MatchInfo.Cmp2)))) {
- return IsApplicableForCombine();
+ return IsApplicableForCombine(/*OuterIsMin=*/true);
}
}
@@ -153,7 +151,7 @@ bool AMDGPUPreLegalizerCombinerImpl::matchClampI64ToI16(
m_GSMax(m_Reg(Base), m_ICst(MatchInfo.Cmp1)))) {
if (mi_match(Base, MRI,
m_GSMin(m_Reg(MatchInfo.Origin), m_ICst(MatchInfo.Cmp2)))) {
- return IsApplicableForCombine();
+ return IsApplicableForCombine(/*OuterIsMin=*/false);
}
}
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-short-clamp-inverted-bounds.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-short-clamp-inverted-bounds.mir
new file mode 100644
index 0000000000000..8f02f7fb113cb
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-short-clamp-inverted-bounds.mir
@@ -0,0 +1,71 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=amdgpu9.00-amd-amdhsa -run-pass=amdgpu-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+# matchClampI64ToI16 must not fire on inverted bounds: smin(smax(x,100),0) is
+# constant 0, not a [0,100] clamp.
+
+---
+name: clamp_i64_i16_inverted_bounds
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $vgpr0, $vgpr1
+
+ ; CHECK-LABEL: name: clamp_i64_i16_inverted_bounds
+ ; CHECK: liveins: $vgpr0, $vgpr1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $vgpr1
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY]](s32), [[COPY1]](s32)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 100
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(s64) = G_SMAX [[MV]], [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(s64) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[SMIN]](s64)
+ ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[TRUNC]](s16)
+ ; CHECK-NEXT: $vgpr0 = COPY [[ANYEXT]](s32)
+ %0:_(s32) = COPY $vgpr0
+ %1:_(s32) = COPY $vgpr1
+ %2:_(s64) = G_MERGE_VALUES %0(s32), %1(s32)
+ %3:_(s64) = G_CONSTANT i64 100
+ %4:_(s64) = G_SMAX %2, %3
+ %5:_(s64) = G_CONSTANT i64 0
+ %6:_(s64) = G_SMIN %4, %5
+ %7:_(s16) = G_TRUNC %6(s64)
+ %8:_(s32) = G_ANYEXT %7(s16)
+ $vgpr0 = COPY %8(s32)
+
+...
+
+---
+name: clamp_i64_i16_inverted_bounds_minmax
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $vgpr0, $vgpr1
+
+ ; CHECK-LABEL: name: clamp_i64_i16_inverted_bounds_minmax
+ ; CHECK: liveins: $vgpr0, $vgpr1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $vgpr1
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY]](s32), [[COPY1]](s32)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(s64) = G_SMIN [[MV]], [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 100
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(s64) = G_SMAX [[SMIN]], [[C1]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[SMAX]](s64)
+ ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[TRUNC]](s16)
+ ; CHECK-NEXT: $vgpr0 = COPY [[ANYEXT]](s32)
+ %0:_(s32) = COPY $vgpr0
+ %1:_(s32) = COPY $vgpr1
+ %2:_(s64) = G_MERGE_VALUES %0(s32), %1(s32)
+ %3:_(s64) = G_CONSTANT i64 0
+ %4:_(s64) = G_SMIN %2, %3
+ %5:_(s64) = G_CONSTANT i64 100
+ %6:_(s64) = G_SMAX %4, %5
+ %7:_(s16) = G_TRUNC %6(s64)
+ %8:_(s32) = G_ANYEXT %7(s16)
+ $vgpr0 = COPY %8(s32)
+
+...
>From 590c8f0cad5ab0446103c8267bbfce25c25c6a5c Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 27 Aug 2026 16:47:13 +0200
Subject: [PATCH 2/2] add tests
---
.../combine-short-clamp-inverted-bounds.mir | 66 +++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-short-clamp-inverted-bounds.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-short-clamp-inverted-bounds.mir
index 8f02f7fb113cb..924b58f720444 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-short-clamp-inverted-bounds.mir
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-short-clamp-inverted-bounds.mir
@@ -69,3 +69,69 @@ body: |
$vgpr0 = COPY %8(s32)
...
+
+---
+name: clamp_i64_i16_inverted_bounds_extreme
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $vgpr0, $vgpr1
+
+ ; CHECK-LABEL: name: clamp_i64_i16_inverted_bounds_extreme
+ ; CHECK: liveins: $vgpr0, $vgpr1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $vgpr1
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY]](s32), [[COPY1]](s32)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 32767
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(s64) = G_SMAX [[MV]], [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 -32768
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(s64) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[SMIN]](s64)
+ ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[TRUNC]](s16)
+ ; CHECK-NEXT: $vgpr0 = COPY [[ANYEXT]](s32)
+ %0:_(s32) = COPY $vgpr0
+ %1:_(s32) = COPY $vgpr1
+ %2:_(s64) = G_MERGE_VALUES %0(s32), %1(s32)
+ %3:_(s64) = G_CONSTANT i64 32767
+ %4:_(s64) = G_SMAX %2, %3
+ %5:_(s64) = G_CONSTANT i64 -32768
+ %6:_(s64) = G_SMIN %4, %5
+ %7:_(s16) = G_TRUNC %6(s64)
+ %8:_(s32) = G_ANYEXT %7(s16)
+ $vgpr0 = COPY %8(s32)
+
+...
+
+---
+name: clamp_i64_i16_inverted_bounds_extreme_minmax
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $vgpr0, $vgpr1
+
+ ; CHECK-LABEL: name: clamp_i64_i16_inverted_bounds_extreme_minmax
+ ; CHECK: liveins: $vgpr0, $vgpr1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $vgpr0
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $vgpr1
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY]](s32), [[COPY1]](s32)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 -32768
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(s64) = G_SMIN [[MV]], [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 32767
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(s64) = G_SMAX [[SMIN]], [[C1]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[SMAX]](s64)
+ ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[TRUNC]](s16)
+ ; CHECK-NEXT: $vgpr0 = COPY [[ANYEXT]](s32)
+ %0:_(s32) = COPY $vgpr0
+ %1:_(s32) = COPY $vgpr1
+ %2:_(s64) = G_MERGE_VALUES %0(s32), %1(s32)
+ %3:_(s64) = G_CONSTANT i64 -32768
+ %4:_(s64) = G_SMIN %2, %3
+ %5:_(s64) = G_CONSTANT i64 32767
+ %6:_(s64) = G_SMAX %4, %5
+ %7:_(s16) = G_TRUNC %6(s64)
+ %8:_(s32) = G_ANYEXT %7(s16)
+ $vgpr0 = COPY %8(s32)
+
+...
More information about the llvm-commits
mailing list