[llvm] [SLP][NFC]Add extra tests for fadd/fsub reordering, NFC (PR #213002)
Alexey Bataev via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 30 04:54:06 PDT 2026
https://github.com/alexey-bataev created https://github.com/llvm/llvm-project/pull/213002
None
>From e97ac1db2b53cc0e53af2284454783583d1f5b2f Mon Sep 17 00:00:00 2001
From: Alexey Bataev <a.bataev at outlook.com>
Date: Thu, 30 Jul 2026 04:53:53 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
=?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.7
---
.../SLPVectorizer/X86/reassociate-ops.ll | 598 ++++++++++++++++++
1 file changed, 598 insertions(+)
diff --git a/llvm/test/Transforms/SLPVectorizer/X86/reassociate-ops.ll b/llvm/test/Transforms/SLPVectorizer/X86/reassociate-ops.ll
index fe00dc00df394..8c60348ce1707 100644
--- a/llvm/test/Transforms/SLPVectorizer/X86/reassociate-ops.ll
+++ b/llvm/test/Transforms/SLPVectorizer/X86/reassociate-ops.ll
@@ -600,3 +600,601 @@ entry:
store i8 %add1, ptr %idxS1, align 8
ret void
}
+
+; Mixed add/sub chain: lane 0 subtracts B and D, lane 1 subtracts D and B.
+; The flattened subtracted columns must be realigned among themselves only
+; (a subtracted leaf never lands in an added column), so the subtrahends
+; regroup into the consecutive B and D load columns.
+;
+; S[0] = (A[0] - B[0]) + (C[0] - D[0])
+; S[1] = (A[1] - D[1]) + (C[1] - B[1])
+define void @test_reassoc_add_sub(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Darray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_add_sub(
+; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[DARRAY:%.*]], 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:%.*]] = load <2 x i32>, ptr [[DARRAY]], align 4
+; CHECK-NEXT: [[TMP4:%.*]] = shufflevector <2 x i32> [[TMP1]], <2 x i32> [[TMP3]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP5:%.*]] = sub <2 x i32> [[TMP0]], [[TMP4]]
+; CHECK-NEXT: [[TMP8:%.*]] = shufflevector <2 x i32> [[TMP3]], <2 x i32> [[TMP1]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP7:%.*]] = sub <2 x i32> [[TMP2]], [[TMP8]]
+; CHECK-NEXT: [[TMP6:%.*]] = add <2 x i32> [[TMP5]], [[TMP7]]
+; 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
+ %idxD1 = getelementptr inbounds i32, ptr %Darray, 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
+
+ %D0 = load i32, ptr %Darray, align 4
+ %D1 = load i32, ptr %idxD1, align 4
+
+ %subA0B0 = sub i32 %A0, %B0
+ %subC0D0 = sub i32 %C0, %D0
+ %add0 = add i32 %subA0B0, %subC0D0
+
+ %subA1D1 = sub i32 %A1, %D1
+ %subC1B1 = sub i32 %C1, %B1
+ %add1 = add i32 %subA1D1, %subC1B1
+
+ store i32 %add0, ptr %Sarray, align 4
+ store i32 %add1, ptr %idxS1, align 4
+ ret void
+}
+
+; A pure subtract chain flattens as a single added leaf minus the summed
+; subtrahends. Every scalar below is "nsw nuw", but a regrouped partial sum
+; can exceed the original running total (the minuend is not part of the
+; negated group), so both wrap flags must be dropped on the vector ops.
+;
+; S[0] = (A[0] - B[0]) - C[0]
+; S[1] = (A[1] - C[1]) - B[1]
+define void @test_reassoc_sub_chain_wrapflags(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_sub_chain_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> [[TMP1]], <2 x i32> [[TMP2]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP6:%.*]] = sub nuw nsw <2 x i32> [[TMP0]], [[TMP3]]
+; CHECK-NEXT: [[TMP5:%.*]] = shufflevector <2 x i32> [[TMP2]], <2 x i32> [[TMP1]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP4:%.*]] = sub nuw nsw <2 x i32> [[TMP6]], [[TMP5]]
+; 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
+
+ %subA0B0 = sub nsw nuw i32 %A0, %B0
+ %sub0 = sub nsw nuw i32 %subA0B0, %C0
+
+ %subA1C1 = sub nsw nuw i32 %A1, %C1
+ %sub1 = sub nsw nuw i32 %subA1C1, %B1
+
+ store i32 %sub0, ptr %Sarray, align 4
+ store i32 %sub1, ptr %idxS1, align 4
+ ret void
+}
+
+; Both lanes pair the same added terms with the operands swapped, so each
+; vector combine exactly reproduces a real source instruction (the added
+; pair is keyed operand-order independently): the (A + B) add and the final
+; subtract keep their own flags instead of falling back to conservative
+; dropping.
+;
+; S[0] = (A[0] + B[0]) - C[0]
+; S[1] = (B[1] + A[1]) - C[1]
+define void @test_reassoc_sub_preserves_flags(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_sub_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> [[TMP1]], [[TMP0]]
+; CHECK-NEXT: [[TMP4:%.*]] = sub 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
+ %sub0 = sub nuw nsw i32 %addA0B0, %C0
+
+ %addB1A1 = add nuw nsw i32 %B1, %A1
+ %sub1 = sub nuw nsw i32 %addB1A1, %C1
+
+ store i32 %sub0, ptr %Sarray, align 4
+ store i32 %sub1, ptr %idxS1, align 4
+ ret void
+}
+
+; The fadd/fsub counterpart of test_reassoc_add_sub: "fast" carries reassoc,
+; so the float subtracts flatten too; nnan/ninf are dropped on the
+; regrouped vector ops, the rest of the fast-math set is kept.
+;
+; S[0] = (A[0] - B[0]) + (C[0] - D[0])
+; S[1] = (A[1] - D[1]) + (C[1] - B[1])
+define void @test_reassoc_fadd_fsub(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Darray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_fadd_fsub(
+; 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> [[TMP1]], <2 x double> [[TMP3]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP5:%.*]] = fsub fast <2 x double> [[TMP0]], [[TMP4]]
+; CHECK-NEXT: [[TMP8:%.*]] = shufflevector <2 x double> [[TMP3]], <2 x double> [[TMP1]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP7:%.*]] = fsub fast <2 x double> [[TMP2]], [[TMP8]]
+; CHECK-NEXT: [[TMP6:%.*]] = fadd fast <2 x double> [[TMP5]], [[TMP7]]
+; 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
+ %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
+
+ %subA0B0 = fsub fast double %A0, %B0
+ %subC0D0 = fsub fast double %C0, %D0
+ %add0 = fadd fast double %subA0B0, %subC0D0
+
+ %subA1D1 = fsub fast double %A1, %D1
+ %subC1B1 = fsub fast double %C1, %B1
+ %add1 = fadd fast double %subA1D1, %subC1B1
+
+ store double %add0, ptr %Sarray, align 8
+ store double %add1, ptr %idxS1, align 8
+ ret void
+}
+
+; Without reassoc a float subtract chain cannot be regrouped, so the
+; flattening must leave it alone entirely.
+;
+; S[0] = (A[0] - B[0]) - C[0]
+; S[1] = (A[1] - B[1]) - C[1]
+define void @test_reassoc_fsub_no_reassoc_flag(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_fsub_no_reassoc_flag(
+; 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:%.*]] = fsub <2 x double> [[TMP0]], [[TMP1]]
+; CHECK-NEXT: [[TMP4:%.*]] = fsub <2 x double> [[TMP3]], [[TMP2]]
+; CHECK-NEXT: store <2 x double> [[TMP4]], 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
+
+ %subA0B0 = fsub double %A0, %B0
+ %sub0 = fsub double %subA0B0, %C0
+
+ %subA1B1 = fsub double %A1, %B1
+ %sub1 = fsub double %subA1B1, %C1
+
+ store double %sub0, ptr %Sarray, align 8
+ store double %sub1, ptr %idxS1, align 8
+ ret void
+}
+
+; A pure float subtract chain with reassoc flattens like the integer one:
+; the single added leaf minus the summed subtrahends.
+;
+; S[0] = (A[0] - B[0]) - C[0]
+; S[1] = (A[1] - C[1]) - B[1]
+define void @test_reassoc_fsub_chain(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_fsub_chain(
+; 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> [[TMP1]], <2 x double> [[TMP2]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP4:%.*]] = fsub reassoc <2 x double> [[TMP0]], [[TMP3]]
+; CHECK-NEXT: [[TMP5:%.*]] = shufflevector <2 x double> [[TMP2]], <2 x double> [[TMP1]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP6:%.*]] = fsub reassoc <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
+
+ %subA0B0 = fsub reassoc double %A0, %B0
+ %sub0 = fsub reassoc double %subA0B0, %C0
+
+ %subA1C1 = fsub reassoc double %A1, %C1
+ %sub1 = fsub reassoc double %subA1C1, %B1
+
+ store double %sub0, ptr %Sarray, align 8
+ store double %sub1, ptr %idxS1, align 8
+ ret void
+}
+
+; A subtract nested in the subtracted operand flips its own second operand
+; back to a positive leaf: lane 0 is (A + B) - (C - D) = A + B - C + D,
+; lane 1 is (A + D) - (C - B) = A + B - C + D too, so all four terms
+; realign into consecutive load columns, three of them added and C
+; subtracted.
+define void @test_reassoc_sub_nested_signs(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Darray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_sub_nested_signs(
+; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[DARRAY:%.*]], 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:%.*]] = load <2 x i32>, ptr [[DARRAY]], align 4
+; CHECK-NEXT: [[TMP4:%.*]] = shufflevector <2 x i32> [[TMP1]], <2 x i32> [[TMP3]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP5:%.*]] = add <2 x i32> [[TMP0]], [[TMP4]]
+; CHECK-NEXT: [[TMP8:%.*]] = shufflevector <2 x i32> [[TMP3]], <2 x i32> [[TMP1]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP7:%.*]] = sub <2 x i32> [[TMP2]], [[TMP8]]
+; CHECK-NEXT: [[TMP6:%.*]] = sub <2 x i32> [[TMP5]], [[TMP7]]
+; 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
+ %idxD1 = getelementptr inbounds i32, ptr %Darray, 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
+
+ %D0 = load i32, ptr %Darray, align 4
+ %D1 = load i32, ptr %idxD1, align 4
+
+ %addA0B0 = add i32 %A0, %B0
+ %subC0D0 = sub i32 %C0, %D0
+ %sub0 = sub i32 %addA0B0, %subC0D0
+
+ %addA1D1 = add i32 %A1, %D1
+ %subC1B1 = sub i32 %C1, %B1
+ %sub1 = sub i32 %addA1D1, %subC1B1
+
+ store i32 %sub0, ptr %Sarray, align 4
+ store i32 %sub1, ptr %idxS1, align 4
+ ret void
+}
+
+; The subtrahends come from a peeled add, so the negated-group combine
+; exactly reproduces that add lane by lane and reuses its flags, and the
+; final subtract exactly reproduces the root scalar and reuses its flags.
+; Only the exact matches permit this; the fallback would have dropped both
+; wrap flags because a negated leaf is present.
+;
+; S[0] = A[0] - (B[0] + C[0])
+; S[1] = A[1] - (C[1] + B[1])
+define void @test_reassoc_sub_neg_group_exact(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_sub_neg_group_exact(
+; 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> [[TMP2]], [[TMP1]]
+; CHECK-NEXT: [[TMP4:%.*]] = sub nuw nsw <2 x i32> [[TMP0]], [[TMP3]]
+; 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
+
+ %addB0C0 = add nuw nsw i32 %B0, %C0
+ %sub0 = sub nuw nsw i32 %A0, %addB0C0
+
+ %addC1B1 = add nuw nsw i32 %C1, %B1
+ %sub1 = sub nuw nsw i32 %A1, %addC1B1
+
+ store i32 %sub0, ptr %Sarray, align 4
+ store i32 %sub1, ptr %idxS1, align 4
+ ret void
+}
+
+; Same shape as above, but the lanes disagree on flags: flag propagation
+; intersects across lanes, so even though every combine exactly reproduces
+; a source instruction, no wrap flags survive.
+;
+; S[0] = A[0] - (B[0] + C[0])
+; S[1] = A[1] - (C[1] + B[1])
+define void @test_reassoc_sub_mixed_lane_flags(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_sub_mixed_lane_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 <2 x i32> [[TMP2]], [[TMP1]]
+; CHECK-NEXT: [[TMP4:%.*]] = sub <2 x i32> [[TMP0]], [[TMP3]]
+; 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
+
+ %addB0C0 = add nuw nsw i32 %B0, %C0
+ %sub0 = sub nuw nsw i32 %A0, %addB0C0
+
+ %addC1B1 = add i32 %C1, %B1
+ %sub1 = sub i32 %A1, %addC1B1
+
+ store i32 %sub0, ptr %Sarray, align 4
+ store i32 %sub1, ptr %idxS1, align 4
+ ret void
+}
+
+; Lane 2 of the peeled subtract column is not an instruction at all: it
+; stands in as a copyable identity leaf (sub(x, 0) == x). The peel happens,
+; but the identity zeros break the load-column structure, so the tie rule
+; keeps the natural two-operand form; the vectorized result must still be
+; correct (note the 0 lane in the subtrahend operand).
+;
+; S[0] = (A[0] - B[0]) + C[0]
+; S[1] = (A[1] - B[1]) + C[1]
+; S[2] = x + C[2]
+; S[3] = (A[3] - B[3]) + C[3]
+define void @test_reassoc_sub_copyable_lane(ptr %Aarray, ptr %Barray, ptr %Carray, i32 %x, ptr %Sarray) {
+; CHECK-LABEL: define void @test_reassoc_sub_copyable_lane(
+; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], i32 [[X:%.*]], ptr [[SARRAY:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[IDXA2:%.*]] = getelementptr inbounds i32, ptr [[AARRAY]], i64 2
+; CHECK-NEXT: [[IDXA3:%.*]] = getelementptr inbounds i32, ptr [[AARRAY]], i64 3
+; CHECK-NEXT: [[IDXB2:%.*]] = getelementptr inbounds i32, ptr [[BARRAY]], i64 2
+; CHECK-NEXT: [[IDXB3:%.*]] = getelementptr inbounds i32, ptr [[BARRAY]], i64 3
+; CHECK-NEXT: [[A3:%.*]] = load i32, ptr [[IDXA3]], align 4
+; CHECK-NEXT: [[B3:%.*]] = load i32, ptr [[IDXB3]], align 4
+; 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 <4 x i32>, ptr [[CARRAY]], align 4
+; CHECK-NEXT: [[TMP3:%.*]] = insertelement <4 x i32> poison, i32 [[X]], i64 2
+; CHECK-NEXT: [[TMP4:%.*]] = insertelement <4 x i32> [[TMP3]], i32 [[A3]], i64 3
+; CHECK-NEXT: [[TMP5:%.*]] = shufflevector <2 x i32> [[TMP0]], <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP6:%.*]] = shufflevector <4 x i32> [[TMP4]], <4 x i32> [[TMP5]], <4 x i32> <i32 4, i32 5, i32 2, i32 3>
+; CHECK-NEXT: [[TMP7:%.*]] = insertelement <4 x i32> <i32 poison, i32 poison, i32 0, i32 poison>, i32 [[B3]], i64 3
+; CHECK-NEXT: [[TMP8:%.*]] = shufflevector <2 x i32> [[TMP1]], <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 poison, i32 poison>
+; CHECK-NEXT: [[TMP9:%.*]] = shufflevector <4 x i32> [[TMP7]], <4 x i32> [[TMP8]], <4 x i32> <i32 4, i32 5, i32 2, i32 3>
+; CHECK-NEXT: [[TMP10:%.*]] = sub <4 x i32> [[TMP6]], [[TMP9]]
+; CHECK-NEXT: [[TMP11:%.*]] = add <4 x i32> [[TMP10]], [[TMP2]]
+; CHECK-NEXT: store <4 x i32> [[TMP11]], ptr [[SARRAY]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %idxA1 = getelementptr inbounds i32, ptr %Aarray, i64 1
+ %idxA2 = getelementptr inbounds i32, ptr %Aarray, i64 2
+ %idxA3 = getelementptr inbounds i32, ptr %Aarray, i64 3
+ %idxB1 = getelementptr inbounds i32, ptr %Barray, i64 1
+ %idxB2 = getelementptr inbounds i32, ptr %Barray, i64 2
+ %idxB3 = getelementptr inbounds i32, ptr %Barray, i64 3
+ %idxC1 = getelementptr inbounds i32, ptr %Carray, i64 1
+ %idxC2 = getelementptr inbounds i32, ptr %Carray, i64 2
+ %idxC3 = getelementptr inbounds i32, ptr %Carray, i64 3
+ %idxS1 = getelementptr inbounds i32, ptr %Sarray, i64 1
+ %idxS2 = getelementptr inbounds i32, ptr %Sarray, i64 2
+ %idxS3 = getelementptr inbounds i32, ptr %Sarray, i64 3
+
+ %A0 = load i32, ptr %Aarray, align 4
+ %A1 = load i32, ptr %idxA1, align 4
+ %A3 = load i32, ptr %idxA3, align 4
+
+ %B0 = load i32, ptr %Barray, align 4
+ %B1 = load i32, ptr %idxB1, align 4
+ %B3 = load i32, ptr %idxB3, align 4
+
+ %C0 = load i32, ptr %Carray, align 4
+ %C1 = load i32, ptr %idxC1, align 4
+ %C2 = load i32, ptr %idxC2, align 4
+ %C3 = load i32, ptr %idxC3, align 4
+
+ %subA0B0 = sub i32 %A0, %B0
+ %subA1B1 = sub i32 %A1, %B1
+ %subA3B3 = sub i32 %A3, %B3
+
+ %add0 = add i32 %subA0B0, %C0
+ %add1 = add i32 %subA1B1, %C1
+ %add2 = add i32 %x, %C2
+ %add3 = add i32 %subA3B3, %C3
+
+ store i32 %add0, ptr %Sarray, align 4
+ store i32 %add1, ptr %idxS1, align 4
+ store i32 %add2, ptr %idxS2, align 4
+ store i32 %add3, ptr %idxS3, align 4
+ ret void
+}
+
+; Scheduling stress: the flattened tree's leaves and its root scalar in
+; lane 0 have uses outside the tree (only the peeled links must be
+; single-use), so the peeled links' scheduling deps and the external-use
+; bookkeeping must survive flattening.
+define void @test_reassoc_sub_external_uses(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Darray, ptr %Sarray, ptr %Tarray) {
+; CHECK-LABEL: define void @test_reassoc_sub_external_uses(
+; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[DARRAY:%.*]], ptr [[SARRAY:%.*]], ptr [[TARRAY:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[TMP0:%.*]] = load <4 x i32>, ptr [[AARRAY]], align 4
+; CHECK-NEXT: [[A0:%.*]] = load i32, ptr [[AARRAY]], align 4
+; CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, ptr [[BARRAY]], align 4
+; CHECK-NEXT: [[TMP2:%.*]] = load <4 x i32>, ptr [[CARRAY]], align 4
+; CHECK-NEXT: [[TMP3:%.*]] = load <4 x i32>, ptr [[DARRAY]], align 4
+; CHECK-NEXT: [[TMP4:%.*]] = shufflevector <4 x i32> [[TMP1]], <4 x i32> [[TMP3]], <4 x i32> <i32 0, i32 5, i32 2, i32 7>
+; CHECK-NEXT: [[TMP5:%.*]] = sub <4 x i32> [[TMP0]], [[TMP4]]
+; CHECK-NEXT: [[TMP8:%.*]] = shufflevector <4 x i32> [[TMP3]], <4 x i32> [[TMP1]], <4 x i32> <i32 0, i32 5, i32 2, i32 7>
+; CHECK-NEXT: [[TMP9:%.*]] = sub <4 x i32> [[TMP2]], [[TMP8]]
+; CHECK-NEXT: [[TMP6:%.*]] = add <4 x i32> [[TMP5]], [[TMP9]]
+; CHECK-NEXT: store <4 x i32> [[TMP6]], ptr [[SARRAY]], align 4
+; CHECK-NEXT: [[TMP7:%.*]] = extractelement <4 x i32> [[TMP6]], i64 0
+; CHECK-NEXT: [[EXT:%.*]] = add i32 [[TMP7]], [[A0]]
+; CHECK-NEXT: store i32 [[EXT]], ptr [[TARRAY]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %idxA1 = getelementptr inbounds i32, ptr %Aarray, i64 1
+ %idxA2 = getelementptr inbounds i32, ptr %Aarray, i64 2
+ %idxA3 = getelementptr inbounds i32, ptr %Aarray, i64 3
+ %idxB1 = getelementptr inbounds i32, ptr %Barray, i64 1
+ %idxB2 = getelementptr inbounds i32, ptr %Barray, i64 2
+ %idxB3 = getelementptr inbounds i32, ptr %Barray, i64 3
+ %idxC1 = getelementptr inbounds i32, ptr %Carray, i64 1
+ %idxC2 = getelementptr inbounds i32, ptr %Carray, i64 2
+ %idxC3 = getelementptr inbounds i32, ptr %Carray, i64 3
+ %idxD1 = getelementptr inbounds i32, ptr %Darray, i64 1
+ %idxD2 = getelementptr inbounds i32, ptr %Darray, i64 2
+ %idxD3 = getelementptr inbounds i32, ptr %Darray, i64 3
+ %idxS1 = getelementptr inbounds i32, ptr %Sarray, i64 1
+ %idxS2 = getelementptr inbounds i32, ptr %Sarray, i64 2
+ %idxS3 = getelementptr inbounds i32, ptr %Sarray, i64 3
+
+ %A0 = load i32, ptr %Aarray, align 4
+ %A1 = load i32, ptr %idxA1, align 4
+ %A2 = load i32, ptr %idxA2, align 4
+ %A3 = load i32, ptr %idxA3, align 4
+
+ %B0 = load i32, ptr %Barray, align 4
+ %B1 = load i32, ptr %idxB1, align 4
+ %B2 = load i32, ptr %idxB2, align 4
+ %B3 = load i32, ptr %idxB3, align 4
+
+ %C0 = load i32, ptr %Carray, align 4
+ %C1 = load i32, ptr %idxC1, align 4
+ %C2 = load i32, ptr %idxC2, align 4
+ %C3 = load i32, ptr %idxC3, align 4
+
+ %D0 = load i32, ptr %Darray, align 4
+ %D1 = load i32, ptr %idxD1, align 4
+ %D2 = load i32, ptr %idxD2, align 4
+ %D3 = load i32, ptr %idxD3, align 4
+
+ %subA0B0 = sub i32 %A0, %B0
+ %subC0D0 = sub i32 %C0, %D0
+ %add0 = add i32 %subA0B0, %subC0D0
+
+ %subA1D1 = sub i32 %A1, %D1
+ %subC1B1 = sub i32 %C1, %B1
+ %add1 = add i32 %subA1D1, %subC1B1
+
+ %subA2B2 = sub i32 %A2, %B2
+ %subC2D2 = sub i32 %C2, %D2
+ %add2 = add i32 %subA2B2, %subC2D2
+
+ %subA3D3 = sub i32 %A3, %D3
+ %subC3B3 = sub i32 %C3, %B3
+ %add3 = add i32 %subA3D3, %subC3B3
+
+ store i32 %add0, ptr %Sarray, align 4
+ store i32 %add1, ptr %idxS1, align 4
+ store i32 %add2, ptr %idxS2, align 4
+ store i32 %add3, ptr %idxS3, align 4
+
+ %ext = add i32 %add0, %A0
+ store i32 %ext, ptr %Tarray, align 4
+ ret void
+}
More information about the llvm-commits
mailing list