diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:41:05 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:41:05 +0000 |
commit | 01095a5d43bbfde13731688ddcf6048ebb8b7721 (patch) | |
tree | 4def12e759965de927d963ac65840d663ef9d1ea /test/Transforms/LoopVectorize | |
parent | f0f4822ed4b66e3579e92a89f368f8fb860e218e (diff) |
Diffstat (limited to 'test/Transforms/LoopVectorize')
62 files changed, 3904 insertions, 170 deletions
diff --git a/test/Transforms/LoopVectorize/AArch64/backedge-overflow.ll b/test/Transforms/LoopVectorize/AArch64/backedge-overflow.ll new file mode 100644 index 0000000000000..aba47f6c628f4 --- /dev/null +++ b/test/Transforms/LoopVectorize/AArch64/backedge-overflow.ll @@ -0,0 +1,166 @@ +; RUN: opt -mtriple=aarch64--linux-gnueabi -loop-vectorize -force-vector-width=4 -force-vector-interleave=1 < %s -S | FileCheck %s + +; The following tests contain loops for which SCEV cannot determine the backedge +; taken count. This is because the backedge taken condition is produced by an +; icmp with one of the sides being a loop varying non-AddRec expression. +; However, there is a possibility to normalize this to an AddRec expression +; using SCEV predicates. This allows us to compute a 'guarded' backedge count. +; The Loop Vectorizer is able to version to loop in order to use this guarded +; backedge count and vectorize more loops. + + +; CHECK-LABEL: test_sge +; CHECK-LABEL: vector.scevcheck +; CHECK-LABEL: vector.body +define void @test_sge(i32* noalias %A, + i32* noalias %B, + i32* noalias %C, i32 %N) { +entry: + %cmp13 = icmp eq i32 %N, 0 + br i1 %cmp13, label %for.end, label %for.body.preheader + +for.body.preheader: + br label %for.body + +for.body: + %indvars.iv = phi i16 [ %indvars.next, %for.body ], [ 0, %for.body.preheader ] + %indvars.next = add i16 %indvars.iv, 1 + %indvars.ext = zext i16 %indvars.iv to i32 + + %arrayidx = getelementptr inbounds i32, i32* %B, i32 %indvars.ext + %0 = load i32, i32* %arrayidx, align 4 + %arrayidx3 = getelementptr inbounds i32, i32* %C, i32 %indvars.ext + %1 = load i32, i32* %arrayidx3, align 4 + + %mul4 = mul i32 %1, %0 + + %arrayidx7 = getelementptr inbounds i32, i32* %A, i32 %indvars.ext + store i32 %mul4, i32* %arrayidx7, align 4 + + %exitcond = icmp sge i32 %indvars.ext, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: + br label %for.end + +for.end: + ret void +} + +; CHECK-LABEL: test_uge +; CHECK-LABEL: vector.scevcheck +; CHECK-LABEL: vector.body +define void @test_uge(i32* noalias %A, + i32* noalias %B, + i32* noalias %C, i32 %N, i32 %Offset) { +entry: + %cmp13 = icmp eq i32 %N, 0 + br i1 %cmp13, label %for.end, label %for.body.preheader + +for.body.preheader: + br label %for.body + +for.body: + %indvars.iv = phi i16 [ %indvars.next, %for.body ], [ 0, %for.body.preheader ] + %indvars.next = add i16 %indvars.iv, 1 + + %indvars.ext = sext i16 %indvars.iv to i32 + %indvars.access = add i32 %Offset, %indvars.ext + + %arrayidx = getelementptr inbounds i32, i32* %B, i32 %indvars.access + %0 = load i32, i32* %arrayidx, align 4 + %arrayidx3 = getelementptr inbounds i32, i32* %C, i32 %indvars.access + %1 = load i32, i32* %arrayidx3, align 4 + + %mul4 = add i32 %1, %0 + + %arrayidx7 = getelementptr inbounds i32, i32* %A, i32 %indvars.access + store i32 %mul4, i32* %arrayidx7, align 4 + + %exitcond = icmp uge i32 %indvars.ext, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: + br label %for.end + +for.end: + ret void +} + +; CHECK-LABEL: test_ule +; CHECK-LABEL: vector.scevcheck +; CHECK-LABEL: vector.body +define void @test_ule(i32* noalias %A, + i32* noalias %B, + i32* noalias %C, i32 %N, + i16 %M) { +entry: + %cmp13 = icmp eq i32 %N, 0 + br i1 %cmp13, label %for.end, label %for.body.preheader + +for.body.preheader: + br label %for.body + +for.body: + %indvars.iv = phi i16 [ %indvars.next, %for.body ], [ %M, %for.body.preheader ] + %indvars.next = sub i16 %indvars.iv, 1 + %indvars.ext = zext i16 %indvars.iv to i32 + + %arrayidx = getelementptr inbounds i32, i32* %B, i32 %indvars.ext + %0 = load i32, i32* %arrayidx, align 4 + %arrayidx3 = getelementptr inbounds i32, i32* %C, i32 %indvars.ext + %1 = load i32, i32* %arrayidx3, align 4 + + %mul4 = mul i32 %1, %0 + + %arrayidx7 = getelementptr inbounds i32, i32* %A, i32 %indvars.ext + store i32 %mul4, i32* %arrayidx7, align 4 + + %exitcond = icmp ule i32 %indvars.ext, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: + br label %for.end + +for.end: + ret void +} + +; CHECK-LABEL: test_sle +; CHECK-LABEL: vector.scevcheck +; CHECK-LABEL: vector.body +define void @test_sle(i32* noalias %A, + i32* noalias %B, + i32* noalias %C, i32 %N, + i16 %M) { +entry: + %cmp13 = icmp eq i32 %N, 0 + br i1 %cmp13, label %for.end, label %for.body.preheader + +for.body.preheader: + br label %for.body + +for.body: + %indvars.iv = phi i16 [ %indvars.next, %for.body ], [ %M, %for.body.preheader ] + %indvars.next = sub i16 %indvars.iv, 1 + %indvars.ext = sext i16 %indvars.iv to i32 + + %arrayidx = getelementptr inbounds i32, i32* %B, i32 %indvars.ext + %0 = load i32, i32* %arrayidx, align 4 + %arrayidx3 = getelementptr inbounds i32, i32* %C, i32 %indvars.ext + %1 = load i32, i32* %arrayidx3, align 4 + + %mul4 = mul i32 %1, %0 + + %arrayidx7 = getelementptr inbounds i32, i32* %A, i32 %indvars.ext + store i32 %mul4, i32* %arrayidx7, align 4 + + %exitcond = icmp sle i32 %indvars.ext, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: + br label %for.end + +for.end: + ret void +} diff --git a/test/Transforms/LoopVectorize/AArch64/first-order-recurrence.ll b/test/Transforms/LoopVectorize/AArch64/first-order-recurrence.ll new file mode 100644 index 0000000000000..5129568075f06 --- /dev/null +++ b/test/Transforms/LoopVectorize/AArch64/first-order-recurrence.ll @@ -0,0 +1,299 @@ +; RUN: opt < %s -loop-vectorize -force-vector-width=4 -force-vector-interleave=1 -dce -instcombine -S | FileCheck %s +; RUN: opt < %s -loop-vectorize -force-vector-width=4 -force-vector-interleave=2 -dce -instcombine -S | FileCheck %s --check-prefix=UNROLL + +target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" + +; CHECK-LABEL: @recurrence_1 +; +; void recurrence_1(int *a, int *b, int n) { +; for(int i = 0; i < n; i++) +; b[i] = a[i] + a[i - 1] +; } +; +; CHECK: vector.ph: +; CHECK: %vector.recur.init = insertelement <4 x i32> undef, i32 %pre_load, i32 3 +; +; CHECK: vector.body: +; CHECK: %vector.recur = phi <4 x i32> [ %vector.recur.init, %vector.ph ], [ [[L1:%[a-zA-Z0-9.]+]], %vector.body ] +; CHECK: [[L1]] = load <4 x i32> +; CHECK: {{.*}} = shufflevector <4 x i32> %vector.recur, <4 x i32> [[L1]], <4 x i32> <i32 3, i32 4, i32 5, i32 6> +; +; CHECK: middle.block: +; CHECK: %vector.recur.extract = extractelement <4 x i32> [[L1]], i32 3 +; +; CHECK: scalar.ph: +; CHECK: %scalar.recur.init = phi i32 [ %vector.recur.extract, %middle.block ], [ %pre_load, %vector.memcheck ], [ %pre_load, %min.iters.checked ], [ %pre_load, %for.preheader ] +; +; CHECK: scalar.body: +; CHECK: %scalar.recur = phi i32 [ %scalar.recur.init, %scalar.ph ], [ {{.*}}, %scalar.body ] +; +; UNROLL: vector.body: +; UNROLL: %vector.recur = phi <4 x i32> [ %vector.recur.init, %vector.ph ], [ [[L2:%[a-zA-Z0-9.]+]], %vector.body ] +; UNROLL: [[L1:%[a-zA-Z0-9.]+]] = load <4 x i32> +; UNROLL: [[L2]] = load <4 x i32> +; UNROLL: {{.*}} = shufflevector <4 x i32> %vector.recur, <4 x i32> [[L1]], <4 x i32> <i32 3, i32 4, i32 5, i32 6> +; UNROLL: {{.*}} = shufflevector <4 x i32> [[L1]], <4 x i32> [[L2]], <4 x i32> <i32 3, i32 4, i32 5, i32 6> +; +; UNROLL: middle.block: +; UNROLL: %vector.recur.extract = extractelement <4 x i32> [[L2]], i32 3 +; +define void @recurrence_1(i32* nocapture readonly %a, i32* nocapture %b, i32 %n) { +entry: + br label %for.preheader + +for.preheader: + %arrayidx.phi.trans.insert = getelementptr inbounds i32, i32* %a, i64 0 + %pre_load = load i32, i32* %arrayidx.phi.trans.insert + br label %scalar.body + +scalar.body: + %0 = phi i32 [ %pre_load, %for.preheader ], [ %1, %scalar.body ] + %indvars.iv = phi i64 [ 0, %for.preheader ], [ %indvars.iv.next, %scalar.body ] + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %arrayidx32 = getelementptr inbounds i32, i32* %a, i64 %indvars.iv.next + %1 = load i32, i32* %arrayidx32 + %arrayidx34 = getelementptr inbounds i32, i32* %b, i64 %indvars.iv + %add35 = add i32 %1, %0 + store i32 %add35, i32* %arrayidx34 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %n + br i1 %exitcond, label %for.exit, label %scalar.body + +for.exit: + ret void +} + +; CHECK-LABEL: @recurrence_2 +; +; int recurrence_2(int *a, int n) { +; int minmax; +; for (int i = 0; i < n; ++i) +; minmax = min(minmax, max(a[i] - a[i-1], 0)); +; return minmax; +; } +; +; CHECK: vector.ph: +; CHECK: %vector.recur.init = insertelement <4 x i32> undef, i32 %.pre, i32 3 +; +; CHECK: vector.body: +; CHECK: %vector.recur = phi <4 x i32> [ %vector.recur.init, %vector.ph ], [ [[L1:%[a-zA-Z0-9.]+]], %vector.body ] +; CHECK: [[L1]] = load <4 x i32> +; CHECK: {{.*}} = shufflevector <4 x i32> %vector.recur, <4 x i32> [[L1]], <4 x i32> <i32 3, i32 4, i32 5, i32 6> +; +; CHECK: middle.block: +; CHECK: %vector.recur.extract = extractelement <4 x i32> [[L1]], i32 3 +; +; CHECK: scalar.ph: +; CHECK: %scalar.recur.init = phi i32 [ %vector.recur.extract, %middle.block ], [ %.pre, %min.iters.checked ], [ %.pre, %for.preheader ] +; +; CHECK: scalar.body: +; CHECK: %scalar.recur = phi i32 [ %scalar.recur.init, %scalar.ph ], [ {{.*}}, %scalar.body ] +; +; UNROLL: vector.body: +; UNROLL: %vector.recur = phi <4 x i32> [ %vector.recur.init, %vector.ph ], [ [[L2:%[a-zA-Z0-9.]+]], %vector.body ] +; UNROLL: [[L1:%[a-zA-Z0-9.]+]] = load <4 x i32> +; UNROLL: [[L2]] = load <4 x i32> +; UNROLL: {{.*}} = shufflevector <4 x i32> %vector.recur, <4 x i32> [[L1]], <4 x i32> <i32 3, i32 4, i32 5, i32 6> +; UNROLL: {{.*}} = shufflevector <4 x i32> [[L1]], <4 x i32> [[L2]], <4 x i32> <i32 3, i32 4, i32 5, i32 6> +; +; UNROLL: middle.block: +; UNROLL: %vector.recur.extract = extractelement <4 x i32> [[L2]], i32 3 +; +define i32 @recurrence_2(i32* nocapture readonly %a, i32 %n) { +entry: + %cmp27 = icmp sgt i32 %n, 0 + br i1 %cmp27, label %for.preheader, label %for.cond.cleanup + +for.preheader: + %arrayidx2.phi.trans.insert = getelementptr inbounds i32, i32* %a, i64 -1 + %.pre = load i32, i32* %arrayidx2.phi.trans.insert, align 4 + br label %scalar.body + +for.cond.cleanup.loopexit: + %minmax.0.cond.lcssa = phi i32 [ %minmax.0.cond, %scalar.body ] + br label %for.cond.cleanup + +for.cond.cleanup: + %minmax.0.lcssa = phi i32 [ undef, %entry ], [ %minmax.0.cond.lcssa, %for.cond.cleanup.loopexit ] + ret i32 %minmax.0.lcssa + +scalar.body: + %0 = phi i32 [ %.pre, %for.preheader ], [ %1, %scalar.body ] + %indvars.iv = phi i64 [ 0, %for.preheader ], [ %indvars.iv.next, %scalar.body ] + %minmax.028 = phi i32 [ undef, %for.preheader ], [ %minmax.0.cond, %scalar.body ] + %arrayidx = getelementptr inbounds i32, i32* %a, i64 %indvars.iv + %1 = load i32, i32* %arrayidx, align 4 + %sub3 = sub nsw i32 %1, %0 + %cmp4 = icmp sgt i32 %sub3, 0 + %cond = select i1 %cmp4, i32 %sub3, i32 0 + %cmp5 = icmp slt i32 %minmax.028, %cond + %minmax.0.cond = select i1 %cmp5, i32 %minmax.028, i32 %cond + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %n + br i1 %exitcond, label %for.cond.cleanup.loopexit, label %scalar.body +} + +; CHECK-LABEL: @recurrence_3 +; +; void recurrence_3(short *a, double *b, int n, float f, short p) { +; b[0] = (double)a[0] - f * (double)p; +; for (int i = 1; i < n; i++) +; b[i] = (double)a[i] - f * (double)a[i - 1]; +; } +; +; +; CHECK: vector.ph: +; CHECK: %vector.recur.init = insertelement <4 x i16> undef, i16 %0, i32 3 +; +; CHECK: vector.body: +; CHECK: %vector.recur = phi <4 x i16> [ %vector.recur.init, %vector.ph ], [ [[L1:%[a-zA-Z0-9.]+]], %vector.body ] +; CHECK: [[L1]] = load <4 x i16> +; CHECK: {{.*}} = shufflevector <4 x i16> %vector.recur, <4 x i16> [[L1]], <4 x i32> <i32 3, i32 4, i32 5, i32 6> +; +; CHECK: middle.block: +; CHECK: %vector.recur.extract = extractelement <4 x i16> [[L1]], i32 3 +; +; CHECK: scalar.ph: +; CHECK: %scalar.recur.init = phi i16 [ %vector.recur.extract, %middle.block ], [ %0, %vector.memcheck ], [ %0, %min.iters.checked ], [ %0, %for.preheader ] +; +; CHECK: scalar.body: +; CHECK: %scalar.recur = phi i16 [ %scalar.recur.init, %scalar.ph ], [ {{.*}}, %scalar.body ] +; +; UNROLL: vector.body: +; UNROLL: %vector.recur = phi <4 x i16> [ %vector.recur.init, %vector.ph ], [ [[L2:%[a-zA-Z0-9.]+]], %vector.body ] +; UNROLL: [[L1:%[a-zA-Z0-9.]+]] = load <4 x i16> +; UNROLL: [[L2]] = load <4 x i16> +; UNROLL: {{.*}} = shufflevector <4 x i16> %vector.recur, <4 x i16> [[L1]], <4 x i32> <i32 3, i32 4, i32 5, i32 6> +; UNROLL: {{.*}} = shufflevector <4 x i16> [[L1]], <4 x i16> [[L2]], <4 x i32> <i32 3, i32 4, i32 5, i32 6> +; +; UNROLL: middle.block: +; UNROLL: %vector.recur.extract = extractelement <4 x i16> [[L2]], i32 3 +; +define void @recurrence_3(i16* nocapture readonly %a, double* nocapture %b, i32 %n, float %f, i16 %p) { +entry: + %0 = load i16, i16* %a, align 2 + %conv = sitofp i16 %0 to double + %conv1 = fpext float %f to double + %conv2 = sitofp i16 %p to double + %mul = fmul fast double %conv2, %conv1 + %sub = fsub fast double %conv, %mul + store double %sub, double* %b, align 8 + %cmp25 = icmp sgt i32 %n, 1 + br i1 %cmp25, label %for.preheader, label %for.end + +for.preheader: + br label %scalar.body + +scalar.body: + %1 = phi i16 [ %0, %for.preheader ], [ %2, %scalar.body ] + %advars.iv = phi i64 [ %advars.iv.next, %scalar.body ], [ 1, %for.preheader ] + %arrayidx5 = getelementptr inbounds i16, i16* %a, i64 %advars.iv + %2 = load i16, i16* %arrayidx5, align 2 + %conv6 = sitofp i16 %2 to double + %conv11 = sitofp i16 %1 to double + %mul12 = fmul fast double %conv11, %conv1 + %sub13 = fsub fast double %conv6, %mul12 + %arrayidx15 = getelementptr inbounds double, double* %b, i64 %advars.iv + store double %sub13, double* %arrayidx15, align 8 + %advars.iv.next = add nuw nsw i64 %advars.iv, 1 + %lftr.wideiv = trunc i64 %advars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %n + br i1 %exitcond, label %for.end.loopexit, label %scalar.body + +for.end.loopexit: + br label %for.end + +for.end: + ret void +} + +; CHECK-LABEL: @PR26734 +; +; void PR26734(short *a, int *b, int *c, int d, short *e) { +; for (; d != 21; d++) { +; *b &= *c; +; *e = *a - 6; +; *c = *e; +; } +; } +; +; CHECK-NOT: vector.ph: +; +define void @PR26734(i16* %a, i32* %b, i32* %c, i32 %d, i16* %e) { +entry: + %cmp4 = icmp eq i32 %d, 21 + br i1 %cmp4, label %entry.for.end_crit_edge, label %for.body.lr.ph + +entry.for.end_crit_edge: + %.pre = load i32, i32* %b, align 4 + br label %for.end + +for.body.lr.ph: + %0 = load i16, i16* %a, align 2 + %sub = add i16 %0, -6 + %conv2 = sext i16 %sub to i32 + %c.promoted = load i32, i32* %c, align 4 + %b.promoted = load i32, i32* %b, align 4 + br label %for.body + +for.body: + %inc7 = phi i32 [ %d, %for.body.lr.ph ], [ %inc, %for.body ] + %and6 = phi i32 [ %b.promoted, %for.body.lr.ph ], [ %and, %for.body ] + %conv25 = phi i32 [ %c.promoted, %for.body.lr.ph ], [ %conv2, %for.body ] + %and = and i32 %and6, %conv25 + %inc = add nsw i32 %inc7, 1 + %cmp = icmp eq i32 %inc, 21 + br i1 %cmp, label %for.cond.for.end_crit_edge, label %for.body + +for.cond.for.end_crit_edge: + %and.lcssa = phi i32 [ %and, %for.body ] + store i32 %conv2, i32* %c, align 4 + store i32 %and.lcssa, i32* %b, align 4 + store i16 %sub, i16* %e, align 2 + br label %for.end + +for.end: + ret void +} + +; CHECK-LABEL: @PR27246 +; +; int PR27246() { +; unsigned int e, n; +; for (int i = 1; i < 49; ++i) { +; for (int k = i; k > 1; --k) +; e = k; +; n = e; +; } +; return n; +; } +; +; CHECK-NOT: vector.ph: +; +define i32 @PR27246() { +entry: + br label %for.cond1.preheader + +for.cond1.preheader: + %i.016 = phi i32 [ 1, %entry ], [ %inc, %for.cond.cleanup3 ] + %e.015 = phi i32 [ undef, %entry ], [ %e.1.lcssa, %for.cond.cleanup3 ] + br label %for.cond1 + +for.cond.cleanup: + %e.1.lcssa.lcssa = phi i32 [ %e.1.lcssa, %for.cond.cleanup3 ] + ret i32 %e.1.lcssa.lcssa + +for.cond1: + %e.1 = phi i32 [ %k.0, %for.cond1 ], [ %e.015, %for.cond1.preheader ] + %k.0 = phi i32 [ %dec, %for.cond1 ], [ %i.016, %for.cond1.preheader ] + %cmp2 = icmp sgt i32 %k.0, 1 + %dec = add nsw i32 %k.0, -1 + br i1 %cmp2, label %for.cond1, label %for.cond.cleanup3 + +for.cond.cleanup3: + %e.1.lcssa = phi i32 [ %e.1, %for.cond1 ] + %inc = add nuw nsw i32 %i.016, 1 + %exitcond = icmp eq i32 %inc, 49 + br i1 %exitcond, label %for.cond.cleanup, label %for.cond1.preheader +} diff --git a/test/Transforms/LoopVectorize/AArch64/interleaved_cost.ll b/test/Transforms/LoopVectorize/AArch64/interleaved_cost.ll index a0e741a3cdbe8..df1f9c6194085 100644 --- a/test/Transforms/LoopVectorize/AArch64/interleaved_cost.ll +++ b/test/Transforms/LoopVectorize/AArch64/interleaved_cost.ll @@ -14,6 +14,7 @@ entry: ; 8xi8 and 16xi8 are valid i8 vector types, so the cost of the interleaved ; access group is 2. +; CHECK: LV: Checking a loop in "test_byte_interleaved_cost" ; CHECK: LV: Found an estimated cost of 2 for VF 8 For instruction: %tmp = load i8, i8* %arrayidx0, align 4 ; CHECK: LV: Found an estimated cost of 2 for VF 16 For instruction: %tmp = load i8, i8* %arrayidx0, align 4 @@ -37,3 +38,44 @@ for.body: ; preds = %for.body, %entry for.end: ; preds = %for.body ret void } + +%ig.factor.8 = type { double*, double, double, double, double, double, double, double } +define double @wide_interleaved_group(%ig.factor.8* %s, double %a, double %b, i32 %n) { +entry: + br label %for.body + +; Check the default cost of a strided load with a factor that is greater than +; the maximum allowed. In this test, the interleave factor would be 8, which is +; not supported. + +; CHECK: LV: Checking a loop in "wide_interleaved_group" +; CHECK: LV: Found an estimated cost of 6 for VF 2 For instruction: %1 = load double, double* %0, align 8 +; CHECK: LV: Found an estimated cost of 0 for VF 2 For instruction: %5 = load double, double* %4, align 8 +; CHECK: LV: Found an estimated cost of 10 for VF 2 For instruction: store double %9, double* %10, align 8 + +for.body: + %i = phi i64 [ 0, %entry ], [ %i.next, %for.body ] + %r = phi double [ 0.000000e+00, %entry ], [ %12, %for.body ] + %0 = getelementptr inbounds %ig.factor.8, %ig.factor.8* %s, i64 %i, i32 2 + %1 = load double, double* %0, align 8 + %2 = fcmp fast olt double %1, %a + %3 = select i1 %2, double 0.000000e+00, double %1 + %4 = getelementptr inbounds %ig.factor.8, %ig.factor.8* %s, i64 %i, i32 6 + %5 = load double, double* %4, align 8 + %6 = fcmp fast olt double %5, %a + %7 = select i1 %6, double 0.000000e+00, double %5 + %8 = fmul fast double %7, %b + %9 = fadd fast double %8, %3 + %10 = getelementptr inbounds %ig.factor.8, %ig.factor.8* %s, i64 %i, i32 3 + store double %9, double* %10, align 8 + %11 = fmul fast double %9, %9 + %12 = fadd fast double %11, %r + %i.next = add nuw nsw i64 %i, 1 + %13 = trunc i64 %i.next to i32 + %cond = icmp eq i32 %13, %n + br i1 %cond, label %for.exit, label %for.body + +for.exit: + %r.lcssa = phi double [ %12, %for.body ] + ret double %r.lcssa +} diff --git a/test/Transforms/LoopVectorize/AArch64/loop-vectorization-factors.ll b/test/Transforms/LoopVectorize/AArch64/loop-vectorization-factors.ll index 51f899c2f645a..c7ced757581aa 100644 --- a/test/Transforms/LoopVectorize/AArch64/loop-vectorization-factors.ll +++ b/test/Transforms/LoopVectorize/AArch64/loop-vectorization-factors.ll @@ -205,5 +205,63 @@ for.body: ; preds = %for.body, %for.body br i1 %exitcond, label %for.cond.cleanup, label %for.body } +; CHECK-LABEL: @add_phifail( +; CHECK: load <16 x i8>, <16 x i8>* +; CHECK: add nuw nsw <16 x i32> +; CHECK: store <16 x i8> +; Function Attrs: nounwind +define void @add_phifail(i8* noalias nocapture readonly %p, i8* noalias nocapture %q, i32 %len) #0 { +entry: + %cmp8 = icmp sgt i32 %len, 0 + br i1 %cmp8, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.body, %entry + ret void + +for.body: ; preds = %entry, %for.body + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ] + %a_phi = phi i32 [ %conv, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i8, i8* %p, i64 %indvars.iv + %0 = load i8, i8* %arrayidx + %conv = zext i8 %0 to i32 + %add = add nuw nsw i32 %conv, 2 + %conv1 = trunc i32 %add to i8 + %arrayidx3 = getelementptr inbounds i8, i8* %q, i64 %indvars.iv + store i8 %conv1, i8* %arrayidx3 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %len + br i1 %exitcond, label %for.cond.cleanup, label %for.body +} + +; CHECK-LABEL: @add_phifail2( +; CHECK: load <16 x i8>, <16 x i8>* +; CHECK: add nuw nsw <16 x i32> +; CHECK: store <16 x i8> +; Function Attrs: nounwind +define i8 @add_phifail2(i8* noalias nocapture readonly %p, i8* noalias nocapture %q, i32 %len) #0 { +entry: + br label %for.body + +for.cond.cleanup: ; preds = %for.body, %entry + %ret = trunc i32 %a_phi to i8 + ret i8 %ret + +for.body: ; preds = %entry, %for.body + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ] + %a_phi = phi i32 [ %conv, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i8, i8* %p, i64 %indvars.iv + %0 = load i8, i8* %arrayidx + %conv = zext i8 %0 to i32 + %add = add nuw nsw i32 %conv, 2 + %conv1 = trunc i32 %add to i8 + %arrayidx3 = getelementptr inbounds i8, i8* %q, i64 %indvars.iv + store i8 %conv1, i8* %arrayidx3 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %len + br i1 %exitcond, label %for.cond.cleanup, label %for.body +} attributes #0 = { nounwind } + diff --git a/test/Transforms/LoopVectorize/AArch64/max-vf-for-interleaved.ll b/test/Transforms/LoopVectorize/AArch64/max-vf-for-interleaved.ll new file mode 100644 index 0000000000000..8b9589aebba4e --- /dev/null +++ b/test/Transforms/LoopVectorize/AArch64/max-vf-for-interleaved.ll @@ -0,0 +1,56 @@ +; RUN: opt < %s -force-vector-interleave=1 -store-to-load-forwarding-conflict-detection=false -loop-vectorize -dce -instcombine -S | FileCheck %s + +target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" +target triple = "aarch64--linux-gnu" + +%struct.pair = type { i32, i32 } + +; Check vectorization of interleaved access groups with positive dependence +; distances. In this test, the maximum safe dependence distance for +; vectorization is 16 bytes. Normally, this would lead to a maximum VF of 4. +; However, for interleaved groups, the effective VF is VF * IF, where IF is the +; interleave factor. Here, the maximum safe dependence distance is recomputed +; as 16 / IF bytes, resulting in VF=2. Since IF=2, we should generate <4 x i32> +; loads and stores instead of <8 x i32> accesses. +; +; Note: LAA's conflict detection optimization has to be disabled for this test +; to be vectorized. + +; struct pair { +; int x; +; int y; +; }; +; +; void max_vf(struct pair *restrict p) { +; for (int i = 0; i < 1000; i++) { +; p[i + 2].x = p[i].x +; p[i + 2].y = p[i].y +; } +; } + +; CHECK-LABEL: @max_vf +; CHECK: load <4 x i32> +; CHECK: store <4 x i32> + +define void @max_vf(%struct.pair* noalias nocapture %p) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ 0, %entry ], [ %i.next, %for.body ] + %0 = add nuw nsw i64 %i, 2 + %p_i.x = getelementptr inbounds %struct.pair, %struct.pair* %p, i64 %i, i32 0 + %p_i_plus_2.x = getelementptr inbounds %struct.pair, %struct.pair* %p, i64 %0, i32 0 + %1 = load i32, i32* %p_i.x, align 4 + store i32 %1, i32* %p_i_plus_2.x, align 4 + %p_i.y = getelementptr inbounds %struct.pair, %struct.pair* %p, i64 %i, i32 1 + %p_i_plus_2.y = getelementptr inbounds %struct.pair, %struct.pair* %p, i64 %0, i32 1 + %2 = load i32, i32* %p_i.y, align 4 + store i32 %2, i32* %p_i_plus_2.y, align 4 + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp eq i64 %i.next, 1000 + br i1 %cond, label %for.exit, label %for.body + +for.exit: + ret void +} diff --git a/test/Transforms/LoopVectorize/AArch64/type-shrinkage-insertelt.ll b/test/Transforms/LoopVectorize/AArch64/type-shrinkage-insertelt.ll new file mode 100644 index 0000000000000..ffe8480138d0a --- /dev/null +++ b/test/Transforms/LoopVectorize/AArch64/type-shrinkage-insertelt.ll @@ -0,0 +1,47 @@ +; RUN: opt -S < %s -loop-vectorize -force-vector-width=4 | FileCheck %s + +target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" +target triple = "aarch64--linux-gnu" + +; CHECK-LABEL: test0 +define void @test0(i16* noalias %M3) { +entry: + br label %if.then1165.us + +if.then1165.us: ; preds = %if.then1165.us, %entry + %indvars.iv1783 = phi i64 [ 0, %entry ], [ %indvars.iv.next1784, %if.then1165.us ] + %conv1177.us = zext i16 undef to i32 + %add1178.us = add nsw i32 %conv1177.us, undef + %conv1179.us = trunc i32 %add1178.us to i16 + %idxprom1181.us = ashr exact i64 undef, 32 + %arrayidx1185.us = getelementptr inbounds i16, i16* %M3, i64 %idxprom1181.us + store i16 %conv1179.us, i16* %arrayidx1185.us, align 2 + %indvars.iv.next1784 = add nuw nsw i64 %indvars.iv1783, 1 + %exitcond1785 = icmp eq i64 %indvars.iv.next1784, 16 + br i1 %exitcond1785, label %for.inc1286.loopexit, label %if.then1165.us + +for.inc1286.loopexit: ; preds = %if.then1165.us + ret void +} + +; CHECK-LABEL: test1 +define void @test1(i16* noalias %M3) { +entry: + br label %if.then1165.us + +if.then1165.us: ; preds = %if.then1165.us, %entry + %indvars.iv1783 = phi i64 [ 0, %entry ], [ %indvars.iv.next1784, %if.then1165.us ] + %fptr = load i32, i32* undef, align 4 + %conv1177.us = zext i16 undef to i32 + %add1178.us = add nsw i32 %conv1177.us, %fptr + %conv1179.us = trunc i32 %add1178.us to i16 + %idxprom1181.us = ashr exact i64 undef, 32 + %arrayidx1185.us = getelementptr inbounds i16, i16* %M3, i64 %idxprom1181.us + store i16 %conv1179.us, i16* %arrayidx1185.us, align 2 + %indvars.iv.next1784 = add nuw nsw i64 %indvars.iv1783, 1 + %exitcond1785 = icmp eq i64 %indvars.iv.next1784, 16 + br i1 %exitcond1785, label %for.inc1286.loopexit, label %if.then1165.us + +for.inc1286.loopexit: ; preds = %if.then1165.us + ret void +} diff --git a/test/Transforms/LoopVectorize/ARM/arm-ieee-vectorize.ll b/test/Transforms/LoopVectorize/ARM/arm-ieee-vectorize.ll new file mode 100644 index 0000000000000..369568f6dfaaf --- /dev/null +++ b/test/Transforms/LoopVectorize/ARM/arm-ieee-vectorize.ll @@ -0,0 +1,330 @@ +; RUN: opt -mtriple armv7-linux-gnueabihf -loop-vectorize -S %s -debug-only=loop-vectorize -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=LINUX +; RUN: opt -mtriple armv8-linux-gnu -loop-vectorize -S %s -debug-only=loop-vectorize -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=LINUX +; RUN: opt -mtriple armv7-unknwon-darwin -loop-vectorize -S %s -debug-only=loop-vectorize -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=DARWIN +; REQUIRES: asserts + +; Testing the ability of the loop vectorizer to tell when SIMD is safe or not +; regarding IEEE 754 standard. +; On Linux, we only want the vectorizer to work when -ffast-math flag is set, +; because NEON is not IEEE compliant. +; Darwin, on the other hand, doesn't support subnormals, and all optimizations +; are allowed, even without -ffast-math. + +; Integer loops are always vectorizeable +; CHECK: Checking a loop in "sumi" +; CHECK: We can vectorize this loop! +define void @sumi(i32* noalias nocapture readonly %A, i32* noalias nocapture readonly %B, i32* noalias nocapture %C, i32 %N) { +entry: + %cmp5 = icmp eq i32 %N, 0 + br i1 %cmp5, label %for.end, label %for.body.preheader + +for.body.preheader: ; preds = %entry + br label %for.body + +for.body: ; preds = %for.body.preheader, %for.body + %i.06 = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] + %arrayidx = getelementptr inbounds i32, i32* %A, i32 %i.06 + %0 = load i32, i32* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds i32, i32* %B, i32 %i.06 + %1 = load i32, i32* %arrayidx1, align 4 + %mul = mul nsw i32 %1, %0 + %arrayidx2 = getelementptr inbounds i32, i32* %C, i32 %i.06 + store i32 %mul, i32* %arrayidx2, align 4 + %inc = add nuw nsw i32 %i.06, 1 + %exitcond = icmp eq i32 %inc, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + br label %for.end + +for.end: ; preds = %for.end.loopexit, %entry + ret void +} + +; Floating-point loops need fast-math to be vectorizeable +; LINUX: Checking a loop in "sumf" +; LINUX: Potentially unsafe FP op prevents vectorization +; DARWIN: Checking a loop in "sumf" +; DARWIN: We can vectorize this loop! +define void @sumf(float* noalias nocapture readonly %A, float* noalias nocapture readonly %B, float* noalias nocapture %C, i32 %N) { +entry: + %cmp5 = icmp eq i32 %N, 0 + br i1 %cmp5, label %for.end, label %for.body.preheader + +for.body.preheader: ; preds = %entry + br label %for.body + +for.body: ; preds = %for.body.preheader, %for.body + %i.06 = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] + %arrayidx = getelementptr inbounds float, float* %A, i32 %i.06 + %0 = load float, float* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds float, float* %B, i32 %i.06 + %1 = load float, float* %arrayidx1, align 4 + %mul = fmul float %0, %1 + %arrayidx2 = getelementptr inbounds float, float* %C, i32 %i.06 + store float %mul, float* %arrayidx2, align 4 + %inc = add nuw nsw i32 %i.06, 1 + %exitcond = icmp eq i32 %inc, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + br label %for.end + +for.end: ; preds = %for.end.loopexit, %entry + ret void +} + +; Integer loops are always vectorizeable +; CHECK: Checking a loop in "redi" +; CHECK: We can vectorize this loop! +define i32 @redi(i32* noalias nocapture readonly %a, i32* noalias nocapture readonly %b, i32 %N) { +entry: + %cmp5 = icmp eq i32 %N, 0 + br i1 %cmp5, label %for.end, label %for.body.preheader + +for.body.preheader: ; preds = %entry + br label %for.body + +for.body: ; preds = %for.body.preheader, %for.body + %i.07 = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] + %Red.06 = phi i32 [ %add, %for.body ], [ undef, %for.body.preheader ] + %arrayidx = getelementptr inbounds i32, i32* %a, i32 %i.07 + %0 = load i32, i32* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds i32, i32* %b, i32 %i.07 + %1 = load i32, i32* %arrayidx1, align 4 + %mul = mul nsw i32 %1, %0 + %add = add nsw i32 %mul, %Red.06 + %inc = add nuw nsw i32 %i.07, 1 + %exitcond = icmp eq i32 %inc, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + %add.lcssa = phi i32 [ %add, %for.body ] + br label %for.end + +for.end: ; preds = %for.end.loopexit, %entry + %Red.0.lcssa = phi i32 [ undef, %entry ], [ %add.lcssa, %for.end.loopexit ] + ret i32 %Red.0.lcssa +} + +; Floating-point loops need fast-math to be vectorizeable +; LINUX: Checking a loop in "redf" +; LINUX: Potentially unsafe FP op prevents vectorization +; DARWIN: Checking a loop in "redf" +; DARWIN: We can vectorize this loop! +define float @redf(float* noalias nocapture readonly %a, float* noalias nocapture readonly %b, i32 %N) { +entry: + %cmp5 = icmp eq i32 %N, 0 + br i1 %cmp5, label %for.end, label %for.body.preheader + +for.body.preheader: ; preds = %entry + br label %for.body + +for.body: ; preds = %for.body.preheader, %for.body + %i.07 = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] + %Red.06 = phi float [ %add, %for.body ], [ undef, %for.body.preheader ] + %arrayidx = getelementptr inbounds float, float* %a, i32 %i.07 + %0 = load float, float* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds float, float* %b, i32 %i.07 + %1 = load float, float* %arrayidx1, align 4 + %mul = fmul float %0, %1 + %add = fadd float %Red.06, %mul + %inc = add nuw nsw i32 %i.07, 1 + %exitcond = icmp eq i32 %inc, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + %add.lcssa = phi float [ %add, %for.body ] + br label %for.end + +for.end: ; preds = %for.end.loopexit, %entry + %Red.0.lcssa = phi float [ undef, %entry ], [ %add.lcssa, %for.end.loopexit ] + ret float %Red.0.lcssa +} + +; Make sure calls that turn into builtins are also covered +; LINUX: Checking a loop in "fabs" +; LINUX: Potentially unsafe FP op prevents vectorization +; DARWIN: Checking a loop in "fabs" +; DARWIN: We can vectorize this loop! +define void @fabs(float* noalias nocapture readonly %A, float* noalias nocapture readonly %B, float* noalias nocapture %C, i32 %N) { +entry: + %cmp10 = icmp eq i32 %N, 0 + br i1 %cmp10, label %for.end, label %for.body + +for.body: ; preds = %entry, %for.body + %i.011 = phi i32 [ %inc, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr inbounds float, float* %A, i32 %i.011 + %0 = load float, float* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds float, float* %B, i32 %i.011 + %1 = load float, float* %arrayidx1, align 4 + %fabsf = tail call float @fabsf(float %1) #1 + %conv3 = fmul float %0, %fabsf + %arrayidx4 = getelementptr inbounds float, float* %C, i32 %i.011 + store float %conv3, float* %arrayidx4, align 4 + %inc = add nuw nsw i32 %i.011, 1 + %exitcond = icmp eq i32 %inc, %N + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body, %entry + ret void +} + +; Integer loops are always vectorizeable +; CHECK: Checking a loop in "sumi_fast" +; CHECK: We can vectorize this loop! +define void @sumi_fast(i32* noalias nocapture readonly %A, i32* noalias nocapture readonly %B, i32* noalias nocapture %C, i32 %N) { +entry: + %cmp5 = icmp eq i32 %N, 0 + br i1 %cmp5, label %for.end, label %for.body.preheader + +for.body.preheader: ; preds = %entry + br label %for.body + +for.body: ; preds = %for.body.preheader, %for.body + %i.06 = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] + %arrayidx = getelementptr inbounds i32, i32* %A, i32 %i.06 + %0 = load i32, i32* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds i32, i32* %B, i32 %i.06 + %1 = load i32, i32* %arrayidx1, align 4 + %mul = mul nsw i32 %1, %0 + %arrayidx2 = getelementptr inbounds i32, i32* %C, i32 %i.06 + store i32 %mul, i32* %arrayidx2, align 4 + %inc = add nuw nsw i32 %i.06, 1 + %exitcond = icmp eq i32 %inc, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + br label %for.end + +for.end: ; preds = %for.end.loopexit, %entry + ret void +} + +; Floating-point loops can be vectorizeable with fast-math +; CHECK: Checking a loop in "sumf_fast" +; CHECK: We can vectorize this loop! +define void @sumf_fast(float* noalias nocapture readonly %A, float* noalias nocapture readonly %B, float* noalias nocapture %C, i32 %N) { +entry: + %cmp5 = icmp eq i32 %N, 0 + br i1 %cmp5, label %for.end, label %for.body.preheader + +for.body.preheader: ; preds = %entry + br label %for.body + +for.body: ; preds = %for.body.preheader, %for.body + %i.06 = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] + %arrayidx = getelementptr inbounds float, float* %A, i32 %i.06 + %0 = load float, float* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds float, float* %B, i32 %i.06 + %1 = load float, float* %arrayidx1, align 4 + %mul = fmul fast float %1, %0 + %arrayidx2 = getelementptr inbounds float, float* %C, i32 %i.06 + store float %mul, float* %arrayidx2, align 4 + %inc = add nuw nsw i32 %i.06, 1 + %exitcond = icmp eq i32 %inc, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + br label %for.end + +for.end: ; preds = %for.end.loopexit, %entry + ret void +} + +; Integer loops are always vectorizeable +; CHECK: Checking a loop in "redi_fast" +; CHECK: We can vectorize this loop! +define i32 @redi_fast(i32* noalias nocapture readonly %a, i32* noalias nocapture readonly %b, i32 %N) { +entry: + %cmp5 = icmp eq i32 %N, 0 + br i1 %cmp5, label %for.end, label %for.body.preheader + +for.body.preheader: ; preds = %entry + br label %for.body + +for.body: ; preds = %for.body.preheader, %for.body + %i.07 = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] + %Red.06 = phi i32 [ %add, %for.body ], [ undef, %for.body.preheader ] + %arrayidx = getelementptr inbounds i32, i32* %a, i32 %i.07 + %0 = load i32, i32* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds i32, i32* %b, i32 %i.07 + %1 = load i32, i32* %arrayidx1, align 4 + %mul = mul nsw i32 %1, %0 + %add = add nsw i32 %mul, %Red.06 + %inc = add nuw nsw i32 %i.07, 1 + %exitcond = icmp eq i32 %inc, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + %add.lcssa = phi i32 [ %add, %for.body ] + br label %for.end + +for.end: ; preds = %for.end.loopexit, %entry + %Red.0.lcssa = phi i32 [ undef, %entry ], [ %add.lcssa, %for.end.loopexit ] + ret i32 %Red.0.lcssa +} + +; Floating-point loops can be vectorizeable with fast-math +; CHECK: Checking a loop in "redf_fast" +; CHECK: We can vectorize this loop! +define float @redf_fast(float* noalias nocapture readonly %a, float* noalias nocapture readonly %b, i32 %N) { +entry: + %cmp5 = icmp eq i32 %N, 0 + br i1 %cmp5, label %for.end, label %for.body.preheader + +for.body.preheader: ; preds = %entry + br label %for.body + +for.body: ; preds = %for.body.preheader, %for.body + %i.07 = phi i32 [ %inc, %for.body ], [ 0, %for.body.preheader ] + %Red.06 = phi float [ %add, %for.body ], [ undef, %for.body.preheader ] + %arrayidx = getelementptr inbounds float, float* %a, i32 %i.07 + %0 = load float, float* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds float, float* %b, i32 %i.07 + %1 = load float, float* %arrayidx1, align 4 + %mul = fmul fast float %1, %0 + %add = fadd fast float %mul, %Red.06 + %inc = add nuw nsw i32 %i.07, 1 + %exitcond = icmp eq i32 %inc, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + %add.lcssa = phi float [ %add, %for.body ] + br label %for.end + +for.end: ; preds = %for.end.loopexit, %entry + %Red.0.lcssa = phi float [ undef, %entry ], [ %add.lcssa, %for.end.loopexit ] + ret float %Red.0.lcssa +} + +; Make sure calls that turn into builtins are also covered +; CHECK: Checking a loop in "fabs_fast" +; CHECK: We can vectorize this loop! +define void @fabs_fast(float* noalias nocapture readonly %A, float* noalias nocapture readonly %B, float* noalias nocapture %C, i32 %N) { +entry: + %cmp10 = icmp eq i32 %N, 0 + br i1 %cmp10, label %for.end, label %for.body + +for.body: ; preds = %entry, %for.body + %i.011 = phi i32 [ %inc, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr inbounds float, float* %A, i32 %i.011 + %0 = load float, float* %arrayidx, align 4 + %arrayidx1 = getelementptr inbounds float, float* %B, i32 %i.011 + %1 = load float, float* %arrayidx1, align 4 + %fabsf = tail call fast float @fabsf(float %1) #2 + %conv3 = fmul fast float %fabsf, %0 + %arrayidx4 = getelementptr inbounds float, float* %C, i32 %i.011 + store float %conv3, float* %arrayidx4, align 4 + %inc = add nuw nsw i32 %i.011, 1 + %exitcond = icmp eq i32 %inc, %N + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body, %entry + ret void +} + +declare float @fabsf(float) + +attributes #1 = { nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a8" "target-features"="+dsp,+neon,+vfp3" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a8" "target-features"="+dsp,+neon,+vfp3" "unsafe-fp-math"="true" "use-soft-float"="false" } diff --git a/test/Transforms/LoopVectorize/PowerPC/large-loop-rdx.ll b/test/Transforms/LoopVectorize/PowerPC/large-loop-rdx.ll index de6595f64ed3a..c88b496e4e9c3 100644 --- a/test/Transforms/LoopVectorize/PowerPC/large-loop-rdx.ll +++ b/test/Transforms/LoopVectorize/PowerPC/large-loop-rdx.ll @@ -12,7 +12,9 @@ ; CHECK-NEXT: fadd ; CHECK-NEXT: fadd ; CHECK-NEXT: fadd -; CHECK-NEXT-NOT: fadd +; CHECK-NEXT: = +; CHECK-NOT: fadd +; CHECK-SAME: > target datalayout = "e-m:e-i64:64-n32:64" target triple = "powerpc64le-ibm-linux-gnu" diff --git a/test/Transforms/LoopVectorize/PowerPC/small-loop-rdx.ll b/test/Transforms/LoopVectorize/PowerPC/small-loop-rdx.ll index 2898af2986d33..cdd5f042350a2 100644 --- a/test/Transforms/LoopVectorize/PowerPC/small-loop-rdx.ll +++ b/test/Transforms/LoopVectorize/PowerPC/small-loop-rdx.ll @@ -12,7 +12,9 @@ ; CHECK-NEXT: fadd ; CHECK-NEXT: fadd ; CHECK-NEXT: fadd -; CHECK-NEXT-NOT: fadd +; CHECK-NEXT: = +; CHECK-NOT: fadd +; CHECK-SAME: > target datalayout = "e-m:e-i64:64-n32:64" target triple = "powerpc64le-ibm-linux-gnu" diff --git a/test/Transforms/LoopVectorize/PowerPC/vectorize-only-for-real.ll b/test/Transforms/LoopVectorize/PowerPC/vectorize-only-for-real.ll new file mode 100644 index 0000000000000..8abc25ece35c6 --- /dev/null +++ b/test/Transforms/LoopVectorize/PowerPC/vectorize-only-for-real.ll @@ -0,0 +1,62 @@ +; RUN: opt -S -loop-vectorize < %s | FileCheck %s +target datalayout = "E-m:e-i64:64-n32:64" +target triple = "powerpc64-bgq-linux" + +; Function Attrs: nounwind +define zeroext i32 @test() #0 { +; CHECK-LABEL: @test +; CHECK-NOT: x i32> + +entry: + %a = alloca [1600 x i32], align 4 + %c = alloca [1600 x i32], align 4 + %0 = bitcast [1600 x i32]* %a to i8* + call void @llvm.lifetime.start(i64 6400, i8* %0) #3 + br label %for.body + +for.cond.cleanup: ; preds = %for.body + %1 = bitcast [1600 x i32]* %c to i8* + call void @llvm.lifetime.start(i64 6400, i8* %1) #3 + %arraydecay = getelementptr inbounds [1600 x i32], [1600 x i32]* %a, i64 0, i64 0 + %arraydecay1 = getelementptr inbounds [1600 x i32], [1600 x i32]* %c, i64 0, i64 0 + %call = call signext i32 @bar(i32* %arraydecay, i32* %arraydecay1) #3 + br label %for.body6 + +for.body: ; preds = %for.body, %entry + %indvars.iv25 = phi i64 [ 0, %entry ], [ %indvars.iv.next26, %for.body ] + %arrayidx = getelementptr inbounds [1600 x i32], [1600 x i32]* %a, i64 0, i64 %indvars.iv25 + %2 = trunc i64 %indvars.iv25 to i32 + store i32 %2, i32* %arrayidx, align 4 + %indvars.iv.next26 = add nuw nsw i64 %indvars.iv25, 1 + %exitcond27 = icmp eq i64 %indvars.iv.next26, 1600 + br i1 %exitcond27, label %for.cond.cleanup, label %for.body + +for.cond.cleanup5: ; preds = %for.body6 + call void @llvm.lifetime.end(i64 6400, i8* nonnull %1) #3 + call void @llvm.lifetime.end(i64 6400, i8* %0) #3 + ret i32 %add + +for.body6: ; preds = %for.body6, %for.cond.cleanup + %indvars.iv = phi i64 [ 0, %for.cond.cleanup ], [ %indvars.iv.next, %for.body6 ] + %s.022 = phi i32 [ 0, %for.cond.cleanup ], [ %add, %for.body6 ] + %arrayidx8 = getelementptr inbounds [1600 x i32], [1600 x i32]* %c, i64 0, i64 %indvars.iv + %3 = load i32, i32* %arrayidx8, align 4 + %add = add i32 %3, %s.022 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond = icmp eq i64 %indvars.iv.next, 1600 + br i1 %exitcond, label %for.cond.cleanup5, label %for.body6 +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.start(i64, i8* nocapture) #1 + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.end(i64, i8* nocapture) #1 + +declare signext i32 @bar(i32*, i32*) #2 + +attributes #0 = { nounwind "target-cpu"="a2q" "target-features"="+qpx,-altivec,-bpermd,-crypto,-direct-move,-extdiv,-power8-vector,-vsx" } +attributes #1 = { argmemonly nounwind } +attributes #2 = { "target-cpu"="a2q" "target-features"="+qpx,-altivec,-bpermd,-crypto,-direct-move,-extdiv,-power8-vector,-vsx" } +attributes #3 = { nounwind } + diff --git a/test/Transforms/LoopVectorize/PowerPC/vsx-tsvc-s173.ll b/test/Transforms/LoopVectorize/PowerPC/vsx-tsvc-s173.ll index 65b3919585e31..fed186b9b6750 100644 --- a/test/Transforms/LoopVectorize/PowerPC/vsx-tsvc-s173.ll +++ b/test/Transforms/LoopVectorize/PowerPC/vsx-tsvc-s173.ll @@ -43,7 +43,7 @@ for.end12: ; preds = %for.end, %entry ; CHECK-LABEL: @s173 ; CHECK: load <4 x float>, <4 x float>* -; CHECK: add i64 %index, 16000 +; CHECK: add nsw i64 %1, 16000 ; CHECK: ret i32 0 } diff --git a/test/Transforms/LoopVectorize/X86/avx1.ll b/test/Transforms/LoopVectorize/X86/avx1.ll index 37977c43ac308..d384a8162ba19 100644 --- a/test/Transforms/LoopVectorize/X86/avx1.ll +++ b/test/Transforms/LoopVectorize/X86/avx1.ll @@ -1,11 +1,12 @@ -; RUN: opt < %s -loop-vectorize -mtriple=x86_64-apple-macosx10.8.0 -mcpu=corei7-avx -S | FileCheck %s +; RUN: opt < %s -loop-vectorize -mattr=avx,+slow-unaligned-mem-32 -S | FileCheck %s --check-prefix=SLOWMEM32 --check-prefix=CHECK +; RUN: opt < %s -loop-vectorize -mattr=avx,-slow-unaligned-mem-32 -S | FileCheck %s --check-prefix=FASTMEM32 --check-prefix=CHECK target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.8.0" -;CHECK-LABEL: @read_mod_write_single_ptr( -;CHECK: load <8 x float> -;CHECK: ret i32 +; CHECK-LABEL: @read_mod_write_single_ptr( +; CHECK: load <8 x float> +; CHECK: ret i32 define i32 @read_mod_write_single_ptr(float* nocapture %a, i32 %n) nounwind uwtable ssp { %1 = icmp sgt i32 %n, 0 br i1 %1, label %.lr.ph, label %._crit_edge @@ -26,9 +27,10 @@ define i32 @read_mod_write_single_ptr(float* nocapture %a, i32 %n) nounwind uwta } -;CHECK-LABEL: @read_mod_i64( -;CHECK: load <2 x i64> -;CHECK: ret i32 +; CHECK-LABEL: @read_mod_i64( +; SLOWMEM32: load <2 x i64> +; FASTMEM32: load <4 x i64> +; CHECK: ret i32 define i32 @read_mod_i64(i64* nocapture %a, i32 %n) nounwind uwtable ssp { %1 = icmp sgt i32 %n, 0 br i1 %1, label %.lr.ph, label %._crit_edge @@ -47,3 +49,4 @@ define i32 @read_mod_i64(i64* nocapture %a, i32 %n) nounwind uwtable ssp { ._crit_edge: ; preds = %.lr.ph, %0 ret i32 undef } + diff --git a/test/Transforms/LoopVectorize/X86/cost-model.ll b/test/Transforms/LoopVectorize/X86/cost-model.ll index 013657102e6b4..699dd5bf0354d 100644 --- a/test/Transforms/LoopVectorize/X86/cost-model.ll +++ b/test/Transforms/LoopVectorize/X86/cost-model.ll @@ -39,3 +39,44 @@ for.body: ; preds = %for.body, %entry for.end: ; preds = %for.body ret void } + +; This function uses a stride that is generally too big to benefit from vectorization without +; really good support for a gather load. We were not computing an accurate cost for the +; vectorization and subsequent scalarization of the pointer induction variables. + +define float @PR27826(float* nocapture readonly %a, float* nocapture readonly %b, i32 %n) { +; CHECK-LABEL: @PR27826( +; CHECK-NOT: <4 x float> +; CHECK-NOT: <8 x float> +; CHECK: ret float %s.0.lcssa + +entry: + %cmp = icmp sgt i32 %n, 0 + br i1 %cmp, label %preheader, label %for.end + +preheader: + %t0 = sext i32 %n to i64 + br label %for + +for: + %indvars.iv = phi i64 [ 0, %preheader ], [ %indvars.iv.next, %for ] + %s.02 = phi float [ 0.0, %preheader ], [ %add4, %for ] + %arrayidx = getelementptr inbounds float, float* %a, i64 %indvars.iv + %t1 = load float, float* %arrayidx, align 4 + %arrayidx3 = getelementptr inbounds float, float* %b, i64 %indvars.iv + %t2 = load float, float* %arrayidx3, align 4 + %add = fadd fast float %t1, %s.02 + %add4 = fadd fast float %add, %t2 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 8 + %cmp1 = icmp slt i64 %indvars.iv.next, %t0 + br i1 %cmp1, label %for, label %loopexit + +loopexit: + %add4.lcssa = phi float [ %add4, %for ] + br label %for.end + +for.end: + %s.0.lcssa = phi float [ 0.0, %entry ], [ %add4.lcssa, %loopexit ] + ret float %s.0.lcssa +} + diff --git a/test/Transforms/LoopVectorize/X86/force-ifcvt.ll b/test/Transforms/LoopVectorize/X86/force-ifcvt.ll new file mode 100644 index 0000000000000..00764943556de --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/force-ifcvt.ll @@ -0,0 +1,41 @@ +; RUN: opt -loop-vectorize -S < %s | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: norecurse nounwind uwtable +define void @Test(i32* nocapture %res, i32* nocapture readnone %c, i32* nocapture readonly %d, i32* nocapture readonly %p) #0 { +entry: + br label %for.body + +; CHECK-LABEL: @Test +; CHECK: <4 x i32> + +for.body: ; preds = %cond.end, %entry + %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %cond.end ] + %arrayidx = getelementptr inbounds i32, i32* %p, i64 %indvars.iv + %0 = load i32, i32* %arrayidx, align 4, !llvm.mem.parallel_loop_access !0 + %cmp1 = icmp eq i32 %0, 0 + %arrayidx3 = getelementptr inbounds i32, i32* %res, i64 %indvars.iv + %1 = load i32, i32* %arrayidx3, align 4, !llvm.mem.parallel_loop_access !0 + br i1 %cmp1, label %cond.end, label %cond.false + +cond.false: ; preds = %for.body + %arrayidx7 = getelementptr inbounds i32, i32* %d, i64 %indvars.iv + %2 = load i32, i32* %arrayidx7, align 4, !llvm.mem.parallel_loop_access !0 + %add = add nsw i32 %2, %1 + br label %cond.end + +cond.end: ; preds = %for.body, %cond.false + %cond = phi i32 [ %add, %cond.false ], [ %1, %for.body ] + store i32 %cond, i32* %arrayidx3, align 4, !llvm.mem.parallel_loop_access !0 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond = icmp eq i64 %indvars.iv.next, 16 + br i1 %exitcond, label %for.end, label %for.body, !llvm.loop !0 + +for.end: ; preds = %cond.end + ret void +} + +attributes #0 = { norecurse nounwind uwtable "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" } + +!0 = distinct !{!0} diff --git a/test/Transforms/LoopVectorize/X86/funclet.ll b/test/Transforms/LoopVectorize/X86/funclet.ll new file mode 100644 index 0000000000000..88f15e7e14854 --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/funclet.ll @@ -0,0 +1,45 @@ +; RUN: opt -S -loop-vectorize < %s | FileCheck %s +target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32" +target triple = "i686-pc-windows-msvc18.0.0" + +define void @test1() #0 personality i32 (...)* @__CxxFrameHandler3 { +entry: + invoke void @_CxxThrowException(i8* null, i8* null) + to label %unreachable unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchswitch within none [label %catch] unwind to caller + +catch: ; preds = %catch.dispatch + %1 = catchpad within %0 [i8* null, i32 64, i8* null] + br label %for.body + +for.cond.cleanup: ; preds = %for.body + catchret from %1 to label %try.cont + +for.body: ; preds = %for.body, %catch + %i.07 = phi i32 [ 0, %catch ], [ %inc, %for.body ] + %call = call double @floor(double 1.0) #1 [ "funclet"(token %1) ] + %inc = add nuw nsw i32 %i.07, 1 + %exitcond = icmp eq i32 %inc, 1024 + br i1 %exitcond, label %for.cond.cleanup, label %for.body + +try.cont: ; preds = %for.cond.cleanup + ret void + +unreachable: ; preds = %entry + unreachable +} + +; CHECK-LABEL: define void @test1( +; CHECK: %[[cpad:.*]] = catchpad within {{.*}} [i8* null, i32 64, i8* null] +; CHECK: call <16 x double> @llvm.floor.v16f64(<16 x double> {{.*}}) [ "funclet"(token %[[cpad]]) ] + +declare x86_stdcallcc void @_CxxThrowException(i8*, i8*) + +declare i32 @__CxxFrameHandler3(...) + +declare double @floor(double) #1 + +attributes #0 = { "target-features"="+sse2" } +attributes #1 = { nounwind readnone } diff --git a/test/Transforms/LoopVectorize/X86/gather_scatter.ll b/test/Transforms/LoopVectorize/X86/gather_scatter.ll new file mode 100644 index 0000000000000..222dd7eef6b68 --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/gather_scatter.ll @@ -0,0 +1,236 @@ +; RUN: opt < %s -O3 -mcpu=knl -S | FileCheck %s -check-prefix=AVX512 + +;AVX1-NOT: llvm.masked + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc_linux" + +; The source code: +; +;void foo1(float * __restrict__ in, float * __restrict__ out, int * __restrict__ trigger, int * __restrict__ index) { +; +; for (int i=0; i < SIZE; ++i) { +; if (trigger[i] > 0) { +; out[i] = in[index[i]] + (float) 0.5; +; } +; } +;} + +;AVX512-LABEL: @foo1 +;AVX512: llvm.masked.load.v16i32 +;AVX512: llvm.masked.gather.v16f32 +;AVX512: llvm.masked.store.v16f32 +;AVX512: ret void + +; Function Attrs: nounwind uwtable +define void @foo1(float* noalias %in, float* noalias %out, i32* noalias %trigger, i32* noalias %index) { +entry: + %in.addr = alloca float*, align 8 + %out.addr = alloca float*, align 8 + %trigger.addr = alloca i32*, align 8 + %index.addr = alloca i32*, align 8 + %i = alloca i32, align 4 + store float* %in, float** %in.addr, align 8 + store float* %out, float** %out.addr, align 8 + store i32* %trigger, i32** %trigger.addr, align 8 + store i32* %index, i32** %index.addr, align 8 + store i32 0, i32* %i, align 4 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %0 = load i32, i32* %i, align 4 + %cmp = icmp slt i32 %0, 4096 + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %1 = load i32, i32* %i, align 4 + %idxprom = sext i32 %1 to i64 + %2 = load i32*, i32** %trigger.addr, align 8 + %arrayidx = getelementptr inbounds i32, i32* %2, i64 %idxprom + %3 = load i32, i32* %arrayidx, align 4 + %cmp1 = icmp sgt i32 %3, 0 + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + %4 = load i32, i32* %i, align 4 + %idxprom2 = sext i32 %4 to i64 + %5 = load i32*, i32** %index.addr, align 8 + %arrayidx3 = getelementptr inbounds i32, i32* %5, i64 %idxprom2 + %6 = load i32, i32* %arrayidx3, align 4 + %idxprom4 = sext i32 %6 to i64 + %7 = load float*, float** %in.addr, align 8 + %arrayidx5 = getelementptr inbounds float, float* %7, i64 %idxprom4 + %8 = load float, float* %arrayidx5, align 4 + %add = fadd float %8, 5.000000e-01 + %9 = load i32, i32* %i, align 4 + %idxprom6 = sext i32 %9 to i64 + %10 = load float*, float** %out.addr, align 8 + %arrayidx7 = getelementptr inbounds float, float* %10, i64 %idxprom6 + store float %add, float* %arrayidx7, align 4 + br label %if.end + +if.end: ; preds = %if.then, %for.body + br label %for.inc + +for.inc: ; preds = %if.end + %11 = load i32, i32* %i, align 4 + %inc = add nsw i32 %11, 1 + store i32 %inc, i32* %i, align 4 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; The source code +;void foo2 (In * __restrict__ in, float * __restrict__ out, int * __restrict__ trigger) { +; +; for (int i=0; i<SIZE; ++i) { +; if (trigger[i] > 0) { +; out[i] = in[i].b + (float) 0.5; +; } +; } +;} + +%struct.In = type { float, float } + +;AVX512-LABEL: @foo2 +;AVX512: getelementptr inbounds %struct.In, %struct.In* %in, <16 x i64> %{{.*}}, i32 1 +;AVX512: llvm.masked.gather.v16f32 +;AVX512: llvm.masked.store.v16f32 +;AVX512: ret void +define void @foo2(%struct.In* noalias %in, float* noalias %out, i32* noalias %trigger, i32* noalias %index) #0 { +entry: + %in.addr = alloca %struct.In*, align 8 + %out.addr = alloca float*, align 8 + %trigger.addr = alloca i32*, align 8 + %index.addr = alloca i32*, align 8 + %i = alloca i32, align 4 + store %struct.In* %in, %struct.In** %in.addr, align 8 + store float* %out, float** %out.addr, align 8 + store i32* %trigger, i32** %trigger.addr, align 8 + store i32* %index, i32** %index.addr, align 8 + store i32 0, i32* %i, align 4 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %0 = load i32, i32* %i, align 4 + %cmp = icmp slt i32 %0, 4096 + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %1 = load i32, i32* %i, align 4 + %idxprom = sext i32 %1 to i64 + %2 = load i32*, i32** %trigger.addr, align 8 + %arrayidx = getelementptr inbounds i32, i32* %2, i64 %idxprom + %3 = load i32, i32* %arrayidx, align 4 + %cmp1 = icmp sgt i32 %3, 0 + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + %4 = load i32, i32* %i, align 4 + %idxprom2 = sext i32 %4 to i64 + %5 = load %struct.In*, %struct.In** %in.addr, align 8 + %arrayidx3 = getelementptr inbounds %struct.In, %struct.In* %5, i64 %idxprom2 + %b = getelementptr inbounds %struct.In, %struct.In* %arrayidx3, i32 0, i32 1 + %6 = load float, float* %b, align 4 + %add = fadd float %6, 5.000000e-01 + %7 = load i32, i32* %i, align 4 + %idxprom4 = sext i32 %7 to i64 + %8 = load float*, float** %out.addr, align 8 + %arrayidx5 = getelementptr inbounds float, float* %8, i64 %idxprom4 + store float %add, float* %arrayidx5, align 4 + br label %if.end + +if.end: ; preds = %if.then, %for.body + br label %for.inc + +for.inc: ; preds = %if.end + %9 = load i32, i32* %i, align 4 + %inc = add nsw i32 %9, 1 + store i32 %inc, i32* %i, align 4 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + +; The source code +;struct Out { +; float a; +; float b; +;}; +;void foo3 (In * __restrict__ in, Out * __restrict__ out, int * __restrict__ trigger) { +; +; for (int i=0; i<SIZE; ++i) { +; if (trigger[i] > 0) { +; out[i].b = in[i].b + (float) 0.5; +; } +; } +;} + +;AVX512-LABEL: @foo3 +;AVX512: getelementptr inbounds %struct.In, %struct.In* %in, <16 x i64> %{{.*}}, i32 1 +;AVX512: llvm.masked.gather.v16f32 +;AVX512: fadd <16 x float> +;AVX512: getelementptr inbounds %struct.Out, %struct.Out* %out, <16 x i64> %{{.*}}, i32 1 +;AVX512: llvm.masked.scatter.v16f32 +;AVX512: ret void + +%struct.Out = type { float, float } + +define void @foo3(%struct.In* noalias %in, %struct.Out* noalias %out, i32* noalias %trigger) { +entry: + %in.addr = alloca %struct.In*, align 8 + %out.addr = alloca %struct.Out*, align 8 + %trigger.addr = alloca i32*, align 8 + %i = alloca i32, align 4 + store %struct.In* %in, %struct.In** %in.addr, align 8 + store %struct.Out* %out, %struct.Out** %out.addr, align 8 + store i32* %trigger, i32** %trigger.addr, align 8 + store i32 0, i32* %i, align 4 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %0 = load i32, i32* %i, align 4 + %cmp = icmp slt i32 %0, 4096 + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %1 = load i32, i32* %i, align 4 + %idxprom = sext i32 %1 to i64 + %2 = load i32*, i32** %trigger.addr, align 8 + %arrayidx = getelementptr inbounds i32, i32* %2, i64 %idxprom + %3 = load i32, i32* %arrayidx, align 4 + %cmp1 = icmp sgt i32 %3, 0 + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + %4 = load i32, i32* %i, align 4 + %idxprom2 = sext i32 %4 to i64 + %5 = load %struct.In*, %struct.In** %in.addr, align 8 + %arrayidx3 = getelementptr inbounds %struct.In, %struct.In* %5, i64 %idxprom2 + %b = getelementptr inbounds %struct.In, %struct.In* %arrayidx3, i32 0, i32 1 + %6 = load float, float* %b, align 4 + %add = fadd float %6, 5.000000e-01 + %7 = load i32, i32* %i, align 4 + %idxprom4 = sext i32 %7 to i64 + %8 = load %struct.Out*, %struct.Out** %out.addr, align 8 + %arrayidx5 = getelementptr inbounds %struct.Out, %struct.Out* %8, i64 %idxprom4 + %b6 = getelementptr inbounds %struct.Out, %struct.Out* %arrayidx5, i32 0, i32 1 + store float %add, float* %b6, align 4 + br label %if.end + +if.end: ; preds = %if.then, %for.body + br label %for.inc + +for.inc: ; preds = %if.end + %9 = load i32, i32* %i, align 4 + %inc = add nsw i32 %9, 1 + store i32 %inc, i32* %i, align 4 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} +declare void @llvm.masked.scatter.v16f32(<16 x float>, <16 x float*>, i32, <16 x i1>) diff --git a/test/Transforms/LoopVectorize/X86/imprecise-through-phis.ll b/test/Transforms/LoopVectorize/X86/imprecise-through-phis.ll new file mode 100644 index 0000000000000..ee0a245c5e0dc --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/imprecise-through-phis.ll @@ -0,0 +1,75 @@ +; RUN: opt -S -loop-vectorize -mtriple=x86_64-apple-darwin %s | FileCheck %s + +; Two mostly identical functions. The only difference is the presence of +; fast-math flags on the second. The loop is a pretty simple reduction: + +; for (int i = 0; i < 32; ++i) +; if (arr[i] != 42) +; tot += arr[i]; + +define double @sumIfScalar(double* nocapture readonly %arr) { +; CHECK-LABEL: define double @sumIfScalar +; CHECK-NOT: <2 x double> + +entry: + br label %loop + +loop: + %i = phi i32 [0, %entry], [%i.next, %next.iter] + %tot = phi double [0.0, %entry], [%tot.next, %next.iter] + + %addr = getelementptr double, double* %arr, i32 %i + %nextval = load double, double* %addr + + %tst = fcmp une double %nextval, 42.0 + br i1 %tst, label %do.add, label %no.add + +do.add: + %tot.new = fadd double %tot, %nextval + br label %next.iter + +no.add: + br label %next.iter + +next.iter: + %tot.next = phi double [%tot, %no.add], [%tot.new, %do.add] + %i.next = add i32 %i, 1 + %again = icmp ult i32 %i.next, 32 + br i1 %again, label %loop, label %done + +done: + ret double %tot.next +} + +define double @sumIfVector(double* nocapture readonly %arr) { +; CHECK-LABEL: define double @sumIfVector +; CHECK: <2 x double> +entry: + br label %loop + +loop: + %i = phi i32 [0, %entry], [%i.next, %next.iter] + %tot = phi double [0.0, %entry], [%tot.next, %next.iter] + + %addr = getelementptr double, double* %arr, i32 %i + %nextval = load double, double* %addr + + %tst = fcmp fast une double %nextval, 42.0 + br i1 %tst, label %do.add, label %no.add + +do.add: + %tot.new = fadd fast double %tot, %nextval + br label %next.iter + +no.add: + br label %next.iter + +next.iter: + %tot.next = phi double [%tot, %no.add], [%tot.new, %do.add] + %i.next = add i32 %i, 1 + %again = icmp ult i32 %i.next, 32 + br i1 %again, label %loop, label %done + +done: + ret double %tot.next +} diff --git a/test/Transforms/LoopVectorize/X86/masked_load_store.ll b/test/Transforms/LoopVectorize/X86/masked_load_store.ll index abe7d6de3f352..1227344daff6d 100644 --- a/test/Transforms/LoopVectorize/X86/masked_load_store.ll +++ b/test/Transforms/LoopVectorize/X86/masked_load_store.ll @@ -1,9 +1,7 @@ -; RUN: opt < %s -O3 -mcpu=corei7-avx -S | FileCheck %s -check-prefix=AVX1 -; RUN: opt < %s -O3 -mcpu=core-avx2 -S | FileCheck %s -check-prefix=AVX2 +; RUN: opt < %s -O3 -mcpu=corei7-avx -S | FileCheck %s -check-prefix=AVX -check-prefix=AVX1 +; RUN: opt < %s -O3 -mcpu=core-avx2 -S | FileCheck %s -check-prefix=AVX -check-prefix=AVX2 ; RUN: opt < %s -O3 -mcpu=knl -S | FileCheck %s -check-prefix=AVX512 -;AVX1-NOT: llvm.masked - target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc_linux" @@ -18,18 +16,18 @@ target triple = "x86_64-pc_linux" ; } ;} -;AVX2-LABEL: @foo1 -;AVX2: icmp slt <8 x i32> %wide.load, <i32 100, i32 100, i32 100 -;AVX2: call <8 x i32> @llvm.masked.load.v8i32 -;AVX2: add nsw <8 x i32> -;AVX2: call void @llvm.masked.store.v8i32 -;AVX2: ret void +;AVX-LABEL: @foo1 +;AVX: icmp slt <8 x i32> %wide.load, <i32 100, i32 100, i32 100 +;AVX: call <8 x i32> @llvm.masked.load.v8i32.p0v8i32 +;AVX: add nsw <8 x i32> +;AVX: call void @llvm.masked.store.v8i32.p0v8i32 +;AVX: ret void ;AVX512-LABEL: @foo1 ;AVX512: icmp slt <16 x i32> %wide.load, <i32 100, i32 100, i32 100 -;AVX512: call <16 x i32> @llvm.masked.load.v16i32 +;AVX512: call <16 x i32> @llvm.masked.load.v16i32.p0v16i32 ;AVX512: add nsw <16 x i32> -;AVX512: call void @llvm.masked.store.v16i32 +;AVX512: call void @llvm.masked.store.v16i32.p0v16i32 ;AVX512: ret void ; Function Attrs: nounwind uwtable @@ -91,6 +89,81 @@ for.end: ; preds = %for.cond ret void } +; The same as @foo1 but all the pointers are address space 1 pointers. + +;AVX-LABEL: @foo1_addrspace1 +;AVX: icmp slt <8 x i32> %wide.load, <i32 100, i32 100, i32 100 +;AVX: call <8 x i32> @llvm.masked.load.v8i32.p1v8i32 +;AVX: add nsw <8 x i32> +;AVX: call void @llvm.masked.store.v8i32.p1v8i32 +;AVX: ret void + +;AVX512-LABEL: @foo1_addrspace1 +;AVX512: icmp slt <16 x i32> %wide.load, <i32 100, i32 100, i32 100 +;AVX512: call <16 x i32> @llvm.masked.load.v16i32.p1v16i32 +;AVX512: add nsw <16 x i32> +;AVX512: call void @llvm.masked.store.v16i32.p1v16i32 +;AVX512: ret void + +; Function Attrs: nounwind uwtable +define void @foo1_addrspace1(i32 addrspace(1)* %A, i32 addrspace(1)* %B, i32 addrspace(1)* %trigger) { +entry: + %A.addr = alloca i32 addrspace(1)*, align 8 + %B.addr = alloca i32 addrspace(1)*, align 8 + %trigger.addr = alloca i32 addrspace(1)*, align 8 + %i = alloca i32, align 4 + store i32 addrspace(1)* %A, i32 addrspace(1)** %A.addr, align 8 + store i32 addrspace(1)* %B, i32 addrspace(1)** %B.addr, align 8 + store i32 addrspace(1)* %trigger, i32 addrspace(1)** %trigger.addr, align 8 + store i32 0, i32* %i, align 4 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %0 = load i32, i32* %i, align 4 + %cmp = icmp slt i32 %0, 10000 + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %1 = load i32, i32* %i, align 4 + %idxprom = sext i32 %1 to i64 + %2 = load i32 addrspace(1)*, i32 addrspace(1)** %trigger.addr, align 8 + %arrayidx = getelementptr inbounds i32, i32 addrspace(1)* %2, i64 %idxprom + %3 = load i32, i32 addrspace(1)* %arrayidx, align 4 + %cmp1 = icmp slt i32 %3, 100 + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %for.body + %4 = load i32, i32* %i, align 4 + %idxprom2 = sext i32 %4 to i64 + %5 = load i32 addrspace(1)*, i32 addrspace(1)** %B.addr, align 8 + %arrayidx3 = getelementptr inbounds i32, i32 addrspace(1)* %5, i64 %idxprom2 + %6 = load i32, i32 addrspace(1)* %arrayidx3, align 4 + %7 = load i32, i32* %i, align 4 + %idxprom4 = sext i32 %7 to i64 + %8 = load i32 addrspace(1)*, i32 addrspace(1)** %trigger.addr, align 8 + %arrayidx5 = getelementptr inbounds i32, i32 addrspace(1)* %8, i64 %idxprom4 + %9 = load i32, i32 addrspace(1)* %arrayidx5, align 4 + %add = add nsw i32 %6, %9 + %10 = load i32, i32* %i, align 4 + %idxprom6 = sext i32 %10 to i64 + %11 = load i32 addrspace(1)*, i32 addrspace(1)** %A.addr, align 8 + %arrayidx7 = getelementptr inbounds i32, i32 addrspace(1)* %11, i64 %idxprom6 + store i32 %add, i32 addrspace(1)* %arrayidx7, align 4 + br label %if.end + +if.end: ; preds = %if.then, %for.body + br label %for.inc + +for.inc: ; preds = %if.end + %12 = load i32, i32* %i, align 4 + %inc = add nsw i32 %12, 1 + store i32 %inc, i32* %i, align 4 + br label %for.cond + +for.end: ; preds = %for.cond + ret void +} + ; The source code: ; ;void foo2(float *A, float *B, int *trigger) { @@ -102,18 +175,18 @@ for.end: ; preds = %for.cond ; } ;} -;AVX2-LABEL: @foo2 -;AVX2: icmp slt <8 x i32> %wide.load, <i32 100, i32 100, i32 100 -;AVX2: call <8 x float> @llvm.masked.load.v8f32 -;AVX2: fadd <8 x float> -;AVX2: call void @llvm.masked.store.v8f32 -;AVX2: ret void +;AVX-LABEL: @foo2 +;AVX: icmp slt <8 x i32> %wide.load, <i32 100, i32 100, i32 100 +;AVX: call <8 x float> @llvm.masked.load.v8f32.p0v8f32 +;AVX: fadd <8 x float> +;AVX: call void @llvm.masked.store.v8f32.p0v8f32 +;AVX: ret void ;AVX512-LABEL: @foo2 ;AVX512: icmp slt <16 x i32> %wide.load, <i32 100, i32 100, i32 100 -;AVX512: call <16 x float> @llvm.masked.load.v16f32 +;AVX512: call <16 x float> @llvm.masked.load.v16f32.p0v16f32 ;AVX512: fadd <16 x float> -;AVX512: call void @llvm.masked.store.v16f32 +;AVX512: call void @llvm.masked.store.v16f32.p0v16f32 ;AVX512: ret void ; Function Attrs: nounwind uwtable @@ -187,20 +260,20 @@ for.end: ; preds = %for.cond ; } ;} -;AVX2-LABEL: @foo3 -;AVX2: icmp slt <4 x i32> %wide.load, <i32 100, i32 100, -;AVX2: call <4 x double> @llvm.masked.load.v4f64 -;AVX2: sitofp <4 x i32> %wide.load to <4 x double> -;AVX2: fadd <4 x double> -;AVX2: call void @llvm.masked.store.v4f64 -;AVX2: ret void +;AVX-LABEL: @foo3 +;AVX: icmp slt <4 x i32> %wide.load, <i32 100, i32 100, +;AVX: call <4 x double> @llvm.masked.load.v4f64.p0v4f64 +;AVX: sitofp <4 x i32> %wide.load to <4 x double> +;AVX: fadd <4 x double> +;AVX: call void @llvm.masked.store.v4f64.p0v4f64 +;AVX: ret void ;AVX512-LABEL: @foo3 ;AVX512: icmp slt <8 x i32> %wide.load, <i32 100, i32 100, -;AVX512: call <8 x double> @llvm.masked.load.v8f64 +;AVX512: call <8 x double> @llvm.masked.load.v8f64.p0v8f64 ;AVX512: sitofp <8 x i32> %wide.load to <8 x double> ;AVX512: fadd <8 x double> -;AVX512: call void @llvm.masked.store.v8f64 +;AVX512: call void @llvm.masked.store.v8f64.p0v8f64 ;AVX512: ret void @@ -275,12 +348,13 @@ for.end: ; preds = %for.cond ; } ;} -;AVX2-LABEL: @foo4 -;AVX2-NOT: llvm.masked -;AVX2: ret void +;AVX-LABEL: @foo4 +;AVX-NOT: llvm.masked +;AVX: ret void ;AVX512-LABEL: @foo4 -;AVX512-NOT: llvm.masked +;AVX512-NOT: llvm.masked.load +;AVX512: llvm.masked.gather ;AVX512: ret void ; Function Attrs: nounwind uwtable @@ -349,10 +423,10 @@ for.end: ; preds = %for.cond ; The loop here should not be vectorized due to trapping ; constant expression -;AVX2-LABEL: @foo5 -;AVX2-NOT: llvm.masked -;AVX2: store i32 sdiv -;AVX2: ret void +;AVX-LABEL: @foo5 +;AVX-NOT: llvm.masked +;AVX: store i32 sdiv +;AVX: ret void ;AVX512-LABEL: @foo5 ;AVX512-NOT: llvm.masked @@ -430,17 +504,17 @@ for.end: ; preds = %for.cond ;AVX2-LABEL: @foo6 ;AVX2: icmp sgt <4 x i32> %reverse, zeroinitializer ;AVX2: shufflevector <4 x i1>{{.*}}<4 x i32> <i32 3, i32 2, i32 1, i32 0> -;AVX2: call <4 x double> @llvm.masked.load.v4f64 +;AVX2: call <4 x double> @llvm.masked.load.v4f64.p0v4f64 ;AVX2: fadd <4 x double> -;AVX2: call void @llvm.masked.store.v4f64 +;AVX2: call void @llvm.masked.store.v4f64.p0v4f64 ;AVX2: ret void ;AVX512-LABEL: @foo6 ;AVX512: icmp sgt <8 x i32> %reverse, zeroinitializer ;AVX512: shufflevector <8 x i1>{{.*}}<8 x i32> <i32 7, i32 6, i32 5, i32 4 -;AVX512: call <8 x double> @llvm.masked.load.v8f64 +;AVX512: call <8 x double> @llvm.masked.load.v8f64.p0v8f64 ;AVX512: fadd <8 x double> -;AVX512: call void @llvm.masked.store.v8f64 +;AVX512: call void @llvm.masked.store.v8f64.p0v8f64 ;AVX512: ret void @@ -508,8 +582,8 @@ for.end: ; preds = %for.cond ; } ;AVX512-LABEL: @foo7 -;AVX512: call <8 x double*> @llvm.masked.load.v8p0f64(<8 x double*>* -;AVX512: call void @llvm.masked.store.v8f64 +;AVX512: call <8 x double*> @llvm.masked.load.v8p0f64.p0v8p0f64(<8 x double*>* +;AVX512: call void @llvm.masked.store.v8f64.p0v8f64 ;AVX512: ret void define void @foo7(double* noalias %out, double** noalias %in, i8* noalias %trigger, i32 %size) #0 { @@ -580,8 +654,8 @@ for.end: ; preds = %for.cond ;} ;AVX512-LABEL: @foo8 -;AVX512: call <8 x i32 ()*> @llvm.masked.load.v8p0f_i32f(<8 x i32 ()*>* % -;AVX512: call void @llvm.masked.store.v8f64 +;AVX512: call <8 x i32 ()*> @llvm.masked.load.v8p0f_i32f.p0v8p0f_i32f(<8 x i32 ()*>* % +;AVX512: call void @llvm.masked.store.v8f64.p0v8f64 ;AVX512: ret void define void @foo8(double* noalias %out, i32 ()** noalias %in, i8* noalias %trigger, i32 %size) #0 { diff --git a/test/Transforms/LoopVectorize/X86/max-mstore.ll b/test/Transforms/LoopVectorize/X86/max-mstore.ll new file mode 100644 index 0000000000000..a9ac04d456068 --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/max-mstore.ll @@ -0,0 +1,46 @@ +; RUN: opt -basicaa -loop-vectorize -force-vector-interleave=1 -S -mcpu=core-avx2 + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@b = common global [256 x i32] zeroinitializer, align 16 +@a = common global [256 x i32] zeroinitializer, align 16 + +; unsigned int a[256], b[256]; +; void foo() { +; for (i = 0; i < 256; i++) { +; if (b[i] > a[i]) +; a[i] = b[i]; +; } +; } + +; CHECK-LABEL: foo +; CHECK: load <8 x i32> +; CHECK: icmp ugt <8 x i32> +; CHECK: masked.store + +define void @foo() { +entry: + br label %for.body + +for.body: ; preds = %for.inc, %entry + %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.inc ] + %arrayidx = getelementptr inbounds [256 x i32], [256 x i32]* @b, i64 0, i64 %indvars.iv + %0 = load i32, i32* %arrayidx, align 4 + %arrayidx2 = getelementptr inbounds [256 x i32], [256 x i32]* @a, i64 0, i64 %indvars.iv + %1 = load i32, i32* %arrayidx2, align 4 + %cmp3 = icmp ugt i32 %0, %1 + br i1 %cmp3, label %if.then, label %for.inc + +if.then: ; preds = %for.body + store i32 %0, i32* %arrayidx2, align 4 + br label %for.inc + +for.inc: ; preds = %for.body, %if.then + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond = icmp eq i64 %indvars.iv.next, 256 + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.inc + ret void +} diff --git a/test/Transforms/LoopVectorize/X86/no_fpmath.ll b/test/Transforms/LoopVectorize/X86/no_fpmath.ll index 0bb78ce177fe9..055d81a390b0b 100644 --- a/test/Transforms/LoopVectorize/X86/no_fpmath.ll +++ b/test/Transforms/LoopVectorize/X86/no_fpmath.ll @@ -71,6 +71,7 @@ for.body: ; preds = %for.body.preheader, attributes #0 = { nounwind } +!llvm.dbg.cu = !{!28} !llvm.module.flags = !{!0, !1} !llvm.ident = !{!2} @@ -78,7 +79,7 @@ attributes #0 = { nounwind } !1 = !{i32 1, !"PIC Level", i32 2} !2 = !{!"clang version 3.7.0"} !3 = !DILocation(line: 5, column: 20, scope: !4) -!4 = distinct !DISubprogram(name: "cond_sum", scope: !5, file: !5, line: 1, type: !6, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, variables: !7) +!4 = distinct !DISubprogram(name: "cond_sum", scope: !5, file: !5, line: 1, type: !6, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, unit: !28, variables: !7) !5 = !DIFile(filename: "no_fpmath.c", directory: "") !6 = !DISubroutineType(types: !7) !7 = !{} @@ -94,7 +95,7 @@ attributes #0 = { nounwind } !17 = distinct !{!17, !18} !18 = !{!"llvm.loop.unroll.disable"} !19 = !DILocation(line: 16, column: 20, scope: !20) -!20 = distinct !DISubprogram(name: "cond_sum_loop_hint", scope: !5, file: !5, line: 12, type: !6, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: true, variables: !7) +!20 = distinct !DISubprogram(name: "cond_sum_loop_hint", scope: !5, file: !5, line: 12, type: !6, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: true, unit: !28, variables: !7) !21 = !DILocation(line: 16, column: 3, scope: !20) !22 = !DILocation(line: 17, column: 14, scope: !20) !23 = !DILocation(line: 20, column: 3, scope: !20) @@ -102,3 +103,7 @@ attributes #0 = { nounwind } !25 = !DILocation(line: 17, column: 11, scope: !20) !26 = distinct !{!26, !27, !18} !27 = !{!"llvm.loop.vectorize.enable", i1 true} +!28 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang", + file: !5, + isOptimized: true, flags: "-O2", + splitDebugFilename: "abc.debug", emissionKind: 2) diff --git a/test/Transforms/LoopVectorize/X86/propagate-metadata.ll b/test/Transforms/LoopVectorize/X86/propagate-metadata.ll new file mode 100644 index 0000000000000..2825ddbac9d89 --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/propagate-metadata.ll @@ -0,0 +1,25 @@ +; RUN: opt -S -mtriple="x86_64-unknown-linux-gnu" -loop-vectorize < %s | FileCheck %s + +; Don't crash on unknown metadata +; CHECK-LABEL: @no_propagate_range_metadata( +; CHECK: load <16 x i8> +; CHECK: store <16 x i8> +define void @no_propagate_range_metadata(i8* readonly %first.coerce, i8* readnone %last.coerce, i8* nocapture %result) { +for.body.preheader: + br label %for.body + +for.body: ; preds = %for.body, %for.body.preheader + %result.addr.05 = phi i8* [ %incdec.ptr, %for.body ], [ %result, %for.body.preheader ] + %first.sroa.0.04 = phi i8* [ %incdec.ptr.i.i.i, %for.body ], [ %first.coerce, %for.body.preheader ] + %0 = load i8, i8* %first.sroa.0.04, align 1, !range !0 + store i8 %0, i8* %result.addr.05, align 1 + %incdec.ptr.i.i.i = getelementptr inbounds i8, i8* %first.sroa.0.04, i64 1 + %incdec.ptr = getelementptr inbounds i8, i8* %result.addr.05, i64 1 + %lnot.i = icmp eq i8* %incdec.ptr.i.i.i, %last.coerce + br i1 %lnot.i, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + ret void +} + +!0 = !{i8 0, i8 2} diff --git a/test/Transforms/LoopVectorize/X86/reg-usage.ll b/test/Transforms/LoopVectorize/X86/reg-usage.ll new file mode 100644 index 0000000000000..47a6e1029eda2 --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/reg-usage.ll @@ -0,0 +1,71 @@ +; RUN: opt < %s -debug-only=loop-vectorize -loop-vectorize -vectorizer-maximize-bandwidth -O2 -S 2>&1 | FileCheck %s +; REQUIRES: asserts + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@a = global [1024 x i8] zeroinitializer, align 16 +@b = global [1024 x i8] zeroinitializer, align 16 + +define i32 @foo() { +; This function has a loop of SAD pattern. Here we check when VF = 16 the +; register usage doesn't exceed 16. +; +; CHECK-LABEL: foo +; CHECK: LV(REG): VF = 4 +; CHECK-NEXT: LV(REG): Found max usage: 4 +; CHECK: LV(REG): VF = 8 +; CHECK-NEXT: LV(REG): Found max usage: 7 +; CHECK: LV(REG): VF = 16 +; CHECK-NEXT: LV(REG): Found max usage: 13 + +entry: + br label %for.body + +for.cond.cleanup: + %add.lcssa = phi i32 [ %add, %for.body ] + ret i32 %add.lcssa + +for.body: + %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] + %s.015 = phi i32 [ 0, %entry ], [ %add, %for.body ] + %arrayidx = getelementptr inbounds [1024 x i8], [1024 x i8]* @a, i64 0, i64 %indvars.iv + %0 = load i8, i8* %arrayidx, align 1 + %conv = zext i8 %0 to i32 + %arrayidx2 = getelementptr inbounds [1024 x i8], [1024 x i8]* @b, i64 0, i64 %indvars.iv + %1 = load i8, i8* %arrayidx2, align 1 + %conv3 = zext i8 %1 to i32 + %sub = sub nsw i32 %conv, %conv3 + %ispos = icmp sgt i32 %sub, -1 + %neg = sub nsw i32 0, %sub + %2 = select i1 %ispos, i32 %sub, i32 %neg + %add = add nsw i32 %2, %s.015 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond = icmp eq i64 %indvars.iv.next, 1024 + br i1 %exitcond, label %for.cond.cleanup, label %for.body +} + +define i64 @bar(i64* nocapture %a) { +; CHECK-LABEL: bar +; CHECK: LV(REG): VF = 2 +; CHECK: LV(REG): Found max usage: 4 +; +entry: + br label %for.body + +for.cond.cleanup: + %add2.lcssa = phi i64 [ %add2, %for.body ] + ret i64 %add2.lcssa + +for.body: + %i.012 = phi i64 [ 0, %entry ], [ %inc, %for.body ] + %s.011 = phi i64 [ 0, %entry ], [ %add2, %for.body ] + %arrayidx = getelementptr inbounds i64, i64* %a, i64 %i.012 + %0 = load i64, i64* %arrayidx, align 8 + %add = add nsw i64 %0, %i.012 + store i64 %add, i64* %arrayidx, align 8 + %add2 = add nsw i64 %add, %s.011 + %inc = add nuw nsw i64 %i.012, 1 + %exitcond = icmp eq i64 %inc, 1024 + br i1 %exitcond, label %for.cond.cleanup, label %for.body +} diff --git a/test/Transforms/LoopVectorize/X86/register-assumption.ll b/test/Transforms/LoopVectorize/X86/register-assumption.ll new file mode 100644 index 0000000000000..1add87db611e4 --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/register-assumption.ll @@ -0,0 +1,32 @@ +; RUN: opt < %s -loop-vectorize -instcombine -mtriple=x86_64-apple-macosx10.8.0 -mcpu=corei7 -S | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @test1() { +entry: + %alloca = alloca float, align 4 + br label %loop_exit.dim.11.critedge + +loop_exit.dim.11.critedge: ; preds = %loop_body.dim.0 + %ptrint = ptrtoint float* %alloca to i64 + %maskedptr = and i64 %ptrint, 4 + %maskcond = icmp eq i64 %maskedptr, 0 + br label %loop_header.dim.017.preheader + +loop_header.dim.017.preheader: ; preds = %loop_exit.dim.016, %loop_exit.dim.11.critedge + br label %loop_body.dim.018 + +loop_body.dim.018: ; preds = %loop_body.dim.018, %loop_header.dim.017.preheader + %invar_address.dim.019.0135 = phi i64 [ 0, %loop_header.dim.017.preheader ], [ %0, %loop_body.dim.018 ] + call void @llvm.assume(i1 %maskcond) +; CHECK: call void @llvm.assume( +; CHECK-NOT: call void @llvm.assume( + %0 = add nuw nsw i64 %invar_address.dim.019.0135, 1 + %1 = icmp eq i64 %0, 256 + br i1 %1, label %loop_header.dim.017.preheader, label %loop_body.dim.018 +} + +; Function Attrs: nounwind +declare void @llvm.assume(i1) #0 + +attributes #0 = { nounwind } diff --git a/test/Transforms/LoopVectorize/X86/scatter_crash.ll b/test/Transforms/LoopVectorize/X86/scatter_crash.ll new file mode 100755 index 0000000000000..d5c882858ebd8 --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/scatter_crash.ll @@ -0,0 +1,218 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -loop-vectorize -S | FileCheck %s +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +; This test checks vector GEP before scatter. +; The code bellow crashed due to destroyed SSA while incorrect vectorization of +; the GEP. + +@d = global [10 x [10 x i32]] zeroinitializer, align 16 +@c = external global i32, align 4 +@a = external global i32, align 4 +@b = external global i64, align 8 + +; Function Attrs: norecurse nounwind ssp uwtable +define void @_Z3fn1v() #0 { +; CHECK-LABEL: @_Z3fn1v( +; CHECK: vector.body: +; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %vector.ph ], [ [[INDEX:%.*]].next, %vector.body ] +; CHECK-NEXT: [[VEC_IND:%.*]] = phi <16 x i64> [ +; CHECK-NEXT: [[VEC_IND3:%.*]] = phi <16 x i64> [ +; CHECK-NEXT: [[STEP_ADD:%.*]] = add <16 x i64> [[VEC_IND]], <i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32> +; CHECK-NEXT: [[STEP_ADD4:%.*]] = add <16 x i64> [[VEC_IND3]], <i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32, i64 32> +; CHECK-NEXT: [[TMP10:%.*]] = sub nsw <16 x i64> <i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8, i64 8>, [[VEC_IND]] +; CHECK-NEXT: [[TMP11:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 0 +; CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP11]] +; CHECK-NEXT: [[TMP13:%.*]] = insertelement <16 x [10 x i32]*> undef, [10 x i32]* [[TMP12]], i32 0 +; CHECK-NEXT: [[TMP14:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 1 +; CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP14]] +; CHECK-NEXT: [[TMP16:%.*]] = insertelement <16 x [10 x i32]*> [[TMP13]], [10 x i32]* [[TMP15]], i32 1 +; CHECK-NEXT: [[TMP17:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 2 +; CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP17]] +; CHECK-NEXT: [[TMP19:%.*]] = insertelement <16 x [10 x i32]*> [[TMP16]], [10 x i32]* [[TMP18]], i32 2 +; CHECK-NEXT: [[TMP20:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 3 +; CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP20]] +; CHECK-NEXT: [[TMP22:%.*]] = insertelement <16 x [10 x i32]*> [[TMP19]], [10 x i32]* [[TMP21]], i32 3 +; CHECK-NEXT: [[TMP23:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 4 +; CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP23]] +; CHECK-NEXT: [[TMP25:%.*]] = insertelement <16 x [10 x i32]*> [[TMP22]], [10 x i32]* [[TMP24]], i32 4 +; CHECK-NEXT: [[TMP26:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 5 +; CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP26]] +; CHECK-NEXT: [[TMP28:%.*]] = insertelement <16 x [10 x i32]*> [[TMP25]], [10 x i32]* [[TMP27]], i32 5 +; CHECK-NEXT: [[TMP29:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 6 +; CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP29]] +; CHECK-NEXT: [[TMP31:%.*]] = insertelement <16 x [10 x i32]*> [[TMP28]], [10 x i32]* [[TMP30]], i32 6 +; CHECK-NEXT: [[TMP32:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 7 +; CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP32]] +; CHECK-NEXT: [[TMP34:%.*]] = insertelement <16 x [10 x i32]*> [[TMP31]], [10 x i32]* [[TMP33]], i32 7 +; CHECK-NEXT: [[TMP35:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 8 +; CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP35]] +; CHECK-NEXT: [[TMP37:%.*]] = insertelement <16 x [10 x i32]*> [[TMP34]], [10 x i32]* [[TMP36]], i32 8 +; CHECK-NEXT: [[TMP38:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 9 +; CHECK-NEXT: [[TMP39:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP38]] +; CHECK-NEXT: [[TMP40:%.*]] = insertelement <16 x [10 x i32]*> [[TMP37]], [10 x i32]* [[TMP39]], i32 9 +; CHECK-NEXT: [[TMP41:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 10 +; CHECK-NEXT: [[TMP42:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP41]] +; CHECK-NEXT: [[TMP43:%.*]] = insertelement <16 x [10 x i32]*> [[TMP40]], [10 x i32]* [[TMP42]], i32 10 +; CHECK-NEXT: [[TMP44:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 11 +; CHECK-NEXT: [[TMP45:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP44]] +; CHECK-NEXT: [[TMP46:%.*]] = insertelement <16 x [10 x i32]*> [[TMP43]], [10 x i32]* [[TMP45]], i32 11 +; CHECK-NEXT: [[TMP47:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 12 +; CHECK-NEXT: [[TMP48:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP47]] +; CHECK-NEXT: [[TMP49:%.*]] = insertelement <16 x [10 x i32]*> [[TMP46]], [10 x i32]* [[TMP48]], i32 12 +; CHECK-NEXT: [[TMP50:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 13 +; CHECK-NEXT: [[TMP51:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP50]] +; CHECK-NEXT: [[TMP52:%.*]] = insertelement <16 x [10 x i32]*> [[TMP49]], [10 x i32]* [[TMP51]], i32 13 +; CHECK-NEXT: [[TMP53:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 14 +; CHECK-NEXT: [[TMP54:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP53]] +; CHECK-NEXT: [[TMP55:%.*]] = insertelement <16 x [10 x i32]*> [[TMP52]], [10 x i32]* [[TMP54]], i32 14 +; CHECK-NEXT: [[TMP56:%.*]] = extractelement <16 x i64> [[VEC_IND]], i32 15 +; CHECK-NEXT: [[TMP57:%.*]] = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 [[TMP56]] +; CHECK-NEXT: [[TMP58:%.*]] = insertelement <16 x [10 x i32]*> [[TMP55]], [10 x i32]* [[TMP57]], i32 15 +; CHECK-NEXT: [[TMP59:%.*]] = add nsw <16 x i64> [[TMP10]], [[VEC_IND3]] +; CHECK-NEXT: [[TMP60:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 0 +; CHECK-NEXT: [[TMP61:%.*]] = extractelement <16 x i64> [[TMP59]], i32 0 +; CHECK-NEXT: [[TMP62:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP60]], i64 [[TMP61]], i64 0 +; CHECK-NEXT: [[TMP63:%.*]] = insertelement <16 x i32*> undef, i32* [[TMP62]], i32 0 +; CHECK-NEXT: [[TMP64:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 1 +; CHECK-NEXT: [[TMP65:%.*]] = extractelement <16 x i64> [[TMP59]], i32 1 +; CHECK-NEXT: [[TMP66:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP64]], i64 [[TMP65]], i64 0 +; CHECK-NEXT: [[TMP67:%.*]] = insertelement <16 x i32*> [[TMP63]], i32* [[TMP66]], i32 1 +; CHECK-NEXT: [[TMP68:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 2 +; CHECK-NEXT: [[TMP69:%.*]] = extractelement <16 x i64> [[TMP59]], i32 2 +; CHECK-NEXT: [[TMP70:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP68]], i64 [[TMP69]], i64 0 +; CHECK-NEXT: [[TMP71:%.*]] = insertelement <16 x i32*> [[TMP67]], i32* [[TMP70]], i32 2 +; CHECK-NEXT: [[TMP72:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 3 +; CHECK-NEXT: [[TMP73:%.*]] = extractelement <16 x i64> [[TMP59]], i32 3 +; CHECK-NEXT: [[TMP74:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP72]], i64 [[TMP73]], i64 0 +; CHECK-NEXT: [[TMP75:%.*]] = insertelement <16 x i32*> [[TMP71]], i32* [[TMP74]], i32 3 +; CHECK-NEXT: [[TMP76:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 4 +; CHECK-NEXT: [[TMP77:%.*]] = extractelement <16 x i64> [[TMP59]], i32 4 +; CHECK-NEXT: [[TMP78:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP76]], i64 [[TMP77]], i64 0 +; CHECK-NEXT: [[TMP79:%.*]] = insertelement <16 x i32*> [[TMP75]], i32* [[TMP78]], i32 4 +; CHECK-NEXT: [[TMP80:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 5 +; CHECK-NEXT: [[TMP81:%.*]] = extractelement <16 x i64> [[TMP59]], i32 5 +; CHECK-NEXT: [[TMP82:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP80]], i64 [[TMP81]], i64 0 +; CHECK-NEXT: [[TMP83:%.*]] = insertelement <16 x i32*> [[TMP79]], i32* [[TMP82]], i32 5 +; CHECK-NEXT: [[TMP84:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 6 +; CHECK-NEXT: [[TMP85:%.*]] = extractelement <16 x i64> [[TMP59]], i32 6 +; CHECK-NEXT: [[TMP86:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP84]], i64 [[TMP85]], i64 0 +; CHECK-NEXT: [[TMP87:%.*]] = insertelement <16 x i32*> [[TMP83]], i32* [[TMP86]], i32 6 +; CHECK-NEXT: [[TMP88:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 7 +; CHECK-NEXT: [[TMP89:%.*]] = extractelement <16 x i64> [[TMP59]], i32 7 +; CHECK-NEXT: [[TMP90:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP88]], i64 [[TMP89]], i64 0 +; CHECK-NEXT: [[TMP91:%.*]] = insertelement <16 x i32*> [[TMP87]], i32* [[TMP90]], i32 7 +; CHECK-NEXT: [[TMP92:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 8 +; CHECK-NEXT: [[TMP93:%.*]] = extractelement <16 x i64> [[TMP59]], i32 8 +; CHECK-NEXT: [[TMP94:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP92]], i64 [[TMP93]], i64 0 +; CHECK-NEXT: [[TMP95:%.*]] = insertelement <16 x i32*> [[TMP91]], i32* [[TMP94]], i32 8 +; CHECK-NEXT: [[TMP96:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 9 +; CHECK-NEXT: [[TMP97:%.*]] = extractelement <16 x i64> [[TMP59]], i32 9 +; CHECK-NEXT: [[TMP98:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP96]], i64 [[TMP97]], i64 0 +; CHECK-NEXT: [[TMP99:%.*]] = insertelement <16 x i32*> [[TMP95]], i32* [[TMP98]], i32 9 +; CHECK-NEXT: [[TMP100:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 10 +; CHECK-NEXT: [[TMP101:%.*]] = extractelement <16 x i64> [[TMP59]], i32 10 +; CHECK-NEXT: [[TMP102:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP100]], i64 [[TMP101]], i64 0 +; CHECK-NEXT: [[TMP103:%.*]] = insertelement <16 x i32*> [[TMP99]], i32* [[TMP102]], i32 10 +; CHECK-NEXT: [[TMP104:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 11 +; CHECK-NEXT: [[TMP105:%.*]] = extractelement <16 x i64> [[TMP59]], i32 11 +; CHECK-NEXT: [[TMP106:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP104]], i64 [[TMP105]], i64 0 +; CHECK-NEXT: [[TMP107:%.*]] = insertelement <16 x i32*> [[TMP103]], i32* [[TMP106]], i32 11 +; CHECK-NEXT: [[TMP108:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 12 +; CHECK-NEXT: [[TMP109:%.*]] = extractelement <16 x i64> [[TMP59]], i32 12 +; CHECK-NEXT: [[TMP110:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP108]], i64 [[TMP109]], i64 0 +; CHECK-NEXT: [[TMP111:%.*]] = insertelement <16 x i32*> [[TMP107]], i32* [[TMP110]], i32 12 +; CHECK-NEXT: [[TMP112:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 13 +; CHECK-NEXT: [[TMP113:%.*]] = extractelement <16 x i64> [[TMP59]], i32 13 +; CHECK-NEXT: [[TMP114:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP112]], i64 [[TMP113]], i64 0 +; CHECK-NEXT: [[TMP115:%.*]] = insertelement <16 x i32*> [[TMP111]], i32* [[TMP114]], i32 13 +; CHECK-NEXT: [[TMP116:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 14 +; CHECK-NEXT: [[TMP117:%.*]] = extractelement <16 x i64> [[TMP59]], i32 14 +; CHECK-NEXT: [[TMP118:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP116]], i64 [[TMP117]], i64 0 +; CHECK-NEXT: [[TMP119:%.*]] = insertelement <16 x i32*> [[TMP115]], i32* [[TMP118]], i32 14 +; CHECK-NEXT: [[TMP120:%.*]] = extractelement <16 x [10 x i32]*> [[TMP58]], i32 15 +; CHECK-NEXT: [[TMP121:%.*]] = extractelement <16 x i64> [[TMP59]], i32 15 +; CHECK-NEXT: [[TMP122:%.*]] = getelementptr inbounds [10 x i32], [10 x i32]* [[TMP120]], i64 [[TMP121]], i64 0 +; CHECK-NEXT: [[TMP123:%.*]] = insertelement <16 x i32*> [[TMP119]], i32* [[TMP122]], i32 15 +; CHECK-NEXT: [[VECTORGEP:%.*]] = getelementptr inbounds [10 x i32], <16 x [10 x i32]*> [[TMP58]], <16 x i64> [[TMP59]], i64 0 +; CHECK-NEXT: call void @llvm.masked.scatter.v16i32(<16 x i32> <i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8>, <16 x i32*> [[VECTORGEP]], i32 16, <16 x i1> <i1 true, i1 true, i1 true, i1 true, i1 true, i1 true, i1 true, i1 true, i1 true, i1 true, i1 true, i1 true, i1 true, i1 true, i1 true, i1 true>) +entry: + %0 = load i32, i32* @c, align 4 + %cmp34 = icmp sgt i32 %0, 8 + br i1 %cmp34, label %for.body.lr.ph, label %for.cond.cleanup + +for.body.lr.ph: ; preds = %entry + %1 = load i32, i32* @a, align 4 + %tobool = icmp eq i32 %1, 0 + %2 = load i64, i64* @b, align 8 + %mul = mul i64 %2, 4063299859190 + %tobool6 = icmp eq i64 %mul, 0 + %3 = sext i32 %0 to i64 + br i1 %tobool, label %for.body.us.preheader, label %for.body.preheader + +for.body.preheader: ; preds = %for.body.lr.ph + br label %for.body + +for.body.us.preheader: ; preds = %for.body.lr.ph + br label %for.body.us + +for.body.us: ; preds = %for.body.us.preheader, %for.cond.cleanup4.us-lcssa.us.us + %indvars.iv78 = phi i64 [ %indvars.iv.next79, %for.cond.cleanup4.us-lcssa.us.us ], [ 8, %for.body.us.preheader ] + %indvars.iv70 = phi i64 [ %indvars.iv.next71, %for.cond.cleanup4.us-lcssa.us.us ], [ 0, %for.body.us.preheader ] + %4 = sub nsw i64 8, %indvars.iv78 + %add.ptr.us = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 %indvars.iv78 + %5 = add nsw i64 %4, %indvars.iv70 + %arraydecay.us.us.us = getelementptr inbounds [10 x i32], [10 x i32]* %add.ptr.us, i64 %5, i64 0 + br i1 %tobool6, label %for.body5.us.us.us.preheader, label %for.body5.us.us48.preheader + +for.body5.us.us48.preheader: ; preds = %for.body.us + store i32 8, i32* %arraydecay.us.us.us, align 16 + %indvars.iv.next66 = or i64 %indvars.iv70, 1 + %6 = add nsw i64 %4, %indvars.iv.next66 + %arraydecay.us.us55.1 = getelementptr inbounds [10 x i32], [10 x i32]* %add.ptr.us, i64 %6, i64 0 + store i32 8, i32* %arraydecay.us.us55.1, align 8 + br label %for.cond.cleanup4.us-lcssa.us.us + +for.body5.us.us.us.preheader: ; preds = %for.body.us + store i32 7, i32* %arraydecay.us.us.us, align 16 + %indvars.iv.next73 = or i64 %indvars.iv70, 1 + %7 = add nsw i64 %4, %indvars.iv.next73 + %arraydecay.us.us.us.1 = getelementptr inbounds [10 x i32], [10 x i32]* %add.ptr.us, i64 %7, i64 0 + store i32 7, i32* %arraydecay.us.us.us.1, align 8 + br label %for.cond.cleanup4.us-lcssa.us.us + +for.cond.cleanup4.us-lcssa.us.us: ; preds = %for.body5.us.us48.preheader, %for.body5.us.us.us.preheader + %indvars.iv.next79 = add nuw nsw i64 %indvars.iv78, 2 + %cmp.us = icmp slt i64 %indvars.iv.next79, %3 + %indvars.iv.next71 = add nuw nsw i64 %indvars.iv70, 2 + br i1 %cmp.us, label %for.body.us, label %for.cond.cleanup.loopexit + +for.cond.cleanup.loopexit: ; preds = %for.cond.cleanup4.us-lcssa.us.us + br label %for.cond.cleanup + +for.cond.cleanup.loopexit99: ; preds = %for.body + br label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.cond.cleanup.loopexit99, %for.cond.cleanup.loopexit, %entry + ret void + +for.body: ; preds = %for.body.preheader, %for.body + %indvars.iv95 = phi i64 [ %indvars.iv.next96, %for.body ], [ 8, %for.body.preheader ] + %indvars.iv87 = phi i64 [ %indvars.iv.next88, %for.body ], [ 0, %for.body.preheader ] + %8 = sub nsw i64 8, %indvars.iv95 + %add.ptr = getelementptr inbounds [10 x [10 x i32]], [10 x [10 x i32]]* @d, i64 0, i64 %indvars.iv95 + %9 = add nsw i64 %8, %indvars.iv87 + %arraydecay.us31 = getelementptr inbounds [10 x i32], [10 x i32]* %add.ptr, i64 %9, i64 0 + store i32 8, i32* %arraydecay.us31, align 16 + %indvars.iv.next90 = or i64 %indvars.iv87, 1 + %10 = add nsw i64 %8, %indvars.iv.next90 + %arraydecay.us31.1 = getelementptr inbounds [10 x i32], [10 x i32]* %add.ptr, i64 %10, i64 0 + store i32 8, i32* %arraydecay.us31.1, align 8 + %indvars.iv.next96 = add nuw nsw i64 %indvars.iv95, 2 + %cmp = icmp slt i64 %indvars.iv.next96, %3 + %indvars.iv.next88 = add nuw nsw i64 %indvars.iv87, 2 + br i1 %cmp, label %for.body, label %for.cond.cleanup.loopexit99 +} + +attributes #0 = { norecurse nounwind ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="knl" "target-features"="+adx,+aes,+avx,+avx2,+avx512cd,+avx512er,+avx512f,+avx512pf,+bmi,+bmi2,+cx16,+f16c,+fma,+fsgsbase,+fxsr,+lzcnt,+mmx,+movbe,+pclmul,+popcnt,+prefetchwt1,+rdrnd,+rdseed,+rtm,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave,+xsaveopt" "unsafe-fp-math"="false" "use-soft-float"="false" } diff --git a/test/Transforms/LoopVectorize/X86/uint64_to_fp64-cost-model.ll b/test/Transforms/LoopVectorize/X86/uint64_to_fp64-cost-model.ll index 38af11c443d0e..387eec4d5ede4 100644 --- a/test/Transforms/LoopVectorize/X86/uint64_to_fp64-cost-model.ll +++ b/test/Transforms/LoopVectorize/X86/uint64_to_fp64-cost-model.ll @@ -5,8 +5,8 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3 target triple = "x86_64-apple-macosx10.8.0" -; CHECK: cost of 20 for VF 2 For instruction: %conv = uitofp i64 %tmp to double -; CHECK: cost of 40 for VF 4 For instruction: %conv = uitofp i64 %tmp to double +; CHECK: cost of 10 for VF 2 For instruction: %conv = uitofp i64 %tmp to double +; CHECK: cost of 20 for VF 4 For instruction: %conv = uitofp i64 %tmp to double define void @uint64_to_double_cost(i64* noalias nocapture %a, double* noalias nocapture readonly %b) nounwind { entry: br label %for.body diff --git a/test/Transforms/LoopVectorize/X86/uniform-phi.ll b/test/Transforms/LoopVectorize/X86/uniform-phi.ll new file mode 100644 index 0000000000000..1759cb819760b --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/uniform-phi.ll @@ -0,0 +1,50 @@ +; RUN: opt < %s -loop-vectorize -mtriple=x86_64-apple-macosx10.8.0 -mcpu=corei7 -debug-only=loop-vectorize -S 2>&1 | FileCheck %s +; REQUIRES: asserts +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; CHECK-LABEL: test +; CHECK-DAG: LV: Found uniform instruction: %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] +; CHECK-DAG: LV: Found uniform instruction: %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 +; CHECK-DAG: LV: Found uniform instruction: %exitcond = icmp eq i64 %indvars.iv, 1599 + +define void @test(float* noalias nocapture %a, float* noalias nocapture readonly %b) #0 { +entry: + br label %for.body + +for.body: ; preds = %for.body, %entry + %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] + %arrayidx = getelementptr inbounds float, float* %b, i64 %indvars.iv + %tmp0 = load float, float* %arrayidx, align 4 + %add = fadd float %tmp0, 1.000000e+00 + %arrayidx5 = getelementptr inbounds float, float* %a, i64 %indvars.iv + store float %add, float* %arrayidx5, align 4 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond = icmp eq i64 %indvars.iv, 1599 + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body + ret void +} + +; CHECK-LABEL: foo +; CHECK-DAG: LV: Found uniform instruction: %cond = icmp eq i64 %i.next, %n +; CHECK-DAG: LV: Found uniform instruction: %tmp1 = getelementptr inbounds i32, i32* %a, i32 %tmp0 +; CHECK-NOT: LV: Found uniform instruction: %i = phi i64 [ %i.next, %for.body ], [ 0, %entry ] + +define void @foo(i32* %a, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %for.body ], [ 0, %entry ] + %tmp0 = trunc i64 %i to i32 + %tmp1 = getelementptr inbounds i32, i32* %a, i32 %tmp0 + store i32 %tmp0, i32* %tmp1, align 4 + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp eq i64 %i.next, %n + br i1 %cond, label %for.end, label %for.body + +for.end: + ret void +} diff --git a/test/Transforms/LoopVectorize/X86/uniform_load.ll b/test/Transforms/LoopVectorize/X86/uniform_load.ll new file mode 100644 index 0000000000000..e71292265c2a0 --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/uniform_load.ll @@ -0,0 +1,47 @@ +; RUN: opt -basicaa -loop-vectorize -S -mcpu=core-avx2 < %s | FileCheck %s + +;float inc = 0.5; +;void foo(float *A, unsigned N) { +; +; for (unsigned i=0; i<N; i++){ +; A[i] += inc; +; } +;} + +; CHECK-LABEL: foo +; CHECK: vector.body +; CHECK: load <8 x float> +; CHECK: fadd <8 x float> +; CHECK: store <8 x float> + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@inc = global float 5.000000e-01, align 4 + +define void @foo(float* nocapture %A, i32 %N) #0 { +entry: + %cmp3 = icmp eq i32 %N, 0 + br i1 %cmp3, label %for.end, label %for.body.preheader + +for.body.preheader: ; preds = %entry + br label %for.body + +for.body: ; preds = %for.body.preheader, %for.body + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %for.body.preheader ] + %0 = load float, float* @inc, align 4 + %arrayidx = getelementptr inbounds float, float* %A, i64 %indvars.iv + %1 = load float, float* %arrayidx, align 4 + %add = fadd float %0, %1 + store float %add, float* %arrayidx, align 4 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + br label %for.end + +for.end: ; preds = %for.end.loopexit, %entry + ret void +} diff --git a/test/Transforms/LoopVectorize/X86/vector_max_bandwidth.ll b/test/Transforms/LoopVectorize/X86/vector_max_bandwidth.ll index e6dc39c2afada..fe9d59efc8b37 100644 --- a/test/Transforms/LoopVectorize/X86/vector_max_bandwidth.ll +++ b/test/Transforms/LoopVectorize/X86/vector_max_bandwidth.ll @@ -16,7 +16,7 @@ target triple = "x86_64-unknown-linux-gnu" ; -vectorizer-maximize-bandwidth is indicated. ; ; CHECK-label: foo -; CHECK: LV: Selecting VF: 16. +; CHECK: LV: Selecting VF: 32. define void @foo() { entry: br label %for.body diff --git a/test/Transforms/LoopVectorize/X86/vectorization-remarks-loopid-dbg.ll b/test/Transforms/LoopVectorize/X86/vectorization-remarks-loopid-dbg.ll new file mode 100644 index 0000000000000..1d51b9c4beaa3 --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/vectorization-remarks-loopid-dbg.ll @@ -0,0 +1,74 @@ +; RUN: opt < %s -loop-vectorize -mtriple=x86_64-unknown-linux -S -pass-remarks='loop-vectorize' 2>&1 | FileCheck -check-prefix=VECTORIZED %s +; RUN: opt < %s -loop-vectorize -force-vector-width=1 -force-vector-interleave=4 -mtriple=x86_64-unknown-linux -S -pass-remarks='loop-vectorize' 2>&1 | FileCheck -check-prefix=UNROLLED %s +; RUN: opt < %s -loop-vectorize -force-vector-width=1 -force-vector-interleave=1 -mtriple=x86_64-unknown-linux -S -pass-remarks-analysis='loop-vectorize' 2>&1 | FileCheck -check-prefix=NONE %s + +; RUN: llc < %s -mtriple x86_64-pc-linux-gnu -o - | FileCheck -check-prefix=DEBUG-OUTPUT %s +; DEBUG-OUTPUT-NOT: .loc +; DEBUG-OUTPUT-NOT: {{.*}}.debug_info + +; VECTORIZED: remark: vectorization-remarks.c:17:8: vectorized loop (vectorization width: 4, interleaved count: 1) +; UNROLLED: remark: vectorization-remarks.c:17:8: interleaved loop (interleaved count: 4) +; NONE: remark: vectorization-remarks.c:17:8: loop not vectorized: vectorization and interleaving are explicitly disabled, or vectorize width and interleave count are both set to 1 + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define i32 @foo(i32 %n) #0 !dbg !4 { +entry: + %diff = alloca i32, align 4 + %cb = alloca [16 x i8], align 16 + %cc = alloca [16 x i8], align 16 + store i32 0, i32* %diff, align 4, !tbaa !11 + br label %for.body + +for.body: ; preds = %for.body, %entry + %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] + %add8 = phi i32 [ 0, %entry ], [ %add, %for.body ] + %arrayidx = getelementptr inbounds [16 x i8], [16 x i8]* %cb, i64 0, i64 %indvars.iv + %0 = load i8, i8* %arrayidx, align 1, !tbaa !21 + %conv = sext i8 %0 to i32 + %arrayidx2 = getelementptr inbounds [16 x i8], [16 x i8]* %cc, i64 0, i64 %indvars.iv + %1 = load i8, i8* %arrayidx2, align 1, !tbaa !21 + %conv3 = sext i8 %1 to i32 + %sub = sub i32 %conv, %conv3 + %add = add nsw i32 %sub, %add8 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond = icmp eq i64 %indvars.iv.next, 16 + br i1 %exitcond, label %for.end, label %for.body, !llvm.loop !25 + +for.end: ; preds = %for.body + store i32 %add, i32* %diff, align 4, !tbaa !11 + call void @ibar(i32* %diff) #2 + ret i32 0 +} + +declare void @ibar(i32*) #1 + +!llvm.module.flags = !{!7, !8} +!llvm.ident = !{!9} +!llvm.dbg.cu = !{!24} + +!1 = !DIFile(filename: "vectorization-remarks.c", directory: ".") +!2 = !{} +!3 = !{!4} +!4 = distinct !DISubprogram(name: "foo", line: 5, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !24, scopeLine: 6, file: !1, scope: !5, type: !6, variables: !2) +!5 = !DIFile(filename: "vectorization-remarks.c", directory: ".") +!6 = !DISubroutineType(types: !2) +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 1, !"Debug Info Version", i32 3} +!9 = !{!"clang version 3.5.0 "} +!10 = !DILocation(line: 8, column: 3, scope: !4) +!11 = !{!12, !12, i64 0} +!12 = !{!"int", !13, i64 0} +!13 = !{!"omnipotent char", !14, i64 0} +!14 = !{!"Simple C/C++ TBAA"} +!15 = !DILocation(line: 17, column: 8, scope: !16) +!16 = distinct !DILexicalBlock(line: 17, column: 8, file: !1, scope: !17) +!17 = distinct !DILexicalBlock(line: 17, column: 8, file: !1, scope: !18) +!18 = distinct !DILexicalBlock(line: 17, column: 3, file: !1, scope: !4) +!19 = !DILocation(line: 18, column: 5, scope: !20) +!20 = distinct !DILexicalBlock(line: 17, column: 27, file: !1, scope: !18) +!21 = !{!13, !13, i64 0} +!22 = !DILocation(line: 20, column: 3, scope: !4) +!23 = !DILocation(line: 21, column: 3, scope: !4) +!24 = distinct !DICompileUnit(language: DW_LANG_C89, file: !1, emissionKind: NoDebug) +!25 = !{!25, !15} diff --git a/test/Transforms/LoopVectorize/X86/vectorization-remarks-missed.ll b/test/Transforms/LoopVectorize/X86/vectorization-remarks-missed.ll index 02fab44473413..419f2e02456be 100644 --- a/test/Transforms/LoopVectorize/X86/vectorization-remarks-missed.ll +++ b/test/Transforms/LoopVectorize/X86/vectorization-remarks-missed.ll @@ -54,8 +54,9 @@ for.body: ; preds = %entry, %for.body %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ] %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv, !dbg !16 %0 = trunc i64 %indvars.iv to i32, !dbg !16 + %ld = load i32, i32* %arrayidx, align 4 store i32 %0, i32* %arrayidx, align 4, !dbg !16, !tbaa !18 - %cmp3 = icmp sle i32 %0, %Length, !dbg !22 + %cmp3 = icmp sle i32 %ld, %Length, !dbg !22 %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1, !dbg !12 %1 = trunc i64 %indvars.iv.next to i32 %cmp = icmp slt i32 %1, %Length, !dbg !12 @@ -122,15 +123,14 @@ attributes #0 = { nounwind } !llvm.module.flags = !{!9, !10} !llvm.ident = !{!11} -!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5.0", isOptimized: true, runtimeVersion: 6, emissionKind: 2, file: !1, enums: !2, retainedTypes: !2, subprograms: !3, globals: !2, imports: !2) +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5.0", isOptimized: true, runtimeVersion: 6, emissionKind: LineTablesOnly, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2) !1 = !DIFile(filename: "source.cpp", directory: ".") !2 = !{} -!3 = !{!4, !7, !8} -!4 = distinct !DISubprogram(name: "test", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, scopeLine: 1, file: !1, scope: !5, type: !6, variables: !2) +!4 = distinct !DISubprogram(name: "test", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, scopeLine: 1, file: !1, scope: !5, type: !6, variables: !2) !5 = !DIFile(filename: "source.cpp", directory: ".") !6 = !DISubroutineType(types: !2) -!7 = distinct !DISubprogram(name: "test_disabled", line: 10, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, scopeLine: 10, file: !1, scope: !5, type: !6, variables: !2) -!8 = distinct !DISubprogram(name: "test_array_bounds", line: 16, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, scopeLine: 16, file: !1, scope: !5, type: !6, variables: !2) +!7 = distinct !DISubprogram(name: "test_disabled", line: 10, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, scopeLine: 10, file: !1, scope: !5, type: !6, variables: !2) +!8 = distinct !DISubprogram(name: "test_array_bounds", line: 16, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, scopeLine: 16, file: !1, scope: !5, type: !6, variables: !2) !9 = !{i32 2, !"Dwarf Version", i32 2} !10 = !{i32 2, !"Debug Info Version", i32 3} !11 = !{!"clang version 3.5.0"} diff --git a/test/Transforms/LoopVectorize/X86/vectorization-remarks-profitable.ll b/test/Transforms/LoopVectorize/X86/vectorization-remarks-profitable.ll index df8c668f1262e..fc9f97328fb7c 100644 --- a/test/Transforms/LoopVectorize/X86/vectorization-remarks-profitable.ll +++ b/test/Transforms/LoopVectorize/X86/vectorization-remarks-profitable.ll @@ -82,13 +82,12 @@ attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fp !llvm.module.flags = !{!7, !8} !llvm.ident = !{!9} -!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.8.0 (trunk 250016)", isOptimized: false, runtimeVersion: 0, emissionKind: 2, enums: !2, subprograms: !3) +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.8.0 (trunk 250016)", isOptimized: false, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2) !1 = !DIFile(filename: "vectorization-remarks-profitable.c", directory: "") !2 = !{} -!3 = !{!4, !6} -!4 = distinct !DISubprogram(name: "do_not_interleave", scope: !1, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, variables: !2) +!4 = distinct !DISubprogram(name: "do_not_interleave", scope: !1, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) !5 = !DISubroutineType(types: !2) -!6 = distinct !DISubprogram(name: "interleave_not_profitable", scope: !1, file: !1, line: 8, type: !5, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped, isOptimized: false, variables: !2) +!6 = distinct !DISubprogram(name: "interleave_not_profitable", scope: !1, file: !1, line: 8, type: !5, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) !7 = !{i32 2, !"Dwarf Version", i32 4} !8 = !{i32 2, !"Debug Info Version", i32 3} !9 = !{!"clang version 3.8.0 (trunk 250016)"} diff --git a/test/Transforms/LoopVectorize/X86/vectorization-remarks.ll b/test/Transforms/LoopVectorize/X86/vectorization-remarks.ll index 77a405ebb434d..c14a2cb91b60e 100644 --- a/test/Transforms/LoopVectorize/X86/vectorization-remarks.ll +++ b/test/Transforms/LoopVectorize/X86/vectorization-remarks.ll @@ -2,9 +2,6 @@ ; RUN: opt < %s -loop-vectorize -force-vector-width=1 -force-vector-interleave=4 -mtriple=x86_64-unknown-linux -S -pass-remarks='loop-vectorize' 2>&1 | FileCheck -check-prefix=UNROLLED %s ; RUN: opt < %s -loop-vectorize -force-vector-width=1 -force-vector-interleave=1 -mtriple=x86_64-unknown-linux -S -pass-remarks-analysis='loop-vectorize' 2>&1 | FileCheck -check-prefix=NONE %s -; This code has all the !dbg annotations needed to track source line information, -; but is missing the llvm.dbg.cu annotation. This prevents code generation from -; emitting debug info in the final output. ; RUN: llc < %s -mtriple x86_64-pc-linux-gnu -o - | FileCheck -check-prefix=DEBUG-OUTPUT %s ; DEBUG-OUTPUT-NOT: .loc ; DEBUG-OUTPUT-NOT: {{.*}}.debug_info @@ -48,11 +45,12 @@ declare void @ibar(i32*) #1 !llvm.module.flags = !{!7, !8} !llvm.ident = !{!9} +!llvm.dbg.cu = !{!24} !1 = !DIFile(filename: "vectorization-remarks.c", directory: ".") !2 = !{} !3 = !{!4} -!4 = distinct !DISubprogram(name: "foo", line: 5, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, scopeLine: 6, file: !1, scope: !5, type: !6, variables: !2) +!4 = distinct !DISubprogram(name: "foo", line: 5, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !24, scopeLine: 6, file: !1, scope: !5, type: !6, variables: !2) !5 = !DIFile(filename: "vectorization-remarks.c", directory: ".") !6 = !DISubroutineType(types: !2) !7 = !{i32 2, !"Dwarf Version", i32 4} @@ -72,3 +70,4 @@ declare void @ibar(i32*) #1 !21 = !{!13, !13, i64 0} !22 = !DILocation(line: 20, column: 3, scope: !4) !23 = !DILocation(line: 21, column: 3, scope: !4) +!24 = distinct !DICompileUnit(language: DW_LANG_C89, file: !1, emissionKind: NoDebug) diff --git a/test/Transforms/LoopVectorize/X86/vectorize-only-for-real.ll b/test/Transforms/LoopVectorize/X86/vectorize-only-for-real.ll new file mode 100644 index 0000000000000..d1473552c98af --- /dev/null +++ b/test/Transforms/LoopVectorize/X86/vectorize-only-for-real.ll @@ -0,0 +1,39 @@ +; RUN: opt -S -basicaa -loop-vectorize < %s | FileCheck %s +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +define i32 @accum(i32* nocapture readonly %x, i32 %N) #0 { +entry: +; CHECK-LABEL: @accum +; CHECK-NOT: x i32> + + %cmp1 = icmp sgt i32 %N, 0 + br i1 %cmp1, label %for.inc.preheader, label %for.end + +for.inc.preheader: + br label %for.inc + +for.inc: + %indvars.iv = phi i64 [ %indvars.iv.next, %for.inc ], [ 0, %for.inc.preheader ] + %sum.02 = phi i32 [ %add, %for.inc ], [ 0, %for.inc.preheader ] + %arrayidx = getelementptr inbounds i32, i32* %x, i64 %indvars.iv + %0 = load i32, i32* %arrayidx, align 4 + %add = add nsw i32 %0, %sum.02 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %N + br i1 %exitcond, label %for.end.loopexit, label %for.inc + +for.end.loopexit: + %add.lcssa = phi i32 [ %add, %for.inc ] + br label %for.end + +for.end: + %sum.0.lcssa = phi i32 [ 0, %entry ], [ %add.lcssa, %for.end.loopexit ] + ret i32 %sum.0.lcssa + +; CHECK: ret i32 +} + +attributes #0 = { "target-cpu"="core2" "target-features"="+sse,-avx,-avx2,-sse2" } + diff --git a/test/Transforms/LoopVectorize/cast-induction.ll b/test/Transforms/LoopVectorize/cast-induction.ll index fae89976a7bc3..54f68b7bd076e 100644 --- a/test/Transforms/LoopVectorize/cast-induction.ll +++ b/test/Transforms/LoopVectorize/cast-induction.ll @@ -8,7 +8,7 @@ target triple = "x86_64-apple-macosx10.8.0" @a = common global [2048 x i32] zeroinitializer, align 16 ;CHECK-LABEL: @example12( -;CHECK: trunc i64 +;CHECK: %vec.ind1 = phi <4 x i32> ;CHECK: store <4 x i32> ;CHECK: ret void define void @example12() nounwind uwtable ssp { diff --git a/test/Transforms/LoopVectorize/conditional-assignment.ll b/test/Transforms/LoopVectorize/conditional-assignment.ll index 8d820e277b261..0115b09582f97 100644 --- a/test/Transforms/LoopVectorize/conditional-assignment.ll +++ b/test/Transforms/LoopVectorize/conditional-assignment.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -loop-vectorize -S -pass-remarks-missed='loop-vectorize' -pass-remarks-analysis='loop-vectorize' 2>&1 | FileCheck %s +; RUN: opt < %s -passes=loop-vectorize -S -pass-remarks-missed='loop-vectorize' -pass-remarks-analysis='loop-vectorize' 2>&1 | FileCheck %s ; CHECK: remark: source.c:2:8: loop not vectorized: store that is conditionally executed prevents vectorization @@ -36,11 +37,10 @@ attributes #0 = { nounwind } !llvm.module.flags = !{!7, !8} !llvm.ident = !{!9} -!0 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang version 3.6.0", isOptimized: true, emissionKind: 2, file: !1, enums: !2, retainedTypes: !2, subprograms: !3, globals: !2, imports: !2) +!0 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang version 3.6.0", isOptimized: true, emissionKind: LineTablesOnly, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2) !1 = !DIFile(filename: "source.c", directory: ".") !2 = !{} -!3 = !{!4} -!4 = distinct !DISubprogram(name: "conditional_store", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, scopeLine: 1, file: !1, scope: !5, type: !6, variables: !2) +!4 = distinct !DISubprogram(name: "conditional_store", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, scopeLine: 1, file: !1, scope: !5, type: !6, variables: !2) !5 = !DIFile(filename: "source.c", directory: ".") !6 = !DISubroutineType(types: !2) !7 = !{i32 2, !"Dwarf Version", i32 2} diff --git a/test/Transforms/LoopVectorize/control-flow.ll b/test/Transforms/LoopVectorize/control-flow.ll index a2fc69a6e907f..78ce29eff5275 100644 --- a/test/Transforms/LoopVectorize/control-flow.ll +++ b/test/Transforms/LoopVectorize/control-flow.ll @@ -55,11 +55,10 @@ attributes #0 = { nounwind } !llvm.module.flags = !{!7, !8} !llvm.ident = !{!9} -!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5.0", isOptimized: true, runtimeVersion: 6, emissionKind: 2, file: !1, enums: !2, retainedTypes: !2, subprograms: !3, globals: !2, imports: !2) +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5.0", isOptimized: true, runtimeVersion: 6, emissionKind: LineTablesOnly, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2) !1 = !DIFile(filename: "source.cpp", directory: ".") !2 = !{} -!3 = !{!4} -!4 = distinct !DISubprogram(name: "test", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, scopeLine: 2, file: !1, scope: !5, type: !6, variables: !2) +!4 = distinct !DISubprogram(name: "test", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, scopeLine: 2, file: !1, scope: !5, type: !6, variables: !2) !5 = !DIFile(filename: "source.cpp", directory: ".") !6 = !DISubroutineType(types: !2) !7 = !{i32 2, !"Dwarf Version", i32 2} diff --git a/test/Transforms/LoopVectorize/dbg.value.ll b/test/Transforms/LoopVectorize/dbg.value.ll index f68b6865b072e..d7d3ff6d9f968 100644 --- a/test/Transforms/LoopVectorize/dbg.value.ll +++ b/test/Transforms/LoopVectorize/dbg.value.ll @@ -44,10 +44,9 @@ attributes #1 = { nounwind readnone } !llvm.dbg.cu = !{!0} !llvm.module.flags = !{!26} -!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang", isOptimized: true, emissionKind: 0, file: !25, enums: !1, retainedTypes: !1, subprograms: !2, globals: !11) +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang", isOptimized: true, emissionKind: FullDebug, file: !25, enums: !1, retainedTypes: !1, globals: !11) !1 = !{} -!2 = !{!3} -!3 = distinct !DISubprogram(name: "test", linkageName: "test", line: 5, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, scopeLine: 5, file: !25, scope: !4, type: !5, variables: !8) +!3 = distinct !DISubprogram(name: "test", linkageName: "test", line: 5, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, scopeLine: 5, file: !25, scope: !4, type: !5, variables: !8) !4 = !DIFile(filename: "test", directory: "/path/to/somewhere") !5 = !DISubroutineType(types: !6) !6 = !{!7} diff --git a/test/Transforms/LoopVectorize/debugloc.ll b/test/Transforms/LoopVectorize/debugloc.ll index 0214f1c4847c6..45cb9a2baeaf7 100644 --- a/test/Transforms/LoopVectorize/debugloc.ll +++ b/test/Transforms/LoopVectorize/debugloc.ll @@ -63,11 +63,10 @@ attributes #1 = { nounwind readnone } !llvm.dbg.cu = !{!0} !llvm.module.flags = !{!18, !27} -!0 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang version 3.4 (trunk 185038) (llvm/trunk 185097)", isOptimized: true, emissionKind: 0, file: !1, enums: !2, retainedTypes: !2, subprograms: !3, globals: !2, imports: !2) +!0 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang version 3.4 (trunk 185038) (llvm/trunk 185097)", isOptimized: true, emissionKind: FullDebug, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2) !1 = !DIFile(filename: "-", directory: "/Volumes/Data/backedup/dev/os/llvm/debug") !2 = !{} -!3 = !{!4} -!4 = distinct !DISubprogram(name: "f", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, scopeLine: 3, file: !5, scope: !6, type: !7, variables: !12) +!4 = distinct !DISubprogram(name: "f", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, scopeLine: 3, file: !5, scope: !6, type: !7, variables: !12) !5 = !DIFile(filename: "<stdin>", directory: "/Volumes/Data/backedup/dev/os/llvm/debug") !6 = !DIFile(filename: "<stdin>", directory: "/Volumes/Data/backedup/dev/os/llvm/debug") !7 = !DISubroutineType(types: !8) diff --git a/test/Transforms/LoopVectorize/gcc-examples.ll b/test/Transforms/LoopVectorize/gcc-examples.ll index 188090186158c..95b0d16d57f9b 100644 --- a/test/Transforms/LoopVectorize/gcc-examples.ll +++ b/test/Transforms/LoopVectorize/gcc-examples.ll @@ -368,7 +368,7 @@ define void @example11() nounwind uwtable ssp { } ;CHECK-LABEL: @example12( -;CHECK: trunc i64 +;CHECK: %vec.ind1 = phi <4 x i32> ;CHECK: store <4 x i32> ;CHECK: ret void define void @example12() nounwind uwtable ssp { diff --git a/test/Transforms/LoopVectorize/gep_with_bitcast.ll b/test/Transforms/LoopVectorize/gep_with_bitcast.ll index ab2fd5e4e1c6f..e73b6eacbe175 100644 --- a/test/Transforms/LoopVectorize/gep_with_bitcast.ll +++ b/test/Transforms/LoopVectorize/gep_with_bitcast.ll @@ -12,10 +12,11 @@ target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" ; CHECK-LABEL: @foo ; CHECK: vector.body -; CHECK: %0 = getelementptr inbounds double*, double** %in, i64 %index -; CHECK: %1 = bitcast double** %0 to <4 x i64>* -; CHECK: %wide.load = load <4 x i64>, <4 x i64>* %1, align 8 -; CHECK: %2 = icmp eq <4 x i64> %wide.load, zeroinitializer +; CHECK: %[[IV:.+]] = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; CHECK: %[[v0:.+]] = getelementptr inbounds double*, double** %in, i64 %[[IV]] +; CHECK: %[[v1:.+]] = bitcast double** %[[v0]] to <4 x i64>* +; CHECK: %wide.load = load <4 x i64>, <4 x i64>* %[[v1]], align 8 +; CHECK: icmp eq <4 x i64> %wide.load, zeroinitializer ; CHECK: br i1 define void @foo(double** noalias nocapture readonly %in, double** noalias nocapture readnone %out, i8* noalias nocapture %res) #0 { @@ -37,4 +38,4 @@ for.body: for.end: ret void -}
\ No newline at end of file +} diff --git a/test/Transforms/LoopVectorize/global_alias.ll b/test/Transforms/LoopVectorize/global_alias.ll index 84fa48cd51484..0da841bcbbd0e 100644 --- a/test/Transforms/LoopVectorize/global_alias.ll +++ b/test/Transforms/LoopVectorize/global_alias.ll @@ -12,7 +12,7 @@ target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3 @PA = external global i32* -;; === First, the tests that should always vectorize, wither statically or by adding run-time checks === +;; === First, the tests that should always vectorize, whether statically or by adding run-time checks === ; /// Different objects, positive induction, constant distance @@ -387,7 +387,7 @@ for.end: ; preds = %for.cond ; return Foo.A[a]; ; } ; CHECK-LABEL: define i32 @noAlias08( -; CHECK: sub <4 x i32> +; CHECK: sub nuw nsw <4 x i32> ; CHECK: ret define i32 @noAlias08(i32 %a) #0 { @@ -439,7 +439,7 @@ for.end: ; preds = %for.cond ; return Foo.A[a]; ; } ; CHECK-LABEL: define i32 @noAlias09( -; CHECK: sub <4 x i32> +; CHECK: sub nuw nsw <4 x i32> ; CHECK: ret define i32 @noAlias09(i32 %a) #0 { @@ -721,7 +721,7 @@ for.end: ; preds = %for.cond ; return Foo.A[a]; ; } ; CHECK-LABEL: define i32 @noAlias14( -; CHECK: sub <4 x i32> +; CHECK: sub nuw nsw <4 x i32> ; CHECK: ret define i32 @noAlias14(i32 %a) #0 { diff --git a/test/Transforms/LoopVectorize/hints-trans.ll b/test/Transforms/LoopVectorize/hints-trans.ll new file mode 100644 index 0000000000000..ec5ddbb03ce1e --- /dev/null +++ b/test/Transforms/LoopVectorize/hints-trans.ll @@ -0,0 +1,30 @@ +; RUN: opt -S -loop-vectorize -force-vector-interleave=1 -force-vector-width=4 -instsimplify -simplifycfg < %s | FileCheck %s +; Note: -instsimplify -simplifycfg remove the (now dead) original loop, making +; it easy to test that the llvm.loop.unroll.disable hint is still present. +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: norecurse nounwind uwtable +define void @foo(i32* nocapture %b) #0 { +entry: + br label %for.body + +for.cond.cleanup: ; preds = %for.body + ret void + +for.body: ; preds = %for.body, %entry + %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %b, i64 %indvars.iv + store i32 1, i32* %arrayidx, align 4 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond = icmp eq i64 %indvars.iv.next, 16 + br i1 %exitcond, label %for.cond.cleanup, label %for.body, !llvm.loop !0 +} + +; CHECK-LABEL: @foo +; CHECK: = !{!"llvm.loop.unroll.disable"} + +attributes #0 = { norecurse nounwind uwtable } + +!0 = distinct !{!0, !1} +!1 = !{!"llvm.loop.unroll.disable"} diff --git a/test/Transforms/LoopVectorize/if-conversion.ll b/test/Transforms/LoopVectorize/if-conversion.ll index fb5416976525c..daa8f147e2103 100644 --- a/test/Transforms/LoopVectorize/if-conversion.ll +++ b/test/Transforms/LoopVectorize/if-conversion.ll @@ -73,7 +73,7 @@ for.end: ;CHECK-LABEL: @reduction_func( ;CHECK: load <4 x i32> ;CHECK: add <4 x i32> -;CHECK: icmp sle <4 x i32> +;CHECK: icmp slt <4 x i32> ;CHECK: select <4 x i1> ;CHECK: ret i32 define i32 @reduction_func(i32* nocapture %A, i32 %n) nounwind uwtable readonly ssp { diff --git a/test/Transforms/LoopVectorize/if-pred-stores.ll b/test/Transforms/LoopVectorize/if-pred-stores.ll index 0d70f557f8345..f39e774bb8957 100644 --- a/test/Transforms/LoopVectorize/if-pred-stores.ll +++ b/test/Transforms/LoopVectorize/if-pred-stores.ll @@ -1,7 +1,7 @@ -; RUN: opt -S -vectorize-num-stores-pred=1 -force-vector-width=1 -force-vector-interleave=2 -loop-vectorize -simplifycfg < %s | FileCheck %s --check-prefix=UNROLL -; RUN: opt -S -vectorize-num-stores-pred=1 -force-vector-width=1 -force-vector-interleave=2 -loop-vectorize < %s | FileCheck %s --check-prefix=UNROLL-NOSIMPLIFY -; RUN: opt -S -vectorize-num-stores-pred=1 -force-vector-width=2 -force-vector-interleave=1 -loop-vectorize -enable-cond-stores-vec -simplifycfg < %s | FileCheck %s --check-prefix=VEC -; RUN: opt -S -vectorize-num-stores-pred=1 -force-vector-width=2 -force-vector-interleave=1 -loop-vectorize -enable-cond-stores-vec -simplifycfg -instcombine < %s | FileCheck %s --check-prefix=VEC-IC +; RUN: opt -S -vectorize-num-stores-pred=1 -force-vector-width=1 -force-vector-interleave=2 -loop-vectorize -verify-loop-info -simplifycfg < %s | FileCheck %s --check-prefix=UNROLL +; RUN: opt -S -vectorize-num-stores-pred=1 -force-vector-width=1 -force-vector-interleave=2 -loop-vectorize -verify-loop-info < %s | FileCheck %s --check-prefix=UNROLL-NOSIMPLIFY +; RUN: opt -S -vectorize-num-stores-pred=1 -force-vector-width=2 -force-vector-interleave=1 -loop-vectorize -enable-cond-stores-vec -verify-loop-info -simplifycfg < %s | FileCheck %s --check-prefix=VEC +; RUN: opt -S -vectorize-num-stores-pred=1 -force-vector-width=2 -force-vector-interleave=1 -loop-vectorize -enable-cond-stores-vec -verify-loop-info -simplifycfg -instcombine < %s | FileCheck %s --check-prefix=VEC-IC target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.9.0" diff --git a/test/Transforms/LoopVectorize/induction-step.ll b/test/Transforms/LoopVectorize/induction-step.ll new file mode 100644 index 0000000000000..f56456e82dfa0 --- /dev/null +++ b/test/Transforms/LoopVectorize/induction-step.ll @@ -0,0 +1,124 @@ +; RUN: opt < %s -loop-vectorize -force-vector-interleave=1 -force-vector-width=8 -S | FileCheck %s + +; int int_inc; +; +;int induction_with_global(int init, int *restrict A, int N) { +; int x = init; +; for (int i=0;i<N;i++){ +; A[i] = x; +; x += int_inc; +; } +; return x; +;} + +; CHECK-LABEL: @induction_with_global( +; CHECK: %[[INT_INC:.*]] = load i32, i32* @int_inc, align 4 +; CHECK: vector.body: +; CHECK: %[[VAR1:.*]] = insertelement <8 x i32> undef, i32 %[[INT_INC]], i32 0 +; CHECK: %[[VAR2:.*]] = shufflevector <8 x i32> %[[VAR1]], <8 x i32> undef, <8 x i32> zeroinitializer +; CHECK: mul <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>, %[[VAR2]] + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + +@int_inc = common global i32 0, align 4 + +define i32 @induction_with_global(i32 %init, i32* noalias nocapture %A, i32 %N) { +entry: + %cmp4 = icmp sgt i32 %N, 0 + br i1 %cmp4, label %for.body.lr.ph, label %for.end + +for.body.lr.ph: ; preds = %entry + %0 = load i32, i32* @int_inc, align 4 + %1 = mul i32 %0, %N + br label %for.body + +for.body: ; preds = %for.body, %for.body.lr.ph + %indvars.iv = phi i64 [ 0, %for.body.lr.ph ], [ %indvars.iv.next, %for.body ] + %x.05 = phi i32 [ %init, %for.body.lr.ph ], [ %add, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + store i32 %x.05, i32* %arrayidx, align 4 + %add = add nsw i32 %0, %x.05 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %N + br i1 %exitcond, label %for.end.loopexit, label %for.body + +for.end.loopexit: ; preds = %for.body + %2 = add i32 %1, %init + br label %for.end + +for.end: ; preds = %for.end.loopexit, %entry + %x.0.lcssa = phi i32 [ %init, %entry ], [ %2, %for.end.loopexit ] + ret i32 %x.0.lcssa +} + + +;int induction_with_loop_inv(int init, int *restrict A, int N, int M) { +; int x = init; +; for (int j = 0; j < M; j++) { +; for (int i=0; i<N; i++){ +; A[i] = x; +; x += j; // induction step is a loop invariant variable +; } +; } +; return x; +;} + +; CHECK-LABEL: @induction_with_loop_inv( +; CHECK: for.cond1.preheader: +; CHECK: %[[INDVAR0:.*]] = phi i32 [ 0, +; CHECK: %[[INDVAR1:.*]] = phi i32 [ 0, +; CHECK: vector.body: +; CHECK: %[[VAR1:.*]] = insertelement <8 x i32> undef, i32 %[[INDVAR1]], i32 0 +; CHECK: %[[VAR2:.*]] = shufflevector <8 x i32> %[[VAR1]], <8 x i32> undef, <8 x i32> zeroinitializer +; CHECK: mul <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>, %[[VAR2]] + +define i32 @induction_with_loop_inv(i32 %init, i32* noalias nocapture %A, i32 %N, i32 %M) { +entry: + %cmp10 = icmp sgt i32 %M, 0 + br i1 %cmp10, label %for.cond1.preheader.lr.ph, label %for.end6 + +for.cond1.preheader.lr.ph: ; preds = %entry + %cmp27 = icmp sgt i32 %N, 0 + br label %for.cond1.preheader + +for.cond1.preheader: ; preds = %for.inc4, %for.cond1.preheader.lr.ph + %indvars.iv15 = phi i32 [ 0, %for.cond1.preheader.lr.ph ], [ %indvars.iv.next16, %for.inc4 ] + %j.012 = phi i32 [ 0, %for.cond1.preheader.lr.ph ], [ %inc5, %for.inc4 ] + %x.011 = phi i32 [ %init, %for.cond1.preheader.lr.ph ], [ %x.1.lcssa, %for.inc4 ] + br i1 %cmp27, label %for.body3.preheader, label %for.inc4 + +for.body3.preheader: ; preds = %for.cond1.preheader + br label %for.body3 + +for.body3: ; preds = %for.body3.preheader, %for.body3 + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body3 ], [ 0, %for.body3.preheader ] + %x.18 = phi i32 [ %add, %for.body3 ], [ %x.011, %for.body3.preheader ] + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + store i32 %x.18, i32* %arrayidx, align 4 + %add = add nsw i32 %x.18, %j.012 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32 + %exitcond = icmp eq i32 %lftr.wideiv, %N + br i1 %exitcond, label %for.inc4.loopexit, label %for.body3 + +for.inc4.loopexit: ; preds = %for.body3 + %0 = add i32 %x.011, %indvars.iv15 + br label %for.inc4 + +for.inc4: ; preds = %for.inc4.loopexit, %for.cond1.preheader + %x.1.lcssa = phi i32 [ %x.011, %for.cond1.preheader ], [ %0, %for.inc4.loopexit ] + %inc5 = add nuw nsw i32 %j.012, 1 + %indvars.iv.next16 = add i32 %indvars.iv15, %N + %exitcond17 = icmp eq i32 %inc5, %M + br i1 %exitcond17, label %for.end6.loopexit, label %for.cond1.preheader + +for.end6.loopexit: ; preds = %for.inc4 + %x.1.lcssa.lcssa = phi i32 [ %x.1.lcssa, %for.inc4 ] + br label %for.end6 + +for.end6: ; preds = %for.end6.loopexit, %entry + %x.0.lcssa = phi i32 [ %init, %entry ], [ %x.1.lcssa.lcssa, %for.end6.loopexit ] + ret i32 %x.0.lcssa +} diff --git a/test/Transforms/LoopVectorize/induction.ll b/test/Transforms/LoopVectorize/induction.ll index 59ee66a4a35df..c1f0bd95dbd78 100644 --- a/test/Transforms/LoopVectorize/induction.ll +++ b/test/Transforms/LoopVectorize/induction.ll @@ -1,4 +1,8 @@ ; RUN: opt < %s -loop-vectorize -force-vector-interleave=1 -force-vector-width=2 -S | FileCheck %s +; RUN: opt < %s -loop-vectorize -force-vector-interleave=1 -force-vector-width=2 -instcombine -S | FileCheck %s --check-prefix=IND +; RUN: opt < %s -loop-vectorize -force-vector-interleave=2 -force-vector-width=2 -instcombine -S | FileCheck %s --check-prefix=UNROLL +; RUN: opt < %s -loop-vectorize -force-vector-interleave=2 -force-vector-width=2 -S | FileCheck %s --check-prefix=UNROLL-NO-IC +; RUN: opt < %s -loop-vectorize -force-vector-interleave=2 -force-vector-width=4 -enable-interleaved-mem-accesses -instcombine -S | FileCheck %s --check-prefix=INTERLEAVE target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" @@ -27,8 +31,6 @@ for.end: ret void } -; RUN: opt < %s -loop-vectorize -force-vector-interleave=1 -force-vector-width=2 -instcombine -S | FileCheck %s --check-prefix=IND - ; Make sure we remove unneeded vectorization of induction variables. ; In order for instcombine to cleanup the vectorized induction variables that we ; create in the loop vectorizer we need to perform some form of redundancy @@ -66,6 +68,185 @@ loopexit: ret void } +; Make sure we don't create a vector induction phi node that is unused. +; Scalarize the step vectors instead. +; +; for (int i = 0; i < n; ++i) +; sum += a[i]; +; +; CHECK-LABEL: @scalarize_induction_variable_01( +; CHECK: vector.body: +; CHECK: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; CHECK: %[[i0:.+]] = add i64 %index, 0 +; CHECK: %[[i1:.+]] = add i64 %index, 1 +; CHECK: getelementptr inbounds i64, i64* %a, i64 %[[i0]] +; CHECK: getelementptr inbounds i64, i64* %a, i64 %[[i1]] +; +; UNROLL-NO-IC-LABEL: @scalarize_induction_variable_01( +; UNROLL-NO-IC: vector.body: +; UNROLL-NO-IC: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; UNROLL-NO-IC: %[[i0:.+]] = add i64 %index, 0 +; UNROLL-NO-IC: %[[i1:.+]] = add i64 %index, 1 +; UNROLL-NO-IC: %[[i2:.+]] = add i64 %index, 2 +; UNROLL-NO-IC: %[[i3:.+]] = add i64 %index, 3 +; UNROLL-NO-IC: getelementptr inbounds i64, i64* %a, i64 %[[i0]] +; UNROLL-NO-IC: getelementptr inbounds i64, i64* %a, i64 %[[i1]] +; UNROLL-NO-IC: getelementptr inbounds i64, i64* %a, i64 %[[i2]] +; UNROLL-NO-IC: getelementptr inbounds i64, i64* %a, i64 %[[i3]] +; +; IND-LABEL: @scalarize_induction_variable_01( +; IND: vector.body: +; IND: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; IND-NOT: add i64 {{.*}}, 2 +; IND: getelementptr inbounds i64, i64* %a, i64 %index +; +; UNROLL-LABEL: @scalarize_induction_variable_01( +; UNROLL: vector.body: +; UNROLL: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; UNROLL-NOT: add i64 {{.*}}, 4 +; UNROLL: %[[g1:.+]] = getelementptr inbounds i64, i64* %a, i64 %index +; UNROLL: getelementptr i64, i64* %[[g1]], i64 2 + +define i64 @scalarize_induction_variable_01(i64 *%a, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %for.body ], [ 0, %entry ] + %sum = phi i64 [ %2, %for.body ], [ 0, %entry ] + %0 = getelementptr inbounds i64, i64* %a, i64 %i + %1 = load i64, i64* %0, align 8 + %2 = add i64 %1, %sum + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + %3 = phi i64 [ %2, %for.body ] + ret i64 %3 +} + +; Make sure we scalarize the step vectors used for the pointer arithmetic. We +; can't easily simplify vectorized step vectors. +; +; float s = 0; +; for (int i ; 0; i < n; i += 8) +; s += (a[i] + b[i] + 1.0f); +; +; CHECK-LABEL: @scalarize_induction_variable_02( +; CHECK: vector.body: +; CHECK: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; CHECK: %offset.idx = shl i64 %index, 3 +; CHECK: %[[i0:.+]] = add i64 %offset.idx, 0 +; CHECK: %[[i1:.+]] = add i64 %offset.idx, 8 +; CHECK: getelementptr inbounds float, float* %a, i64 %[[i0]] +; CHECK: getelementptr inbounds float, float* %a, i64 %[[i1]] +; CHECK: getelementptr inbounds float, float* %b, i64 %[[i0]] +; CHECK: getelementptr inbounds float, float* %b, i64 %[[i1]] +; +; UNROLL-NO-IC-LABEL: @scalarize_induction_variable_02( +; UNROLL-NO-IC: vector.body: +; UNROLL-NO-IC: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; UNROLL-NO-IC: %offset.idx = shl i64 %index, 3 +; UNROLL-NO-IC: %[[i0:.+]] = add i64 %offset.idx, 0 +; UNROLL-NO-IC: %[[i1:.+]] = add i64 %offset.idx, 8 +; UNROLL-NO-IC: %[[i2:.+]] = add i64 %offset.idx, 16 +; UNROLL-NO-IC: %[[i3:.+]] = add i64 %offset.idx, 24 +; UNROLL-NO-IC: getelementptr inbounds float, float* %a, i64 %[[i0]] +; UNROLL-NO-IC: getelementptr inbounds float, float* %a, i64 %[[i1]] +; UNROLL-NO-IC: getelementptr inbounds float, float* %a, i64 %[[i2]] +; UNROLL-NO-IC: getelementptr inbounds float, float* %a, i64 %[[i3]] +; UNROLL-NO-IC: getelementptr inbounds float, float* %b, i64 %[[i0]] +; UNROLL-NO-IC: getelementptr inbounds float, float* %b, i64 %[[i1]] +; UNROLL-NO-IC: getelementptr inbounds float, float* %b, i64 %[[i2]] +; UNROLL-NO-IC: getelementptr inbounds float, float* %b, i64 %[[i3]] +; +; IND-LABEL: @scalarize_induction_variable_02( +; IND: vector.body: +; IND: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; IND: %[[i0:.+]] = shl i64 %index, 3 +; IND: %[[i1:.+]] = or i64 %[[i0]], 8 +; IND: getelementptr inbounds float, float* %a, i64 %[[i0]] +; IND: getelementptr inbounds float, float* %a, i64 %[[i1]] +; +; UNROLL-LABEL: @scalarize_induction_variable_02( +; UNROLL: vector.body: +; UNROLL: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; UNROLL: %[[i0:.+]] = shl i64 %index, 3 +; UNROLL: %[[i1:.+]] = or i64 %[[i0]], 8 +; UNROLL: %[[i2:.+]] = or i64 %[[i0]], 16 +; UNROLL: %[[i3:.+]] = or i64 %[[i0]], 24 +; UNROLL: getelementptr inbounds float, float* %a, i64 %[[i0]] +; UNROLL: getelementptr inbounds float, float* %a, i64 %[[i1]] +; UNROLL: getelementptr inbounds float, float* %a, i64 %[[i2]] +; UNROLL: getelementptr inbounds float, float* %a, i64 %[[i3]] + +define float @scalarize_induction_variable_02(float* %a, float* %b, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ 0, %entry ], [ %i.next, %for.body ] + %s = phi float [ 0.0, %entry ], [ %6, %for.body ] + %0 = getelementptr inbounds float, float* %a, i64 %i + %1 = load float, float* %0, align 4 + %2 = getelementptr inbounds float, float* %b, i64 %i + %3 = load float, float* %2, align 4 + %4 = fadd fast float %s, 1.0 + %5 = fadd fast float %4, %1 + %6 = fadd fast float %5, %3 + %i.next = add nuw nsw i64 %i, 8 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + %s.lcssa = phi float [ %6, %for.body ] + ret float %s.lcssa +} + +; Make sure we scalarize the step vectors used for the pointer arithmetic. We +; can't easily simplify vectorized step vectors. (Interleaved accesses.) +; +; for (int i = 0; i < n; ++i) +; a[i].f ^= y; +; +; INTERLEAVE-LABEL: @scalarize_induction_variable_03( +; INTERLEAVE: vector.body: +; INTERLEAVE: %[[i0:.+]] = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; INTERLEAVE: %[[i1:.+]] = or i64 %[[i0]], 1 +; INTERLEAVE: %[[i2:.+]] = or i64 %[[i0]], 2 +; INTERLEAVE: %[[i3:.+]] = or i64 %[[i0]], 3 +; INTERLEAVE: %[[i4:.+]] = or i64 %[[i0]], 4 +; INTERLEAVE: %[[i5:.+]] = or i64 %[[i0]], 5 +; INTERLEAVE: %[[i6:.+]] = or i64 %[[i0]], 6 +; INTERLEAVE: %[[i7:.+]] = or i64 %[[i0]], 7 +; INTERLEAVE: getelementptr inbounds %pair, %pair* %p, i64 %[[i0]], i32 1 +; INTERLEAVE: getelementptr inbounds %pair, %pair* %p, i64 %[[i1]], i32 1 +; INTERLEAVE: getelementptr inbounds %pair, %pair* %p, i64 %[[i2]], i32 1 +; INTERLEAVE: getelementptr inbounds %pair, %pair* %p, i64 %[[i3]], i32 1 +; INTERLEAVE: getelementptr inbounds %pair, %pair* %p, i64 %[[i4]], i32 1 +; INTERLEAVE: getelementptr inbounds %pair, %pair* %p, i64 %[[i5]], i32 1 +; INTERLEAVE: getelementptr inbounds %pair, %pair* %p, i64 %[[i6]], i32 1 +; INTERLEAVE: getelementptr inbounds %pair, %pair* %p, i64 %[[i7]], i32 1 + +%pair = type { i32, i32 } +define void @scalarize_induction_variable_03(%pair *%p, i32 %y, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %for.body ], [ 0, %entry ] + %f = getelementptr inbounds %pair, %pair* %p, i64 %i, i32 1 + %0 = load i32, i32* %f, align 8 + %1 = xor i32 %0, %y + store i32 %1, i32* %f, align 8 + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + ret void +} ; Make sure that the loop exit count computation does not overflow for i8 and ; i16. The exit count of these loops is i8/i16 max + 1. If we don't cast the @@ -114,9 +295,11 @@ define i32 @i16_loop() nounwind readnone ssp uwtable { ; CHECK-LABEL: max_i32_backedgetaken ; CHECK: br i1 true, label %scalar.ph, label %min.iters.checked +; CHECK: middle.block: +; CHECK: %[[v9:.+]] = extractelement <2 x i32> %bin.rdx, i32 0 ; CHECK: scalar.ph: -; CHECK: %bc.resume.val = phi i32 [ 0, %middle.block ], [ 0, %0 ] -; CHECK: %bc.merge.rdx = phi i32 [ 1, %0 ], [ 1, %min.iters.checked ], [ %5, %middle.block ] +; CHECK: %bc.resume.val = phi i32 [ 0, %middle.block ], [ 0, %[[v0:.+]] ] +; CHECK: %bc.merge.rdx = phi i32 [ 1, %[[v0:.+]] ], [ 1, %min.iters.checked ], [ %[[v9]], %middle.block ] define i32 @max_i32_backedgetaken() nounwind readnone ssp uwtable { @@ -166,3 +349,186 @@ cond.end.i: loopexit: ret i32 %and.i } + +; The SCEV expression of %sphi is (zext i8 {%t,+,1}<%loop> to i32) +; In order to recognize %sphi as an induction PHI and vectorize this loop, +; we need to convert the SCEV expression into an AddRecExpr. +; The expression gets converted to {zext i8 %t to i32,+,1}. + +; CHECK-LABEL: wrappingindvars1 +; CHECK-LABEL: vector.scevcheck +; CHECK-LABEL: vector.ph +; CHECK: %[[START:.*]] = add <2 x i32> %{{.*}}, <i32 0, i32 1> +; CHECK-LABEL: vector.body +; CHECK: %[[PHI:.*]] = phi <2 x i32> [ %[[START]], %vector.ph ], [ %[[STEP:.*]], %vector.body ] +; CHECK: %[[STEP]] = add <2 x i32> %[[PHI]], <i32 2, i32 2> +define void @wrappingindvars1(i8 %t, i32 %len, i32 *%A) { + entry: + %st = zext i8 %t to i16 + %ext = zext i8 %t to i32 + %ecmp = icmp ult i16 %st, 42 + br i1 %ecmp, label %loop, label %exit + + loop: + + %idx = phi i8 [ %t, %entry ], [ %idx.inc, %loop ] + %idx.b = phi i32 [ 0, %entry ], [ %idx.b.inc, %loop ] + %sphi = phi i32 [ %ext, %entry ], [%idx.inc.ext, %loop] + + %ptr = getelementptr inbounds i32, i32* %A, i8 %idx + store i32 %sphi, i32* %ptr + + %idx.inc = add i8 %idx, 1 + %idx.inc.ext = zext i8 %idx.inc to i32 + %idx.b.inc = add nuw nsw i32 %idx.b, 1 + + %c = icmp ult i32 %idx.b, %len + br i1 %c, label %loop, label %exit + + exit: + ret void +} + +; The SCEV expression of %sphi is (4 * (zext i8 {%t,+,1}<%loop> to i32)) +; In order to recognize %sphi as an induction PHI and vectorize this loop, +; we need to convert the SCEV expression into an AddRecExpr. +; The expression gets converted to ({4 * (zext %t to i32),+,4}). +; CHECK-LABEL: wrappingindvars2 +; CHECK-LABEL: vector.scevcheck +; CHECK-LABEL: vector.ph +; CHECK: %[[START:.*]] = add <2 x i32> %{{.*}}, <i32 0, i32 4> +; CHECK-LABEL: vector.body +; CHECK: %[[PHI:.*]] = phi <2 x i32> [ %[[START]], %vector.ph ], [ %[[STEP:.*]], %vector.body ] +; CHECK: %[[STEP]] = add <2 x i32> %[[PHI]], <i32 8, i32 8> +define void @wrappingindvars2(i8 %t, i32 %len, i32 *%A) { + +entry: + %st = zext i8 %t to i16 + %ext = zext i8 %t to i32 + %ext.mul = mul i32 %ext, 4 + + %ecmp = icmp ult i16 %st, 42 + br i1 %ecmp, label %loop, label %exit + + loop: + + %idx = phi i8 [ %t, %entry ], [ %idx.inc, %loop ] + %sphi = phi i32 [ %ext.mul, %entry ], [%mul, %loop] + %idx.b = phi i32 [ 0, %entry ], [ %idx.b.inc, %loop ] + + %ptr = getelementptr inbounds i32, i32* %A, i8 %idx + store i32 %sphi, i32* %ptr + + %idx.inc = add i8 %idx, 1 + %idx.inc.ext = zext i8 %idx.inc to i32 + %mul = mul i32 %idx.inc.ext, 4 + %idx.b.inc = add nuw nsw i32 %idx.b, 1 + + %c = icmp ult i32 %idx.b, %len + br i1 %c, label %loop, label %exit + + exit: + ret void +} + +; Check that we generate vectorized IVs in the pre-header +; instead of widening the scalar IV inside the loop, when +; we know how to do that. +; IND-LABEL: veciv +; IND: vector.body: +; IND: %index = phi i32 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; IND: %vec.ind = phi <2 x i32> [ <i32 0, i32 1>, %vector.ph ], [ %step.add, %vector.body ] +; IND: %step.add = add <2 x i32> %vec.ind, <i32 2, i32 2> +; IND: %index.next = add i32 %index, 2 +; IND: %[[CMP:.*]] = icmp eq i32 %index.next +; IND: br i1 %[[CMP]] +; UNROLL-LABEL: veciv +; UNROLL: vector.body: +; UNROLL: %index = phi i32 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; UNROLL: %vec.ind = phi <2 x i32> [ <i32 0, i32 1>, %vector.ph ], [ %step.add1, %vector.body ] +; UNROLL: %step.add = add <2 x i32> %vec.ind, <i32 2, i32 2> +; UNROLL: %step.add1 = add <2 x i32> %vec.ind, <i32 4, i32 4> +; UNROLL: %index.next = add i32 %index, 4 +; UNROLL: %[[CMP:.*]] = icmp eq i32 %index.next +; UNROLL: br i1 %[[CMP]] +define void @veciv(i32* nocapture %a, i32 %start, i32 %k) { +for.body.preheader: + br label %for.body + +for.body: + %indvars.iv = phi i32 [ %indvars.iv.next, %for.body ], [ 0, %for.body.preheader ] + %arrayidx = getelementptr inbounds i32, i32* %a, i32 %indvars.iv + store i32 %indvars.iv, i32* %arrayidx, align 4 + %indvars.iv.next = add nuw nsw i32 %indvars.iv, 1 + %exitcond = icmp eq i32 %indvars.iv.next, %k + br i1 %exitcond, label %exit, label %for.body + +exit: + ret void +} + +; IND-LABEL: trunciv +; IND: vector.body: +; IND: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; IND: %[[VECIND:.*]] = phi <2 x i32> [ <i32 0, i32 1>, %vector.ph ], [ %[[STEPADD:.*]], %vector.body ] +; IND: %[[STEPADD]] = add <2 x i32> %[[VECIND]], <i32 2, i32 2> +; IND: %index.next = add i64 %index, 2 +; IND: %[[CMP:.*]] = icmp eq i64 %index.next +; IND: br i1 %[[CMP]] +define void @trunciv(i32* nocapture %a, i32 %start, i64 %k) { +for.body.preheader: + br label %for.body + +for.body: + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %for.body.preheader ] + %trunc.iv = trunc i64 %indvars.iv to i32 + %arrayidx = getelementptr inbounds i32, i32* %a, i32 %trunc.iv + store i32 %trunc.iv, i32* %arrayidx, align 4 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1 + %exitcond = icmp eq i64 %indvars.iv.next, %k + br i1 %exitcond, label %exit, label %for.body + +exit: + ret void +} + +; IND-LABEL: nonprimary +; IND-LABEL: vector.ph +; IND: %[[INSERT:.*]] = insertelement <2 x i32> undef, i32 %i, i32 0 +; IND: %[[SPLAT:.*]] = shufflevector <2 x i32> %[[INSERT]], <2 x i32> undef, <2 x i32> zeroinitializer +; IND: %[[START:.*]] = add <2 x i32> %[[SPLAT]], <i32 0, i32 42> +; IND-LABEL: vector.body: +; IND: %index = phi i32 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; IND: %vec.ind = phi <2 x i32> [ %[[START]], %vector.ph ], [ %step.add, %vector.body ] +; IND: %step.add = add <2 x i32> %vec.ind, <i32 84, i32 84> +; IND: %index.next = add i32 %index, 2 +; IND: %[[CMP:.*]] = icmp eq i32 %index.next +; IND: br i1 %[[CMP]] +; UNROLL-LABEL: nonprimary +; UNROLL-LABEL: vector.ph +; UNROLL: %[[INSERT:.*]] = insertelement <2 x i32> undef, i32 %i, i32 0 +; UNROLL: %[[SPLAT:.*]] = shufflevector <2 x i32> %[[INSERT]], <2 x i32> undef, <2 x i32> zeroinitializer +; UNROLL: %[[START:.*]] = add <2 x i32> %[[SPLAT]], <i32 0, i32 42> +; UNROLL-LABEL: vector.body: +; UNROLL: %index = phi i32 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; UNROLL: %vec.ind = phi <2 x i32> [ %[[START]], %vector.ph ], [ %step.add1, %vector.body ] +; UNROLL: %step.add = add <2 x i32> %vec.ind, <i32 84, i32 84> +; UNROLL: %step.add1 = add <2 x i32> %vec.ind, <i32 168, i32 168> +; UNROLL: %index.next = add i32 %index, 4 +; UNROLL: %[[CMP:.*]] = icmp eq i32 %index.next +; UNROLL: br i1 %[[CMP]] +define void @nonprimary(i32* nocapture %a, i32 %start, i32 %i, i32 %k) { +for.body.preheader: + br label %for.body + +for.body: + %indvars.iv = phi i32 [ %indvars.iv.next, %for.body ], [ %i, %for.body.preheader ] + %arrayidx = getelementptr inbounds i32, i32* %a, i32 %indvars.iv + store i32 %indvars.iv, i32* %arrayidx, align 4 + %indvars.iv.next = add nuw nsw i32 %indvars.iv, 42 + %exitcond = icmp eq i32 %indvars.iv.next, %k + br i1 %exitcond, label %exit, label %for.body + +exit: + ret void +} diff --git a/test/Transforms/LoopVectorize/induction_plus.ll b/test/Transforms/LoopVectorize/induction_plus.ll index 7c4c8f2edcbf0..5e96d4196cae7 100644 --- a/test/Transforms/LoopVectorize/induction_plus.ll +++ b/test/Transforms/LoopVectorize/induction_plus.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -loop-vectorize -force-vector-interleave=1 -force-vector-width=4 -instcombine -S | FileCheck %s +; RUN: opt < %s -loop-vectorize -force-vector-interleave=1 -force-vector-width=4 -S | FileCheck %s target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.8.0" @@ -6,8 +6,11 @@ target triple = "x86_64-apple-macosx10.8.0" @array = common global [1024 x i32] zeroinitializer, align 16 ;CHECK-LABEL: @array_at_plus_one( -;CHECK: add i64 %index, 12 -;CHECK: trunc i64 +;CHECK: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +;CHECK: %vec.ind = phi <4 x i64> [ <i64 0, i64 1, i64 2, i64 3>, %vector.ph ], [ %step.add, %vector.body ] +;CHECK: %vec.ind1 = phi <4 x i32> [ <i32 0, i32 1, i32 2, i32 3>, %vector.ph ], [ %step.add2, %vector.body ] +;CHECK: add <4 x i64> %vec.ind, <i64 4, i64 4, i64 4, i64 4> +;CHECK: add nsw <4 x i64> %vec.ind, <i64 12, i64 12, i64 12, i64 12> ;CHECK: ret i32 define i32 @array_at_plus_one(i32 %n) nounwind uwtable ssp { %1 = icmp sgt i32 %n, 0 diff --git a/test/Transforms/LoopVectorize/interleaved-accesses-pred-stores.ll b/test/Transforms/LoopVectorize/interleaved-accesses-pred-stores.ll new file mode 100644 index 0000000000000..9ee6e6d529ad0 --- /dev/null +++ b/test/Transforms/LoopVectorize/interleaved-accesses-pred-stores.ll @@ -0,0 +1,164 @@ +; RUN: opt -S -loop-vectorize -instcombine -force-vector-width=2 -force-vector-interleave=1 -enable-interleaved-mem-accesses -vectorize-num-stores-pred=1 -enable-cond-stores-vec < %s | FileCheck %s + +target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" +%pair = type { i64, i64 } + +; Ensure that we vectorize the interleaved load group even though the loop +; contains a conditional store. The store group contains gaps and is not +; vectorized. +; +; CHECK-LABEL: @interleaved_with_cond_store_0( +; +; CHECK: min.iters.checked +; CHECK: %n.mod.vf = and i64 %[[N:.+]], 1 +; CHECK: %[[IsZero:[a-zA-Z0-9]+]] = icmp eq i64 %n.mod.vf, 0 +; CHECK: %[[R:.+]] = select i1 %[[IsZero]], i64 2, i64 %n.mod.vf +; CHECK: %n.vec = sub i64 %[[N]], %[[R]] +; +; CHECK: vector.body: +; CHECK: %wide.vec = load <4 x i64>, <4 x i64>* %{{.*}} +; CHECK: %strided.vec = shufflevector <4 x i64> %wide.vec, <4 x i64> undef, <2 x i32> <i32 0, i32 2> +; +; CHECK: pred.store.if +; CHECK: %[[X1:.+]] = extractelement <4 x i64> %wide.vec, i32 0 +; CHECK: store i64 %[[X1]], {{.*}} +; +; CHECK: pred.store.if +; CHECK: %[[X2:.+]] = extractelement <4 x i64> %wide.vec, i32 2 +; CHECK: store i64 %[[X2]], {{.*}} + +define void @interleaved_with_cond_store_0(%pair *%p, i64 %x, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %if.merge ], [ 0, %entry ] + %p.1 = getelementptr inbounds %pair, %pair* %p, i64 %i, i32 1 + %0 = load i64, i64* %p.1, align 8 + %1 = icmp eq i64 %0, %x + br i1 %1, label %if.then, label %if.merge + +if.then: + store i64 %0, i64* %p.1, align 8 + br label %if.merge + +if.merge: + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + ret void +} + +; Ensure that we don't form a single interleaved group for the two loads. The +; conditional store prevents the second load from being hoisted. The two load +; groups are separately vectorized. The store group contains gaps and is not +; vectorized. +; +; CHECK-LABEL: @interleaved_with_cond_store_1( +; +; CHECK: min.iters.checked +; CHECK: %n.mod.vf = and i64 %[[N:.+]], 1 +; CHECK: %[[IsZero:[a-zA-Z0-9]+]] = icmp eq i64 %n.mod.vf, 0 +; CHECK: %[[R:.+]] = select i1 %[[IsZero]], i64 2, i64 %n.mod.vf +; CHECK: %n.vec = sub i64 %[[N]], %[[R]] +; +; CHECK: vector.body: +; CHECK: %[[L1:.+]] = load <4 x i64>, <4 x i64>* %{{.*}} +; CHECK: %strided.vec = shufflevector <4 x i64> %[[L1]], <4 x i64> undef, <2 x i32> <i32 0, i32 2> +; +; CHECK: pred.store.if +; CHECK: %[[X1:.+]] = extractelement <4 x i64> %wide.vec, i32 0 +; CHECK: store i64 %[[X1]], {{.*}} +; +; CHECK: pred.store.if +; CHECK: %[[X2:.+]] = extractelement <4 x i64> %wide.vec, i32 2 +; CHECK: store i64 %[[X2]], {{.*}} +; +; CHECK: pred.store.continue +; CHECK: %[[L2:.+]] = load <4 x i64>, <4 x i64>* {{.*}} +; CHECK: %[[X3:.+]] = extractelement <4 x i64> %[[L2]], i32 0 +; CHECK: store i64 %[[X3]], {{.*}} +; CHECK: %[[X4:.+]] = extractelement <4 x i64> %[[L2]], i32 2 +; CHECK: store i64 %[[X4]], {{.*}} + +define void @interleaved_with_cond_store_1(%pair *%p, i64 %x, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %if.merge ], [ 0, %entry ] + %p.0 = getelementptr inbounds %pair, %pair* %p, i64 %i, i32 0 + %p.1 = getelementptr inbounds %pair, %pair* %p, i64 %i, i32 1 + %0 = load i64, i64* %p.1, align 8 + %1 = icmp eq i64 %0, %x + br i1 %1, label %if.then, label %if.merge + +if.then: + store i64 %0, i64* %p.0, align 8 + br label %if.merge + +if.merge: + %2 = load i64, i64* %p.0, align 8 + store i64 %2, i64 *%p.1, align 8 + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + ret void +} + +; Ensure that we don't create a single interleaved group for the two stores. +; The second store is conditional and we can't sink the first store inside the +; predicated block. The load group is vectorized, and the store groups contain +; gaps and are not vectorized. +; +; CHECK-LABEL: @interleaved_with_cond_store_2( +; +; CHECK: min.iters.checked +; CHECK: %n.mod.vf = and i64 %[[N:.+]], 1 +; CHECK: %[[IsZero:[a-zA-Z0-9]+]] = icmp eq i64 %n.mod.vf, 0 +; CHECK: %[[R:.+]] = select i1 %[[IsZero]], i64 2, i64 %n.mod.vf +; CHECK: %n.vec = sub i64 %[[N]], %[[R]] +; +; CHECK: vector.body: +; CHECK: %[[L1:.+]] = load <4 x i64>, <4 x i64>* %{{.*}} +; CHECK: %strided.vec = shufflevector <4 x i64> %[[L1]], <4 x i64> undef, <2 x i32> <i32 0, i32 2> +; CHECK: store i64 %x, {{.*}} +; CHECK: store i64 %x, {{.*}} +; +; CHECK: pred.store.if +; CHECK: %[[X1:.+]] = extractelement <4 x i64> %wide.vec, i32 0 +; CHECK: store i64 %[[X1]], {{.*}} +; +; CHECK: pred.store.if +; CHECK: %[[X2:.+]] = extractelement <4 x i64> %wide.vec, i32 2 +; CHECK: store i64 %[[X2]], {{.*}} + +define void @interleaved_with_cond_store_2(%pair *%p, i64 %x, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %if.merge ], [ 0, %entry ] + %p.0 = getelementptr inbounds %pair, %pair* %p, i64 %i, i32 0 + %p.1 = getelementptr inbounds %pair, %pair* %p, i64 %i, i32 1 + %0 = load i64, i64* %p.1, align 8 + store i64 %x, i64* %p.0, align 8 + %1 = icmp eq i64 %0, %x + br i1 %1, label %if.then, label %if.merge + +if.then: + store i64 %0, i64* %p.1, align 8 + br label %if.merge + +if.merge: + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + ret void +} diff --git a/test/Transforms/LoopVectorize/interleaved-accesses.ll b/test/Transforms/LoopVectorize/interleaved-accesses.ll index 54ce3e29293a2..868c3a2cdabf7 100644 --- a/test/Transforms/LoopVectorize/interleaved-accesses.ll +++ b/test/Transforms/LoopVectorize/interleaved-accesses.ll @@ -284,18 +284,24 @@ for.body: ; preds = %for.body, %entry } ; Check vectorization on an interleaved load group of factor 2 with 1 gap -; (missing the load of odd elements). +; (missing the load of odd elements). Because the vectorized loop would +; speculatively access memory out-of-bounds, we must execute at least one +; iteration of the scalar loop. -; void even_load(int *A, int *B) { +; void even_load_static_tc(int *A, int *B) { ; for (unsigned i = 0; i < 1024; i+=2) ; B[i/2] = A[i] * 2; ; } -; CHECK-LABEL: @even_load( -; CHECK-NOT: %wide.vec = load <8 x i32>, <8 x i32>* %{{.*}}, align 4 -; CHECK-NOT: %strided.vec = shufflevector <8 x i32> %wide.vec, <8 x i32> undef, <4 x i32> <i32 0, i32 2, i32 4, i32 6> +; CHECK-LABEL: @even_load_static_tc( +; CHECK: vector.body: +; CHECK: %wide.vec = load <8 x i32>, <8 x i32>* %{{.*}}, align 4 +; CHECK: %strided.vec = shufflevector <8 x i32> %wide.vec, <8 x i32> undef, <4 x i32> <i32 0, i32 2, i32 4, i32 6> +; CHECK: icmp eq i64 %index.next, 508 +; CHECK: middle.block: +; CHECK: br i1 false, label %for.cond.cleanup, label %scalar.ph -define void @even_load(i32* noalias nocapture readonly %A, i32* noalias nocapture %B) { +define void @even_load_static_tc(i32* noalias nocapture readonly %A, i32* noalias nocapture %B) { entry: br label %for.body @@ -315,6 +321,93 @@ for.body: ; preds = %for.body, %entry br i1 %cmp, label %for.body, label %for.cond.cleanup } +; Check vectorization on an interleaved load group of factor 2 with 1 gap +; (missing the load of odd elements). Because the vectorized loop would +; speculatively access memory out-of-bounds, we must execute at least one +; iteration of the scalar loop. + +; void even_load_dynamic_tc(int *A, int *B, unsigned N) { +; for (unsigned i = 0; i < N; i+=2) +; B[i/2] = A[i] * 2; +; } + +; CHECK-LABEL: @even_load_dynamic_tc( +; CHECK: min.iters.checked: +; CHECK: %n.mod.vf = and i64 %[[N:[a-zA-Z0-9]+]], 3 +; CHECK: %[[IsZero:[a-zA-Z0-9]+]] = icmp eq i64 %n.mod.vf, 0 +; CHECK: %[[R:[a-zA-Z0-9]+]] = select i1 %[[IsZero]], i64 4, i64 %n.mod.vf +; CHECK: %n.vec = sub i64 %[[N]], %[[R]] +; CHECK: vector.body: +; CHECK: %wide.vec = load <8 x i32>, <8 x i32>* %{{.*}}, align 4 +; CHECK: %strided.vec = shufflevector <8 x i32> %wide.vec, <8 x i32> undef, <4 x i32> <i32 0, i32 2, i32 4, i32 6> +; CHECK: icmp eq i64 %index.next, %n.vec +; CHECK: middle.block: +; CHECK: br i1 false, label %for.cond.cleanup, label %scalar.ph + +define void @even_load_dynamic_tc(i32* noalias nocapture readonly %A, i32* noalias nocapture %B, i64 %N) { +entry: + br label %for.body + +for.cond.cleanup: ; preds = %for.body + ret void + +for.body: ; preds = %for.body, %entry + %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv + %tmp = load i32, i32* %arrayidx, align 4 + %mul = shl nsw i32 %tmp, 1 + %tmp1 = lshr exact i64 %indvars.iv, 1 + %arrayidx2 = getelementptr inbounds i32, i32* %B, i64 %tmp1 + store i32 %mul, i32* %arrayidx2, align 4 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 2 + %cmp = icmp ult i64 %indvars.iv.next, %N + br i1 %cmp, label %for.body, label %for.cond.cleanup +} + +; Check vectorization on a reverse interleaved load group of factor 2 with 1 +; gap and a reverse interleaved store group of factor 2. The interleaved load +; group should be removed since it has a gap and is reverse. + +; struct pair { +; int x; +; int y; +; }; +; +; void load_gap_reverse(struct pair *P1, struct pair *P2, int X) { +; for (int i = 1023; i >= 0; i--) { +; int a = X + i; +; int b = A[i].y - i; +; B[i].x = a; +; B[i].y = b; +; } +; } + +; CHECK-LABEL: @load_gap_reverse( +; CHECK-NOT: %wide.vec = load <8 x i64>, <8 x i64>* %{{.*}}, align 8 +; CHECK-NOT: %strided.vec = shufflevector <8 x i64> %wide.vec, <8 x i64> undef, <4 x i32> <i32 0, i32 2, i32 4, i32 6> + +%pair = type { i64, i64 } +define void @load_gap_reverse(%pair* noalias nocapture readonly %P1, %pair* noalias nocapture readonly %P2, i64 %X) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ 1023, %entry ], [ %i.next, %for.body ] + %0 = add nsw i64 %X, %i + %1 = getelementptr inbounds %pair, %pair* %P1, i64 %i, i32 0 + %2 = getelementptr inbounds %pair, %pair* %P2, i64 %i, i32 1 + %3 = load i64, i64* %2, align 8 + %4 = sub nsw i64 %3, %i + store i64 %0, i64* %1, align 8 + store i64 %4, i64* %2, align 8 + %i.next = add nsw i64 %i, -1 + %cond = icmp sgt i64 %i, 0 + br i1 %cond, label %for.body, label %for.exit + +for.exit: + ret void +} + ; Check vectorization on interleaved access groups identified from mixed ; loads/stores. ; void mixed_load2_store2(int *A, int *B) { @@ -462,4 +555,309 @@ for.body: ; preds = %for.body, %entry br i1 %exitcond, label %for.cond.cleanup, label %for.body } +; Check vectorization of interleaved access groups in the presence of +; dependences (PR27626). The following tests check that we don't reorder +; dependent loads and stores when generating code for interleaved access +; groups. Stores should be scalarized because the required code motion would +; break dependences, and the remaining interleaved load groups should have +; gaps. + +; PR27626_0: Ensure a strided store is not moved after a dependent (zero +; distance) strided load. + +; void PR27626_0(struct pair *p, int z, int n) { +; for (int i = 0; i < n; i++) { +; p[i].x = z; +; p[i].y = p[i].x; +; } +; } + +; CHECK-LABEL: @PR27626_0( +; CHECK: min.iters.checked: +; CHECK: %n.mod.vf = and i64 %[[N:.+]], 3 +; CHECK: %[[IsZero:[a-zA-Z0-9]+]] = icmp eq i64 %n.mod.vf, 0 +; CHECK: %[[R:[a-zA-Z0-9]+]] = select i1 %[[IsZero]], i64 4, i64 %n.mod.vf +; CHECK: %n.vec = sub i64 %[[N]], %[[R]] +; CHECK: vector.body: +; CHECK: %[[L1:.+]] = load <8 x i32>, <8 x i32>* {{.*}} +; CHECK: %[[X1:.+]] = extractelement <8 x i32> %[[L1]], i32 0 +; CHECK: store i32 %[[X1]], {{.*}} +; CHECK: %[[X2:.+]] = extractelement <8 x i32> %[[L1]], i32 2 +; CHECK: store i32 %[[X2]], {{.*}} +; CHECK: %[[X3:.+]] = extractelement <8 x i32> %[[L1]], i32 4 +; CHECK: store i32 %[[X3]], {{.*}} +; CHECK: %[[X4:.+]] = extractelement <8 x i32> %[[L1]], i32 6 +; CHECK: store i32 %[[X4]], {{.*}} + +%pair.i32 = type { i32, i32 } +define void @PR27626_0(%pair.i32 *%p, i32 %z, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %for.body ], [ 0, %entry ] + %p_i.x = getelementptr inbounds %pair.i32, %pair.i32* %p, i64 %i, i32 0 + %p_i.y = getelementptr inbounds %pair.i32, %pair.i32* %p, i64 %i, i32 1 + store i32 %z, i32* %p_i.x, align 4 + %0 = load i32, i32* %p_i.x, align 4 + store i32 %0, i32 *%p_i.y, align 4 + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + ret void +} + +; PR27626_1: Ensure a strided load is not moved before a dependent (zero +; distance) strided store. + +; void PR27626_1(struct pair *p, int n) { +; int s = 0; +; for (int i = 0; i < n; i++) { +; p[i].y = p[i].x; +; s += p[i].y +; } +; } + +; CHECK-LABEL: @PR27626_1( +; CHECK: min.iters.checked: +; CHECK: %n.mod.vf = and i64 %[[N:.+]], 3 +; CHECK: %[[IsZero:[a-zA-Z0-9]+]] = icmp eq i64 %n.mod.vf, 0 +; CHECK: %[[R:[a-zA-Z0-9]+]] = select i1 %[[IsZero]], i64 4, i64 %n.mod.vf +; CHECK: %n.vec = sub i64 %[[N]], %[[R]] +; CHECK: vector.body: +; CHECK: %[[Phi:.+]] = phi <4 x i32> [ zeroinitializer, %vector.ph ], [ {{.*}}, %vector.body ] +; CHECK: %[[L1:.+]] = load <8 x i32>, <8 x i32>* {{.*}} +; CHECK: %[[X1:.+]] = extractelement <8 x i32> %[[L1:.+]], i32 0 +; CHECK: store i32 %[[X1:.+]], {{.*}} +; CHECK: %[[X2:.+]] = extractelement <8 x i32> %[[L1:.+]], i32 2 +; CHECK: store i32 %[[X2:.+]], {{.*}} +; CHECK: %[[X3:.+]] = extractelement <8 x i32> %[[L1:.+]], i32 4 +; CHECK: store i32 %[[X3:.+]], {{.*}} +; CHECK: %[[X4:.+]] = extractelement <8 x i32> %[[L1:.+]], i32 6 +; CHECK: store i32 %[[X4:.+]], {{.*}} +; CHECK: %[[L2:.+]] = load <8 x i32>, <8 x i32>* {{.*}} +; CHECK: %[[S1:.+]] = shufflevector <8 x i32> %[[L2]], <8 x i32> undef, <4 x i32> <i32 0, i32 2, i32 4, i32 6> +; CHECK: add nsw <4 x i32> %[[S1]], %[[Phi]] + +define i32 @PR27626_1(%pair.i32 *%p, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %for.body ], [ 0, %entry ] + %s = phi i32 [ %2, %for.body ], [ 0, %entry ] + %p_i.x = getelementptr inbounds %pair.i32, %pair.i32* %p, i64 %i, i32 0 + %p_i.y = getelementptr inbounds %pair.i32, %pair.i32* %p, i64 %i, i32 1 + %0 = load i32, i32* %p_i.x, align 4 + store i32 %0, i32* %p_i.y, align 4 + %1 = load i32, i32* %p_i.y, align 4 + %2 = add nsw i32 %1, %s + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + %3 = phi i32 [ %2, %for.body ] + ret i32 %3 +} + +; PR27626_2: Ensure a strided store is not moved after a dependent (negative +; distance) strided load. + +; void PR27626_2(struct pair *p, int z, int n) { +; for (int i = 0; i < n; i++) { +; p[i].x = z; +; p[i].y = p[i - 1].x; +; } +; } + +; CHECK-LABEL: @PR27626_2( +; CHECK: min.iters.checked: +; CHECK: %n.mod.vf = and i64 %[[N:.+]], 3 +; CHECK: %[[IsZero:[a-zA-Z0-9]+]] = icmp eq i64 %n.mod.vf, 0 +; CHECK: %[[R:[a-zA-Z0-9]+]] = select i1 %[[IsZero]], i64 4, i64 %n.mod.vf +; CHECK: %n.vec = sub i64 %[[N]], %[[R]] +; CHECK: vector.body: +; CHECK: %[[L1:.+]] = load <8 x i32>, <8 x i32>* {{.*}} +; CHECK: %[[X1:.+]] = extractelement <8 x i32> %[[L1]], i32 0 +; CHECK: store i32 %[[X1]], {{.*}} +; CHECK: %[[X2:.+]] = extractelement <8 x i32> %[[L1]], i32 2 +; CHECK: store i32 %[[X2]], {{.*}} +; CHECK: %[[X3:.+]] = extractelement <8 x i32> %[[L1]], i32 4 +; CHECK: store i32 %[[X3]], {{.*}} +; CHECK: %[[X4:.+]] = extractelement <8 x i32> %[[L1]], i32 6 +; CHECK: store i32 %[[X4]], {{.*}} + +define void @PR27626_2(%pair.i32 *%p, i64 %n, i32 %z) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %for.body ], [ 0, %entry ] + %i_minus_1 = add nuw nsw i64 %i, -1 + %p_i.x = getelementptr inbounds %pair.i32, %pair.i32* %p, i64 %i, i32 0 + %p_i_minus_1.x = getelementptr inbounds %pair.i32, %pair.i32* %p, i64 %i_minus_1, i32 0 + %p_i.y = getelementptr inbounds %pair.i32, %pair.i32* %p, i64 %i, i32 1 + store i32 %z, i32* %p_i.x, align 4 + %0 = load i32, i32* %p_i_minus_1.x, align 4 + store i32 %0, i32 *%p_i.y, align 4 + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + ret void +} + +; PR27626_3: Ensure a strided load is not moved before a dependent (negative +; distance) strided store. + +; void PR27626_3(struct pair *p, int z, int n) { +; for (int i = 0; i < n; i++) { +; p[i + 1].y = p[i].x; +; s += p[i].y; +; } +; } + +; CHECK-LABEL: @PR27626_3( +; CHECK: min.iters.checked: +; CHECK: %n.mod.vf = and i64 %[[N:.+]], 3 +; CHECK: %[[IsZero:[a-zA-Z0-9]+]] = icmp eq i64 %n.mod.vf, 0 +; CHECK: %[[R:[a-zA-Z0-9]+]] = select i1 %[[IsZero]], i64 4, i64 %n.mod.vf +; CHECK: %n.vec = sub i64 %[[N]], %[[R]] +; CHECK: vector.body: +; CHECK: %[[Phi:.+]] = phi <4 x i32> [ zeroinitializer, %vector.ph ], [ {{.*}}, %vector.body ] +; CHECK: %[[L1:.+]] = load <8 x i32>, <8 x i32>* {{.*}} +; CHECK: %[[X1:.+]] = extractelement <8 x i32> %[[L1:.+]], i32 0 +; CHECK: store i32 %[[X1:.+]], {{.*}} +; CHECK: %[[X2:.+]] = extractelement <8 x i32> %[[L1:.+]], i32 2 +; CHECK: store i32 %[[X2:.+]], {{.*}} +; CHECK: %[[X3:.+]] = extractelement <8 x i32> %[[L1:.+]], i32 4 +; CHECK: store i32 %[[X3:.+]], {{.*}} +; CHECK: %[[X4:.+]] = extractelement <8 x i32> %[[L1:.+]], i32 6 +; CHECK: store i32 %[[X4:.+]], {{.*}} +; CHECK: %[[L2:.+]] = load <8 x i32>, <8 x i32>* {{.*}} +; CHECK: %[[S1:.+]] = shufflevector <8 x i32> %[[L2]], <8 x i32> undef, <4 x i32> <i32 0, i32 2, i32 4, i32 6> +; CHECK: add nsw <4 x i32> %[[S1]], %[[Phi]] + +define i32 @PR27626_3(%pair.i32 *%p, i64 %n, i32 %z) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %for.body ], [ 0, %entry ] + %s = phi i32 [ %2, %for.body ], [ 0, %entry ] + %i_plus_1 = add nuw nsw i64 %i, 1 + %p_i.x = getelementptr inbounds %pair.i32, %pair.i32* %p, i64 %i, i32 0 + %p_i.y = getelementptr inbounds %pair.i32, %pair.i32* %p, i64 %i, i32 1 + %p_i_plus_1.y = getelementptr inbounds %pair.i32, %pair.i32* %p, i64 %i_plus_1, i32 1 + %0 = load i32, i32* %p_i.x, align 4 + store i32 %0, i32* %p_i_plus_1.y, align 4 + %1 = load i32, i32* %p_i.y, align 4 + %2 = add nsw i32 %1, %s + %i.next = add nuw nsw i64 %i, 1 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + %3 = phi i32 [ %2, %for.body ] + ret i32 %3 +} + +; PR27626_4: Ensure we form an interleaved group for strided stores in the +; presence of a write-after-write dependence. We create a group for +; (2) and (3) while excluding (1). + +; void PR27626_4(int *a, int x, int y, int z, int n) { +; for (int i = 0; i < n; i += 2) { +; a[i] = x; // (1) +; a[i] = y; // (2) +; a[i + 1] = z; // (3) +; } +; } + +; CHECK-LABEL: @PR27626_4( +; CHECK: vector.ph: +; CHECK: %[[INS_Y:.+]] = insertelement <4 x i32> undef, i32 %y, i32 0 +; CHECK: %[[SPLAT_Y:.+]] = shufflevector <4 x i32> %[[INS_Y]], <4 x i32> undef, <4 x i32> zeroinitializer +; CHECK: %[[INS_Z:.+]] = insertelement <4 x i32> undef, i32 %z, i32 0 +; CHECK: %[[SPLAT_Z:.+]] = shufflevector <4 x i32> %[[INS_Z]], <4 x i32> undef, <4 x i32> zeroinitializer +; CHECK: vector.body: +; CHECK: store i32 %x, {{.*}} +; CHECK: store i32 %x, {{.*}} +; CHECK: store i32 %x, {{.*}} +; CHECK: store i32 %x, {{.*}} +; CHECK: %[[VEC:.+]] = shufflevector <4 x i32> %[[SPLAT_Y]], <4 x i32> %[[SPLAT_Z]], <8 x i32> <i32 0, i32 4, i32 1, i32 5, i32 2, i32 6, i32 3, i32 7> +; CHECK: store <8 x i32> %[[VEC]], {{.*}} + +define void @PR27626_4(i32 *%a, i32 %x, i32 %y, i32 %z, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %for.body ], [ 0, %entry ] + %i_plus_1 = add i64 %i, 1 + %a_i = getelementptr inbounds i32, i32* %a, i64 %i + %a_i_plus_1 = getelementptr inbounds i32, i32* %a, i64 %i_plus_1 + store i32 %x, i32* %a_i, align 4 + store i32 %y, i32* %a_i, align 4 + store i32 %z, i32* %a_i_plus_1, align 4 + %i.next = add nuw nsw i64 %i, 2 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + ret void +} + +; PR27626_5: Ensure we do not form an interleaved group for strided stores in +; the presence of a write-after-write dependence. + +; void PR27626_5(int *a, int x, int y, int z, int n) { +; for (int i = 3; i < n; i += 2) { +; a[i - 1] = x; +; a[i - 3] = y; +; a[i] = z; +; } +; } + +; CHECK-LABEL: @PR27626_5( +; CHECK: vector.body: +; CHECK: store i32 %x, {{.*}} +; CHECK: store i32 %x, {{.*}} +; CHECK: store i32 %x, {{.*}} +; CHECK: store i32 %x, {{.*}} +; CHECK: store i32 %y, {{.*}} +; CHECK: store i32 %y, {{.*}} +; CHECK: store i32 %y, {{.*}} +; CHECK: store i32 %y, {{.*}} +; CHECK: store i32 %z, {{.*}} +; CHECK: store i32 %z, {{.*}} +; CHECK: store i32 %z, {{.*}} +; CHECK: store i32 %z, {{.*}} + +define void @PR27626_5(i32 *%a, i32 %x, i32 %y, i32 %z, i64 %n) { +entry: + br label %for.body + +for.body: + %i = phi i64 [ %i.next, %for.body ], [ 3, %entry ] + %i_minus_1 = sub i64 %i, 1 + %i_minus_3 = sub i64 %i_minus_1, 2 + %a_i = getelementptr inbounds i32, i32* %a, i64 %i + %a_i_minus_1 = getelementptr inbounds i32, i32* %a, i64 %i_minus_1 + %a_i_minus_3 = getelementptr inbounds i32, i32* %a, i64 %i_minus_3 + store i32 %x, i32* %a_i_minus_1, align 4 + store i32 %y, i32* %a_i_minus_3, align 4 + store i32 %z, i32* %a_i, align 4 + %i.next = add nuw nsw i64 %i, 2 + %cond = icmp slt i64 %i.next, %n + br i1 %cond, label %for.body, label %for.end + +for.end: + ret void +} + attributes #0 = { "unsafe-fp-math"="true" } diff --git a/test/Transforms/LoopVectorize/iv_outside_user.ll b/test/Transforms/LoopVectorize/iv_outside_user.ll new file mode 100644 index 0000000000000..d536d1023f413 --- /dev/null +++ b/test/Transforms/LoopVectorize/iv_outside_user.ll @@ -0,0 +1,135 @@ +; RUN: opt -S -loop-vectorize -force-vector-interleave=1 -force-vector-width=2 < %s | FileCheck %s + +; CHECK-LABEL: @postinc +; CHECK-LABEL: scalar.ph: +; CHECK: %bc.resume.val = phi i32 [ %n.vec, %middle.block ], [ 0, %entry ] +; CHECK-LABEL: for.end: +; CHECK: %[[RET:.*]] = phi i32 [ {{.*}}, %for.body ], [ %n.vec, %middle.block ] +; CHECK: ret i32 %[[RET]] +define i32 @postinc(i32 %k) { +entry: + br label %for.body + +for.body: + %inc.phi = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %inc = add nsw i32 %inc.phi, 1 + %cmp = icmp eq i32 %inc, %k + br i1 %cmp, label %for.end, label %for.body + +for.end: + ret i32 %inc +} + +; CHECK-LABEL: @preinc +; CHECK-LABEL: middle.block: +; CHECK: %[[v3:.+]] = sub i32 %n.vec, 1 +; CHECK: %ind.escape = add i32 0, %[[v3]] +; CHECK-LABEL: scalar.ph: +; CHECK: %bc.resume.val = phi i32 [ %n.vec, %middle.block ], [ 0, %entry ] +; CHECK-LABEL: for.end: +; CHECK: %[[RET:.*]] = phi i32 [ {{.*}}, %for.body ], [ %ind.escape, %middle.block ] +; CHECK: ret i32 %[[RET]] +define i32 @preinc(i32 %k) { +entry: + br label %for.body + +for.body: + %inc.phi = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %inc = add nsw i32 %inc.phi, 1 + %cmp = icmp eq i32 %inc, %k + br i1 %cmp, label %for.end, label %for.body + +for.end: + ret i32 %inc.phi +} + +; CHECK-LABEL: @constpre +; CHECK-LABEL: for.end: +; CHECK: %[[RET:.*]] = phi i32 [ {{.*}}, %for.body ], [ 2, %middle.block ] +; CHECK: ret i32 %[[RET]] +define i32 @constpre() { +entry: + br label %for.body + +for.body: + %inc.phi = phi i32 [ 32, %entry ], [ %inc, %for.body ] + %inc = sub nsw i32 %inc.phi, 2 + %cmp = icmp eq i32 %inc, 0 + br i1 %cmp, label %for.end, label %for.body + +for.end: + ret i32 %inc.phi +} + +; CHECK-LABEL: @geppre +; CHECK-LABEL: middle.block: +; CHECK: %ind.escape = getelementptr i32, i32* %ptr, i64 124 +; CHECK-LABEL: for.end: +; CHECK: %[[RET:.*]] = phi i32* [ {{.*}}, %for.body ], [ %ind.escape, %middle.block ] +; CHECK: ret i32* %[[RET]] +define i32* @geppre(i32* %ptr) { +entry: + br label %for.body + +for.body: + %inc.phi = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %ptr.phi = phi i32* [ %ptr, %entry ], [ %inc.ptr, %for.body ] + %inc = add nsw i32 %inc.phi, 1 + %inc.ptr = getelementptr i32, i32* %ptr.phi, i32 4 + %cmp = icmp eq i32 %inc, 32 + br i1 %cmp, label %for.end, label %for.body + +for.end: + ret i32* %ptr.phi +} + +; CHECK-LABEL: @both +; CHECK-LABEL: middle.block: +; CHECK: %[[END:.*]] = sub i64 %n.vec, 1 +; CHECK: %ind.escape = getelementptr i32, i32* %base, i64 %[[END]] +; CHECK-LABEL: for.end: +; CHECK: %[[RET:.*]] = phi i32* [ %inc.lag1, %for.body ], [ %ind.escape, %middle.block ] +; CHECK: ret i32* %[[RET]] + +define i32* @both(i32 %k) { +entry: + %base = getelementptr inbounds i32, i32* undef, i64 1 + br label %for.body + +for.body: + %inc.phi = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %inc.lag1 = phi i32* [ %base, %entry ], [ %tmp, %for.body] + %inc.lag2 = phi i32* [ undef, %entry ], [ %inc.lag1, %for.body] + %tmp = getelementptr inbounds i32, i32* %inc.lag1, i64 1 + %inc = add nsw i32 %inc.phi, 1 + %cmp = icmp eq i32 %inc, %k + br i1 %cmp, label %for.end, label %for.body + +for.end: + ret i32* %inc.lag1 +} + +; CHECK-LABEL: @multiphi +; CHECK-LABEL: scalar.ph: +; CHECK: %bc.resume.val = phi i32 [ %n.vec, %middle.block ], [ 0, %entry ] +; CHECK-LABEL: for.end: +; CHECK: %phi = phi i32 [ {{.*}}, %for.body ], [ %n.vec, %middle.block ] +; CHECK: %phi2 = phi i32 [ {{.*}}, %for.body ], [ %n.vec, %middle.block ] +; CHECK: store i32 %phi2, i32* %p +; CHECK: ret i32 %phi +define i32 @multiphi(i32 %k, i32* %p) { +entry: + br label %for.body + +for.body: + %inc.phi = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %inc = add nsw i32 %inc.phi, 1 + %cmp = icmp eq i32 %inc, %k + br i1 %cmp, label %for.end, label %for.body + +for.end: + %phi = phi i32 [ %inc, %for.body ] + %phi2 = phi i32 [ %inc, %for.body ] + store i32 %phi2, i32* %p + ret i32 %phi +} diff --git a/test/Transforms/LoopVectorize/multiple-strides-vectorization.ll b/test/Transforms/LoopVectorize/multiple-strides-vectorization.ll new file mode 100644 index 0000000000000..adadbfc9e1dcb --- /dev/null +++ b/test/Transforms/LoopVectorize/multiple-strides-vectorization.ll @@ -0,0 +1,65 @@ +; RUN: opt -loop-vectorize -force-vector-width=4 -S < %s | FileCheck %s + +; This is the test case from PR26314. +; When we were retrying dependence checking with memchecks only, +; the loop-invariant access in the inner loop was incorrectly determined to be wrapping +; because it was not strided in the inner loop. +; Improved wrapping detection allows vectorization in the following case. + +; #define Z 32 +; typedef struct s { +; int v1[Z]; +; int v2[Z]; +; int v3[Z][Z]; +; } s; +; +; void slow_function (s* const obj) { +; for (int j=0; j<Z; j++) { +; for (int k=0; k<Z; k++) { +; int x = obj->v1[k] + obj->v2[j]; +; obj->v3[j][k] += x; +; } +; } +; } + +; CHECK-LABEL: Test +; CHECK: <4 x i64> +; CHECK: <4 x i32>, <4 x i32> +; CHECK: llvm.loop.vectorize.width + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.s = type { [32 x i32], [32 x i32], [32 x [32 x i32]] } + +define void @Test(%struct.s* nocapture %obj) #0 { + br label %.outer.preheader + + +.outer.preheader: + %i = phi i64 [ 0, %0 ], [ %i.next, %.outer ] + %1 = getelementptr inbounds %struct.s, %struct.s* %obj, i64 0, i32 1, i64 %i + br label %.inner + +.exit: + ret void + +.outer: + %i.next = add nuw nsw i64 %i, 1 + %exitcond.outer = icmp eq i64 %i.next, 32 + br i1 %exitcond.outer, label %.exit, label %.outer.preheader + +.inner: + %j = phi i64 [ 0, %.outer.preheader ], [ %j.next, %.inner ] + %2 = getelementptr inbounds %struct.s, %struct.s* %obj, i64 0, i32 0, i64 %j + %3 = load i32, i32* %2 + %4 = load i32, i32* %1 + %5 = add nsw i32 %4, %3 + %6 = getelementptr inbounds %struct.s, %struct.s* %obj, i64 0, i32 2, i64 %i, i64 %j + %7 = load i32, i32* %6 + %8 = add nsw i32 %5, %7 + store i32 %8, i32* %6 + %j.next = add nuw nsw i64 %j, 1 + %exitcond.inner = icmp eq i64 %j.next, 32 + br i1 %exitcond.inner, label %.outer, label %.inner +} diff --git a/test/Transforms/LoopVectorize/no_array_bounds.ll b/test/Transforms/LoopVectorize/no_array_bounds.ll index 13cec71fc4552..44412bce27fbb 100644 --- a/test/Transforms/LoopVectorize/no_array_bounds.ll +++ b/test/Transforms/LoopVectorize/no_array_bounds.ll @@ -72,11 +72,10 @@ attributes #0 = { nounwind } !llvm.module.flags = !{!7, !8} !llvm.ident = !{!9} -!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5.0", isOptimized: true, emissionKind: 2, file: !1, enums: !2, retainedTypes: !2, subprograms: !3, globals: !2, imports: !2) +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5.0", isOptimized: true, emissionKind: LineTablesOnly, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2) !1 = !DIFile(filename: "no_array_bounds.cpp", directory: ".") !2 = !{} -!3 = !{!4} -!4 = distinct !DISubprogram(name: "test", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, scopeLine: 2, file: !1, scope: !5, type: !6, variables: !2) +!4 = distinct !DISubprogram(name: "test", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, scopeLine: 2, file: !1, scope: !5, type: !6, variables: !2) !5 = !DIFile(filename: "no_array_bounds.cpp", directory: ".") !6 = !DISubroutineType(types: !2) !7 = !{i32 2, !"Dwarf Version", i32 2} diff --git a/test/Transforms/LoopVectorize/no_outside_user.ll b/test/Transforms/LoopVectorize/no_outside_user.ll index 2683b42dc7172..39363f6034c4e 100644 --- a/test/Transforms/LoopVectorize/no_outside_user.ll +++ b/test/Transforms/LoopVectorize/no_outside_user.ll @@ -1,7 +1,6 @@ ; RUN: opt -S -loop-vectorize -force-vector-interleave=1 -force-vector-width=2 < %s 2>&1 | FileCheck %s ; CHECK: remark: {{.*}}: loop not vectorized: value could not be identified as an induction or reduction variable -; CHECK: remark: {{.*}}: loop not vectorized: use of induction value outside of the loop is not handled by vectorizer target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" @@ -41,34 +40,3 @@ f1.exit.loopexit: %.lcssa = phi i32 [ %tmp17, %bb16 ] ret i32 %.lcssa } - -; Don't vectorize this loop. Its phi node (induction variable) has an outside -; loop user. We currently don't handle this case. -; PR17179 - -; CHECK-LABEL: @test2( -; CHECK-NOT: <2 x - -@x1 = common global i32 0, align 4 -@x2 = common global i32 0, align 4 -@x0 = common global i32 0, align 4 - -define i32 @test2() { -entry: - store i32 0, i32* @x1, align 4 - %0 = load i32, i32* @x0, align 4 - br label %for.cond1.preheader - -for.cond1.preheader: - %inc7 = phi i32 [ 0, %entry ], [ %inc, %for.cond1.preheader ] - %inc = add nsw i32 %inc7, 1 - %cmp = icmp eq i32 %inc, 52 - br i1 %cmp, label %for.end5, label %for.cond1.preheader - -for.end5: - %inc7.lcssa = phi i32 [ %inc7, %for.cond1.preheader ] - %xor = xor i32 %inc7.lcssa, %0 - store i32 52, i32* @x1, align 4 - store i32 1, i32* @x2, align 4 - ret i32 %xor -} diff --git a/test/Transforms/LoopVectorize/no_switch.ll b/test/Transforms/LoopVectorize/no_switch.ll index 842d262d31920..181304a409ee4 100644 --- a/test/Transforms/LoopVectorize/no_switch.ll +++ b/test/Transforms/LoopVectorize/no_switch.ll @@ -67,11 +67,10 @@ attributes #0 = { nounwind } !llvm.module.flags = !{!7, !8} !llvm.ident = !{!9} -!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5.0", isOptimized: true, runtimeVersion: 6, emissionKind: 2, file: !1, enums: !2, retainedTypes: !2, subprograms: !3, globals: !2, imports: !2) +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5.0", isOptimized: true, runtimeVersion: 6, emissionKind: LineTablesOnly, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2) !1 = !DIFile(filename: "source.cpp", directory: ".") !2 = !{} -!3 = !{!4} -!4 = distinct !DISubprogram(name: "test_switch", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, scopeLine: 1, file: !1, scope: !5, type: !6, variables: !2) +!4 = distinct !DISubprogram(name: "test_switch", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, scopeLine: 1, file: !1, scope: !5, type: !6, variables: !2) !5 = !DIFile(filename: "source.cpp", directory: ".") !6 = !DISubroutineType(types: !2) !7 = !{i32 2, !"Dwarf Version", i32 2} diff --git a/test/Transforms/LoopVectorize/noalias-md-licm.ll b/test/Transforms/LoopVectorize/noalias-md-licm.ll new file mode 100644 index 0000000000000..233d530dc1029 --- /dev/null +++ b/test/Transforms/LoopVectorize/noalias-md-licm.ll @@ -0,0 +1,59 @@ +; RUN: opt -basicaa -scoped-noalias -loop-vectorize -licm -force-vector-width=2 \ +; RUN: -force-vector-interleave=1 -S < %s | FileCheck %s + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" + +; In order to vectorize the inner loop, it needs to be versioned with +; memchecks between {A} x {B, C} first: +; +; for (i = 0; i < n; i++) +; for (j = 0; j < m; j++) +; A[j] += B[i] + C[j]; +; +; Since in the versioned vector loop A and B can no longer alias, B[i] can be +; LICM'ed from the inner loop. + + +define void @f(i32* %a, i32* %b, i32* %c) { +entry: + br label %outer + +outer: + %i.2 = phi i64 [ 0, %entry ], [ %i, %inner.end ] + %arrayidxB = getelementptr inbounds i32, i32* %b, i64 %i.2 + br label %inner.ph + +inner.ph: +; CHECK: vector.ph: +; CHECK: load i32, i32* %arrayidxB, +; CHECK: br label %vector.body + br label %inner + +inner: + %j.2 = phi i64 [ 0, %inner.ph ], [ %j, %inner ] + + %arrayidxA = getelementptr inbounds i32, i32* %a, i64 %j.2 + %loadA = load i32, i32* %arrayidxA, align 4 + + %loadB = load i32, i32* %arrayidxB, align 4 + + %arrayidxC = getelementptr inbounds i32, i32* %c, i64 %j.2 + %loadC = load i32, i32* %arrayidxC, align 4 + + %add = add nuw i32 %loadA, %loadB + %add2 = add nuw i32 %add, %loadC + + store i32 %add2, i32* %arrayidxA, align 4 + + %j = add nuw nsw i64 %j.2, 1 + %cond1 = icmp eq i64 %j, 20 + br i1 %cond1, label %inner.end, label %inner + +inner.end: + %i = add nuw nsw i64 %i.2, 1 + %cond2 = icmp eq i64 %i, 30 + br i1 %cond2, label %outer.end, label %outer + +outer.end: + ret void +} diff --git a/test/Transforms/LoopVectorize/noalias-md.ll b/test/Transforms/LoopVectorize/noalias-md.ll new file mode 100644 index 0000000000000..787ea88f9457b --- /dev/null +++ b/test/Transforms/LoopVectorize/noalias-md.ll @@ -0,0 +1,78 @@ +; RUN: opt -basicaa -loop-vectorize -force-vector-width=2 \ +; RUN: -force-vector-interleave=1 -S < %s \ +; RUN: | FileCheck %s -check-prefix=BOTH -check-prefix=LV +; RUN: opt -basicaa -scoped-noalias -loop-vectorize -dse -force-vector-width=2 \ +; RUN: -force-vector-interleave=1 -S < %s \ +; RUN: | FileCheck %s -check-prefix=BOTH -check-prefix=DSE + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" + +; This loop needs to be versioned with memchecks between {A, B} x {C} before +; it can be vectorized. +; +; for (i = 0; i < n; i++) { +; C[i] = A[i] + 1; +; C[i] += B[i]; +; } +; +; Check that the corresponding noalias metadata is added to the vector loop +; but not to the scalar loop. +; +; Since in the versioned vector loop C and B can no longer alias, the first +; store to C[i] can be DSE'd. + + +define void @f(i32* %a, i32* %b, i32* %c) { +entry: + br label %for.body + +; BOTH: vector.memcheck: +; BOTH: vector.body: +for.body: ; preds = %for.body, %entry + %ind = phi i64 [ 0, %entry ], [ %inc, %for.body ] + + %arrayidxA = getelementptr inbounds i32, i32* %a, i64 %ind +; Scope 1 +; LV: = load {{.*}} !alias.scope !0 + %loadA = load i32, i32* %arrayidxA, align 4 + + %add = add nuw i32 %loadA, 2 + + %arrayidxC = getelementptr inbounds i32, i32* %c, i64 %ind +; Noalias with scope 1 and 6 +; LV: store {{.*}} !alias.scope !3, !noalias !5 +; DSE-NOT: store + store i32 %add, i32* %arrayidxC, align 4 + + %arrayidxB = getelementptr inbounds i32, i32* %b, i64 %ind +; Scope 6 +; LV: = load {{.*}} !alias.scope !7 + %loadB = load i32, i32* %arrayidxB, align 4 + + %add2 = add nuw i32 %add, %loadB + +; Noalias with scope 1 and 6 +; LV: store {{.*}} !alias.scope !3, !noalias !5 +; DSE: store + store i32 %add2, i32* %arrayidxC, align 4 + + %inc = add nuw nsw i64 %ind, 1 + %exitcond = icmp eq i64 %inc, 20 + br i1 %exitcond, label %for.end, label %for.body + +; BOTH: for.body: +; BOTH-NOT: !alias.scope +; BOTH-NOT: !noalias + +for.end: ; preds = %for.body + ret void +} + +; LV: !0 = !{!1} +; LV: !1 = distinct !{!1, !2} +; LV: !2 = distinct !{!2, !"LVerDomain"} +; LV: !3 = !{!4} +; LV: !4 = distinct !{!4, !2} +; LV: !5 = !{!1, !6} +; LV: !6 = distinct !{!6, !2} +; LV: !7 = !{!6} diff --git a/test/Transforms/LoopVectorize/phi-hang.ll b/test/Transforms/LoopVectorize/phi-hang.ll index bbce239afa71f..eb1aaeffde87a 100644 --- a/test/Transforms/LoopVectorize/phi-hang.ll +++ b/test/Transforms/LoopVectorize/phi-hang.ll @@ -18,7 +18,7 @@ bb4: ; preds = %bb3 bb5: ; preds = %bb4, %bb1 %tmp6 = phi i32 [ 0, %bb4 ], [ %tmp, %bb1 ] - %tmp7 = phi i32 [ 0, %bb4 ], [ %tmp6, %bb1 ] + %tmp7 = phi i32 [ 0, %bb4 ], [ %tmp, %bb1 ] %tmp8 = phi i32 [ 0, %bb4 ], [ %tmp, %bb1 ] %tmp9 = add nsw i32 %tmp2, 1 %tmp10 = icmp eq i32 %tmp9, 0 diff --git a/test/Transforms/LoopVectorize/pr25281.ll b/test/Transforms/LoopVectorize/pr25281.ll new file mode 100644 index 0000000000000..6001a200c94e4 --- /dev/null +++ b/test/Transforms/LoopVectorize/pr25281.ll @@ -0,0 +1,59 @@ +; RUN: opt < %s -scev-aa -loop-vectorize -print-alias-sets -S -o - 2>&1 | FileCheck %s +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; PR25281 +; Just check that we don't crash on this test. +; CHECK-LABEL: @foo +define void @foo(float** noalias nocapture readonly %in, i32* noalias nocapture readonly %isCompressed, float* noalias nocapture readonly %out) { +entry_block: + %tmp = getelementptr float*, float** %in, i32 0 + %in_0 = load float*, float** %tmp, !alias.scope !0 + %tmp1 = getelementptr i32, i32* %isCompressed, i32 0 + %isCompressed_0 = load i32, i32* %tmp1, !alias.scope !1 + %tmp2 = getelementptr float*, float** %in, i32 1 + %in_1 = load float*, float** %tmp2, !alias.scope !2 + %tmp3 = getelementptr i32, i32* %isCompressed, i32 1 + %isCompressed_1 = load i32, i32* %tmp3, !alias.scope !3 + br label %for_each_frames + +for_each_frames: + %frameIndex = phi i32 [ 0, %entry_block ], [ %nextFrameIndex, %for_each_frames_end ] + %nextFrameIndex = add nuw nsw i32 %frameIndex, 2 + br label %for_each_channel + +for_each_channel: + %channelIndex = phi i32 [ 0, %for_each_frames ], [ %nextChannelIndex, %for_each_channel ] + %nextChannelIndex = add nuw nsw i32 %channelIndex, 1 + %tmp4 = add i32 %frameIndex, %channelIndex + %tmp5 = xor i32 %isCompressed_0, 1 + %tmp6 = mul i32 %frameIndex, %tmp5 + %offset0 = add i32 %tmp6, %channelIndex + %tmp7 = getelementptr float, float* %in_0, i32 %offset0 + %in_0_index = load float, float* %tmp7, align 4, !alias.scope !4 + %tmp8 = xor i32 %isCompressed_1, 1 + %tmp9 = mul i32 %frameIndex, %tmp8 + %offset1 = add i32 %tmp9, %channelIndex + %tmp10 = getelementptr float, float* %in_1, i32 %offset1 + %in_1_index = load float, float* %tmp10, align 4, !alias.scope !5 + %tmp11 = fadd float %in_0_index, %in_1_index + %tmp12 = getelementptr float, float* %out, i32 %tmp4 + store float %tmp11, float* %tmp12, align 4, !alias.noalias !6 + %tmp13 = icmp eq i32 %nextChannelIndex, 2 + br i1 %tmp13, label %for_each_frames_end, label %for_each_channel + +for_each_frames_end: + %tmp14 = icmp eq i32 %nextFrameIndex, 512 + br i1 %tmp14, label %return, label %for_each_frames + +return: + ret void +} + +!0 = distinct !{!0} +!1 = distinct !{!1, !0} +!2 = distinct !{!2, !0} +!3 = distinct !{!3, !0} +!4 = distinct !{!4, !0} +!5 = distinct !{!5, !0} +!6 = !{!2, !3, !4, !5, !1} diff --git a/test/Transforms/LoopVectorize/reverse_induction.ll b/test/Transforms/LoopVectorize/reverse_induction.ll index 88dd2e4d66ca5..24ffb6167de3a 100644 --- a/test/Transforms/LoopVectorize/reverse_induction.ll +++ b/test/Transforms/LoopVectorize/reverse_induction.ll @@ -5,9 +5,16 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3 ; Make sure consecutive vector generates correct negative indices. ; PR15882 -; CHECK-LABEL: @reverse_induction_i64( -; CHECK: add <4 x i64> %[[SPLAT:.*]], <i64 0, i64 -1, i64 -2, i64 -3> -; CHECK: add <4 x i64> %[[SPLAT]], <i64 -4, i64 -5, i64 -6, i64 -7> +; CHECK: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; CHECK: %offset.idx = sub i64 %startval, %index +; CHECK: %[[a0:.+]] = add i64 %offset.idx, 0 +; CHECK: %[[a1:.+]] = add i64 %offset.idx, -1 +; CHECK: %[[a2:.+]] = add i64 %offset.idx, -2 +; CHECK: %[[a3:.+]] = add i64 %offset.idx, -3 +; CHECK: %[[a4:.+]] = add i64 %offset.idx, -4 +; CHECK: %[[a5:.+]] = add i64 %offset.idx, -5 +; CHECK: %[[a6:.+]] = add i64 %offset.idx, -6 +; CHECK: %[[a7:.+]] = add i64 %offset.idx, -7 define i32 @reverse_induction_i64(i64 %startval, i32 * %ptr) { entry: @@ -30,8 +37,17 @@ loopend: } ; CHECK-LABEL: @reverse_induction_i128( -; CHECK: add <4 x i128> %[[SPLAT:.*]], <i128 0, i128 -1, i128 -2, i128 -3> -; CHECK: add <4 x i128> %[[SPLAT]], <i128 -4, i128 -5, i128 -6, i128 -7> +; CHECK: %index = phi i128 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; CHECK: %offset.idx = sub i128 %startval, %index +; CHECK: %[[a0:.+]] = add i128 %offset.idx, 0 +; CHECK: %[[a1:.+]] = add i128 %offset.idx, -1 +; CHECK: %[[a2:.+]] = add i128 %offset.idx, -2 +; CHECK: %[[a3:.+]] = add i128 %offset.idx, -3 +; CHECK: %[[a4:.+]] = add i128 %offset.idx, -4 +; CHECK: %[[a5:.+]] = add i128 %offset.idx, -5 +; CHECK: %[[a6:.+]] = add i128 %offset.idx, -6 +; CHECK: %[[a7:.+]] = add i128 %offset.idx, -7 + define i32 @reverse_induction_i128(i128 %startval, i32 * %ptr) { entry: br label %for.body @@ -53,8 +69,16 @@ loopend: } ; CHECK-LABEL: @reverse_induction_i16( -; CHECK: add <4 x i16> %[[SPLAT:.*]], <i16 0, i16 -1, i16 -2, i16 -3> -; CHECK: add <4 x i16> %[[SPLAT]], <i16 -4, i16 -5, i16 -6, i16 -7> +; CHECK: %index = phi i32 [ 0, %vector.ph ], [ %index.next, %vector.body ] +; CHECK: %offset.idx = sub i16 %startval, {{.*}} +; CHECK: %[[a0:.+]] = add i16 %offset.idx, 0 +; CHECK: %[[a1:.+]] = add i16 %offset.idx, -1 +; CHECK: %[[a2:.+]] = add i16 %offset.idx, -2 +; CHECK: %[[a3:.+]] = add i16 %offset.idx, -3 +; CHECK: %[[a4:.+]] = add i16 %offset.idx, -4 +; CHECK: %[[a5:.+]] = add i16 %offset.idx, -5 +; CHECK: %[[a6:.+]] = add i16 %offset.idx, -6 +; CHECK: %[[a7:.+]] = add i16 %offset.idx, -7 define i32 @reverse_induction_i16(i16 %startval, i32 * %ptr) { entry: @@ -96,7 +120,8 @@ loopend: ; CHECK-LABEL: @reverse_forward_induction_i64_i8( ; CHECK: vector.body ; CHECK: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] -; CHECK: %offset.idx = sub i64 1023, %index +; CHECK: %vec.ind = phi <4 x i64> [ <i64 1023, i64 1022, i64 1021, i64 1020>, %vector.ph ] +; CHECK: %step.add = add <4 x i64> %vec.ind, <i64 -4, i64 -4, i64 -4, i64 -4> ; CHECK: trunc i64 %index to i8 define void @reverse_forward_induction_i64_i8() { @@ -122,7 +147,8 @@ while.end: ; CHECK-LABEL: @reverse_forward_induction_i64_i8_signed( ; CHECK: vector.body: ; CHECK: %index = phi i64 [ 0, %vector.ph ], [ %index.next, %vector.body ] -; CHECK: %offset.idx = sub i64 1023, %index +; CHECK: %vec.ind = phi <4 x i64> [ <i64 1023, i64 1022, i64 1021, i64 1020>, %vector.ph ] +; CHECK: %step.add = add <4 x i64> %vec.ind, <i64 -4, i64 -4, i64 -4, i64 -4> define void @reverse_forward_induction_i64_i8_signed() { entry: diff --git a/test/Transforms/LoopVectorize/runtime-check.ll b/test/Transforms/LoopVectorize/runtime-check.ll index 3673b71db30d5..2bd8b43820df4 100644 --- a/test/Transforms/LoopVectorize/runtime-check.ll +++ b/test/Transforms/LoopVectorize/runtime-check.ll @@ -67,13 +67,20 @@ loopexit: ; CHECK: [[BODY_LOC]] = !DILocation(line: 101, column: 1, scope: !{{.*}}) !llvm.module.flags = !{!0, !1} +!llvm.dbg.cu = !{!9} !0 = !{i32 2, !"Dwarf Version", i32 4} !1 = !{i32 2, !"Debug Info Version", i32 3} !2 = !{} !3 = !DISubroutineType(types: !2) !4 = !DIFile(filename: "test.cpp", directory: "/tmp") -!5 = distinct !DISubprogram(name: "foo", scope: !4, file: !4, line: 99, type: !3, isLocal: false, isDefinition: true, scopeLine: 100, flags: DIFlagPrototyped, isOptimized: false, variables: !2) +!5 = distinct !DISubprogram(name: "foo", scope: !4, file: !4, line: 99, type: !3, isLocal: false, isDefinition: true, scopeLine: 100, flags: DIFlagPrototyped, isOptimized: false, unit: !9, variables: !2) !6 = !DILocation(line: 100, column: 1, scope: !5) !7 = !DILocation(line: 101, column: 1, scope: !5) !8 = !DILocation(line: 102, column: 1, scope: !5) +!9 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang", + file: !10, + isOptimized: true, flags: "-O2", + splitDebugFilename: "abc.debug", emissionKind: 2) +!10 = !DIFile(filename: "path/to/file", directory: "/path/to/dir") +!11 = !{i32 2, !"Debug Info Version", i32 3} diff --git a/test/Transforms/LoopVectorize/same-base-access.ll b/test/Transforms/LoopVectorize/same-base-access.ll index 31cff0ee653f6..53fad8afdad8e 100644 --- a/test/Transforms/LoopVectorize/same-base-access.ll +++ b/test/Transforms/LoopVectorize/same-base-access.ll @@ -62,11 +62,9 @@ define i32 @kernel11(double* %x, double* %y, i32 %n) nounwind uwtable ssp { } - -; We don't vectorize this function because A[i*7] is scalarized, and the -; different scalars can in theory wrap around and overwrite other scalar -; elements. At the moment we only allow read/write access to arrays -; that are consecutive. +; A[i*7] is scalarized, and the different scalars can in theory wrap +; around and overwrite other scalar elements. However we can still +; vectorize because we can version the loop to avoid this case. ; ; void foo(int *a) { ; for (int i=0; i<256; ++i) { @@ -78,7 +76,7 @@ define i32 @kernel11(double* %x, double* %y, i32 %n) nounwind uwtable ssp { ; } ; CHECK-LABEL: @func2( -; CHECK-NOT: <4 x i32> +; CHECK: <4 x i32> ; CHECK: ret define i32 @func2(i32* nocapture %a) nounwind uwtable ssp { br label %1 diff --git a/test/Transforms/LoopVectorize/unsafe-dep-remark.ll b/test/Transforms/LoopVectorize/unsafe-dep-remark.ll new file mode 100644 index 0000000000000..5fff82554f4c6 --- /dev/null +++ b/test/Transforms/LoopVectorize/unsafe-dep-remark.ll @@ -0,0 +1,74 @@ +; RUN: opt -loop-vectorize -force-vector-width=2 -pass-remarks-analysis=loop-vectorize < %s 2>&1 | FileCheck %s + +; ModuleID = '/tmp/kk.c' +source_filename = "/tmp/kk.c" +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +; 1 void success (char *A, char *B, char *C, char *D, char *E, int N) { +; 2 for(int i = 0; i < N; i++) { +; 3 A[i + 1] = A[i] + B[i]; +; 4 C[i] = D[i] * E[i]; +; 5 } +; 6 } + +; CHECK: remark: /tmp/kk.c:3:16: loop not vectorized: unsafe dependent memory operations in loop. Use #pragma loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop + +define void @success(i8* nocapture %A, i8* nocapture readonly %B, i8* nocapture %C, i8* nocapture readonly %D, i8* nocapture readonly %E, i32 %N) !dbg !6 { +entry: + %cmp28 = icmp sgt i32 %N, 0, !dbg !8 + br i1 %cmp28, label %for.body, label %for.cond.cleanup, !dbg !9 + +for.body: ; preds = %entry, %for.body + %indvars.iv = phi i64 [ %indvars.iv.next, %for.body ], [ 0, %entry ] + %arrayidx = getelementptr inbounds i8, i8* %A, i64 %indvars.iv, !dbg !11 + %0 = load i8, i8* %arrayidx, align 1, !dbg !11, !tbaa !12 + %arrayidx2 = getelementptr inbounds i8, i8* %B, i64 %indvars.iv, !dbg !15 + %1 = load i8, i8* %arrayidx2, align 1, !dbg !15, !tbaa !12 + %add = add i8 %1, %0, !dbg !16 + %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1, !dbg !9 + %arrayidx7 = getelementptr inbounds i8, i8* %A, i64 %indvars.iv.next, !dbg !17 + store i8 %add, i8* %arrayidx7, align 1, !dbg !18, !tbaa !12 + %arrayidx9 = getelementptr inbounds i8, i8* %D, i64 %indvars.iv, !dbg !19 + %2 = load i8, i8* %arrayidx9, align 1, !dbg !19, !tbaa !12 + %arrayidx12 = getelementptr inbounds i8, i8* %E, i64 %indvars.iv, !dbg !20 + %3 = load i8, i8* %arrayidx12, align 1, !dbg !20, !tbaa !12 + %mul = mul i8 %3, %2, !dbg !21 + %arrayidx16 = getelementptr inbounds i8, i8* %C, i64 %indvars.iv, !dbg !22 + store i8 %mul, i8* %arrayidx16, align 1, !dbg !23, !tbaa !12 + %lftr.wideiv = trunc i64 %indvars.iv.next to i32, !dbg !9 + %exitcond = icmp eq i32 %lftr.wideiv, %N, !dbg !9 + br i1 %exitcond, label %for.cond.cleanup, label %for.body, !dbg !9 + +for.cond.cleanup: ; preds = %for.body, %entry + ret void, !dbg !10 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} +!llvm.ident = !{!5} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.9.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: NoDebug, enums: !2) +!1 = !DIFile(filename: "/tmp/kk.c", directory: "/tmp") +!2 = !{} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"PIC Level", i32 2} +!5 = !{!"clang version 3.9.0 "} +!6 = distinct !DISubprogram(name: "success", scope: !1, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !2) +!7 = !DISubroutineType(types: !2) +!8 = !DILocation(line: 2, column: 20, scope: !6) +!9 = !DILocation(line: 2, column: 3, scope: !6) +!10 = !DILocation(line: 6, column: 1, scope: !6) +!11 = !DILocation(line: 3, column: 16, scope: !6) +!12 = !{!13, !13, i64 0} +!13 = !{!"omnipotent char", !14, i64 0} +!14 = !{!"Simple C/C++ TBAA"} +!15 = !DILocation(line: 3, column: 23, scope: !6) +!16 = !DILocation(line: 3, column: 21, scope: !6) +!17 = !DILocation(line: 3, column: 5, scope: !6) +!18 = !DILocation(line: 3, column: 14, scope: !6) +!19 = !DILocation(line: 4, column: 12, scope: !6) +!20 = !DILocation(line: 4, column: 19, scope: !6) +!21 = !DILocation(line: 4, column: 17, scope: !6) +!22 = !DILocation(line: 4, column: 5, scope: !6) +!23 = !DILocation(line: 4, column: 10, scope: !6) |