[llvm] 2c8d864 - [SLP][NFC]Add a test with the reassociated operands, NFC

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 2 09:35:40 PDT 2026


Author: Alexey Bataev
Date: 2026-07-02T12:35:35-04:00
New Revision: 2c8d8643c32c9b043f37587807585d54be96e53b

URL: https://github.com/llvm/llvm-project/commit/2c8d8643c32c9b043f37587807585d54be96e53b
DIFF: https://github.com/llvm/llvm-project/commit/2c8d8643c32c9b043f37587807585d54be96e53b.diff

LOG: [SLP][NFC]Add a test with the reassociated operands, NFC



Reviewers: 

Pull Request: https://github.com/llvm/llvm-project/pull/207226

Added: 
    llvm/test/Transforms/SLPVectorizer/X86/reassociate-ops.ll

Modified: 
    

Removed: 
    


################################################################################
diff  --git a/llvm/test/Transforms/SLPVectorizer/X86/reassociate-ops.ll b/llvm/test/Transforms/SLPVectorizer/X86/reassociate-ops.ll
new file mode 100644
index 0000000000000..84857883f9b60
--- /dev/null
+++ b/llvm/test/Transforms/SLPVectorizer/X86/reassociate-ops.ll
@@ -0,0 +1,294 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=slp-vectorizer -S < %s -mtriple=x86_64-unknown-linux -mcpu=corei7-avx | FileCheck %s
+
+;
+; Lanes 0 and 1 use the same 3 terms {A, B, C}, just nested/paired
+; 
diff erently, so with reassociation enabled all 3 terms should be loadable
+; as plain contiguous vectors with no insertelement/shufflevector gather at
+; all, unlike the DEFAULT (flag disabled) behavior.
+define void @test_reassoc_add(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_add(
+; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[SARRAY:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load <2 x double>, ptr [[AARRAY]], align 8
+; CHECK-NEXT:    [[TMP1:%.*]] = load <2 x double>, ptr [[BARRAY]], align 8
+; CHECK-NEXT:    [[TMP2:%.*]] = load <2 x double>, ptr [[CARRAY]], align 8
+; CHECK-NEXT:    [[TMP3:%.*]] = shufflevector <2 x double> [[TMP0]], <2 x double> [[TMP2]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP4:%.*]] = fadd fast <2 x double> [[TMP1]], [[TMP3]]
+; CHECK-NEXT:    [[TMP5:%.*]] = shufflevector <2 x double> [[TMP2]], <2 x double> [[TMP0]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP6:%.*]] = fadd fast <2 x double> [[TMP4]], [[TMP5]]
+; CHECK-NEXT:    store <2 x double> [[TMP6]], ptr [[SARRAY]], align 8
+; CHECK-NEXT:    ret void
+;
+entry:
+  %idxA1 = getelementptr inbounds double, ptr %Aarray, i64 1
+  %idxB1 = getelementptr inbounds double, ptr %Barray, i64 1
+  %idxC1 = getelementptr inbounds double, ptr %Carray, i64 1
+  %idxS1 = getelementptr inbounds double, ptr %Sarray, i64 1
+
+  %A0 = load double, ptr %Aarray, align 8
+  %A1 = load double, ptr %idxA1, align 8
+
+  %B0 = load double, ptr %Barray, align 8
+  %B1 = load double, ptr %idxB1, align 8
+
+  %C0 = load double, ptr %Carray, align 8
+  %C1 = load double, ptr %idxC1, align 8
+
+  %addA0B0 = fadd fast double %A0, %B0
+  %addB1C1 = fadd fast double %B1, %C1
+  %add0 = fadd fast double %addA0B0, %C0
+  %add1 = fadd fast double %addB1C1, %A1
+  store double %add0, ptr %Sarray, align 8
+  store double %add1, ptr %idxS1, align 8
+  ret void
+}
+
+; Wrap-flag propagation must not be unsound across reassociation. Every
+; source add below is "nsw nuw", but nsw is never safe to keep on a
+; re-paired combine: a chain that never overflows in its original order can
+; still overflow when re-paired (e.g. large positive and negative terms
+; that cancel out along the original path but not a new one), so nsw must
+; always be dropped from the recombined vector ops. nuw is safe to keep for
+; Add specifically, since every operand is non-negative, so a re-paired
+; partial sum can only be smaller than or equal to the known-in-range
+; total.
+;
+; S[0] = (A[0] + B[0]) + C[0]
+; S[1] = (B[1] + C[1]) + A[1]
+define void @test_reassoc_add_wrapflags(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_add_wrapflags(
+; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[SARRAY:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load <2 x i32>, ptr [[AARRAY]], align 4
+; CHECK-NEXT:    [[TMP1:%.*]] = load <2 x i32>, ptr [[BARRAY]], align 4
+; CHECK-NEXT:    [[TMP2:%.*]] = load <2 x i32>, ptr [[CARRAY]], align 4
+; CHECK-NEXT:    [[TMP3:%.*]] = shufflevector <2 x i32> [[TMP0]], <2 x i32> [[TMP2]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP4:%.*]] = add nuw nsw <2 x i32> [[TMP1]], [[TMP3]]
+; CHECK-NEXT:    [[TMP5:%.*]] = shufflevector <2 x i32> [[TMP2]], <2 x i32> [[TMP0]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP6:%.*]] = add nuw nsw <2 x i32> [[TMP4]], [[TMP5]]
+; CHECK-NEXT:    store <2 x i32> [[TMP6]], ptr [[SARRAY]], align 4
+; CHECK-NEXT:    ret void
+;
+entry:
+  %idxA1 = getelementptr inbounds i32, ptr %Aarray, i64 1
+  %idxB1 = getelementptr inbounds i32, ptr %Barray, i64 1
+  %idxC1 = getelementptr inbounds i32, ptr %Carray, i64 1
+  %idxS1 = getelementptr inbounds i32, ptr %Sarray, i64 1
+
+  %A0 = load i32, ptr %Aarray, align 4
+  %A1 = load i32, ptr %idxA1, align 4
+
+  %B0 = load i32, ptr %Barray, align 4
+  %B1 = load i32, ptr %idxB1, align 4
+
+  %C0 = load i32, ptr %Carray, align 4
+  %C1 = load i32, ptr %idxC1, align 4
+
+  %addA0B0 = add nsw nuw i32 %A0, %B0
+  %addB1C1 = add nsw nuw i32 %B1, %C1
+  %add0 = add nsw nuw i32 %addA0B0, %C0
+  %add1 = add nsw nuw i32 %addB1C1, %A1
+  store i32 %add0, ptr %Sarray, align 4
+  store i32 %add1, ptr %idxS1, align 4
+  ret void
+}
+
+; When every lane nests its chain the same way, a combine *can* exactly
+; reconstruct a real source instruction lane by lane, letting its own flags
+; be reused instead of falling back to the conservative dropping above -
+; but only if the pairwise tree actually ends up combining that same pair
+; of columns together. scanAssociativeOperands() replaces the peeled
+; column in place (A here) and appends the newly exposed one at the end
+; (B here), so the pre-reorder column order is [A, C, B], not [A, B, C];
+; unless reordering happens to move B and A back next to each other, the
+; tree's fixed pairwise-halving (combine position 0 and 1 first) combines
+; A with C - never a real instruction here - and falls back to dropping,
+; exactly like test_reassoc_add_wrapflags above, even though every lane
+; nests identically. See test_reassoc_add_wrapflags's exact-adjacency
+; comment for why the check below still proves nsw is gone.
+;
+; S[0] = (A[0] + B[0]) + C[0]
+; S[1] = (A[1] + B[1]) + C[1]
+define void @test_reassoc_add_preserves_flags(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_add_preserves_flags(
+; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[SARRAY:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load <2 x i32>, ptr [[AARRAY]], align 4
+; CHECK-NEXT:    [[TMP1:%.*]] = load <2 x i32>, ptr [[BARRAY]], align 4
+; CHECK-NEXT:    [[TMP2:%.*]] = load <2 x i32>, ptr [[CARRAY]], align 4
+; CHECK-NEXT:    [[TMP3:%.*]] = add nuw nsw <2 x i32> [[TMP0]], [[TMP1]]
+; CHECK-NEXT:    [[TMP4:%.*]] = add nuw nsw <2 x i32> [[TMP3]], [[TMP2]]
+; CHECK-NEXT:    store <2 x i32> [[TMP4]], ptr [[SARRAY]], align 4
+; CHECK-NEXT:    ret void
+;
+entry:
+  %idxA1 = getelementptr inbounds i32, ptr %Aarray, i64 1
+  %idxB1 = getelementptr inbounds i32, ptr %Barray, i64 1
+  %idxC1 = getelementptr inbounds i32, ptr %Carray, i64 1
+  %idxS1 = getelementptr inbounds i32, ptr %Sarray, i64 1
+
+  %A0 = load i32, ptr %Aarray, align 4
+  %A1 = load i32, ptr %idxA1, align 4
+
+  %B0 = load i32, ptr %Barray, align 4
+  %B1 = load i32, ptr %idxB1, align 4
+
+  %C0 = load i32, ptr %Carray, align 4
+  %C1 = load i32, ptr %idxC1, align 4
+
+  %addA0B0 = add nuw nsw i32 %A0, %B0
+  %addA1B1 = add nuw nsw i32 %A1, %B1
+  %add0 = add nuw nsw i32 %addA0B0, %C0
+  %add1 = add nuw nsw i32 %addA1B1, %C1
+  store i32 %add0, ptr %Sarray, align 4
+  store i32 %add1, ptr %idxS1, align 4
+  ret void
+}
+
+; Same shape, but Mul: unlike Add, nuw is not safe to keep either, since a
+; zero factor anywhere can make a re-paired partial product look in-range
+; (multiplying by zero never overflows) while masking that the other
+; factors' own product is out of range.
+;
+; S[0] = (A[0] * B[0]) * C[0]
+; S[1] = (B[1] * C[1]) * A[1]
+define void @test_reassoc_mul_wrapflags(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_mul_wrapflags(
+; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[SARRAY:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load <2 x i32>, ptr [[AARRAY]], align 4
+; CHECK-NEXT:    [[TMP1:%.*]] = load <2 x i32>, ptr [[BARRAY]], align 4
+; CHECK-NEXT:    [[TMP2:%.*]] = load <2 x i32>, ptr [[CARRAY]], align 4
+; CHECK-NEXT:    [[TMP3:%.*]] = shufflevector <2 x i32> [[TMP0]], <2 x i32> [[TMP2]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP4:%.*]] = mul nuw nsw <2 x i32> [[TMP1]], [[TMP3]]
+; CHECK-NEXT:    [[TMP5:%.*]] = shufflevector <2 x i32> [[TMP2]], <2 x i32> [[TMP0]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP6:%.*]] = mul nuw nsw <2 x i32> [[TMP4]], [[TMP5]]
+; CHECK-NEXT:    store <2 x i32> [[TMP6]], ptr [[SARRAY]], align 4
+; CHECK-NEXT:    ret void
+;
+entry:
+  %idxA1 = getelementptr inbounds i32, ptr %Aarray, i64 1
+  %idxB1 = getelementptr inbounds i32, ptr %Barray, i64 1
+  %idxC1 = getelementptr inbounds i32, ptr %Carray, i64 1
+  %idxS1 = getelementptr inbounds i32, ptr %Sarray, i64 1
+
+  %A0 = load i32, ptr %Aarray, align 4
+  %A1 = load i32, ptr %idxA1, align 4
+
+  %B0 = load i32, ptr %Barray, align 4
+  %B1 = load i32, ptr %idxB1, align 4
+
+  %C0 = load i32, ptr %Carray, align 4
+  %C1 = load i32, ptr %idxC1, align 4
+
+  %mulA0B0 = mul nsw nuw i32 %A0, %B0
+  %mulB1C1 = mul nsw nuw i32 %B1, %C1
+  %mul0 = mul nsw nuw i32 %mulA0B0, %C0
+  %mul1 = mul nsw nuw i32 %mulB1C1, %A1
+  store i32 %mul0, ptr %Sarray, align 4
+  store i32 %mul1, ptr %idxS1, align 4
+  ret void
+}
+
+; Same nnan/ninf-dropping requirement, but for FMul: isAssociative() only
+; requires reassoc for FMul (unlike FAdd, which also requires nsz), but a
+; source using "fast" sets nnan/ninf too, so the same regrouping hazard
+; from test_reassoc_mul_wrapflags applies (a zero-adjacent chain hiding an
+; out-of-range product elsewhere overflows to Inf under FP rules the same
+; way it wraps under integer rules).
+;
+; S[0] = (A[0] * B[0]) * C[0]
+; S[1] = (B[1] * C[1]) * A[1]
+define void @test_reassoc_mul_fast(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_mul_fast(
+; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[SARRAY:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load <2 x double>, ptr [[AARRAY]], align 8
+; CHECK-NEXT:    [[TMP1:%.*]] = load <2 x double>, ptr [[BARRAY]], align 8
+; CHECK-NEXT:    [[TMP2:%.*]] = load <2 x double>, ptr [[CARRAY]], align 8
+; CHECK-NEXT:    [[TMP3:%.*]] = shufflevector <2 x double> [[TMP0]], <2 x double> [[TMP2]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP4:%.*]] = fmul fast <2 x double> [[TMP1]], [[TMP3]]
+; CHECK-NEXT:    [[TMP5:%.*]] = shufflevector <2 x double> [[TMP2]], <2 x double> [[TMP0]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP6:%.*]] = fmul fast <2 x double> [[TMP4]], [[TMP5]]
+; CHECK-NEXT:    store <2 x double> [[TMP6]], ptr [[SARRAY]], align 8
+; CHECK-NEXT:    ret void
+;
+entry:
+  %idxA1 = getelementptr inbounds double, ptr %Aarray, i64 1
+  %idxB1 = getelementptr inbounds double, ptr %Barray, i64 1
+  %idxC1 = getelementptr inbounds double, ptr %Carray, i64 1
+  %idxS1 = getelementptr inbounds double, ptr %Sarray, i64 1
+
+  %A0 = load double, ptr %Aarray, align 8
+  %A1 = load double, ptr %idxA1, align 8
+
+  %B0 = load double, ptr %Barray, align 8
+  %B1 = load double, ptr %idxB1, align 8
+
+  %C0 = load double, ptr %Carray, align 8
+  %C1 = load double, ptr %idxC1, align 8
+
+  %mulA0B0 = fmul fast double %A0, %B0
+  %mulB1C1 = fmul fast double %B1, %C1
+  %mul0 = fmul fast double %mulA0B0, %C0
+  %mul1 = fmul fast double %mulB1C1, %A1
+  store double %mul0, ptr %Sarray, align 8
+  store double %mul1, ptr %idxS1, align 8
+  ret void
+}
+
+; A deeper chain: 4 operands (add of 3 nested adds), same 4 terms {A,B,C,D}
+; shared identically across both lanes, just to exercise more than one level
+; of peeling in scanAssociativeOperands().
+;
+; S[0] = ((A[0] + B[0]) + C[0]) + D[0]
+; S[1] = ((B[1] + C[1]) + D[1]) + A[1]
+define void @test_reassoc_add_deep(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Darray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_add_deep(
+; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[DARRAY:%.*]], ptr [[SARRAY:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load <2 x double>, ptr [[AARRAY]], align 8
+; CHECK-NEXT:    [[TMP1:%.*]] = load <2 x double>, ptr [[BARRAY]], align 8
+; CHECK-NEXT:    [[TMP2:%.*]] = load <2 x double>, ptr [[CARRAY]], align 8
+; CHECK-NEXT:    [[TMP3:%.*]] = load <2 x double>, ptr [[DARRAY]], align 8
+; CHECK-NEXT:    [[TMP4:%.*]] = shufflevector <2 x double> [[TMP0]], <2 x double> [[TMP2]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP5:%.*]] = fadd fast <2 x double> [[TMP1]], [[TMP4]]
+; CHECK-NEXT:    [[TMP6:%.*]] = shufflevector <2 x double> [[TMP2]], <2 x double> [[TMP3]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP7:%.*]] = fadd fast <2 x double> [[TMP5]], [[TMP6]]
+; CHECK-NEXT:    [[TMP8:%.*]] = shufflevector <2 x double> [[TMP3]], <2 x double> [[TMP0]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT:    [[TMP9:%.*]] = fadd fast <2 x double> [[TMP7]], [[TMP8]]
+; CHECK-NEXT:    store <2 x double> [[TMP9]], ptr [[SARRAY]], align 8
+; CHECK-NEXT:    ret void
+;
+entry:
+  %idxA1 = getelementptr inbounds double, ptr %Aarray, i64 1
+  %idxB1 = getelementptr inbounds double, ptr %Barray, i64 1
+  %idxC1 = getelementptr inbounds double, ptr %Carray, i64 1
+  %idxD1 = getelementptr inbounds double, ptr %Darray, i64 1
+  %idxS1 = getelementptr inbounds double, ptr %Sarray, i64 1
+
+  %A0 = load double, ptr %Aarray, align 8
+  %A1 = load double, ptr %idxA1, align 8
+
+  %B0 = load double, ptr %Barray, align 8
+  %B1 = load double, ptr %idxB1, align 8
+
+  %C0 = load double, ptr %Carray, align 8
+  %C1 = load double, ptr %idxC1, align 8
+
+  %D0 = load double, ptr %Darray, align 8
+  %D1 = load double, ptr %idxD1, align 8
+
+  %addA0B0 = fadd fast double %A0, %B0
+  %addA0B0C0 = fadd fast double %addA0B0, %C0
+  %add0 = fadd fast double %addA0B0C0, %D0
+
+  %addB1C1 = fadd fast double %B1, %C1
+  %addB1C1D1 = fadd fast double %addB1C1, %D1
+  %add1 = fadd fast double %addB1C1D1, %A1
+
+  store double %add0, ptr %Sarray, align 8
+  store double %add1, ptr %idxS1, align 8
+  ret void
+}


        


More information about the llvm-commits mailing list