[llvm] [VPlan] Fix miscompile when widening induction with loop-variant step (PR #206010)
David Sherwood via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 26 02:24:22 PDT 2026
================
@@ -0,0 +1,131 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=loop-vectorize -S -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at .str.1 = constant [21 x i8] c"counter = %d\0A\00"
+
+; Variant-step Add induction: counter increments by (cmp ? 1 : 0), which is loop-variant.
+; Without the fix, VPlan replaced the variant step with IndDesc.getStep() and computed
+; the wrong exit value, emitting 'zext i8 2 to i32' and printf(..., i32 2).
+define i32 @_Z1fhi() {
+; CHECK-LABEL: define i32 @_Z1fhi() {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[WHILE_BODY:.*]]
+; CHECK: [[WHILE_BODY]]:
+; CHECK-NEXT: [[COUNTER_07:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[COUNTER_1:%.*]], %[[WHILE_BODY]] ]
+; CHECK-NEXT: [[SUMB_05:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[SUMB_1:%.*]], %[[WHILE_BODY]] ]
+; CHECK-NEXT: [[SUMB_1]] = or i32 0, [[SUMB_05]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[COUNTER_07]], 1
+; CHECK-NEXT: [[INC:%.*]] = zext i1 [[CMP]] to i8
+; CHECK-NEXT: [[COUNTER_1]] = add i8 [[COUNTER_07]], [[INC]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[WHILE_BODY]], label %[[WHILE_END:.*]]
+; CHECK: [[WHILE_END]]:
+; CHECK-NEXT: [[COUNTER_1_LCSSA:%.*]] = phi i8 [ [[COUNTER_1]], %[[WHILE_BODY]] ]
+; CHECK-NEXT: [[SUMB_1_LCSSA:%.*]] = phi i32 [ [[SUMB_1]], %[[WHILE_BODY]] ]
+; CHECK-NEXT: [[CONV2:%.*]] = zext i8 [[COUNTER_1_LCSSA]] to i32
+; CHECK-NEXT: [[CALL3:%.*]] = tail call i32 (ptr, ...) @printf(ptr @.str.1, i32 [[CONV2]])
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ br label %while.body
+
+while.body:
+ %counter.07 = phi i8 [ 0, %entry ], [ %counter.1, %while.body ]
+ %sumB.05 = phi i32 [ 0, %entry ], [ %sumB.1, %while.body ]
+ %sumB.1 = or i32 0, %sumB.05
+ %cmp = icmp ult i8 %counter.07, 1
+ %inc = zext i1 %cmp to i8
+ %counter.1 = add i8 %counter.07, %inc
+ br i1 %cmp, label %while.body, label %while.end
+
+while.end:
+ %sumB.1.lcssa = phi i32 [ %sumB.1, %while.body ]
+ %conv2 = zext i8 %counter.1 to i32
+ %call3 = tail call i32 (ptr, ...) @printf(ptr @.str.1, i32 %conv2)
+ ret i32 0
+}
+
+; Constant-step Sub induction: i decrements by 1 each iteration (step loop-invariant).
+; Exercises the Instruction::Sub branch in hasVariantStepIncrement, which must
+; correctly identify the step as loop-invariant and allow normal VPlan widening.
+define void @test_sub_const_step(i32 %n, ptr nocapture %a) {
+; CHECK-LABEL: define void @test_sub_const_step(
+; CHECK-SAME: i32 [[N:%.*]], ptr captures(none) [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[TMP0:%.*]] = add nsw i32 [[N]], -1
+; CHECK-NEXT: [[SMIN1:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP0]], i32 0)
+; CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[N]], [[SMIN1]]
+; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i32 [[TMP1]], 20
+; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label %[[SCALAR_PH:.*]], label %[[VECTOR_SCEVCHECK:.*]]
+; CHECK: [[VECTOR_SCEVCHECK]]:
+; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[N]], -1
+; CHECK-NEXT: [[TMP3:%.*]] = add nsw i32 [[N]], -1
+; CHECK-NEXT: [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP3]], i32 0)
+; CHECK-NEXT: [[TMP4:%.*]] = sub i32 [[TMP2]], [[SMIN]]
+; CHECK-NEXT: [[TMP5:%.*]] = sub i32 [[N]], [[TMP4]]
+; CHECK-NEXT: [[TMP6:%.*]] = icmp sgt i32 [[TMP5]], [[N]]
+; CHECK-NEXT: br i1 [[TMP6]], label %[[SCALAR_PH]], label %[[VECTOR_PH:.*]]
+; CHECK: [[VECTOR_PH]]:
+; CHECK-NEXT: [[N_MOD_VF:%.*]] = urem i32 [[TMP1]], 8
+; CHECK-NEXT: [[N_VEC:%.*]] = sub i32 [[TMP1]], [[N_MOD_VF]]
+; CHECK-NEXT: [[TMP7:%.*]] = sub i32 [[N]], [[N_VEC]]
+; CHECK-NEXT: [[BROADCAST_SPLATINSERT:%.*]] = insertelement <4 x i32> poison, i32 [[N]], i64 0
+; CHECK-NEXT: [[BROADCAST_SPLAT:%.*]] = shufflevector <4 x i32> [[BROADCAST_SPLATINSERT]], <4 x i32> poison, <4 x i32> zeroinitializer
+; CHECK-NEXT: [[TMP8:%.*]] = sub nsw <4 x i32> [[BROADCAST_SPLAT]], <i32 0, i32 1, i32 2, i32 3>
+; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
+; CHECK: [[VECTOR_BODY]]:
+; CHECK-NEXT: [[INDEX:%.*]] = phi i32 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
+; CHECK-NEXT: [[VEC_IND:%.*]] = phi <4 x i32> [ [[TMP8]], %[[VECTOR_PH]] ], [ [[VEC_IND_NEXT:%.*]], %[[VECTOR_BODY]] ]
+; CHECK-NEXT: [[STEP_ADD:%.*]] = add nsw <4 x i32> [[VEC_IND]], splat (i32 -4)
+; CHECK-NEXT: [[TMP9:%.*]] = sub i32 [[N]], [[INDEX]]
+; CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 [[TMP9]]
+; CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds i32, ptr [[TMP10]], i64 -3
+; CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds i32, ptr [[TMP10]], i64 -7
+; CHECK-NEXT: [[REVERSE:%.*]] = shufflevector <4 x i32> [[VEC_IND]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
+; CHECK-NEXT: [[REVERSE2:%.*]] = shufflevector <4 x i32> [[STEP_ADD]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
+; CHECK-NEXT: store <4 x i32> [[REVERSE]], ptr [[TMP11]], align 4
+; CHECK-NEXT: store <4 x i32> [[REVERSE2]], ptr [[TMP12]], align 4
+; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i32 [[INDEX]], 8
+; CHECK-NEXT: [[VEC_IND_NEXT]] = add nsw <4 x i32> [[STEP_ADD]], splat (i32 -4)
+; CHECK-NEXT: [[TMP13:%.*]] = icmp eq i32 [[INDEX_NEXT]], [[N_VEC]]
+; CHECK-NEXT: br i1 [[TMP13]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP0:![0-9]+]]
+; CHECK: [[MIDDLE_BLOCK]]:
+; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i32 [[TMP1]], [[N_VEC]]
+; CHECK-NEXT: br i1 [[CMP_N]], label %[[FOR_END:.*]], label %[[SCALAR_PH]]
+; CHECK: [[SCALAR_PH]]:
+; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i32 [ [[TMP7]], %[[MIDDLE_BLOCK]] ], [ [[N]], %[[ENTRY]] ], [ [[N]], %[[VECTOR_SCEVCHECK]] ]
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: [[I:%.*]] = phi i32 [ [[BC_RESUME_VAL]], %[[SCALAR_PH]] ], [ [[I_NEXT:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 [[I]]
+; CHECK-NEXT: store i32 [[I]], ptr [[PTR]], align 4
+; CHECK-NEXT: [[I_NEXT]] = sub nsw i32 [[I]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[I_NEXT]], 0
+; CHECK-NEXT: br i1 [[CMP]], label %[[FOR_BODY]], label %[[FOR_END]], !llvm.loop [[LOOP3:![0-9]+]]
+; CHECK: [[FOR_END]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.body
+
+for.body:
----------------
david-arm wrote:
nit: Can rename to `loop:` I think for clarity.
https://github.com/llvm/llvm-project/pull/206010
More information about the llvm-commits
mailing list