diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/2008-06-09-WeakProp.ll b/llvm/test/Transforms/Attributor/IPConstantProp/2008-06-09-WeakProp.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/2008-06-09-WeakProp.ll @@ -0,0 +1,17 @@ +; RUN: opt < %s -passes=attributor -S | FileCheck %s +; Should not propagate the result of a weak function. +; PR2411 + +; CHECK: ret i32 %r + +define weak i32 @foo() nounwind { +entry: + ret i32 1 +} + +define i32 @main() nounwind { +entry: + %r = call i32 @foo( ) nounwind + ret i32 %r +} + diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll b/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll @@ -0,0 +1,40 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s +; Don't constant-propagate byval pointers, since they are not pointers! +; PR5038 +%struct.MYstr = type { i8, i32 } +@mystr = internal global %struct.MYstr zeroinitializer ; <%struct.MYstr*> [#uses=3] +define internal void @vfu1(%struct.MYstr* byval align 4 %u) nounwind { +entry: + %0 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 1 ; [#uses=1] + store i32 99, i32* %0, align 4 +; CHECK: %struct.MYstr* %u + %1 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 0 ; [#uses=1] + store i8 97, i8* %1, align 4 +; CHECK: %struct.MYstr* %u + br label %return + +return: ; preds = %entry + ret void +} + +define internal i32 @vfu2(%struct.MYstr* byval align 4 %u) nounwind readonly { +entry: + %0 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 1 ; [#uses=1] + %1 = load i32, i32* %0 +; CHECK: %struct.MYstr* @mystr, i{{..}} 0, i32 1 + %2 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 0 ; [#uses=1] + %3 = load i8, i8* %2 +; CHECK: %struct.MYstr* @mystr, i{{..}} 0, i32 0 + %4 = zext i8 %3 to i32 + %5 = add i32 %4, %1 + ret i32 %5 +} + +define i32 @unions() nounwind { +entry: + call void @vfu1(%struct.MYstr* byval align 4 @mystr) nounwind + %result = call i32 @vfu2(%struct.MYstr* byval align 4 @mystr) nounwind +; CHECK: ret i32 %result + ret i32 %result +} + diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/PR16052.ll b/llvm/test/Transforms/Attributor/IPConstantProp/PR16052.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/PR16052.ll @@ -0,0 +1,45 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s --check-prefix=ATTRIBUTOR + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i64 @fn2() { +; CHECK-LABEL: define {{[^@]+}}@fn2() +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CALL2:%.*]] = call i64 @fn1(i64 undef) +; CHECK-NEXT: ret i64 [[CALL2]] +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fn2() +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[CONV:%.*]] = sext i32 undef to i64 +; ATTRIBUTOR-NEXT: [[DIV:%.*]] = sdiv i64 8, [[CONV]] +; ATTRIBUTOR-NEXT: [[CALL2:%.*]] = call i64 @fn1(i64 [[DIV]]) +; ATTRIBUTOR-NEXT: ret i64 [[CALL2]] +; +entry: + %conv = sext i32 undef to i64 + %div = sdiv i64 8, %conv + %call2 = call i64 @fn1(i64 %div) + ret i64 %call2 +} + +define internal i64 @fn1(i64 %p1) { +; CHECK-LABEL: define {{[^@]+}}@fn1 +; CHECK-SAME: (i64 [[P1:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND:%.*]] = select i1 undef, i64 undef, i64 undef +; CHECK-NEXT: ret i64 [[COND]] +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fn1 +; ATTRIBUTOR-SAME: (i64 returned [[P1:%.*]]) +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[TOBOOL:%.*]] = icmp ne i64 [[P1]], 0 +; ATTRIBUTOR-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i64 [[P1]], i64 [[P1]] +; ATTRIBUTOR-NEXT: ret i64 [[COND]] +; +entry: + %tobool = icmp ne i64 %p1, 0 + %cond = select i1 %tobool, i64 %p1, i64 %p1 + ret i64 %cond +} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/PR26044.ll b/llvm/test/Transforms/Attributor/IPConstantProp/PR26044.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/PR26044.ll @@ -0,0 +1,126 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s --check-prefixes=ALL,ATTRIBUTOR +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @fn2(i32* %P) { +; IPSCCP-LABEL: define {{[^@]+}}@fn2 +; IPSCCP-SAME: (i32* [[P:%.*]]) +; IPSCCP-NEXT: entry: +; IPSCCP-NEXT: br label [[IF_END:%.*]] +; IPSCCP: for.cond1: +; IPSCCP-NEXT: br i1 false, label [[IF_END]], label [[IF_END]] +; IPSCCP: if.end: +; IPSCCP-NEXT: [[CALL:%.*]] = call i32 @fn1(i32 undef) +; IPSCCP-NEXT: store i32 [[CALL]], i32* [[P]] +; IPSCCP-NEXT: br label [[FOR_COND1:%.*]] +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fn2 +; ATTRIBUTOR-SAME: (i32* nocapture writeonly [[P:%.*]]) +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: br label [[IF_END:%.*]] +; ATTRIBUTOR: for.cond1: +; ATTRIBUTOR-NEXT: br i1 undef, label [[IF_END]], label [[IF_END]] +; ATTRIBUTOR: if.end: +; ATTRIBUTOR-NEXT: [[E_2:%.*]] = phi i32* [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ], [ null, [[FOR_COND1]] ] +; ATTRIBUTOR-NEXT: [[TMP0:%.*]] = load i32, i32* [[E_2]], align 4 +; ATTRIBUTOR-NEXT: [[CALL:%.*]] = call i32 @fn1(i32 [[TMP0]]) +; ATTRIBUTOR-NEXT: store i32 [[CALL]], i32* [[P]] +; ATTRIBUTOR-NEXT: br label [[FOR_COND1]] +; +entry: + br label %if.end + +for.cond1: ; preds = %if.end, %for.end + br i1 undef, label %if.end, label %if.end + +if.end: ; preds = %lbl, %for.cond1 + %e.2 = phi i32* [ undef, %entry ], [ null, %for.cond1 ], [ null, %for.cond1 ] + %0 = load i32, i32* %e.2, align 4 + %call = call i32 @fn1(i32 %0) + store i32 %call, i32* %P + br label %for.cond1 +} + +define internal i32 @fn1(i32 %p1) { +; IPSCCP-LABEL: define {{[^@]+}}@fn1 +; IPSCCP-SAME: (i32 [[P1:%.*]]) +; IPSCCP-NEXT: entry: +; IPSCCP-NEXT: [[COND:%.*]] = select i1 undef, i32 undef, i32 undef +; IPSCCP-NEXT: ret i32 [[COND]] +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fn1 +; ATTRIBUTOR-SAME: (i32 returned [[P1:%.*]]) +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0 +; ATTRIBUTOR-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]] +; ATTRIBUTOR-NEXT: ret i32 [[COND]] +; +entry: + %tobool = icmp ne i32 %p1, 0 + %cond = select i1 %tobool, i32 %p1, i32 %p1 + ret i32 %cond +} + +define void @fn_no_null_opt(i32* %P) #0 { +; IPSCCP-LABEL: define {{[^@]+}}@fn_no_null_opt +; IPSCCP-SAME: (i32* [[P:%.*]]) +; IPSCCP-NEXT: entry: +; IPSCCP-NEXT: br label [[IF_END:%.*]] +; IPSCCP: for.cond1: +; IPSCCP-NEXT: br i1 false, label [[IF_END]], label [[IF_END]] +; IPSCCP: if.end: +; IPSCCP-NEXT: [[TMP0:%.*]] = load i32, i32* null, align 4 +; IPSCCP-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]]) +; IPSCCP-NEXT: store i32 [[CALL]], i32* [[P]] +; IPSCCP-NEXT: br label [[FOR_COND1:%.*]] +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fn_no_null_opt +; ATTRIBUTOR-SAME: (i32* nocapture writeonly [[P:%.*]]) +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: br label [[IF_END:%.*]] +; ATTRIBUTOR: for.cond1: +; ATTRIBUTOR-NEXT: br i1 undef, label [[IF_END]], label [[IF_END]] +; ATTRIBUTOR: if.end: +; ATTRIBUTOR-NEXT: [[E_2:%.*]] = phi i32* [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ], [ null, [[FOR_COND1]] ] +; ATTRIBUTOR-NEXT: [[TMP0:%.*]] = load i32, i32* [[E_2]], align 4 +; ATTRIBUTOR-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]]) +; ATTRIBUTOR-NEXT: store i32 [[CALL]], i32* [[P]] +; ATTRIBUTOR-NEXT: br label [[FOR_COND1]] +; +entry: + br label %if.end + +for.cond1: ; preds = %if.end, %for.end + br i1 undef, label %if.end, label %if.end + +if.end: ; preds = %lbl, %for.cond1 + %e.2 = phi i32* [ undef, %entry ], [ null, %for.cond1 ], [ null, %for.cond1 ] + %0 = load i32, i32* %e.2, align 4 + %call = call i32 @fn0(i32 %0) + store i32 %call, i32* %P + br label %for.cond1 +} + +define internal i32 @fn0(i32 %p1) { +; IPSCCP-LABEL: define {{[^@]+}}@fn0 +; IPSCCP-SAME: (i32 [[P1:%.*]]) +; IPSCCP-NEXT: entry: +; IPSCCP-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0 +; IPSCCP-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]] +; IPSCCP-NEXT: ret i32 [[COND]] +; +; ATTRIBUTOR-LABEL: define {{[^@]+}}@fn0 +; ATTRIBUTOR-SAME: (i32 returned [[P1:%.*]]) +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0 +; ATTRIBUTOR-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]] +; ATTRIBUTOR-NEXT: ret i32 [[COND]] +; +entry: + %tobool = icmp ne i32 %p1, 0 + %cond = select i1 %tobool, i32 %p1, i32 %p1 + ret i32 %cond +} + +attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/PR43857.ll b/llvm/test/Transforms/Attributor/IPConstantProp/PR43857.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/PR43857.ll @@ -0,0 +1,34 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -passes=attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s --check-prefixes=ALL,ATTRIBUTOR + +%struct.wobble = type { i32 } +%struct.zot = type { %struct.wobble, %struct.wobble, %struct.wobble } + +declare dso_local fastcc float @bar(%struct.wobble* noalias, <8 x i32>) unnamed_addr + +define %struct.zot @widget(<8 x i32> %arg) local_unnamed_addr { +; ALL-LABEL: @widget( +; ALL-NEXT: bb: +; ALL-NEXT: ret [[STRUCT_ZOT:%.*]] undef +; +bb: + ret %struct.zot undef +} + +define void @baz(<8 x i32> %arg) local_unnamed_addr { +; IPCP-LABEL: @baz( +; IPCP-NEXT: bb: +; IPCP-NEXT: [[TMP:%.*]] = call [[STRUCT_ZOT:%.*]] @widget(<8 x i32> [[ARG:%.*]]) +; IPCP-NEXT: [[TMP1:%.*]] = extractvalue [[STRUCT_ZOT]] %tmp, 0, 0 +; IPCP-NEXT: ret void +; +; ATTRIBUTOR-LABEL: @baz( +; ATTRIBUTOR-NEXT: bb: +; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = extractvalue [[STRUCT_ZOT:%.*]] undef, 0, 0 +; ATTRIBUTOR-NEXT: ret void +; +bb: + %tmp = call %struct.zot @widget(<8 x i32> %arg) + %tmp1 = extractvalue %struct.zot %tmp, 0, 0 + ret void +} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/arg-count-mismatch.ll b/llvm/test/Transforms/Attributor/IPConstantProp/arg-count-mismatch.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/arg-count-mismatch.ll @@ -0,0 +1,73 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=5 < %s | FileCheck %s + +; The original C source looked like this: +; +; long long a101, b101, e101; +; volatile long c101; +; int d101; +; +; static inline int bar(p1, p2) +; { +; return 0; +; } +; +; void foo(unsigned p1) +; { +; long long *f = &b101, *g = &e101; +; c101 = 0; +; (void)((*f |= a101) - (*g = bar(d101))); +; c101 = (*f |= a101 &= p1) == d101; +; } +; +; When compiled with Clang it gives a warning +; warning: too few arguments in call to 'bar' +; +; This ll reproducer has been reduced to only include tha call. +; +; Note that -lint will report this as UB, but it passes -verify. + +; This test is just to verify that we do not crash/assert due to mismatch in +; argument count between the caller and callee. + +define dso_local void @foo(i16 %a) { +; CHECK-LABEL: @foo( +; CHECK-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar to i16 (i16)*)(i16 [[A:%.*]]) +; CHECK-NEXT: ret void +; + %call = call i16 bitcast (i16 (i16, i16) * @bar to i16 (i16) *)(i16 %a) + ret void +} + +define internal i16 @bar(i16 %p1, i16 %p2) { +; CHECK-LABEL: @bar( +; CHECK-NEXT: ret i16 0 +; + ret i16 0 +} + +;------------------------------------------------------------------------------- +; Additional tests to verify that we still optimize when having a mismatch +; in argument count due to varargs (as long as all non-variadic arguments have +; been provided), + +define dso_local i16 @vararg_tests(i16 %a) { + %call1 = call i16 (i16, ...) @vararg_prop(i16 7, i16 8, i16 %a) + %call2 = call i16 bitcast (i16 (i16, i16, ...) * @vararg_no_prop to i16 (i16) *) (i16 7) + %add = add i16 %call1, %call2 + ret i16 %add +} + +define internal i16 @vararg_prop(i16 %p1, ...) { +; CHECK-LABEL: define internal i16 @vararg_prop( +; CHECK-NEXT: ret i16 7 +; + ret i16 %p1 +} + +define internal i16 @vararg_no_prop(i16 %p1, i16 %p2, ...) { +; CHECK-LABEL: define internal i16 @vararg_no_prop( +; CHECK-NEXT: ret i16 [[P1:%.*]] +; + ret i16 %p1 +} + diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/arg-type-mismatch.ll b/llvm/test/Transforms/Attributor/IPConstantProp/arg-type-mismatch.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/arg-type-mismatch.ll @@ -0,0 +1,23 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s + +; This test is just to verify that we do not crash/assert due to mismatch in +; argument type between the caller and callee. + +define dso_local void @foo(i16 %a) { +; CHECK-LABEL: @foo( +; CHECK-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar to i16 (i16, i32)*)(i16 [[A:%.*]], i32 7) +; CHECK-NEXT: ret void +; + %call = call i16 bitcast (i16 (i16, i16) * @bar to i16 (i16, i32) *)(i16 %a, i32 7) + ret void +} + +define internal i16 @bar(i16 %p1, i16 %p2) { +; CHECK-LABEL: @bar( +; CHECK-NEXT: ret i16 [[P2:%.*]] +; + ret i16 %p2 +} + + diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/comdat-ipo.ll b/llvm/test/Transforms/Attributor/IPConstantProp/comdat-ipo.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/comdat-ipo.ll @@ -0,0 +1,28 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s + +; See PR26774 + +define i32 @baz() { + ret i32 10 +} + +; We can const-prop @baz's return value *into* @foo, but cannot +; constprop @foo's return value into bar. + +define linkonce_odr i32 @foo() { +; CHECK-LABEL: @foo( +; CHECK-NEXT: %val = call i32 @baz() +; CHECK-NEXT: ret i32 10 + + %val = call i32 @baz() + ret i32 %val +} + +define i32 @bar() { +; CHECK-LABEL: @bar( +; CHECK-NEXT: %val = call i32 @foo() +; CHECK-NEXT: ret i32 %val + + %val = call i32 @foo() + ret i32 %val +} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll b/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll @@ -0,0 +1,42 @@ +; RUN: opt -S -passes='internalize,attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s +; PR5569 + +; IPSCCP should prove that the blocks are dead and delete them, and +; properly handle the dangling blockaddress constants. + +; CHECK: @bar.l = internal constant [2 x i8*] [i8* inttoptr (i32 1 to i8*), i8* inttoptr (i32 1 to i8*)] + +@code = global [5 x i32] [i32 0, i32 0, i32 0, i32 0, i32 1], align 4 ; <[5 x i32]*> [#uses=0] +@bar.l = internal constant [2 x i8*] [i8* blockaddress(@bar, %lab0), i8* blockaddress(@bar, %end)] ; <[2 x i8*]*> [#uses=1] + +define void @foo(i32 %x) nounwind readnone { +entry: + %b = alloca i32, align 4 ; [#uses=1] + store volatile i32 -1, i32* %b + ret void +} + +define void @bar(i32* nocapture %pc) nounwind readonly { +entry: + br label %indirectgoto + +lab0: ; preds = %indirectgoto + %indvar.next = add i32 %indvar, 1 ; [#uses=1] + br label %indirectgoto + +end: ; preds = %indirectgoto + ret void + +indirectgoto: ; preds = %lab0, %entry + %indvar = phi i32 [ %indvar.next, %lab0 ], [ 0, %entry ] ; [#uses=2] + %pc.addr.0 = getelementptr i32, i32* %pc, i32 %indvar ; [#uses=1] + %tmp1.pn = load i32, i32* %pc.addr.0 ; [#uses=1] + %indirect.goto.dest.in = getelementptr inbounds [2 x i8*], [2 x i8*]* @bar.l, i32 0, i32 %tmp1.pn ; [#uses=1] + %indirect.goto.dest = load i8*, i8** %indirect.goto.dest.in ; [#uses=1] + indirectbr i8* %indirect.goto.dest, [label %lab0, label %end] +} + +define i32 @main() nounwind readnone { +entry: + ret i32 0 +} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/deadarg.ll b/llvm/test/Transforms/Attributor/IPConstantProp/deadarg.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/deadarg.ll @@ -0,0 +1,6 @@ +; RUN: opt -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 -disable-output < %s +define internal void @foo(i32 %X) { + call void @foo( i32 %X ) + ret void +} + diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/fp-bc-icmp-const-fold.ll b/llvm/test/Transforms/Attributor/IPConstantProp/fp-bc-icmp-const-fold.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/fp-bc-icmp-const-fold.ll @@ -0,0 +1,52 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s +target datalayout = "E-m:e-i64:64-n32:64" +target triple = "powerpc64-bgq-linux" + +define void @test(i32 signext %n) { + +; CHECK-LABEL: @test + +entry: + br i1 undef, label %if.then, label %if.end + +if.then: ; preds = %entry + ret void + +if.end: ; preds = %entry + br i1 undef, label %if.then2, label %if.end4 + +if.then2: ; preds = %if.end + unreachable + +if.end4: ; preds = %if.end + %sub.n = select i1 undef, i32 undef, i32 %n + switch i32 %sub.n, label %if.else14 [ + i32 0, label %if.then9 + i32 1, label %if.then12 + ] + +if.then9: ; preds = %if.end4 + unreachable + +if.then12: ; preds = %if.end4 + unreachable + +if.else14: ; preds = %if.end4 + br label %do.body + +do.body: ; preds = %do.body, %if.else14 + %scale.0 = phi ppc_fp128 [ 0xM3FF00000000000000000000000000000, %if.else14 ], [ %scale.0, %do.body ] + br i1 undef, label %do.body, label %if.then33 + +if.then33: ; preds = %do.body + br i1 undef, label %_ZN5boost4math4signIgEEiRKT_.exit30, label %cond.false.i28 + +cond.false.i28: ; preds = %if.then33 + %0 = bitcast ppc_fp128 %scale.0 to i128 + %tobool.i26 = icmp slt i128 %0, 0 + br label %_ZN5boost4math4signIgEEiRKT_.exit30 + +_ZN5boost4math4signIgEEiRKT_.exit30: ; preds = %cond.false.i28, %if.then33 + unreachable +} + diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/global.ll b/llvm/test/Transforms/Attributor/IPConstantProp/global.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/global.ll @@ -0,0 +1,31 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s --check-prefix=ATTRIBUTOR + +@_ZL6test1g = internal global i32 42, align 4 + +define void @_Z7test1f1v() nounwind { +entry: + %tmp = load i32, i32* @_ZL6test1g, align 4 + %cmp = icmp eq i32 %tmp, 0 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + store i32 0, i32* @_ZL6test1g, align 4 + br label %if.end + +if.end: ; preds = %if.then, %entry + ret void +} + +; CHECK: @_Z7test1f2v() +; CHECK: entry: +; CHECK-NEXT: ret i32 42 + +; ATTRIBUTOR: @_Z7test1f2v() +; ATTRIBUTOR: entry: +; ATTRIBUTOR-NEXT: %tmp = load i32, i32* @_ZL6test1g, align 4 +; ATTRIBUTOR-NEXT: ret i32 %tmp +define i32 @_Z7test1f2v() nounwind { +entry: + %tmp = load i32, i32* @_ZL6test1g, align 4 + ret i32 %tmp +} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll b/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll @@ -0,0 +1,87 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s +; +; +; /---------------------------------------| +; | /----------------------|----| +; | | /-----| | +; V V V | | +; void broker(int (*cb0)(int), int (*cb1)(int), int (*cb2)(int), int, int); +; +; static int cb0(int zero) { +; return zero; +; } +; static int cb1(int unknown) { +; return unknown; +; } +; static int cb2(int unknown) { +; cb0(0); +; return unknown; +; } +; static int cb3(int unknown) { +; return unknown; +; } +; static int cb4(int unknown) { +; return unknown; +; } +; +; void foo() { +; cb0(0); +; cb3(1); +; broker(cb0, cb1, cb0, 0, 1); +; broker(cb1, cb2, cb2, 0, 1); +; broker(cb3, cb2, cb3, 0, 1); +; broker(cb4, cb4, cb4, 0, 1); +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define internal i32 @cb0(i32 %zero) { +entry: +; CHECK: @cb0 +; CHECK-NEXT: entry +; CHECK-NEXT: ret i32 0 + ret i32 %zero +} + +define internal i32 @cb1(i32 %unknown) { +entry: +; CHECK: ret i32 %unknown + ret i32 %unknown +} + +define internal i32 @cb2(i32 %unknown) { +entry: + %call = call i32 @cb0(i32 0) +; CHECK: ret i32 %unknown + ret i32 %unknown +} + +define internal i32 @cb3(i32 %unknown) { +entry: +; CHECK: ret i32 %unknown + ret i32 %unknown +} + +define internal i32 @cb4(i32 %unknown) { +entry: +; CHECK: ret i32 %unknown + ret i32 %unknown +} + +define void @foo() { +entry: + %call = call i32 @cb0(i32 0) + %call1 = call i32 @cb3(i32 1) + call void @broker(i32 (i32)* nonnull @cb0, i32 (i32)* nonnull @cb1, i32 (i32)* nonnull @cb0, i32 0, i32 1) + call void @broker(i32 (i32)* nonnull @cb1, i32 (i32)* nonnull @cb2, i32 (i32)* nonnull @cb2, i32 0, i32 1) + call void @broker(i32 (i32)* nonnull @cb3, i32 (i32)* nonnull @cb2, i32 (i32)* nonnull @cb3, i32 0, i32 1) + call void @broker(i32 (i32)* nonnull @cb4, i32 (i32)* nonnull @cb4, i32 (i32)* nonnull @cb4, i32 0, i32 1) + ret void +} + +declare !callback !3 void @broker(i32 (i32)*, i32 (i32)*, i32 (i32)*, i32, i32) + +!0 = !{i64 0, i64 3, i1 false} +!1 = !{i64 1, i64 4, i1 false} +!2 = !{i64 2, i64 3, i1 false} +!3 = !{!0, !2, !1} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/musttail-call.ll b/llvm/test/Transforms/Attributor/IPConstantProp/musttail-call.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/musttail-call.ll @@ -0,0 +1,69 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=5 < %s | FileCheck %s --check-prefix=ATTRIBUTOR +; PR36485 +; musttail call result can\'t be replaced with a constant, unless the call +; can be removed + +declare i32 @external() + +define i8* @start(i8 %v) { + %c1 = icmp eq i8 %v, 0 + br i1 %c1, label %true, label %false +true: + ; CHECK: %ca = musttail call i8* @side_effects(i8 0) + ; CHECK: ret i8* %ca + ; FIXME: propagate the value information for %v + ; ATTRIBUTOR: %ca = musttail call i8* @side_effects(i8 %v) + ; ATTRIBUTOR: ret i8* %ca + %ca = musttail call i8* @side_effects(i8 %v) + ret i8* %ca +false: + %c2 = icmp eq i8 %v, 1 + br i1 %c2, label %c2_true, label %c2_false +c2_true: + %ca1 = musttail call i8* @no_side_effects(i8 %v) + ; CHECK: ret i8* null + ; FIXME: zap this call + ; ATTRIBUTOR: ret i8* %ca1 + ret i8* %ca1 +c2_false: + ; CHECK: %ca2 = musttail call i8* @dont_zap_me(i8 %v) + ; CHECK: ret i8* %ca2 + ; ATTRIBUTOR: %ca2 = musttail call i8* @dont_zap_me(i8 undef) + ; ATTRIBUTOR: ret i8* %ca2 + %ca2 = musttail call i8* @dont_zap_me(i8 %v) + ret i8* %ca2 +} + +define internal i8* @side_effects(i8 %v) { + %i1 = call i32 @external() + + ; since this goes back to `start` the SCPP should be see that the return value + ; is always `null`. + ; The call can't be removed due to `external` call above, though. + + ; CHECK: %ca = musttail call i8* @start(i8 0) + ; ATTRIBUTOR: %ca = musttail call i8* @start(i8 %v) + %ca = musttail call i8* @start(i8 %v) + + ; Thus the result must be returned anyway + ; CHECK: ret i8* %ca + ; ATTRIBUTOR: ret i8* %ca + ret i8* %ca +} + +define internal i8* @no_side_effects(i8 %v) readonly nounwind { + ; The call to this function is removed, so the return value must be zapped + ; CHECK: ret i8* undef + ; ATTRIBUTOR: ret i8* null + ret i8* null +} + +define internal i8* @dont_zap_me(i8 %v) { + %i1 = call i32 @external() + + ; The call to this function cannot be removed due to side effects. Thus the + ; return value should stay as it is, and should not be zapped. + ; CHECK: ret i8* null + ; ATTRIBUTOR: ret i8* null + ret i8* null +} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/naked-return.ll b/llvm/test/Transforms/Attributor/IPConstantProp/naked-return.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/naked-return.ll @@ -0,0 +1,28 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %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-msvc19.0.24215" + +define i32 @dipsy(i32, i32) local_unnamed_addr #0 { +BasicBlock0: + call void asm "\0D\0Apushl %ebp\0D\0Amovl 8(%esp),%eax\0D\0Amovl 12(%esp), %ebp\0D\0Acalll *%eax\0D\0Apopl %ebp\0D\0Aretl\0D\0A", ""() + ret i32 0 +} + +define void @tinkywinky(i32, i32, i32) local_unnamed_addr #0 { +BasicBlock1: + call void asm "\0D\0A movl 12(%esp), %ebp\0D\0A movl 4(%esp), %eax\0D\0A movl 8(%esp), %esp\0D\0A jmpl *%eax\0D\0A", ""() + ret void +} + +define void @patatino(i32, i32, i32) local_unnamed_addr #1 { +bb: + %3 = tail call i32 @dipsy(i32 %0, i32 %1) #0 +; Check that we don't accidentally propagate zero. +; CHECK: @tinkywinky(i32 %3, i32 %2, i32 %1) #0 + tail call void @tinkywinky(i32 %3, i32 %2, i32 %1) #0 + ret void +} + +attributes #0 = { naked } +attributes #1 = { "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" } diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll b/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll @@ -0,0 +1,120 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s +; +; void bar(int, float, double); +; +; void foo(int N) { +; float p = 3; +; double q = 5; +; N = 7; +; +; #pragma omp parallel for firstprivate(q) +; for (int i = 2; i < N; i++) { +; bar(i, p, q); +; } +; } +; +; Verify the constant value of q is propagated into the outlined function. +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +%struct.ident_t = type { i32, i32, i32, i32, i8* } + +@.str = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1 +@0 = private unnamed_addr global %struct.ident_t { i32 0, i32 514, i32 0, i32 0, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0) }, align 8 +@1 = private unnamed_addr global %struct.ident_t { i32 0, i32 2, i32 0, i32 0, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0) }, align 8 + +define dso_local void @foo(i32 %N) { +entry: + %N.addr = alloca i32, align 4 + %p = alloca float, align 4 + store i32 %N, i32* %N.addr, align 4 + store float 3.000000e+00, float* %p, align 4 + store i32 7, i32* %N.addr, align 4 + call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* nonnull @1, i32 3, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*, float*, i64)* @.omp_outlined. to void (i32*, i32*, ...)*), i32* nonnull %N.addr, float* nonnull %p, i64 4617315517961601024) + ret void +} + +define internal void @.omp_outlined.(i32* noalias %.global_tid., i32* noalias %.bound_tid., i32* dereferenceable(4) %N, float* dereferenceable(4) %p, i64 %q) { +entry: + %q.addr = alloca i64, align 8 + %.omp.lb = alloca i32, align 4 + %.omp.ub = alloca i32, align 4 + %.omp.stride = alloca i32, align 4 + %.omp.is_last = alloca i32, align 4 +; CHECK: store i64 4617315517961601024, i64* %q.addr, align 8 + store i64 %q, i64* %q.addr, align 8 + %conv = bitcast i64* %q.addr to double* + %tmp = load i32, i32* %N, align 4 + %sub3 = add nsw i32 %tmp, -3 + %cmp = icmp sgt i32 %tmp, 2 + br i1 %cmp, label %omp.precond.then, label %omp.precond.end + +omp.precond.then: ; preds = %entry + store i32 0, i32* %.omp.lb, align 4 + store i32 %sub3, i32* %.omp.ub, align 4 + store i32 1, i32* %.omp.stride, align 4 + store i32 0, i32* %.omp.is_last, align 4 + %tmp5 = load i32, i32* %.global_tid., align 4 + call void @__kmpc_for_static_init_4(%struct.ident_t* nonnull @0, i32 %tmp5, i32 34, i32* nonnull %.omp.is_last, i32* nonnull %.omp.lb, i32* nonnull %.omp.ub, i32* nonnull %.omp.stride, i32 1, i32 1) + %tmp6 = load i32, i32* %.omp.ub, align 4 + %cmp6 = icmp sgt i32 %tmp6, %sub3 + br i1 %cmp6, label %cond.true, label %cond.false + +cond.true: ; preds = %omp.precond.then + br label %cond.end + +cond.false: ; preds = %omp.precond.then + %tmp7 = load i32, i32* %.omp.ub, align 4 + br label %cond.end + +cond.end: ; preds = %cond.false, %cond.true + %cond = phi i32 [ %sub3, %cond.true ], [ %tmp7, %cond.false ] + store i32 %cond, i32* %.omp.ub, align 4 + %tmp8 = load i32, i32* %.omp.lb, align 4 + br label %omp.inner.for.cond + +omp.inner.for.cond: ; preds = %omp.inner.for.inc, %cond.end + %.omp.iv.0 = phi i32 [ %tmp8, %cond.end ], [ %add11, %omp.inner.for.inc ] + %tmp9 = load i32, i32* %.omp.ub, align 4 + %cmp8 = icmp sgt i32 %.omp.iv.0, %tmp9 + br i1 %cmp8, label %omp.inner.for.cond.cleanup, label %omp.inner.for.body + +omp.inner.for.cond.cleanup: ; preds = %omp.inner.for.cond + br label %omp.inner.for.end + +omp.inner.for.body: ; preds = %omp.inner.for.cond + %add10 = add nsw i32 %.omp.iv.0, 2 + %tmp10 = load float, float* %p, align 4 + %tmp11 = load double, double* %conv, align 8 + call void @bar(i32 %add10, float %tmp10, double %tmp11) + br label %omp.body.continue + +omp.body.continue: ; preds = %omp.inner.for.body + br label %omp.inner.for.inc + +omp.inner.for.inc: ; preds = %omp.body.continue + %add11 = add nsw i32 %.omp.iv.0, 1 + br label %omp.inner.for.cond + +omp.inner.for.end: ; preds = %omp.inner.for.cond.cleanup + br label %omp.loop.exit + +omp.loop.exit: ; preds = %omp.inner.for.end + %tmp12 = load i32, i32* %.global_tid., align 4 + call void @__kmpc_for_static_fini(%struct.ident_t* nonnull @0, i32 %tmp12) + br label %omp.precond.end + +omp.precond.end: ; preds = %omp.loop.exit, %entry + ret void +} + +declare dso_local void @__kmpc_for_static_init_4(%struct.ident_t*, i32, i32, i32*, i32*, i32*, i32*, i32, i32) + +declare dso_local void @bar(i32, float, double) + +declare dso_local void @__kmpc_for_static_fini(%struct.ident_t*, i32) + +declare !callback !0 dso_local void @__kmpc_fork_call(%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) + +!1 = !{i64 2, i64 -1, i64 -1, i1 true} +!0 = !{!1} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll b/llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll @@ -0,0 +1,49 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s +; +; #include +; +; void *GlobalVPtr; +; +; static void *foo(void *arg) { return arg; } +; static void *bar(void *arg) { return arg; } +; +; int main() { +; pthread_t thread; +; pthread_create(&thread, NULL, foo, NULL); +; pthread_create(&thread, NULL, bar, &GlobalVPtr); +; return 0; +; } +; +; Verify the constant values NULL and &GlobalVPtr are propagated into foo and +; bar, respectively. +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +%union.pthread_attr_t = type { i64, [48 x i8] } + +@GlobalVPtr = common dso_local global i8* null, align 8 + +define dso_local i32 @main() { +entry: + %thread = alloca i64, align 8 + %call = call i32 @pthread_create(i64* nonnull %thread, %union.pthread_attr_t* null, i8* (i8*)* nonnull @foo, i8* null) + %call1 = call i32 @pthread_create(i64* nonnull %thread, %union.pthread_attr_t* null, i8* (i8*)* nonnull @bar, i8* bitcast (i8** @GlobalVPtr to i8*)) + ret i32 0 +} + +declare !callback !0 dso_local i32 @pthread_create(i64*, %union.pthread_attr_t*, i8* (i8*)*, i8*) + +define internal i8* @foo(i8* %arg) { +entry: +; CHECK: ret i8* null + ret i8* %arg +} + +define internal i8* @bar(i8* %arg) { +entry: +; CHECK: ret i8* bitcast (i8** @GlobalVPtr to i8*) + ret i8* %arg +} + +!1 = !{i64 2, i64 3, i1 false} +!0 = !{!1} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/recursion.ll b/llvm/test/Transforms/Attributor/IPConstantProp/recursion.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/recursion.ll @@ -0,0 +1,15 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s + +; CHECK-NOT: %X + +define internal i32 @foo(i32 %X) { + %Y = call i32 @foo( i32 %X ) ; [#uses=1] + %Z = add i32 %Y, 1 ; [#uses=1] + ret i32 %Z +} + +define void @bar() { + call i32 @foo( i32 17 ) ; :1 [#uses=0] + ret void +} + diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/remove-call-inst.ll b/llvm/test/Transforms/Attributor/IPConstantProp/remove-call-inst.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/remove-call-inst.ll @@ -0,0 +1,39 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s --check-prefix=ATTRIBUTOR +; PR5596 + +; IPSCCP should propagate the 0 argument, eliminate the switch, and propagate +; the result. + +; CHECK: define i32 @main() #0 { +; CHECK-NEXT: entry: +; CHECK-NOT: call +; CHECK-NEXT: ret i32 123 + +; FIXME: Remove obsolete calls/instructions +; ATTRIBUTOR: define i32 @main() #0 { +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: call +; ATTRIBUTOR-NEXT: ret i32 123 + +define i32 @main() noreturn nounwind { +entry: + %call2 = tail call i32 @wwrite(i64 0) nounwind + ret i32 %call2 +} + +define internal i32 @wwrite(i64 %i) nounwind readnone { +entry: + switch i64 %i, label %sw.default [ + i64 3, label %return + i64 10, label %return + ] + +sw.default: + ret i32 123 + +return: + ret i32 0 +} + +; CHECK: attributes #0 = { noreturn nounwind } +; CHECK: attributes #1 = { nounwind readnone } diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll b/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll @@ -0,0 +1,60 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=10 < %s | FileCheck %s --check-prefix=ATTRIBUTOR + +; CHECK: add i32 1, 3 +; CHECK: store i32 %Z, i32* %Q +; ATTRIBUTOR: %Z = add i32 %X1, %X2 +; ATTRIBUTOR: store i32 %Z, i32* %W + +;; This function returns its second argument on all return statements +define internal i32* @incdec(i1 %C, i32* %V) { + %X = load i32, i32* %V + br i1 %C, label %T, label %F + +T: ; preds = %0 + %X1 = add i32 %X, 1 + store i32 %X1, i32* %V + ret i32* %V + +F: ; preds = %0 + %X2 = sub i32 %X, 1 + store i32 %X2, i32* %V + ret i32* %V +} + +;; This function returns its first argument as a part of a multiple return +;; value +define internal { i32, i32 } @foo(i32 %A, i32 %B) { + %X = add i32 %A, %B + %Y = insertvalue { i32, i32 } undef, i32 %A, 0 + %Z = insertvalue { i32, i32 } %Y, i32 %X, 1 + ret { i32, i32 } %Z +} + +define void @caller(i1 %C) personality i32 (...)* @__gxx_personality_v0 { + %Q = alloca i32 + ;; Call incdec to see if %W is properly replaced by %Q + %W = call i32* @incdec(i1 %C, i32* %Q ) ; [#uses=1] + ;; Call @foo twice, to prevent the arguments from propagating into the + ;; function (so we can check the returned argument is properly + ;; propagated per-caller). + %S1 = call { i32, i32 } @foo(i32 1, i32 2) + %X1 = extractvalue { i32, i32 } %S1, 0 + %S2 = invoke { i32, i32 } @foo(i32 3, i32 4) to label %OK unwind label %LPAD + +OK: + %X2 = extractvalue { i32, i32 } %S2, 0 + ;; Do some stuff with the returned values which we can grep for + %Z = add i32 %X1, %X2 + store i32 %Z, i32* %W + br label %RET + +LPAD: + %exn = landingpad {i8*, i32} + cleanup + br label %RET + +RET: + ret void +} + +declare i32 @__gxx_personality_v0(...) diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/return-constant.ll b/llvm/test/Transforms/Attributor/IPConstantProp/return-constant.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/return-constant.ll @@ -0,0 +1,38 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s --check-prefix=ATTRIBUTOR + +; CHECK: ret i1 true +; CHECK: ret i1 true +; CHECK-NOT: ret i1 true + +; FIXME: icmp folding is missing +; ATTRIBUTOR: ret i1 %Y +; ATTRIBUTOR: ret i1 %Y + +define internal i32 @foo(i1 %C) { + br i1 %C, label %T, label %F + +T: ; preds = %0 + ret i32 52 + +F: ; preds = %0 + ret i32 52 +} + +define i1 @caller(i1 %C) { + %X = call i32 @foo( i1 %C ) ; [#uses=1] + %Y = icmp ne i32 %X, 0 ; [#uses=1] + ret i1 %Y +} + +define i1 @invokecaller(i1 %C) personality i32 (...)* @__gxx_personality_v0 { + %X = invoke i32 @foo( i1 %C ) to label %OK unwind label %FAIL ; [#uses=1] +OK: + %Y = icmp ne i32 %X, 0 ; [#uses=1] + ret i1 %Y +FAIL: + %exn = landingpad {i8*, i32} + cleanup + ret i1 false +} + +declare i32 @__gxx_personality_v0(...) diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/return-constants.ll b/llvm/test/Transforms/Attributor/IPConstantProp/return-constants.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/return-constants.ll @@ -0,0 +1,52 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=7 < %s | FileCheck %s --check-prefix=ATTRIBUTOR + +;; Check that the 21 constants got propagated properly +; CHECK: %M = add i32 21, 21 +;; Check that the second return values didn't get propagated +; CHECK: %N = add i32 %B, %D + +;; FIXME: support for extractvalue and insertvalue missing. +; ATTRIBUTOR: %M = add i32 %A, %C +;; Check that the second return values didn't get propagated +; ATTRIBUTOR: %N = add i32 %B, %D + +%0 = type { i32, i32 } + +define internal %0 @foo(i1 %Q) { + br i1 %Q, label %T, label %F + +T: ; preds = %0 + %mrv = insertvalue %0 undef, i32 21, 0 + %mrv1 = insertvalue %0 %mrv, i32 22, 1 + ret %0 %mrv1 + +F: ; preds = %0 + %mrv2 = insertvalue %0 undef, i32 21, 0 + %mrv3 = insertvalue %0 %mrv2, i32 23, 1 + ret %0 %mrv3 +} + +define internal %0 @bar(i1 %Q) { + %A = insertvalue %0 undef, i32 21, 0 + br i1 %Q, label %T, label %F + +T: ; preds = %0 + %B = insertvalue %0 %A, i32 22, 1 + ret %0 %B + +F: ; preds = %0 + %C = insertvalue %0 %A, i32 23, 1 + ret %0 %C +} + +define %0 @caller(i1 %Q) { + %X = call %0 @foo(i1 %Q) + %A = extractvalue %0 %X, 0 + %B = extractvalue %0 %X, 1 + %Y = call %0 @bar(i1 %Q) + %C = extractvalue %0 %Y, 0 + %D = extractvalue %0 %Y, 1 + %M = add i32 %A, %C + %N = add i32 %B, %D + ret %0 %X +} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/solve-after-each-resolving-undefs-for-function.ll b/llvm/test/Transforms/Attributor/IPConstantProp/solve-after-each-resolving-undefs-for-function.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/solve-after-each-resolving-undefs-for-function.ll @@ -0,0 +1,59 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s --check-prefix=ATTRIBUTOR + +; CHECK-LABEL: @testf( +; CHECK: ret i32 undef +; ATTRIBUTOR-LABEL: @testf( +; ATTRIBUTOR-NOT: ret i32 +; ATTRIBUTOR: ret i32 10 +; ATTRIBUTOR-NOT: ret i32 +; +define internal i32 @testf(i1 %c) { +entry: + br i1 %c, label %if.cond, label %if.end + +if.cond: + br i1 undef, label %if.then, label %if.end + +if.then: ; preds = %entry, %if.then + ret i32 11 + +if.end: ; preds = %if.then1, %entry + ret i32 10 +} + +; CHECK-LABEL: @test1( +; CHECK: ret i32 undef +; FIXME: Fold the conditional +; ATTRIBUTOR-LABEL: @test1( +; ATTRIBUTOR: %res = icmp eq i32 10, 10 +; ATTRIBUTOR: br i1 %res, label %ret1, label %ret2 +; ATTRIBUTOR: ret i32 99 +; ATTRIBUTOR: ret i32 0 +; +define internal i32 @test1(i1 %c) { +entry: + br label %if.then + +if.then: ; preds = %entry, %if.then + %call = call i32 @testf(i1 %c) + %res = icmp eq i32 %call, 10 + br i1 %res, label %ret1, label %ret2 + +ret1: ; preds = %if.then, %entry + ret i32 99 + +ret2: ; preds = %if.then, %entry + ret i32 0 +} + +; CHECK-LABEL: @main( +; CHECK-NEXT: %res = call i32 @test1( +; CHECK-NEXT: ret i32 99 +; ATTRIBUTOR-LABEL: @main( +; ATTRIBUTOR-NEXT: %res = call i32 @test1( +; ATTRIBUTOR-NEXT: ret i32 %res +; +define i32 @main(i1 %c) { + %res = call i32 @test1(i1 %c) + ret i32 %res +} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/thread_local_acs.ll b/llvm/test/Transforms/Attributor/IPConstantProp/thread_local_acs.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/thread_local_acs.ll @@ -0,0 +1,42 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s +; +; #include +; thread_local int gtl = 0; +; int gsh = 0; +; +; static int callee(int *thread_local_ptr, int *shared_ptr) { +; return *thread_local_ptr + *shared_ptr; +; } +; +; void broker(int *, int (*callee)(int *, int *), int *); +; +; void caller() { +; broker(>l, callee, &gsh); +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +@gtl = dso_local thread_local global i32 0, align 4 +@gsh = dso_local global i32 0, align 4 + +define internal i32 @callee(i32* %thread_local_ptr, i32* %shared_ptr) { +entry: +; CHECK: %tmp = load i32, i32* %thread_local_ptr, align 4 +; CHECK: %tmp1 = load i32, i32* @gsh, align 4 +; CHECK: %add = add nsw i32 %tmp, %tmp1 + %tmp = load i32, i32* %thread_local_ptr, align 4 + %tmp1 = load i32, i32* %shared_ptr, align 4 + %add = add nsw i32 %tmp, %tmp1 + ret i32 %add +} + +define dso_local void @caller() { +entry: + call void @broker(i32* nonnull @gtl, i32 (i32*, i32*)* nonnull @callee, i32* nonnull @gsh) + ret void +} + +declare !callback !0 dso_local void @broker(i32*, i32 (i32*, i32*)*, i32*) + +!1 = !{i64 1, i64 0, i64 2, i1 false} +!0 = !{!1} diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/user-with-multiple-uses.ll b/llvm/test/Transforms/Attributor/IPConstantProp/user-with-multiple-uses.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/IPConstantProp/user-with-multiple-uses.ll @@ -0,0 +1,30 @@ +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s +; PR5596 + +; IPSCCP should propagate the 0 argument, eliminate the switch, and propagate +; the result. + +; CHECK: define i32 @main() #0 { +; CHECK-NEXT: entry: +; CHECK-NEXT: %call2 = tail call i32 @wwrite(i64 0) [[NUW:#[0-9]+]] +; CHECK-NEXT: ret i32 123 + +define i32 @main() noreturn nounwind { +entry: + %call2 = tail call i32 @wwrite(i64 0) nounwind + ret i32 %call2 +} + +define internal i32 @wwrite(i64 %i) nounwind { +entry: + switch i64 %i, label %sw.default [ + i64 3, label %return + i64 10, label %return + ] + +sw.default: + ret i32 123 + +return: + ret i32 0 +}