[llvm] [IR] Handle vector tys in ConstantExpr::getIntrinsicIdentity for smin/max. (PR #208367)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 8 19:58:28 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Justin Lebar (jlebar)

<details>
<summary>Changes</summary>

- **[LoopInfo] Avoid a throwaway vector in getLoopsInPreorder. NFC (#<!-- -->207607)**
- **[IR] Handle vector tys in ConstantExpr::getIntrinsicIdentity for smin/max.**


---
Full diff: https://github.com/llvm/llvm-project/pull/208367.diff


3 Files Affected:

- (modified) llvm/lib/IR/Constants.cpp (+2-2) 
- (modified) llvm/test/Transforms/TailCallElim/accum_recursion.ll (+77) 
- (modified) llvm/test/Transforms/TailCallElim/tre-minmax-intrinsic.ll (+81) 


``````````diff
diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp
index a606a2a27669b..39f5cbce32d9d 100644
--- a/llvm/lib/IR/Constants.cpp
+++ b/llvm/lib/IR/Constants.cpp
@@ -2900,10 +2900,10 @@ Constant *ConstantExpr::getIntrinsicIdentity(Intrinsic::ID ID, Type *Ty) {
     return Constant::getAllOnesValue(Ty);
   case Intrinsic::smax:
     return Constant::getIntegerValue(
-        Ty, APInt::getSignedMinValue(Ty->getIntegerBitWidth()));
+        Ty, APInt::getSignedMinValue(Ty->getScalarSizeInBits()));
   case Intrinsic::smin:
     return Constant::getIntegerValue(
-        Ty, APInt::getSignedMaxValue(Ty->getIntegerBitWidth()));
+        Ty, APInt::getSignedMaxValue(Ty->getScalarSizeInBits()));
   default:
     return nullptr;
   }
diff --git a/llvm/test/Transforms/TailCallElim/accum_recursion.ll b/llvm/test/Transforms/TailCallElim/accum_recursion.ll
index c8b46edd716bb..0ee581c006950 100644
--- a/llvm/test/Transforms/TailCallElim/accum_recursion.ll
+++ b/llvm/test/Transforms/TailCallElim/accum_recursion.ll
@@ -366,3 +366,80 @@ rec:
   %acc = mul nsw i32 %r, %g
   ret i32 %acc
 }
+
+%struct.VecListNode = type { <4 x i32>, ptr }
+
+; The identity for a vector add accumulator is the zero vector.
+define <4 x i32> @test_vector_add_accumulator(ptr %a) local_unnamed_addr {
+; CHECK-LABEL: define <4 x i32> @test_vector_add_accumulator(
+; CHECK-SAME: ptr [[A:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[TAILRECURSE:%.*]]
+; CHECK:       tailrecurse:
+; CHECK-NEXT:    [[ACCUMULATOR_TR:%.*]] = phi <4 x i32> [ zeroinitializer, [[ENTRY:%.*]] ], [ [[ADD:%.*]], [[IF_END:%.*]] ]
+; CHECK-NEXT:    [[A_TR:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[TMP1:%.*]], [[IF_END]] ]
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq ptr [[A_TR]], null
+; CHECK-NEXT:    br i1 [[TOBOOL_NOT]], label [[COMMON_RET:%.*]], label [[IF_END]]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[ACCUMULATOR_RET_TR:%.*]] = add <4 x i32> zeroinitializer, [[ACCUMULATOR_TR]]
+; CHECK-NEXT:    ret <4 x i32> [[ACCUMULATOR_RET_TR]]
+; CHECK:       if.end:
+; CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i32>, ptr [[A_TR]], align 16
+; CHECK-NEXT:    [[NEXT:%.*]] = getelementptr inbounds [[STRUCT_VECLISTNODE:%.*]], ptr [[A_TR]], i64 0, i32 1
+; CHECK-NEXT:    [[TMP1]] = load ptr, ptr [[NEXT]], align 8
+; CHECK-NEXT:    [[ADD]] = add <4 x i32> [[TMP0]], [[ACCUMULATOR_TR]]
+; CHECK-NEXT:    br label [[TAILRECURSE]]
+;
+entry:
+  %tobool.not = icmp eq ptr %a, null
+  br i1 %tobool.not, label %common.ret, label %if.end
+
+common.ret:                                       ; preds = %entry, %if.end
+  %common.ret.op = phi <4 x i32> [ %add, %if.end ], [ zeroinitializer, %entry ]
+  ret <4 x i32> %common.ret.op
+
+if.end:                                           ; preds = %entry
+  %0 = load <4 x i32>, ptr %a
+  %next = getelementptr inbounds %struct.VecListNode, ptr %a, i64 0, i32 1
+  %1 = load ptr, ptr %next
+  %call = tail call <4 x i32> @test_vector_add_accumulator(ptr %1)
+  %add = add <4 x i32> %0, %call
+  br label %common.ret
+}
+
+; Like the scalar case above, vector sadd.sat is not associative and must not
+; be turned into an accumulator.
+define <4 x i32> @test_non_associative_sadd_sat_vector(ptr %a) local_unnamed_addr {
+; CHECK-LABEL: define <4 x i32> @test_non_associative_sadd_sat_vector(
+; CHECK-SAME: ptr [[A:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq ptr [[A]], null
+; CHECK-NEXT:    br i1 [[TOBOOL_NOT]], label [[COMMON_RET:%.*]], label [[IF_END:%.*]]
+; CHECK:       common.ret:
+; CHECK-NEXT:    ret <4 x i32> zeroinitializer
+; CHECK:       if.end:
+; CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i32>, ptr [[A]], align 16
+; CHECK-NEXT:    [[NEXT:%.*]] = getelementptr inbounds [[STRUCT_VECLISTNODE:%.*]], ptr [[A]], i64 0, i32 1
+; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[NEXT]], align 8
+; CHECK-NEXT:    [[CALL:%.*]] = tail call <4 x i32> @test_non_associative_sadd_sat_vector(ptr [[TMP1]])
+; CHECK-NEXT:    [[SAT:%.*]] = tail call <4 x i32> @llvm.sadd.sat.v4i32(<4 x i32> [[TMP0]], <4 x i32> [[CALL]])
+; CHECK-NEXT:    ret <4 x i32> [[SAT]]
+;
+entry:
+  %tobool.not = icmp eq ptr %a, null
+  br i1 %tobool.not, label %common.ret, label %if.end
+
+common.ret:                                       ; preds = %entry, %if.end
+  %common.ret.op = phi <4 x i32> [ %sat, %if.end ], [ zeroinitializer, %entry ]
+  ret <4 x i32> %common.ret.op
+
+if.end:                                           ; preds = %entry
+  %0 = load <4 x i32>, ptr %a
+  %next = getelementptr inbounds %struct.VecListNode, ptr %a, i64 0, i32 1
+  %1 = load ptr, ptr %next
+  %call = tail call <4 x i32> @test_non_associative_sadd_sat_vector(ptr %1)
+  %sat = tail call <4 x i32> @llvm.sadd.sat.v4i32(<4 x i32> %0, <4 x i32> %call)
+  br label %common.ret
+}
+
+declare <4 x i32> @llvm.sadd.sat.v4i32(<4 x i32>, <4 x i32>)
diff --git a/llvm/test/Transforms/TailCallElim/tre-minmax-intrinsic.ll b/llvm/test/Transforms/TailCallElim/tre-minmax-intrinsic.ll
index f8d9f9f9fbcb7..b62a6215876d4 100644
--- a/llvm/test/Transforms/TailCallElim/tre-minmax-intrinsic.ll
+++ b/llvm/test/Transforms/TailCallElim/tre-minmax-intrinsic.ll
@@ -2,6 +2,7 @@
 ; RUN: opt < %s -passes=tailcallelim -verify-dom-info -S | FileCheck %s
 
 %struct.ListNode = type { i32, ptr }
+%struct.VecListNode = type { <4 x i32>, ptr }
 
 define i32 @umin(ptr readonly %a) {
 ; CHECK-LABEL: define i32 @umin
@@ -303,3 +304,83 @@ declare i32 @llvm.umin.i32(i32, i32)
 declare i32 @llvm.umax.i32(i32, i32)
 declare i32 @llvm.smin.i32(i32, i32)
 declare i32 @llvm.smax.i32(i32, i32)
+
+; The identity for a vector min/max accumulator is a splat of the scalar
+; identity.
+
+define <4 x i32> @smax_vector(ptr readonly %a) {
+; CHECK-LABEL: define <4 x i32> @smax_vector
+; CHECK-SAME: (ptr readonly [[A:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[TAILRECURSE:%.*]]
+; CHECK:       tailrecurse:
+; CHECK-NEXT:    [[ACCUMULATOR_TR:%.*]] = phi <4 x i32> [ splat (i32 -2147483648), [[ENTRY:%.*]] ], [ [[MAX:%.*]], [[IF_END:%.*]] ]
+; CHECK-NEXT:    [[A_TR:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[TMP1:%.*]], [[IF_END]] ]
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq ptr [[A_TR]], null
+; CHECK-NEXT:    br i1 [[TOBOOL_NOT]], label [[COMMON_RET:%.*]], label [[IF_END]]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[ACCUMULATOR_RET_TR:%.*]] = tail call <4 x i32> @llvm.smax.v4i32(<4 x i32> zeroinitializer, <4 x i32> [[ACCUMULATOR_TR]])
+; CHECK-NEXT:    ret <4 x i32> [[ACCUMULATOR_RET_TR]]
+; CHECK:       if.end:
+; CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i32>, ptr [[A_TR]], align 16
+; CHECK-NEXT:    [[NEXT:%.*]] = getelementptr inbounds [[STRUCT_VECLISTNODE:%.*]], ptr [[A_TR]], i64 0, i32 1
+; CHECK-NEXT:    [[TMP1]] = load ptr, ptr [[NEXT]], align 8
+; CHECK-NEXT:    [[MAX]] = tail call <4 x i32> @llvm.smax.v4i32(<4 x i32> [[TMP0]], <4 x i32> [[ACCUMULATOR_TR]])
+; CHECK-NEXT:    br label [[TAILRECURSE]]
+;
+entry:
+  %tobool.not = icmp eq ptr %a, null
+  br i1 %tobool.not, label %common.ret, label %if.end
+
+common.ret:                                       ; preds = %entry, %if.end
+  %common.ret.op = phi <4 x i32> [ %max, %if.end ], [ zeroinitializer, %entry ]
+  ret <4 x i32> %common.ret.op
+
+if.end:                                           ; preds = %entry
+  %0 = load <4 x i32>, ptr %a
+  %next = getelementptr inbounds %struct.VecListNode, ptr %a, i64 0, i32 1
+  %1 = load ptr, ptr %next
+  %call = tail call <4 x i32> @smax_vector(ptr %1)
+  %max = tail call <4 x i32> @llvm.smax.v4i32(<4 x i32> %0, <4 x i32> %call)
+  br label %common.ret
+}
+
+define <4 x i32> @smin_vector(ptr readonly %a) {
+; CHECK-LABEL: define <4 x i32> @smin_vector
+; CHECK-SAME: (ptr readonly [[A:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[TAILRECURSE:%.*]]
+; CHECK:       tailrecurse:
+; CHECK-NEXT:    [[ACCUMULATOR_TR:%.*]] = phi <4 x i32> [ splat (i32 2147483647), [[ENTRY:%.*]] ], [ [[MIN:%.*]], [[IF_END:%.*]] ]
+; CHECK-NEXT:    [[A_TR:%.*]] = phi ptr [ [[A]], [[ENTRY]] ], [ [[TMP1:%.*]], [[IF_END]] ]
+; CHECK-NEXT:    [[TOBOOL_NOT:%.*]] = icmp eq ptr [[A_TR]], null
+; CHECK-NEXT:    br i1 [[TOBOOL_NOT]], label [[COMMON_RET:%.*]], label [[IF_END]]
+; CHECK:       common.ret:
+; CHECK-NEXT:    [[ACCUMULATOR_RET_TR:%.*]] = tail call <4 x i32> @llvm.smin.v4i32(<4 x i32> zeroinitializer, <4 x i32> [[ACCUMULATOR_TR]])
+; CHECK-NEXT:    ret <4 x i32> [[ACCUMULATOR_RET_TR]]
+; CHECK:       if.end:
+; CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i32>, ptr [[A_TR]], align 16
+; CHECK-NEXT:    [[NEXT:%.*]] = getelementptr inbounds [[STRUCT_VECLISTNODE:%.*]], ptr [[A_TR]], i64 0, i32 1
+; CHECK-NEXT:    [[TMP1]] = load ptr, ptr [[NEXT]], align 8
+; CHECK-NEXT:    [[MIN]] = tail call <4 x i32> @llvm.smin.v4i32(<4 x i32> [[TMP0]], <4 x i32> [[ACCUMULATOR_TR]])
+; CHECK-NEXT:    br label [[TAILRECURSE]]
+;
+entry:
+  %tobool.not = icmp eq ptr %a, null
+  br i1 %tobool.not, label %common.ret, label %if.end
+
+common.ret:                                       ; preds = %entry, %if.end
+  %common.ret.op = phi <4 x i32> [ %min, %if.end ], [ zeroinitializer, %entry ]
+  ret <4 x i32> %common.ret.op
+
+if.end:                                           ; preds = %entry
+  %0 = load <4 x i32>, ptr %a
+  %next = getelementptr inbounds %struct.VecListNode, ptr %a, i64 0, i32 1
+  %1 = load ptr, ptr %next
+  %call = tail call <4 x i32> @smin_vector(ptr %1)
+  %min = tail call <4 x i32> @llvm.smin.v4i32(<4 x i32> %0, <4 x i32> %call)
+  br label %common.ret
+}
+
+declare <4 x i32> @llvm.smax.v4i32(<4 x i32>, <4 x i32>)
+declare <4 x i32> @llvm.smin.v4i32(<4 x i32>, <4 x i32>)

``````````

</details>


https://github.com/llvm/llvm-project/pull/208367


More information about the llvm-commits mailing list