diff options
Diffstat (limited to 'test/Transforms/GVN')
-rw-r--r-- | test/Transforms/GVN/cond_br2.ll | 12 | ||||
-rw-r--r-- | test/Transforms/GVN/condprop.ll | 48 | ||||
-rw-r--r-- | test/Transforms/GVN/edge.ll | 110 | ||||
-rw-r--r-- | test/Transforms/GVN/fpmath.ll | 4 | ||||
-rw-r--r-- | test/Transforms/GVN/invariant-load.ll | 40 | ||||
-rw-r--r-- | test/Transforms/GVN/load-from-unreachable-predecessor.ll | 20 | ||||
-rw-r--r-- | test/Transforms/GVN/load-pre-nonlocal.ll | 12 | ||||
-rw-r--r-- | test/Transforms/GVN/noalias.ll | 6 | ||||
-rw-r--r-- | test/Transforms/GVN/pre-gep-load.ll | 49 | ||||
-rw-r--r-- | test/Transforms/GVN/pre-no-cost-phi.ll | 31 | ||||
-rw-r--r-- | test/Transforms/GVN/preserve-tbaa.ll | 8 | ||||
-rw-r--r-- | test/Transforms/GVN/range.ll | 32 | ||||
-rw-r--r-- | test/Transforms/GVN/tbaa.ll | 69 | ||||
-rw-r--r-- | test/Transforms/GVN/volatile.ll | 157 |
14 files changed, 544 insertions, 54 deletions
diff --git a/test/Transforms/GVN/cond_br2.ll b/test/Transforms/GVN/cond_br2.ll index 27e6f75..a7ca219 100644 --- a/test/Transforms/GVN/cond_br2.ll +++ b/test/Transforms/GVN/cond_br2.ll @@ -132,9 +132,9 @@ attributes #1 = { nounwind } attributes #2 = { "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" "unsafe-fp-math"="false" "use-soft-float"="false" } attributes #3 = { nounwind "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" "unsafe-fp-math"="false" "use-soft-float"="false" } -!0 = metadata !{metadata !"any pointer", metadata !1} -!1 = metadata !{metadata !"omnipotent char", metadata !2} -!2 = metadata !{metadata !"Simple C/C++ TBAA"} -!3 = metadata !{metadata !"int", metadata !1} -!4 = metadata !{metadata !0, metadata !0, i64 0} -!5 = metadata !{metadata !3, metadata !3, i64 0} +!0 = !{!"any pointer", !1} +!1 = !{!"omnipotent char", !2} +!2 = !{!"Simple C/C++ TBAA"} +!3 = !{!"int", !1} +!4 = !{!0, !0, i64 0} +!5 = !{!3, !3, i64 0} diff --git a/test/Transforms/GVN/condprop.ll b/test/Transforms/GVN/condprop.ll index 708e4b2..845f88e 100644 --- a/test/Transforms/GVN/condprop.ll +++ b/test/Transforms/GVN/condprop.ll @@ -144,6 +144,22 @@ different: ret i1 %cmp3 } +; CHECK-LABEL: @test6_fp( +define i1 @test6_fp(float %x, float %y) { + %cmp2 = fcmp une float %x, %y + %cmp = fcmp oeq float %x, %y + %cmp3 = fcmp oeq float %x, %y + br i1 %cmp, label %same, label %different + +same: +; CHECK: ret i1 false + ret i1 %cmp2 + +different: +; CHECK: ret i1 false + ret i1 %cmp3 +} + ; CHECK-LABEL: @test7( define i1 @test7(i32 %x, i32 %y) { %cmp = icmp sgt i32 %x, %y @@ -160,6 +176,22 @@ different: ret i1 %cmp3 } +; CHECK-LABEL: @test7_fp( +define i1 @test7_fp(float %x, float %y) { + %cmp = fcmp ogt float %x, %y + br i1 %cmp, label %same, label %different + +same: + %cmp2 = fcmp ule float %x, %y +; CHECK: ret i1 false + ret i1 %cmp2 + +different: + %cmp3 = fcmp ogt float %x, %y +; CHECK: ret i1 false + ret i1 %cmp3 +} + ; CHECK-LABEL: @test8( define i1 @test8(i32 %x, i32 %y) { %cmp2 = icmp sle i32 %x, %y @@ -176,6 +208,22 @@ different: ret i1 %cmp3 } +; CHECK-LABEL: @test8_fp( +define i1 @test8_fp(float %x, float %y) { + %cmp2 = fcmp ule float %x, %y + %cmp = fcmp ogt float %x, %y + %cmp3 = fcmp ogt float %x, %y + br i1 %cmp, label %same, label %different + +same: +; CHECK: ret i1 false + ret i1 %cmp2 + +different: +; CHECK: ret i1 false + ret i1 %cmp3 +} + ; PR1768 ; CHECK-LABEL: @test9( define i32 @test9(i32 %i, i32 %j) { diff --git a/test/Transforms/GVN/edge.ll b/test/Transforms/GVN/edge.ll index 646e10c..0c1a3fb 100644 --- a/test/Transforms/GVN/edge.ll +++ b/test/Transforms/GVN/edge.ll @@ -58,3 +58,113 @@ bb2: ; CHECK: call void @g(i1 %y) ret void } + +define double @fcmp_oeq_not_zero(double %x, double %y) { +entry: + %cmp = fcmp oeq double %y, 2.0 + br i1 %cmp, label %if, label %return + +if: + %div = fdiv double %x, %y + br label %return + +return: + %retval = phi double [ %div, %if ], [ %x, %entry ] + ret double %retval + +; CHECK-LABEL: define double @fcmp_oeq_not_zero( +; CHECK: %div = fdiv double %x, 2.0 +} + +define double @fcmp_une_not_zero(double %x, double %y) { +entry: + %cmp = fcmp une double %y, 2.0 + br i1 %cmp, label %return, label %else + +else: + %div = fdiv double %x, %y + br label %return + +return: + %retval = phi double [ %div, %else ], [ %x, %entry ] + ret double %retval + +; CHECK-LABEL: define double @fcmp_une_not_zero( +; CHECK: %div = fdiv double %x, 2.0 +} + +; PR22376 - We can't propagate zero constants because -0.0 +; compares equal to 0.0. If %y is -0.0 in this test case, +; we would produce the wrong sign on the infinity return value. +define double @fcmp_oeq_zero(double %x, double %y) { +entry: + %cmp = fcmp oeq double %y, 0.0 + br i1 %cmp, label %if, label %return + +if: + %div = fdiv double %x, %y + br label %return + +return: + %retval = phi double [ %div, %if ], [ %x, %entry ] + ret double %retval + +; CHECK-LABEL: define double @fcmp_oeq_zero( +; CHECK: %div = fdiv double %x, %y +} + +define double @fcmp_une_zero(double %x, double %y) { +entry: + %cmp = fcmp une double %y, -0.0 + br i1 %cmp, label %return, label %else + +else: + %div = fdiv double %x, %y + br label %return + +return: + %retval = phi double [ %div, %else ], [ %x, %entry ] + ret double %retval + +; CHECK-LABEL: define double @fcmp_une_zero( +; CHECK: %div = fdiv double %x, %y +} + +; We also cannot propagate a value if it's not a constant. +; This is because the value could be 0.0 or -0.0. + +define double @fcmp_oeq_maybe_zero(double %x, double %y, double %z1, double %z2) { +entry: + %z = fadd double %z1, %z2 + %cmp = fcmp oeq double %y, %z + br i1 %cmp, label %if, label %return + +if: + %div = fdiv double %x, %z + br label %return + +return: + %retval = phi double [ %div, %if ], [ %x, %entry ] + ret double %retval + +; CHECK-LABEL: define double @fcmp_oeq_maybe_zero( +; CHECK: %div = fdiv double %x, %z +} + +define double @fcmp_une_maybe_zero(double %x, double %y, double %z1, double %z2) { +entry: + %z = fadd double %z1, %z2 + %cmp = fcmp une double %y, %z + br i1 %cmp, label %return, label %else + +else: + %div = fdiv double %x, %z + br label %return + +return: + %retval = phi double [ %div, %else ], [ %x, %entry ] + ret double %retval + +; CHECK-LABEL: define double @fcmp_une_maybe_zero( +; CHECK: %div = fdiv double %x, %z +} diff --git a/test/Transforms/GVN/fpmath.ll b/test/Transforms/GVN/fpmath.ll index 403df5c..d164fb5 100644 --- a/test/Transforms/GVN/fpmath.ll +++ b/test/Transforms/GVN/fpmath.ll @@ -41,5 +41,5 @@ define double @test4(double %x, double %y) { ret double %foo } -!0 = metadata !{ float 5.0 } -!1 = metadata !{ float 2.5 } +!0 = !{ float 5.0 } +!1 = !{ float 2.5 } diff --git a/test/Transforms/GVN/invariant-load.ll b/test/Transforms/GVN/invariant-load.ll index 80e2226..2a83c45 100644 --- a/test/Transforms/GVN/invariant-load.ll +++ b/test/Transforms/GVN/invariant-load.ll @@ -27,5 +27,43 @@ entry: ret i32 %add } -!0 = metadata !{ } +; With the invariant.load metadata, what would otherwise +; be a case for PRE becomes a full redundancy. +define i32 @test3(i1 %cnd, i32* %p, i32* %q) { +; CHECK-LABEL: test3 +; CHECK-NOT: load +entry: + %v1 = load i32* %p + br i1 %cnd, label %bb1, label %bb2 + +bb1: + store i32 5, i32* %q + br label %bb2 + +bb2: + %v2 = load i32* %p, !invariant.load !0 + %res = sub i32 %v1, %v2 + ret i32 %res +} + +; This test is here to document a case which doesn't optimize +; as well as it could. +define i32 @test4(i1 %cnd, i32* %p, i32* %q) { +; CHECK-LABEL: test4 +; %v2 is redundant, but GVN currently doesn't catch that +entry: + %v1 = load i32* %p, !invariant.load !0 + br i1 %cnd, label %bb1, label %bb2 + +bb1: + store i32 5, i32* %q + br label %bb2 + +bb2: + %v2 = load i32* %p + %res = sub i32 %v1, %v2 + ret i32 %res +} + +!0 = !{ } diff --git a/test/Transforms/GVN/load-from-unreachable-predecessor.ll b/test/Transforms/GVN/load-from-unreachable-predecessor.ll new file mode 100644 index 0000000..b676d95 --- /dev/null +++ b/test/Transforms/GVN/load-from-unreachable-predecessor.ll @@ -0,0 +1,20 @@ +; RUN: opt -gvn -S < %s | FileCheck %s + +; Check that an unreachable predecessor to a PHI node doesn't cause a crash. +; PR21625. + +define i32 @f(i32** %f) { +; CHECK: bb0: +; Load should be removed, since it's ignored. +; CHECK-NEXT: br label +bb0: + %bar = load i32** %f + br label %bb2 +bb1: + %zed = load i32** %f + br i1 false, label %bb1, label %bb2 +bb2: + %foo = phi i32* [ null, %bb0 ], [ %zed, %bb1 ] + %storemerge = load i32* %foo + ret i32 %storemerge +} diff --git a/test/Transforms/GVN/load-pre-nonlocal.ll b/test/Transforms/GVN/load-pre-nonlocal.ll index 7bac1b7..ae508b9 100644 --- a/test/Transforms/GVN/load-pre-nonlocal.ll +++ b/test/Transforms/GVN/load-pre-nonlocal.ll @@ -79,9 +79,9 @@ if.end: ret i32 %add1 } -!1 = metadata !{metadata !2, metadata !2, i64 0} -!2 = metadata !{metadata !"any pointer", metadata !3, i64 0} -!3 = metadata !{metadata !"omnipotent char", metadata !4, i64 0} -!4 = metadata !{metadata !"Simple C/C++ TBAA"} -!5 = metadata !{metadata !6, metadata !6, i64 0} -!6 = metadata !{metadata !"int", metadata !3, i64 0} +!1 = !{!2, !2, i64 0} +!2 = !{!"any pointer", !3, i64 0} +!3 = !{!"omnipotent char", !4, i64 0} +!4 = !{!"Simple C/C++ TBAA"} +!5 = !{!6, !6, i64 0} +!6 = !{!"int", !3, i64 0} diff --git a/test/Transforms/GVN/noalias.ll b/test/Transforms/GVN/noalias.ll index a774f38..6c310fa 100644 --- a/test/Transforms/GVN/noalias.ll +++ b/test/Transforms/GVN/noalias.ll @@ -37,7 +37,7 @@ define i32 @test3(i32* %p, i32* %q) { declare i32 @foo(i32*) readonly -!0 = metadata !{metadata !0} -!1 = metadata !{metadata !1} -!2 = metadata !{metadata !0, metadata !1} +!0 = !{!0} +!1 = !{!1} +!2 = !{!0, !1} diff --git a/test/Transforms/GVN/pre-gep-load.ll b/test/Transforms/GVN/pre-gep-load.ll new file mode 100644 index 0000000..3ee3a37 --- /dev/null +++ b/test/Transforms/GVN/pre-gep-load.ll @@ -0,0 +1,49 @@ +; RUN: opt < %s -basicaa -gvn -enable-load-pre -S | FileCheck %s +target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" +target triple = "aarch64--linux-gnu" + +define double @foo(i32 %stat, i32 %i, double** %p) { +; CHECK-LABEL: @foo( +entry: + switch i32 %stat, label %sw.default [ + i32 0, label %sw.bb + i32 1, label %sw.bb + i32 2, label %sw.bb2 + ] + +sw.bb: ; preds = %entry, %entry + %idxprom = sext i32 %i to i64 + %arrayidx = getelementptr inbounds double** %p, i64 0 + %0 = load double** %arrayidx, align 8 + %arrayidx1 = getelementptr inbounds double* %0, i64 %idxprom + %1 = load double* %arrayidx1, align 8 + %sub = fsub double %1, 1.000000e+00 + %cmp = fcmp olt double %sub, 0.000000e+00 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %sw.bb + br label %return + +if.end: ; preds = %sw.bb + br label %sw.bb2 + +sw.bb2: ; preds = %if.end, %entry + %idxprom3 = sext i32 %i to i64 + %arrayidx4 = getelementptr inbounds double** %p, i64 0 + %2 = load double** %arrayidx4, align 8 + %arrayidx5 = getelementptr inbounds double* %2, i64 %idxprom3 + %3 = load double* %arrayidx5, align 8 +; CHECK: sw.bb2: +; CHECK-NEXT-NOT: sext +; CHECK-NEXT: phi double [ +; CHECK-NOT: load + %sub6 = fsub double 3.000000e+00, %3 + br label %return + +sw.default: ; preds = %entry + br label %return + +return: ; preds = %sw.default, %sw.bb2, %if.then + %retval.0 = phi double [ 0.000000e+00, %sw.default ], [ %sub6, %sw.bb2 ], [ %sub, %if.then ] + ret double %retval.0 +} diff --git a/test/Transforms/GVN/pre-no-cost-phi.ll b/test/Transforms/GVN/pre-no-cost-phi.ll new file mode 100644 index 0000000..4c5afa1 --- /dev/null +++ b/test/Transforms/GVN/pre-no-cost-phi.ll @@ -0,0 +1,31 @@ +; RUN: opt < %s -gvn -S | FileCheck %s +; This testcase tests insertion of no-cost phis. That is, +; when the value is already available in every predecessor, +; and we just need to insert a phi node to merge the available values. + +@c = global i32 0, align 4 +@d = global i32 0, align 4 + + +define i32 @mai(i32 %foo, i32 %a, i32 %b) { + %1 = icmp ne i32 %foo, 0 + br i1 %1, label %bb1, label %bb2 + +bb1: + %2 = add nsw i32 %a, %b + store i32 %2, i32* @c, align 4 + br label %mergeblock + +bb2: + %3 = add nsw i32 %a, %b + store i32 %3, i32* @d, align 4 + br label %mergeblock + +mergeblock: +; CHECK: pre-phi = phi i32 [ %3, %bb2 ], [ %2, %bb1 ] +; CHECK-NEXT: ret i32 %.pre-phi + %4 = add nsw i32 %a, %b + ret i32 %4 +} + + diff --git a/test/Transforms/GVN/preserve-tbaa.ll b/test/Transforms/GVN/preserve-tbaa.ll index c52ed96..587d463 100644 --- a/test/Transforms/GVN/preserve-tbaa.ll +++ b/test/Transforms/GVN/preserve-tbaa.ll @@ -25,7 +25,7 @@ for.end: ; preds = %for.body, %entry ret void } -!0 = metadata !{metadata !3, metadata !3, i64 0} -!1 = metadata !{metadata !"omnipotent char", metadata !2} -!2 = metadata !{metadata !"Simple C/C++ TBAA", null} -!3 = metadata !{metadata !"short", metadata !1} +!0 = !{!3, !3, i64 0} +!1 = !{!"omnipotent char", !2} +!2 = !{!"Simple C/C++ TBAA", null} +!3 = !{!"short", !1} diff --git a/test/Transforms/GVN/range.ll b/test/Transforms/GVN/range.ll index 2115fe8..3720232 100644 --- a/test/Transforms/GVN/range.ll +++ b/test/Transforms/GVN/range.ll @@ -82,20 +82,20 @@ define i32 @test8(i32* %p) { ret i32 %c } -; CHECK: ![[DISJOINT_RANGE]] = metadata !{i32 0, i32 2, i32 3, i32 5} -; CHECK: ![[MERGED_RANGE]] = metadata !{i32 0, i32 5} -; CHECK: ![[MERGED_SIGNED_RANGE]] = metadata !{i32 -3, i32 -2, i32 1, i32 2} -; CHECK: ![[MERGED_TEST6]] = metadata !{i32 10, i32 1} -; CHECK: ![[MERGED_TEST7]] = metadata !{i32 3, i32 4, i32 5, i32 2} +; CHECK: ![[DISJOINT_RANGE]] = !{i32 0, i32 2, i32 3, i32 5} +; CHECK: ![[MERGED_RANGE]] = !{i32 0, i32 5} +; CHECK: ![[MERGED_SIGNED_RANGE]] = !{i32 -3, i32 -2, i32 1, i32 2} +; CHECK: ![[MERGED_TEST6]] = !{i32 10, i32 1} +; CHECK: ![[MERGED_TEST7]] = !{i32 3, i32 4, i32 5, i32 2} -!0 = metadata !{i32 0, i32 2} -!1 = metadata !{i32 3, i32 5} -!2 = metadata !{i32 2, i32 5} -!3 = metadata !{i32 -3, i32 -2} -!4 = metadata !{i32 1, i32 2} -!5 = metadata !{i32 10, i32 1} -!6 = metadata !{i32 12, i32 13} -!7 = metadata !{i32 1, i32 2, i32 3, i32 4} -!8 = metadata !{i32 5, i32 1} -!9 = metadata !{i32 1, i32 5} -!10 = metadata !{i32 5, i32 1} +!0 = !{i32 0, i32 2} +!1 = !{i32 3, i32 5} +!2 = !{i32 2, i32 5} +!3 = !{i32 -3, i32 -2} +!4 = !{i32 1, i32 2} +!5 = !{i32 10, i32 1} +!6 = !{i32 12, i32 13} +!7 = !{i32 1, i32 2, i32 3, i32 4} +!8 = !{i32 5, i32 1} +!9 = !{i32 1, i32 5} +!10 = !{i32 5, i32 1} diff --git a/test/Transforms/GVN/tbaa.ll b/test/Transforms/GVN/tbaa.ll index d6412fc..71fbed41 100644 --- a/test/Transforms/GVN/tbaa.ll +++ b/test/Transforms/GVN/tbaa.ll @@ -1,4 +1,4 @@ -; RUN: opt -basicaa -gvn -S < %s | FileCheck %s +; RUN: opt -tbaa -basicaa -gvn -S < %s | FileCheck %s define i32 @test1(i8* %p, i8* %q) { ; CHECK: @test1(i8* %p, i8* %q) @@ -72,20 +72,57 @@ define i32 @test7(i8* %p, i8* %q) { ret i32 %c } + + +define i32 @test8(i32* %p, i32* %q) { +; CHECK-LABEL: test8 +; CHECK-NEXT: store i32 15, i32* %p +; CHECK-NEXT: ret i32 0 +; Since we know the location is invariant, we can forward the +; load across the potentially aliasing store. + + %a = load i32* %q, !tbaa !10 + store i32 15, i32* %p + %b = load i32* %q, !tbaa !10 + %c = sub i32 %a, %b + ret i32 %c +} +define i32 @test9(i32* %p, i32* %q) { +; CHECK-LABEL: test9 +; CHECK-NEXT: call void @clobber() +; CHECK-NEXT: ret i32 0 +; Since we know the location is invariant, we can forward the +; load across the potentially aliasing store (within the call). + + %a = load i32* %q, !tbaa !10 + call void @clobber() + %b = load i32* %q, !tbaa !10 + %c = sub i32 %a, %b + ret i32 %c +} + + +declare void @clobber() declare i32 @foo(i8*) readonly -; CHECK: [[TAGC]] = metadata !{metadata [[TYPEC:!.*]], metadata [[TYPEC]], i64 0} -; CHECK: [[TYPEC]] = metadata !{metadata !"C", metadata [[TYPEA:!.*]]} -; CHECK: [[TYPEA]] = metadata !{metadata !"A", metadata !{{.*}}} -; CHECK: [[TAGB]] = metadata !{metadata [[TYPEB:!.*]], metadata [[TYPEB]], i64 0} -; CHECK: [[TYPEB]] = metadata !{metadata !"B", metadata [[TYPEA]]} -; CHECK: [[TAGA]] = metadata !{metadata [[TYPEA]], metadata [[TYPEA]], i64 0} -!0 = metadata !{metadata !5, metadata !5, i64 0} -!1 = metadata !{metadata !6, metadata !6, i64 0} -!2 = metadata !{metadata !"tbaa root", null} -!3 = metadata !{metadata !7, metadata !7, i64 0} -!4 = metadata !{metadata !8, metadata !8, i64 0} -!5 = metadata !{metadata !"C", metadata !6} -!6 = metadata !{metadata !"A", metadata !2} -!7 = metadata !{metadata !"B", metadata !6} -!8 = metadata !{metadata !"another root", null} +; CHECK: [[TAGC]] = !{[[TYPEC:!.*]], [[TYPEC]], i64 0} +; CHECK: [[TYPEC]] = !{!"C", [[TYPEA:!.*]]} +; CHECK: [[TYPEA]] = !{!"A", !{{.*}}} +; CHECK: [[TAGB]] = !{[[TYPEB:!.*]], [[TYPEB]], i64 0} +; CHECK: [[TYPEB]] = !{!"B", [[TYPEA]]} +; CHECK: [[TAGA]] = !{[[TYPEA]], [[TYPEA]], i64 0} +!0 = !{!5, !5, i64 0} +!1 = !{!6, !6, i64 0} +!2 = !{!"tbaa root", null} +!3 = !{!7, !7, i64 0} +!4 = !{!8, !8, i64 0} +!5 = !{!"C", !6} +!6 = !{!"A", !2} +!7 = !{!"B", !6} +!8 = !{!"another root", null} + + +;; A TBAA structure who's only point is to have a constant location +!9 = !{!"yet another root"} +!10 = !{!"node", !9, i64 1} + diff --git a/test/Transforms/GVN/volatile.ll b/test/Transforms/GVN/volatile.ll new file mode 100644 index 0000000..5ba03d9 --- /dev/null +++ b/test/Transforms/GVN/volatile.ll @@ -0,0 +1,157 @@ +; Tests that check our handling of volatile instructions encountered +; when scanning for dependencies +; RUN: opt -basicaa -gvn -S < %s | FileCheck %s + +; Check that we can bypass a volatile load when searching +; for dependencies of a non-volatile load +define i32 @test1(i32* nocapture %p, i32* nocapture %q) { +; CHECK-LABEL: test1 +; CHECK: %0 = load volatile i32* %q +; CHECK-NEXT: ret i32 0 +entry: + %x = load i32* %p + load volatile i32* %q + %y = load i32* %p + %add = sub i32 %y, %x + ret i32 %add +} + +; We can not value forward if the query instruction is +; volatile, this would be (in effect) removing the volatile load +define i32 @test2(i32* nocapture %p, i32* nocapture %q) { +; CHECK-LABEL: test2 +; CHECK: %x = load i32* %p +; CHECK-NEXT: %y = load volatile i32* %p +; CHECK-NEXT: %add = sub i32 %y, %x +entry: + %x = load i32* %p + %y = load volatile i32* %p + %add = sub i32 %y, %x + ret i32 %add +} + +; If the query instruction is itself volatile, we *cannot* +; reorder it even if p and q are noalias +define i32 @test3(i32* noalias nocapture %p, i32* noalias nocapture %q) { +; CHECK-LABEL: test3 +; CHECK: %x = load i32* %p +; CHECK-NEXT: %0 = load volatile i32* %q +; CHECK-NEXT: %y = load volatile i32* %p +entry: + %x = load i32* %p + load volatile i32* %q + %y = load volatile i32* %p + %add = sub i32 %y, %x + ret i32 %add +} + +; If an encountered instruction is both volatile and ordered, +; we need to use the strictest ordering of either. In this +; case, the ordering prevents forwarding. +define i32 @test4(i32* noalias nocapture %p, i32* noalias nocapture %q) { +; CHECK-LABEL: test4 +; CHECK: %x = load i32* %p +; CHECK-NEXT: %0 = load atomic volatile i32* %q seq_cst +; CHECK-NEXT: %y = load atomic i32* %p seq_cst +entry: + %x = load i32* %p + load atomic volatile i32* %q seq_cst, align 4 + %y = load atomic i32* %p seq_cst, align 4 + %add = sub i32 %y, %x + ret i32 %add +} + +; Value forwarding from a volatile load is perfectly legal +define i32 @test5(i32* nocapture %p, i32* nocapture %q) { +; CHECK-LABEL: test5 +; CHECK: %x = load volatile i32* %p +; CHECK-NEXT: ret i32 0 +entry: + %x = load volatile i32* %p + %y = load i32* %p + %add = sub i32 %y, %x + ret i32 %add +} + +; Does cross block redundancy elimination work with volatiles? +define i32 @test6(i32* noalias nocapture %p, i32* noalias nocapture %q) { +; CHECK-LABEL: test6 +; CHECK: %y1 = load i32* %p +; CHECK-LABEL: header +; CHECK: %x = load volatile i32* %q +; CHECK-NEXT: %add = sub i32 %y1, %x +entry: + %y1 = load i32* %p + call void @use(i32 %y1) + br label %header +header: + %x = load volatile i32* %q + %y = load i32* %p + %add = sub i32 %y, %x + %cnd = icmp eq i32 %add, 0 + br i1 %cnd, label %exit, label %header +exit: + ret i32 %add +} + +; Does cross block PRE work with volatiles? +define i32 @test7(i1 %c, i32* noalias nocapture %p, i32* noalias nocapture %q) { +; CHECK-LABEL: test7 +; CHECK-LABEL: entry.header_crit_edge: +; CHECK: %y.pre = load i32* %p +; CHECK-LABEL: skip: +; CHECK: %y1 = load i32* %p +; CHECK-LABEL: header: +; CHECK: %y = phi i32 +; CHECK-NEXT: %x = load volatile i32* %q +; CHECK-NEXT: %add = sub i32 %y, %x +entry: + br i1 %c, label %header, label %skip +skip: + %y1 = load i32* %p + call void @use(i32 %y1) + br label %header +header: + %x = load volatile i32* %q + %y = load i32* %p + %add = sub i32 %y, %x + %cnd = icmp eq i32 %add, 0 + br i1 %cnd, label %exit, label %header +exit: + ret i32 %add +} + +; Another volatile PRE case - two paths through a loop +; load in preheader, one path read only, one not +define i32 @test8(i1 %b, i1 %c, i32* noalias %p, i32* noalias %q) { +; CHECK-LABEL: test8 +; CHECK-LABEL: entry +; CHECK: %y1 = load i32* %p +; CHECK-LABEL: header: +; CHECK: %y = phi i32 +; CHECK-NEXT: %x = load volatile i32* %q +; CHECK-NOT: load +; CHECK-LABEL: skip.header_crit_edge: +; CHECK: %y.pre = load i32* %p +entry: + %y1 = load i32* %p + call void @use(i32 %y1) + br label %header +header: + %x = load volatile i32* %q + %y = load i32* %p + call void @use(i32 %y) + br i1 %b, label %skip, label %header +skip: + ; escaping the arguments is explicitly required since we marked + ; them noalias + call void @clobber(i32* %p, i32* %q) + br i1 %c, label %header, label %exit +exit: + %add = sub i32 %y, %x + ret i32 %add +} + +declare void @use(i32) readonly +declare void @clobber(i32* %p, i32* %q) + |