| ; 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 |
| ; differently, 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:%.*]] = fadd reassoc nsz arcp contract afn <2 x double> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = fadd reassoc nsz arcp contract afn <2 x double> [[TMP3]], [[TMP2]] |
| ; 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:%.*]] = add nuw <2 x i32> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = add nuw <2 x i32> [[TMP3]], [[TMP2]] |
| ; 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:%.*]] = mul <2 x i32> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = mul <2 x i32> [[TMP3]], [[TMP2]] |
| ; 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:%.*]] = fmul reassoc nsz arcp contract afn <2 x double> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = fmul reassoc nsz arcp contract afn <2 x double> [[TMP3]], [[TMP2]] |
| ; 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:%.*]] = fadd reassoc nsz arcp contract afn <2 x double> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP5:%.*]] = fadd reassoc nsz arcp contract afn <2 x double> [[TMP2]], [[TMP3]] |
| ; CHECK-NEXT: [[TMP9:%.*]] = fadd reassoc nsz arcp contract afn <2 x double> [[TMP4]], [[TMP5]] |
| ; 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 |
| } |
| |
| ; Both operands of the top-level add are themselves 2-term adds in every |
| ; lane (a balanced tree), so scanAssociativeOperands() used to leave this |
| ; alone entirely (both initial columns were independently peelable). Lane 0 |
| ; pairs {A,B} then {C,D}; lane 1 pairs the same 4 terms as {B,D} then |
| ; {A,C}, a grouping ordinary per-level recursion cannot undo, since it can |
| ; only reorder operands within one node, never move a term across the |
| ; left/right subtree boundary. With balanced chains also flattened and |
| ; columns realigned by value family before the pairwise reorder, all 4 |
| ; terms should still be loadable as plain contiguous vectors. |
| ; |
| ; S[0] = (A[0] + B[0]) + (C[0] + D[0]) |
| ; S[1] = (B[1] + D[1]) + (A[1] + C[1]) |
| define void @test_reassoc_add_balanced_permuted(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Darray, ptr %Sarray) { |
| ; CHECK-LABEL: define void @test_reassoc_add_balanced_permuted( |
| ; 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:%.*]] = fadd reassoc nsz arcp contract afn <2 x double> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP5:%.*]] = fadd reassoc nsz arcp contract afn <2 x double> [[TMP2]], [[TMP3]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = fadd reassoc nsz arcp contract afn <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 |
| %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 |
| %addC0D0 = fadd fast double %C0, %D0 |
| %add0 = fadd fast double %addA0B0, %addC0D0 |
| |
| %addB1D1 = fadd fast double %B1, %D1 |
| %addA1C1 = fadd fast double %A1, %C1 |
| %add1 = fadd fast double %addB1D1, %addA1C1 |
| |
| store double %add0, ptr %Sarray, align 8 |
| store double %add1, ptr %idxS1, align 8 |
| ret void |
| } |
| |
| ; Same balanced shape as immediately above, but both lanes pair the same |
| ; terms the same way, so this was already vectorizable via ordinary |
| ; per-level recursion even before balanced chains were flattened too. |
| ; Flattening it into one reassociated node instead should not regress the |
| ; result: still 4 plain contiguous loads combined in 3 steps either way. |
| ; |
| ; S[0] = (A[0] + B[0]) + (C[0] + D[0]) |
| ; S[1] = (A[1] + B[1]) + (C[1] + D[1]) |
| define void @test_reassoc_add_balanced_aligned(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Darray, ptr %Sarray) { |
| ; CHECK-LABEL: define void @test_reassoc_add_balanced_aligned( |
| ; 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:%.*]] = fadd fast <2 x double> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP5:%.*]] = fadd fast <2 x double> [[TMP2]], [[TMP3]] |
| ; 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 |
| %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 |
| %addC0D0 = fadd fast double %C0, %D0 |
| %add0 = fadd fast double %addA0B0, %addC0D0 |
| |
| %addA1B1 = fadd fast double %A1, %B1 |
| %addC1D1 = fadd fast double %C1, %D1 |
| %add1 = fadd fast double %addA1B1, %addC1D1 |
| |
| store double %add0, ptr %Sarray, align 8 |
| store double %add1, ptr %idxS1, align 8 |
| ret void |
| } |
| |
| ; Same balanced+permuted shape as test_reassoc_add_balanced_permuted, but |
| ; with a bitwise opcode, to check the balanced-chain handling is not |
| ; Add/FAdd specific: isAssociative() also covers And/Or/Xor and Mul/FMul, |
| ; and And needs no fast-math or wrap flags to qualify. |
| ; |
| ; S[0] = (A[0] & B[0]) & (C[0] & D[0]) |
| ; S[1] = (B[1] & D[1]) & (A[1] & C[1]) |
| define void @test_reassoc_and_balanced_permuted(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Darray, ptr %Sarray) { |
| ; CHECK-LABEL: define void @test_reassoc_and_balanced_permuted( |
| ; 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:%.*]] = and <2 x i32> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP5:%.*]] = and <2 x i32> [[TMP2]], [[TMP3]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = and <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 |
| %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 |
| |
| %andA0B0 = and i32 %A0, %B0 |
| %andC0D0 = and i32 %C0, %D0 |
| %and0 = and i32 %andA0B0, %andC0D0 |
| |
| %andB1D1 = and i32 %B1, %D1 |
| %andA1C1 = and i32 %A1, %C1 |
| %and1 = and i32 %andB1D1, %andA1C1 |
| |
| store i32 %and0, ptr %Sarray, align 4 |
| store i32 %and1, ptr %idxS1, align 4 |
| ret void |
| } |
| |
| ; An unbalanced 3-term chain like test_reassoc_add, but the third term is |
| ; the literal 0 (add's identity) instead of a third array, spread across a |
| ; different position per lane: lane 0 combines A and B first, then adds 0; |
| ; lane 1 combines B with 0 first, then adds A. This is the same shape (a |
| ; real value mixed with an unrelated identity placeholder in one column, |
| ; e.g. add(B, 0)) as a real gather-buildvector regression that combined |
| ; several unrelated `or`-with-0 expressions; |
| ; |
| ; S[0] = (A[0] + B[0]) + 0 |
| ; S[1] = (B[1] + 0) + A[1] |
| define void @test_reassoc_add_identity(ptr %Aarray, ptr %Barray, ptr %Sarray) { |
| ; CHECK-LABEL: define void @test_reassoc_add_identity( |
| ; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], 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:%.*]] = add <2 x i32> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: store <2 x i32> [[TMP2]], 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 |
| %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 |
| |
| %addA0B0 = add i32 %A0, %B0 |
| %add0 = add i32 %addA0B0, 0 |
| |
| %addB10 = add i32 %B1, 0 |
| %add1 = add i32 %addB10, %A1 |
| |
| store i32 %add0, ptr %Sarray, align 4 |
| store i32 %add1, ptr %idxS1, align 4 |
| ret void |
| } |
| |
| ; The same 4 load families per lane, but each lane's chain consumes them in |
| ; a different order: lane 0 is ((B+D)+C)+A, lane 1 is ((A+B)+C)+D. Peeling |
| ; goes through transient mixed columns (a load leaf against a nested add), |
| ; after which the columns realign into consecutive-load pairs. |
| define void @test_reassoc_add_permuted_operands(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Darray, ptr %Sarray) { |
| ; CHECK-LABEL: define void @test_reassoc_add_permuted_operands( |
| ; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[DARRAY:%.*]], ptr [[SARRAY:%.*]]) #[[ATTR0]] { |
| ; CHECK-NEXT: [[ENTRY:.*:]] |
| ; CHECK-NEXT: [[IDXS1:%.*]] = getelementptr inbounds i8, ptr [[SARRAY]], i64 1 |
| ; CHECK-NEXT: [[TMP0:%.*]] = load <2 x i8>, ptr [[AARRAY]], align 8 |
| ; CHECK-NEXT: [[TMP1:%.*]] = load <2 x i8>, ptr [[BARRAY]], align 8 |
| ; CHECK-NEXT: [[TMP2:%.*]] = load <2 x i8>, ptr [[CARRAY]], align 8 |
| ; CHECK-NEXT: [[TMP3:%.*]] = load <2 x i8>, ptr [[DARRAY]], align 8 |
| ; CHECK-NEXT: [[TMP4:%.*]] = add nuw <2 x i8> [[TMP2]], [[TMP3]] |
| ; CHECK-NEXT: [[TMP5:%.*]] = add nuw <2 x i8> [[TMP1]], [[TMP0]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = add nuw <2 x i8> [[TMP4]], [[TMP5]] |
| ; CHECK-NEXT: [[TMP7:%.*]] = extractelement <2 x i8> [[TMP6]], i64 0 |
| ; CHECK-NEXT: store i8 [[TMP7]], ptr [[SARRAY]], align 8 |
| ; CHECK-NEXT: [[TMP8:%.*]] = extractelement <2 x i8> [[TMP6]], i64 1 |
| ; CHECK-NEXT: store i8 [[TMP8]], ptr [[IDXS1]], align 8 |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| %idxA1 = getelementptr inbounds i8, ptr %Aarray, i64 1 |
| %idxB1 = getelementptr inbounds i8, ptr %Barray, i64 1 |
| %idxC1 = getelementptr inbounds i8, ptr %Carray, i64 1 |
| %idxD1 = getelementptr inbounds i8, ptr %Darray, i64 1 |
| %idxS1 = getelementptr inbounds i8, ptr %Sarray, i64 1 |
| |
| %A0 = load i8, ptr %Aarray, align 8 |
| %A1 = load i8, ptr %idxA1, align 8 |
| |
| %B0 = load i8, ptr %Barray, align 8 |
| %B1 = load i8, ptr %idxB1, align 8 |
| |
| %C0 = load i8, ptr %Carray, align 8 |
| %C1 = load i8, ptr %idxC1, align 8 |
| |
| %D0 = load i8, ptr %Darray, align 8 |
| %D1 = load i8, ptr %idxD1, align 8 |
| |
| %addB0D0 = add nuw nsw i8 %D0, %B0 |
| %addA1B1 = add nuw nsw i8 %B1, %A1 |
| %addB0C0D0 = add nuw nsw i8 %C0, %addB0D0 |
| %addA1B1C1 = add nuw nsw i8 %addA1B1, %C1 |
| %add0 = add nuw nsw i8 %addB0C0D0, %A0 |
| %add1 = add nuw nsw i8 %D1, %addA1B1C1 |
| store i8 %add0, ptr %Sarray, align 8 |
| store i8 %add1, ptr %idxS1, align 8 |
| ret void |
| } |
| |
| ; Shift leaves sit at different chain positions per lane (lane 0 slots 0,3; |
| ; lane 1 slots 0,1). Same-key shift columns are paired by the family of the |
| ; shift's own operand, so shlA* pairs with shlA* (consecutive loads) instead |
| ; of pairing by encounter order. |
| define void @test_reassoc_add_shl_operands(ptr %Aarray, ptr %Barray, ptr %Carray, ptr %Darray, ptr %Sarray) { |
| ; CHECK-LABEL: define void @test_reassoc_add_shl_operands( |
| ; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[CARRAY:%.*]], ptr [[DARRAY:%.*]], ptr [[SARRAY:%.*]]) #[[ATTR0]] { |
| ; CHECK-NEXT: [[ENTRY:.*:]] |
| ; CHECK-NEXT: [[IDXB1:%.*]] = getelementptr inbounds i8, ptr [[BARRAY]], i64 1 |
| ; CHECK-NEXT: [[IDXS1:%.*]] = getelementptr inbounds i8, ptr [[SARRAY]], i64 1 |
| ; CHECK-NEXT: [[B1:%.*]] = load i8, ptr [[IDXB1]], align 8 |
| ; CHECK-NEXT: [[TMP0:%.*]] = load <2 x i8>, ptr [[AARRAY]], align 8 |
| ; CHECK-NEXT: [[TMP1:%.*]] = load <2 x i8>, ptr [[CARRAY]], align 8 |
| ; CHECK-NEXT: [[TMP2:%.*]] = load <2 x i8>, ptr [[DARRAY]], align 8 |
| ; CHECK-NEXT: [[TMP3:%.*]] = shl nuw <2 x i8> [[TMP0]], splat (i8 3) |
| ; CHECK-NEXT: [[TMP4:%.*]] = insertelement <2 x i8> [[TMP1]], i8 [[B1]], i64 1 |
| ; CHECK-NEXT: [[TMP5:%.*]] = shl nuw <2 x i8> [[TMP4]], splat (i8 3) |
| ; CHECK-NEXT: [[TMP6:%.*]] = add nuw <2 x i8> [[TMP3]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP7:%.*]] = add nuw <2 x i8> [[TMP2]], [[TMP5]] |
| ; CHECK-NEXT: [[TMP8:%.*]] = add nuw <2 x i8> [[TMP6]], [[TMP7]] |
| ; CHECK-NEXT: [[TMP9:%.*]] = extractelement <2 x i8> [[TMP8]], i64 0 |
| ; CHECK-NEXT: store i8 [[TMP9]], ptr [[SARRAY]], align 8 |
| ; CHECK-NEXT: [[TMP10:%.*]] = extractelement <2 x i8> [[TMP8]], i64 1 |
| ; CHECK-NEXT: store i8 [[TMP10]], ptr [[IDXS1]], align 8 |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| %idxA1 = getelementptr inbounds i8, ptr %Aarray, i64 1 |
| %idxB1 = getelementptr inbounds i8, ptr %Barray, i64 1 |
| %idxC1 = getelementptr inbounds i8, ptr %Carray, i64 1 |
| %idxD1 = getelementptr inbounds i8, ptr %Darray, i64 1 |
| %idxS1 = getelementptr inbounds i8, ptr %Sarray, i64 1 |
| |
| %A0 = load i8, ptr %Aarray, align 8 |
| %A1 = load i8, ptr %idxA1, align 8 |
| |
| %B1 = load i8, ptr %idxB1, align 8 |
| |
| %C0 = load i8, ptr %Carray, align 8 |
| %C1 = load i8, ptr %idxC1, align 8 |
| |
| %D0 = load i8, ptr %Darray, align 8 |
| %D1 = load i8, ptr %idxD1, align 8 |
| |
| %shlA0 = shl nuw i8 %A0, 3 |
| %shlA1 = shl nuw i8 %A1, 3 |
| %shlB1 = shl nuw i8 %B1, 3 |
| %shlC0 = shl nuw i8 %C0, 3 |
| |
| %addA0C0 = add nuw nsw i8 %shlA0, %C0 |
| %addA1B1 = add nuw nsw i8 %shlB1, %shlA1 |
| %addA0C0D0 = add nuw nsw i8 %addA0C0, %D0 |
| %addA1B1C1 = add nuw nsw i8 %addA1B1, %C1 |
| %add0 = add nuw nsw i8 %addA0C0D0, %shlC0 |
| %add1 = add nuw nsw i8 %addA1B1C1, %D1 |
| store i8 %add0, ptr %Sarray, align 8 |
| 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: [[TMP5:%.*]] = add <2 x i32> [[TMP0]], [[TMP2]] |
| ; CHECK-NEXT: [[TMP4:%.*]] = add <2 x i32> [[TMP1]], [[TMP3]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = sub <2 x i32> [[TMP5]], [[TMP4]] |
| ; 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:%.*]] = add <2 x i32> [[TMP1]], [[TMP2]] |
| ; 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 |
| |
| %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> [[TMP0]], [[TMP1]] |
| ; 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: [[TMP5:%.*]] = fadd reassoc nsz arcp contract afn <2 x double> [[TMP0]], [[TMP2]] |
| ; CHECK-NEXT: [[TMP4:%.*]] = fadd reassoc nsz arcp contract afn <2 x double> [[TMP1]], [[TMP3]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = fsub reassoc nsz arcp contract afn <2 x double> [[TMP5]], [[TMP4]] |
| ; 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:%.*]] = fsub reassoc <2 x double> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = fsub reassoc <2 x double> [[TMP3]], [[TMP2]] |
| ; 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:%.*]] = add <2 x i32> [[TMP0]], [[TMP1]] |
| ; CHECK-NEXT: [[TMP5:%.*]] = add <2 x i32> [[TMP4]], [[TMP3]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = sub <2 x i32> [[TMP5]], [[TMP2]] |
| ; 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> [[TMP1]], [[TMP2]] |
| ; 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> [[TMP1]], [[TMP2]] |
| ; 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: [[TMP5:%.*]] = add <4 x i32> [[TMP0]], [[TMP2]] |
| ; CHECK-NEXT: [[TMP4:%.*]] = add <4 x i32> [[TMP1]], [[TMP3]] |
| ; CHECK-NEXT: [[TMP6:%.*]] = sub <4 x i32> [[TMP5]], [[TMP4]] |
| ; 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 |
| } |
| |
| ; A copyable lane (A[3] used directly, not a chain link) stands in as a |
| ; subtract-with-zero leaf: it must stay exact while the chain leaves realign |
| ; into the consecutive A load column. |
| ; |
| ; S[0] = (A[0] + X[0]) - B[0] |
| ; S[1] = (X[1] + A[1]) - B[1] |
| ; S[2] = (A[2] + X[2]) - B[2] |
| ; S[3] = A[3] |
| define void @test_reassoc_sub_copyable(ptr %Aarray, ptr %Barray, ptr %Xarray, ptr %Sarray) { |
| ; CHECK-LABEL: define void @test_reassoc_sub_copyable( |
| ; CHECK-SAME: ptr [[AARRAY:%.*]], ptr [[BARRAY:%.*]], ptr [[XARRAY:%.*]], ptr [[SARRAY:%.*]]) #[[ATTR0]] { |
| ; CHECK-NEXT: [[ENTRY:.*:]] |
| ; CHECK-NEXT: [[IDXB2:%.*]] = getelementptr inbounds i32, ptr [[BARRAY]], i64 2 |
| ; CHECK-NEXT: [[IDXX2:%.*]] = getelementptr inbounds i32, ptr [[XARRAY]], i64 2 |
| ; CHECK-NEXT: [[B2:%.*]] = load i32, ptr [[IDXB2]], align 4 |
| ; CHECK-NEXT: [[X2:%.*]] = load i32, ptr [[IDXX2]], align 4 |
| ; CHECK-NEXT: [[TMP0:%.*]] = load <4 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 [[XARRAY]], align 4 |
| ; CHECK-NEXT: [[TMP3:%.*]] = insertelement <4 x i32> <i32 poison, i32 poison, i32 poison, i32 0>, i32 [[X2]], i64 2 |
| ; CHECK-NEXT: [[TMP4:%.*]] = shufflevector <2 x i32> [[TMP2]], <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 poison, i32 poison> |
| ; CHECK-NEXT: [[TMP5:%.*]] = shufflevector <4 x i32> [[TMP3]], <4 x i32> [[TMP4]], <4 x i32> <i32 4, i32 5, i32 2, i32 3> |
| ; CHECK-NEXT: [[TMP6:%.*]] = insertelement <4 x i32> <i32 poison, i32 poison, i32 poison, i32 0>, i32 [[B2]], i64 2 |
| ; CHECK-NEXT: [[TMP7:%.*]] = shufflevector <2 x i32> [[TMP1]], <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 poison, i32 poison> |
| ; CHECK-NEXT: [[TMP8:%.*]] = shufflevector <4 x i32> [[TMP6]], <4 x i32> [[TMP7]], <4 x i32> <i32 4, i32 5, i32 2, i32 3> |
| ; CHECK-NEXT: [[TMP9:%.*]] = add <4 x i32> [[TMP0]], [[TMP5]] |
| ; CHECK-NEXT: [[TMP10:%.*]] = sub <4 x i32> [[TMP9]], [[TMP8]] |
| ; CHECK-NEXT: store <4 x i32> [[TMP10]], 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 |
| %idxX1 = getelementptr inbounds i32, ptr %Xarray, i64 1 |
| %idxX2 = getelementptr inbounds i32, ptr %Xarray, i64 2 |
| %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 |
| |
| %X0 = load i32, ptr %Xarray, align 4 |
| %X1 = load i32, ptr %idxX1, align 4 |
| %X2 = load i32, ptr %idxX2, align 4 |
| |
| %ax0 = add i32 %A0, %X0 |
| %xa1 = add i32 %X1, %A1 |
| %ax2 = add i32 %A2, %X2 |
| |
| %s0 = sub i32 %ax0, %B0 |
| %s1 = sub i32 %xa1, %B1 |
| %s2 = sub i32 %ax2, %B2 |
| |
| store i32 %s0, ptr %Sarray, align 4 |
| store i32 %s1, ptr %idxS1, align 4 |
| store i32 %s2, ptr %idxS2, align 4 |
| store i32 %A3, ptr %idxS3, align 4 |
| ret void |
| } |
| |
| define i32 @gathered_peeled_scalar(i32 %conv) { |
| ; CHECK-LABEL: define i32 @gathered_peeled_scalar( |
| ; CHECK-SAME: i32 [[CONV:%.*]]) #[[ATTR0]] { |
| ; CHECK-NEXT: [[ENTRY:.*]]: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add i32 [[CONV]], 0 |
| ; CHECK-NEXT: [[TMP0:%.*]] = insertelement <2 x i32> <i32 0, i32 poison>, i32 [[INVARIANT_OP]], i64 1 |
| ; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i32> zeroinitializer, [[TMP0]] |
| ; CHECK-NEXT: [[TMP8:%.*]] = insertelement <2 x i32> <i32 0, i32 poison>, i32 [[CONV]], i64 1 |
| ; CHECK-NEXT: [[TMP2:%.*]] = add <2 x i32> <i32 1, i32 0>, [[TMP8]] |
| ; CHECK-NEXT: br label %[[LOOP:.*]] |
| ; CHECK: [[LOOP]]: |
| ; CHECK-NEXT: [[TMP3:%.*]] = phi <2 x i32> [ [[TMP1]], %[[ENTRY]] ], [ zeroinitializer, %[[BACKEDGE:.*]] ] |
| ; CHECK-NEXT: [[TMP4:%.*]] = or <2 x i32> [[TMP3]], zeroinitializer |
| ; CHECK-NEXT: [[TMP5:%.*]] = or <2 x i32> [[TMP2]], zeroinitializer |
| ; CHECK-NEXT: [[TMP6:%.*]] = or <2 x i32> [[TMP4]], zeroinitializer |
| ; CHECK-NEXT: [[TMP7:%.*]] = or <2 x i32> [[TMP6]], [[TMP5]] |
| ; CHECK-NEXT: br label %[[BACKEDGE]] |
| ; CHECK: [[BACKEDGE]]: |
| ; CHECK-NEXT: [[TMP9:%.*]] = phi <2 x i32> [ [[TMP7]], %[[LOOP]] ] |
| ; CHECK-NEXT: br label %[[LOOP]] |
| ; |
| entry: |
| %invariant.op = add i32 %conv, 0 |
| %invariant.op3 = or i32 %invariant.op, 0 |
| %invariant.op5 = add i32 0, 0 |
| %conv16 = add i32 1, 0 |
| br label %loop |
| |
| loop: ; preds = %backedge, %entry |
| %m.019 = phi i32 [ %invariant.op5, %entry ], [ 0, %backedge ] |
| %conv86.lcssa1418 = phi i32 [ %invariant.op3, %entry ], [ 0, %backedge ] |
| %add11.1190.reass = or i32 %m.019, 0 |
| %.reass4.3231 = or i32 %invariant.op3, 0 |
| %conv8171 = or i32 0, %conv86.lcssa1418 |
| %conv8.2210 = or i32 0, %conv8171 |
| %conv8.3232 = or i32 %.reass4.3231, %conv8.2210 |
| %add11.2212.reass = or i32 %add11.1190.reass, 0 |
| %add11.3234.reass = or i32 %add11.2212.reass, %conv16 |
| br label %backedge |
| |
| backedge: ; preds = %loop |
| %add11.6303 = phi i32 [ %add11.3234.reass, %loop ] |
| %conv8.6301 = phi i32 [ %conv8.3232, %loop ] |
| br label %loop |
| } |
| |
| define i32 @gathered_peeled_scalar_late() { |
| ; CHECK-LABEL: define i32 @gathered_peeled_scalar_late( |
| ; CHECK-SAME: ) #[[ATTR0]] { |
| ; CHECK-NEXT: [[DOTPREHEADER_PEEL_BEGIN:.*:]] |
| ; CHECK-NEXT: [[TMP0:%.*]] = trunc i64 0 to i32 |
| ; CHECK-NEXT: [[TMP1:%.*]] = add i32 0, [[TMP0]] |
| ; CHECK-NEXT: [[TMP4:%.*]] = insertelement <2 x i32> <i32 poison, i32 1>, i32 [[TMP0]], i64 0 |
| ; CHECK-NEXT: [[TMP5:%.*]] = insertelement <2 x i32> <i32 poison, i32 0>, i32 [[TMP1]], i64 0 |
| ; CHECK-NEXT: [[TMP6:%.*]] = add <2 x i32> zeroinitializer, [[TMP5]] |
| ; CHECK-NEXT: [[TMP7:%.*]] = or <2 x i32> zeroinitializer, [[TMP4]] |
| ; CHECK-NEXT: br [[DOTPREHEADER:label %.*]] |
| ; CHECK: [[_PREHEADER:.*:]] |
| ; CHECK-NEXT: [[TMP8:%.*]] = phi <2 x i32> [ [[TMP6]], [[DOTPREHEADER_PEEL_BEGIN1:%.*]] ], [ zeroinitializer, %[[K_EXIT_7:.*]] ] |
| ; CHECK-NEXT: br i1 false, label %[[K_EXIT_THREAD:.*]], label %[[K_EXIT:.*]] |
| ; CHECK: [[K_EXIT_THREAD]]: |
| ; CHECK-NEXT: [[TMP9:%.*]] = or <2 x i32> [[TMP8]], zeroinitializer |
| ; CHECK-NEXT: [[TMP10:%.*]] = or <2 x i32> [[TMP9]], zeroinitializer |
| ; CHECK-NEXT: br label %[[K_EXIT_7]] |
| ; CHECK: [[K_EXIT]]: |
| ; CHECK-NEXT: br label %[[K_EXIT_7]] |
| ; CHECK: [[K_EXIT_7]]: |
| ; CHECK-NEXT: [[TMP11:%.*]] = phi <2 x i32> [ [[TMP7]], %[[K_EXIT]] ], [ [[TMP10]], %[[K_EXIT_THREAD]] ] |
| ; CHECK-NEXT: br [[DOTPREHEADER]] |
| ; |
| .preheader.peel.begin: |
| %.reass5.7.peel = add i32 0, 0 |
| %invariant.op = add i32 0, 1 |
| %invariant.op4 = add i32 %invariant.op, 0 |
| %.reass5.4 = or i32 %invariant.op4, 0 |
| %0 = trunc i64 0 to i32 |
| %1 = add i32 0, %0 |
| %2 = add i32 %1, 0 |
| %invariant.op233 = or i32 0, %2 |
| br label %.preheader |
| |
| .preheader: ; preds = %k.exit.7, %.preheader.peel.begin |
| %.0316 = phi i32 [ %2, %.preheader.peel.begin ], [ 0, %k.exit.7 ] |
| %.lcssa1215 = phi i32 [ %.reass5.7.peel, %.preheader.peel.begin ], [ 0, %k.exit.7 ] |
| br i1 false, label %k.exit.thread, label %k.exit |
| |
| k.exit.thread: ; preds = %.preheader |
| %3 = or i32 0, %.lcssa1215 |
| %4 = or i32 0, %3 |
| %.reass244 = or i32 %.0316, 0 |
| %.reass250 = or i32 %.reass244, 0 |
| br label %k.exit.7 |
| |
| k.exit: ; preds = %.preheader |
| br label %k.exit.7 |
| |
| k.exit.7: ; preds = %k.exit, %k.exit.thread |
| %5 = phi i32 [ %invariant.op233, %k.exit ], [ %.reass250, %k.exit.thread ] |
| %6 = phi i32 [ %.reass5.4, %k.exit ], [ %4, %k.exit.thread ] |
| br label %.preheader |
| } |
| |
| @a = global i32 0, align 4 |
| @b = global i64 0, align 8 |
| @c = global i32 0, align 4 |
| |
| ; The copyable element %add4 is peeled into the flattened node; its schedule |
| ; data must be released exactly once. |
| define void @test_reassoc_copyable_chain_link() { |
| ; CHECK-LABEL: define void @test_reassoc_copyable_chain_link( |
| ; CHECK-SAME: ) #[[ATTR0]] { |
| ; CHECK-NEXT: [[ENTRY:.*:]] |
| ; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @a, align 4 |
| ; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr @c, align 4 |
| ; CHECK-NEXT: [[CONV1:%.*]] = sext i32 [[TMP1]] to i64 |
| ; CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr @b, align 8 |
| ; CHECK-NEXT: [[CONV2:%.*]] = zext i32 [[TMP0]] to i64 |
| ; CHECK-NEXT: [[REASS_SUB:%.*]] = sub i64 [[TMP2]], [[CONV2]] |
| ; CHECK-NEXT: [[ADD:%.*]] = add i64 [[REASS_SUB]], 1 |
| ; CHECK-NEXT: [[ADD4:%.*]] = sub nsw i64 7, [[CONV1]] |
| ; CHECK-NEXT: [[REM:%.*]] = srem i64 [[CONV1]], [[ADD4]] |
| ; CHECK-NEXT: [[AND:%.*]] = and i64 [[ADD]], [[REM]] |
| ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i64 [[AND]], 0 |
| ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[IF_END:.*]], label %[[WHILE_BODY:.*]] |
| ; CHECK: [[WHILE_BODY]]: |
| ; CHECK-NEXT: br label %[[WHILE_BODY]] |
| ; CHECK: [[IF_END]]: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| %0 = load i32, ptr @a, align 4 |
| %1 = load i32, ptr @c, align 4 |
| %conv1 = sext i32 %1 to i64 |
| %2 = load i64, ptr @b, align 8 |
| %conv2 = zext i32 %0 to i64 |
| %reass.sub = sub i64 %2, %conv2 |
| %add = add i64 %reass.sub, 1 |
| %add4 = sub nsw i64 7, %conv1 |
| %rem = srem i64 %conv1, %add4 |
| %and = and i64 %add, %rem |
| %tobool.not = icmp eq i64 %and, 0 |
| br i1 %tobool.not, label %if.end, label %while.body |
| |
| while.body: |
| br label %while.body |
| |
| if.end: |
| ret void |
| } |
| |
| ; Same as above, but the copyable element is a sub in an add-family node. |
| define i32 @test_reassoc_copyable_sub_chain_link() { |
| ; CHECK-LABEL: define i32 @test_reassoc_copyable_sub_chain_link( |
| ; CHECK-SAME: ) #[[ATTR0]] { |
| ; CHECK-NEXT: [[ENTRY:.*:]] |
| ; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @b, align 4 |
| ; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 2 |
| ; CHECK-NEXT: store i32 [[ADD]], ptr @c, align 4 |
| ; CHECK-NEXT: [[TMP1:%.*]] = shl i32 [[ADD]], 30 |
| ; CHECK-NEXT: [[ADD1:%.*]] = sub i32 1073741824, [[TMP1]] |
| ; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 2, [[ADD]] |
| ; CHECK-NEXT: [[TMP2:%.*]] = shl i32 [[TMP0]], 1 |
| ; CHECK-NEXT: [[REASS_SUB:%.*]] = sub i32 [[DIV]], [[TMP2]] |
| ; CHECK-NEXT: [[SUB:%.*]] = add i32 [[REASS_SUB]], -5 |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[ADD1]], [[SUB]] |
| ; CHECK-NEXT: br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[IF_END:.*]] |
| ; CHECK: [[IF_THEN]]: |
| ; CHECK-NEXT: store i32 0, ptr @a, align 4 |
| ; CHECK-NEXT: br label %[[IF_END]] |
| ; CHECK: [[IF_END]]: |
| ; CHECK-NEXT: ret i32 0 |
| ; |
| entry: |
| %0 = load i32, ptr @b, align 4 |
| %add = add nsw i32 %0, 2 |
| store i32 %add, ptr @c, align 4 |
| %1 = shl i32 %add, 30 |
| %add1 = sub i32 1073741824, %1 |
| %div = sdiv i32 2, %add |
| %2 = shl i32 %0, 1 |
| %reass.sub = sub i32 %div, %2 |
| %sub = add i32 %reass.sub, -5 |
| %cmp = icmp slt i32 %add1, %sub |
| br i1 %cmp, label %if.then, label %if.end |
| |
| if.then: |
| store i32 0, ptr @a, align 4 |
| br label %if.end |
| |
| if.end: |
| ret i32 0 |
| } |