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,25 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes +; RUN: opt < %s -passes=attributor -S | FileCheck %s +; Should not propagate the result of a weak function. +; PR2411 + +define weak i32 @foo() nounwind { +; CHECK-LABEL: define {{[^@]+}}@foo() +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i32 1 +; +entry: + ret i32 1 +} + +define i32 @main() nounwind { +; CHECK-LABEL: define {{[^@]+}}@main() +; CHECK-NEXT: entry: +; CHECK-NEXT: [[R:%.*]] = call i32 @foo() +; CHECK-NEXT: ret i32 [[R]] +; +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,102 @@ +; 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=2 < %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 { +; CHECK-LABEL: define {{[^@]+}}@vfu1 +; CHECK-SAME: (%struct.MYstr* nocapture nofree nonnull writeonly byval align 8 dereferenceable(8) [[U:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* [[U]], i32 0, i32 1 +; CHECK-NEXT: store i32 99, i32* [[TMP0]], align 4 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* [[U]], i32 0, i32 0 +; CHECK-NEXT: store i8 97, i8* [[TMP1]], align 8 +; CHECK-NEXT: br label [[RETURN:%.*]] +; CHECK: return: +; CHECK-NEXT: ret void +; +entry: + %0 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 1 ; [#uses=1] + store i32 99, i32* %0, align 4 + %1 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 0 ; [#uses=1] + store i8 97, i8* %1, align 4 + br label %return + +return: ; preds = %entry + ret void +} + +define internal i32 @vfu2(%struct.MYstr* byval align 4 %u) nounwind readonly { +; CHECK-LABEL: define {{[^@]+}}@vfu2 +; CHECK-SAME: (%struct.MYstr* nocapture nofree nonnull readonly byval align 8 dereferenceable(8) [[U:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* @mystr, i32 0, i32 1 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP0]] +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* @mystr, i32 0, i32 0 +; CHECK-NEXT: [[TMP3:%.*]] = load i8, i8* [[TMP2]], align 8 +; CHECK-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i32 +; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[TMP4]], [[TMP1]] +; CHECK-NEXT: ret i32 [[TMP5]] +; +entry: + %0 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 1 ; [#uses=1] + %1 = load i32, i32* %0 + %2 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 0 ; [#uses=1] + %3 = load i8, i8* %2 + %4 = zext i8 %3 to i32 + %5 = add i32 %4, %1 + ret i32 %5 +} + +define i32 @unions() nounwind { +; CHECK-LABEL: define {{[^@]+}}@unions() +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @vfu1(%struct.MYstr* nofree nonnull byval align 8 dereferenceable(8) @mystr) +; CHECK-NEXT: [[RESULT:%.*]] = call i32 @vfu2(%struct.MYstr* nofree nonnull byval align 8 dereferenceable(8) @mystr) +; CHECK-NEXT: ret i32 [[RESULT]] +; +entry: + call void @vfu1(%struct.MYstr* byval align 4 @mystr) nounwind + %result = call i32 @vfu2(%struct.MYstr* byval align 4 @mystr) nounwind + ret i32 %result +} + +define internal i32 @vfu2_v2(%struct.MYstr* byval align 4 %u) nounwind readonly { +; CHECK-LABEL: define {{[^@]+}}@vfu2_v2 +; CHECK-SAME: (%struct.MYstr* nocapture nofree nonnull byval align 8 dereferenceable(8) [[U:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[Z:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* %u, i32 0, i32 1 +; CHECK-NEXT: store i32 99, i32* [[Z]], align 4 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* %u, i32 0, i32 1 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP0]] +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* %u, i32 0, i32 0 +; CHECK-NEXT: [[TMP3:%.*]] = load i8, i8* [[TMP2]], align 8 +; CHECK-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i32 +; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[TMP4]], [[TMP1]] +; CHECK-NEXT: ret i32 [[TMP5]] +; +entry: + %z = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 1 + store i32 99, i32* %z, align 4 + %0 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 1 ; [#uses=1] + %1 = load i32, i32* %0 + %2 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 0 ; [#uses=1] + %3 = load i8, i8* %2 + %4 = zext i8 %3 to i32 + %5 = add i32 %4, %1 + ret i32 %5 +} + +define i32 @unions_v2() nounwind { +; CHECK-LABEL: define {{[^@]+}}@unions_v2() +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @vfu1(%struct.MYstr* nofree nonnull byval align 8 dereferenceable(8) @mystr) +; CHECK-NEXT: [[RESULT:%.*]] = call i32 @vfu2_v2(%struct.MYstr* nofree nonnull byval align 8 dereferenceable(8) @mystr) +; CHECK-NEXT: ret i32 [[RESULT]] +; +entry: + call void @vfu1(%struct.MYstr* byval align 4 @mystr) nounwind + %result = call i32 @vfu2_v2(%struct.MYstr* byval align 4 @mystr) nounwind + 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,34 @@ +; 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 + +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: [[CONV:%.*]] = sext i32 undef to i64 +; CHECK-NEXT: [[DIV:%.*]] = sdiv i64 8, [[CONV]] +; CHECK-NEXT: [[CALL2:%.*]] = call i64 @fn1(i64 [[DIV]]) +; CHECK-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 returned [[P1:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i64 [[P1]], 0 +; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i64 [[P1]], i64 [[P1]] +; CHECK-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,90 @@ +; 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 +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) { +; CHECK-LABEL: define {{[^@]+}}@fn2 +; CHECK-SAME: (i32* nocapture nofree writeonly [[P:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[IF_END:%.*]] +; CHECK: for.cond1: +; CHECK-NEXT: br i1 undef, label [[IF_END]], label [[IF_END]] +; CHECK: if.end: +; CHECK-NEXT: [[E_2:%.*]] = phi i32* [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ], [ null, [[FOR_COND1]] ] +; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[E_2]], align 4 +; CHECK-NEXT: [[CALL:%.*]] = call i32 @fn1(i32 [[TMP0]]) +; CHECK-NEXT: store i32 [[CALL]], i32* [[P]] +; CHECK-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) { +; CHECK-LABEL: define {{[^@]+}}@fn1 +; CHECK-SAME: (i32 returned [[P1:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0 +; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]] +; CHECK-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 { +; CHECK-LABEL: define {{[^@]+}}@fn_no_null_opt +; CHECK-SAME: (i32* nocapture nofree writeonly [[P:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[IF_END:%.*]] +; CHECK: for.cond1: +; CHECK-NEXT: br i1 undef, label [[IF_END]], label [[IF_END]] +; CHECK: if.end: +; CHECK-NEXT: [[E_2:%.*]] = phi i32* [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ], [ null, [[FOR_COND1]] ] +; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[E_2]], align 4 +; CHECK-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]]) +; CHECK-NEXT: store i32 [[CALL]], i32* [[P]] +; CHECK-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) { +; CHECK-LABEL: define {{[^@]+}}@fn0 +; CHECK-SAME: (i32 returned [[P1:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0 +; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]] +; CHECK-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,30 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes +; RUN: opt -S -passes=attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s + +%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 { +; CHECK-LABEL: define {{[^@]+}}@widget +; CHECK-SAME: (<8 x i32> [[ARG:%.*]]) local_unnamed_addr +; CHECK-NEXT: bb: +; CHECK-NEXT: ret [[STRUCT_ZOT:%.*]] undef +; +bb: + ret %struct.zot undef +} + +define void @baz(<8 x i32> %arg) local_unnamed_addr { +; CHECK-LABEL: define {{[^@]+}}@baz +; CHECK-SAME: (<8 x i32> [[ARG:%.*]]) local_unnamed_addr +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP1:%.*]] = extractvalue [[STRUCT_ZOT:%.*]] undef, 0, 0 +; CHECK-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,85 @@ +; 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 + +; 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: define {{[^@]+}}@foo +; CHECK-SAME: (i16 [[A:%.*]]) +; 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: define {{[^@]+}}@bar +; CHECK-SAME: (i16 [[P1:%.*]], i16 [[P2:%.*]]) +; 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) { +; CHECK-LABEL: define {{[^@]+}}@vararg_tests +; CHECK-SAME: (i16 [[A:%.*]]) +; CHECK-NEXT: [[CALL1:%.*]] = call i16 (i16, ...) @vararg_prop(i16 7, i16 8, i16 [[A]]) +; CHECK-NEXT: [[CALL2:%.*]] = call i16 bitcast (i16 (i16, i16, ...)* @vararg_no_prop to i16 (i16)*)(i16 7) +; CHECK-NEXT: [[ADD:%.*]] = add i16 [[CALL1]], [[CALL2]] +; CHECK-NEXT: ret i16 [[ADD]] +; + %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 {{[^@]+}}@vararg_prop +; CHECK-SAME: (i16 returned [[P1:%.*]], ...) +; CHECK-NEXT: ret i16 7 +; + ret i16 %p1 +} + +define internal i16 @vararg_no_prop(i16 %p1, i16 %p2, ...) { +; CHECK-LABEL: define {{[^@]+}}@vararg_no_prop +; CHECK-SAME: (i16 returned [[P1:%.*]], i16 [[P2:%.*]], ...) +; 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,25 @@ +; 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 + +; 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: define {{[^@]+}}@foo +; CHECK-SAME: (i16 [[A:%.*]]) +; 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: define {{[^@]+}}@bar +; CHECK-SAME: (i16 [[P1:%.*]], i16 returned [[P2:%.*]]) +; 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,34 @@ +; 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 + +; See PR26774 + +define i32 @baz() { +; CHECK-LABEL: define {{[^@]+}}@baz() +; CHECK-NEXT: ret i32 10 +; + 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: define {{[^@]+}}@foo() +; CHECK-NEXT: [[VAL:%.*]] = call i32 @baz() +; CHECK-NEXT: ret i32 10 +; + + %val = call i32 @baz() + ret i32 %val +} + +define i32 @bar() { +; CHECK-LABEL: define {{[^@]+}}@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,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes +; 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,7 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes +; 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,78 @@ +; 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 +target datalayout = "E-m:e-i64:64-n32:64" +target triple = "powerpc64-bgq-linux" + +define void @test(i32 signext %n) { +; CHECK-LABEL: define {{[^@]+}}@test +; CHECK-SAME: (i32 signext [[N:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 undef, label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: unreachable +; CHECK: if.end: +; CHECK-NEXT: unreachable +; CHECK: if.then2: +; CHECK-NEXT: unreachable +; CHECK: if.end4: +; CHECK-NEXT: unreachable +; CHECK: if.then9: +; CHECK-NEXT: unreachable +; CHECK: if.then12: +; CHECK-NEXT: unreachable +; CHECK: if.else14: +; CHECK-NEXT: unreachable +; CHECK: do.body: +; CHECK-NEXT: unreachable +; CHECK: if.then33: +; CHECK-NEXT: unreachable +; CHECK: cond.false.i28: +; CHECK-NEXT: unreachable +; CHECK: _ZN5boost4math4signIgEEiRKT_.exit30: +; CHECK-NEXT: unreachable +; + +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,40 @@ +; 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 + +@_ZL6test1g = internal global i32 42, align 4 + +define void @_Z7test1f1v() nounwind { +; CHECK-LABEL: define {{[^@]+}}@_Z7test1f1v() +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP:%.*]] = load i32, i32* @_ZL6test1g, align 4 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP]], 0 +; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: store i32 0, i32* @_ZL6test1g, align 4 +; CHECK-NEXT: br label [[IF_END]] +; CHECK: if.end: +; CHECK-NEXT: ret void +; +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 +} + +define i32 @_Z7test1f2v() nounwind { +; CHECK-LABEL: define {{[^@]+}}@_Z7test1f2v() +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP:%.*]] = load i32, i32* @_ZL6test1g, align 4 +; CHECK-NEXT: ret i32 [[TMP]] +; +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,117 @@ +; 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 +; +; +; /---------------------------------------| +; | /----------------------|----| +; | | /-----| | +; 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) { +; CHECK-LABEL: define {{[^@]+}}@cb0 +; CHECK-SAME: (i32 returned [[ZERO:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i32 0 +; +entry: + ret i32 %zero +} + +define internal i32 @cb1(i32 %unknown) { +; CHECK-LABEL: define {{[^@]+}}@cb1 +; CHECK-SAME: (i32 returned [[UNKNOWN:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i32 [[UNKNOWN]] +; +entry: + ret i32 %unknown +} + +define internal i32 @cb2(i32 %unknown) { +; CHECK-LABEL: define {{[^@]+}}@cb2 +; CHECK-SAME: (i32 returned [[UNKNOWN:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CALL:%.*]] = call i32 @cb0(i32 0) +; CHECK-NEXT: ret i32 [[UNKNOWN]] +; +entry: + %call = call i32 @cb0(i32 0) + ret i32 %unknown +} + +define internal i32 @cb3(i32 %unknown) { +; CHECK-LABEL: define {{[^@]+}}@cb3 +; CHECK-SAME: (i32 returned [[UNKNOWN:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i32 [[UNKNOWN]] +; +entry: + ret i32 %unknown +} + +define internal i32 @cb4(i32 %unknown) { +; CHECK-LABEL: define {{[^@]+}}@cb4 +; CHECK-SAME: (i32 returned [[UNKNOWN:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i32 [[UNKNOWN]] +; +entry: + ret i32 %unknown +} + +define void @foo() { +; CHECK-LABEL: define {{[^@]+}}@foo() +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CALL:%.*]] = call i32 @cb0(i32 0) +; CHECK-NEXT: [[CALL1:%.*]] = call i32 @cb3(i32 1) +; CHECK-NEXT: call void @broker(i32 (i32)* nonnull @cb0, i32 (i32)* nonnull @cb1, i32 (i32)* nonnull @cb0, i32 0, i32 1) +; CHECK-NEXT: call void @broker(i32 (i32)* nonnull @cb1, i32 (i32)* nonnull @cb2, i32 (i32)* nonnull @cb2, i32 0, i32 1) +; CHECK-NEXT: call void @broker(i32 (i32)* nonnull @cb3, i32 (i32)* nonnull @cb2, i32 (i32)* nonnull @cb3, i32 0, i32 1) +; CHECK-NEXT: call void @broker(i32 (i32)* nonnull @cb4, i32 (i32)* nonnull @cb4, i32 (i32)* nonnull @cb4, i32 0, i32 1) +; CHECK-NEXT: ret void +; +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,79 @@ +; 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=4 < %s | FileCheck %s +; 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) { +; CHECK-LABEL: define {{[^@]+}}@start +; CHECK-SAME: (i8 [[V:%.*]]) +; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[V]], 0 +; CHECK-NEXT: br i1 [[C1]], label [[TRUE:%.*]], label [[FALSE:%.*]] +; CHECK: true: +; CHECK-NEXT: [[CA:%.*]] = musttail call i8* @side_effects(i8 [[V]]) +; CHECK-NEXT: ret i8* [[CA]] +; CHECK: false: +; CHECK-NEXT: [[C2:%.*]] = icmp eq i8 [[V]], 1 +; CHECK-NEXT: br i1 [[C2]], label [[C2_TRUE:%.*]], label [[C2_FALSE:%.*]] +; CHECK: c2_true: +; CHECK-NEXT: [[CA1:%.*]] = musttail call i8* @no_side_effects(i8 undef) +; CHECK-NEXT: ret i8* [[CA1]] +; CHECK: c2_false: +; CHECK-NEXT: [[CA2:%.*]] = musttail call i8* @dont_zap_me(i8 undef) +; CHECK-NEXT: ret i8* [[CA2]] +; + %c1 = icmp eq i8 %v, 0 + br i1 %c1, label %true, label %false +true: + ; FIXME: propagate the value information for %v + %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) + ; FIXME: zap this call + ret i8* %ca1 +c2_false: + %ca2 = musttail call i8* @dont_zap_me(i8 %v) + ret i8* %ca2 +} + +define internal i8* @side_effects(i8 %v) { +; CHECK-LABEL: define {{[^@]+}}@side_effects +; CHECK-SAME: (i8 [[V:%.*]]) +; CHECK-NEXT: [[I1:%.*]] = call i32 @external() +; CHECK-NEXT: [[CA:%.*]] = musttail call i8* @start(i8 [[V]]) +; CHECK-NEXT: ret i8* [[CA]] +; + %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. + + %ca = musttail call i8* @start(i8 %v) + + ; Thus the result must be returned anyway + ret i8* %ca +} + +define internal i8* @no_side_effects(i8 %v) readonly nounwind { +; CHECK-LABEL: define {{[^@]+}}@no_side_effects +; CHECK-SAME: (i8 [[V:%.*]]) +; CHECK-NEXT: ret i8* null +; + ret i8* null +} + +define internal i8* @dont_zap_me(i8 %v) { +; CHECK-LABEL: define {{[^@]+}}@dont_zap_me +; CHECK-SAME: (i8 [[V:%.*]]) +; CHECK-NEXT: [[I1:%.*]] = call i32 @external() +; CHECK-NEXT: ret i8* null +; + %i1 = call i32 @external() + 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,47 @@ +; 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 + +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 { +; CHECK-LABEL: define {{[^@]+}}@dipsy +; CHECK-SAME: (i32 [[TMP0:%.*]], i32 [[TMP1:%.*]]) local_unnamed_addr +; CHECK-NEXT: BasicBlock0: +; CHECK-NEXT: 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", ""() +; CHECK-NEXT: ret i32 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 { +; CHECK-LABEL: define {{[^@]+}}@tinkywinky +; CHECK-SAME: (i32 [[TMP0:%.*]], i32 [[TMP1:%.*]], i32 [[TMP2:%.*]]) local_unnamed_addr +; CHECK-NEXT: BasicBlock1: +; CHECK-NEXT: 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", ""() +; CHECK-NEXT: ret void +; +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 { +; CHECK-LABEL: define {{[^@]+}}@patatino +; CHECK-SAME: (i32 [[TMP0:%.*]], i32 [[TMP1:%.*]], i32 [[TMP2:%.*]]) local_unnamed_addr +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @dipsy(i32 [[TMP0]], i32 [[TMP1]]) +; CHECK-NEXT: tail call void @tinkywinky(i32 [[TMP3]], i32 [[TMP2]], i32 [[TMP1]]) +; CHECK-NEXT: ret void +; +bb: + %3 = tail call i32 @dipsy(i32 %0, i32 %1) #0 +; Check that we don't accidentally propagate zero. + 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,132 @@ +; 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=2 < %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) { +; CHECK-LABEL: define {{[^@]+}}@foo +; CHECK-SAME: (i32 [[N:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[N_ADDR:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[P:%.*]] = alloca float, align 4 +; CHECK-NEXT: store i32 [[N]], i32* [[N_ADDR]], align 4 +; CHECK-NEXT: store float 3.000000e+00, float* [[P]], align 4 +; CHECK-NEXT: store i32 7, i32* [[N_ADDR]], align 4 +; CHECK-NEXT: 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 align 4 dereferenceable(4) [[N_ADDR]], float* nonnull align 4 dereferenceable(4) [[P]], i64 4617315517961601024) +; CHECK-NEXT: ret void +; +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,65 @@ +; 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 +; +; #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() { +; CHECK-LABEL: define {{[^@]+}}@main() +; CHECK-NEXT: entry: +; CHECK-NEXT: [[THREAD:%.*]] = alloca i64, align 8 +; CHECK-NEXT: [[CALL:%.*]] = call i32 @pthread_create(i64* nonnull [[THREAD]], %union.pthread_attr_t* null, i8* (i8*)* nonnull @foo, i8* noalias null) +; CHECK-NEXT: [[CALL1:%.*]] = call i32 @pthread_create(i64* nonnull [[THREAD]], %union.pthread_attr_t* null, i8* (i8*)* nonnull @bar, i8* nonnull align 8 dereferenceable(8) bitcast (i8** @GlobalVPtr to i8*)) +; CHECK-NEXT: ret i32 0 +; +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) { +; CHECK-LABEL: define {{[^@]+}}@foo +; CHECK-SAME: (i8* noalias nofree readnone returned [[ARG:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i8* null +; +entry: + ret i8* %arg +} + +define internal i8* @bar(i8* %arg) { +; CHECK-LABEL: define {{[^@]+}}@bar +; CHECK-SAME: (i8* nofree nonnull readnone returned align 8 dereferenceable(8) [[ARG:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: ret i8* bitcast (i8** @GlobalVPtr to i8*) +; +entry: + 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,21 @@ +; 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=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() { +; CHECK-LABEL: define {{[^@]+}}@bar() +; CHECK-NEXT: unreachable +; CHECK: .split: +; CHECK-NEXT: unreachable +; + 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,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=2 < %s | FileCheck %s +; PR5596 + +; IPSCCP should propagate the 0 argument, eliminate the switch, and propagate +; the result. + +; FIXME: Remove obsolete calls/instructions + +define i32 @main() noreturn nounwind { +; CHECK-LABEL: define {{[^@]+}}@main() +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CALL2:%.*]] = tail call i32 @wwrite(i64 0) +; CHECK-NEXT: ret i32 123 +; +entry: + %call2 = tail call i32 @wwrite(i64 0) nounwind + ret i32 %call2 +} + +define internal i32 @wwrite(i64 %i) nounwind readnone { +; CHECK-LABEL: define {{[^@]+}}@wwrite +; CHECK-SAME: (i64 [[I:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i64 0, label [[SW_DEFAULT:%.*]] [ +; CHECK-NEXT: i64 3, label [[RETURN:%.*]] +; CHECK-NEXT: i64 10, label [[RETURN]] +; CHECK-NEXT: ] +; CHECK: sw.default: +; CHECK-NEXT: ret i32 123 +; CHECK: return: +; CHECK-NEXT: unreachable +; +entry: + switch i64 %i, label %sw.default [ + i64 3, label %return + i64 10, label %return + ] + +sw.default: + ret i32 123 + +return: + ret i32 0 +} 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,96 @@ +; 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=9 < %s | FileCheck %s + +;; This function returns its second argument on all return statements +define internal i32* @incdec(i1 %C, i32* %V) { +; CHECK-LABEL: define {{[^@]+}}@incdec +; CHECK-SAME: (i1 [[C:%.*]], i32* noalias nofree nonnull returned align 4 dereferenceable(4) [[V:%.*]]) +; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[V]], align 4 +; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; CHECK: T: +; CHECK-NEXT: [[X1:%.*]] = add i32 [[X]], 1 +; CHECK-NEXT: store i32 [[X1]], i32* [[V]], align 4 +; CHECK-NEXT: ret i32* [[V]] +; CHECK: F: +; CHECK-NEXT: [[X2:%.*]] = sub i32 [[X]], 1 +; CHECK-NEXT: store i32 [[X2]], i32* [[V]], align 4 +; CHECK-NEXT: ret 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) { +; CHECK-LABEL: define {{[^@]+}}@foo +; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) +; CHECK-NEXT: [[X:%.*]] = add i32 [[A]], [[B]] +; CHECK-NEXT: [[Y:%.*]] = insertvalue { i32, i32 } undef, i32 [[A]], 0 +; CHECK-NEXT: [[Z:%.*]] = insertvalue { i32, i32 } [[Y]], i32 [[X]], 1 +; CHECK-NEXT: ret { i32, i32 } [[Z]] +; + %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 { +; CHECK-LABEL: define {{[^@]+}}@caller +; CHECK-SAME: (i1 [[C:%.*]]) #2 personality i32 (...)* @__gxx_personality_v0 +; CHECK-NEXT: [[Q:%.*]] = alloca i32 +; CHECK-NEXT: [[W:%.*]] = call align 4 i32* @incdec(i1 [[C]], i32* noalias nofree nonnull align 4 dereferenceable(4) [[Q]]) +; CHECK-NEXT: [[S1:%.*]] = call { i32, i32 } @foo(i32 1, i32 2) +; CHECK-NEXT: [[X1:%.*]] = extractvalue { i32, i32 } [[S1]], 0 +; CHECK-NEXT: [[S2:%.*]] = invoke { i32, i32 } @foo(i32 3, i32 4) +; CHECK-NEXT: to label [[OK:%.*]] unwind label [[LPAD:%.*]] +; CHECK: OK: +; CHECK-NEXT: [[X2:%.*]] = extractvalue { i32, i32 } [[S2]], 0 +; CHECK-NEXT: [[Z:%.*]] = add i32 [[X1]], [[X2]] +; CHECK-NEXT: store i32 [[Z]], i32* [[W]], align 4 +; CHECK-NEXT: br label [[RET:%.*]] +; CHECK: LPAD: +; CHECK-NEXT: [[EXN:%.*]] = landingpad { i8*, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: br label [[RET]] +; CHECK: RET: +; CHECK-NEXT: ret void +; + %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,59 @@ +; 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 + +; FIXME: icmp folding is missing + +define internal i32 @foo(i1 %C) { +; CHECK-LABEL: define {{[^@]+}}@foo +; CHECK-SAME: (i1 [[C:%.*]]) +; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; CHECK: T: +; CHECK-NEXT: ret i32 52 +; CHECK: F: +; CHECK-NEXT: ret i32 52 +; + br i1 %C, label %T, label %F + +T: ; preds = %0 + ret i32 52 + +F: ; preds = %0 + ret i32 52 +} + +define i1 @caller(i1 %C) { +; CHECK-LABEL: define {{[^@]+}}@caller +; CHECK-SAME: (i1 [[C:%.*]]) +; CHECK-NEXT: [[X:%.*]] = call i32 @foo(i1 [[C]]) +; CHECK-NEXT: [[Y:%.*]] = icmp ne i32 52, 0 +; CHECK-NEXT: ret i1 [[Y]] +; + %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 { +; CHECK-LABEL: define {{[^@]+}}@invokecaller +; CHECK-SAME: (i1 [[C:%.*]]) #0 personality i32 (...)* @__gxx_personality_v0 +; CHECK-NEXT: [[X:%.*]] = call i32 @foo(i1 [[C]]) +; CHECK-NEXT: br label [[OK:%.*]] +; CHECK: .i2c: +; CHECK-NEXT: unreachable +; CHECK: OK: +; CHECK-NEXT: [[Y:%.*]] = icmp ne i32 52, 0 +; CHECK-NEXT: ret i1 [[Y]] +; CHECK: FAIL: +; CHECK-NEXT: unreachable +; + %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,69 @@ +; 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=7 < %s | FileCheck %s + +;; FIXME: support for extractvalue and insertvalue missing. + +%0 = type { i32, i32 } + +define internal %0 @foo(i1 %Q) { +; CHECK-LABEL: define {{[^@]+}}@foo +; CHECK-SAME: (i1 [[Q:%.*]]) +; CHECK-NEXT: br i1 [[Q]], label [[T:%.*]], label [[F:%.*]] +; CHECK: T: +; CHECK-NEXT: [[MRV:%.*]] = insertvalue [[TMP0:%.*]] undef, i32 21, 0 +; CHECK-NEXT: [[MRV1:%.*]] = insertvalue [[TMP0]] %mrv, i32 22, 1 +; CHECK-NEXT: ret [[TMP0]] %mrv1 +; CHECK: F: +; CHECK-NEXT: [[MRV2:%.*]] = insertvalue [[TMP0]] undef, i32 21, 0 +; CHECK-NEXT: [[MRV3:%.*]] = insertvalue [[TMP0]] %mrv2, i32 23, 1 +; CHECK-NEXT: ret [[TMP0]] %mrv3 +; + 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) { +; CHECK-LABEL: define {{[^@]+}}@caller +; CHECK-SAME: (i1 [[Q:%.*]]) +; CHECK-NEXT: [[X:%.*]] = call [[TMP0:%.*]] @foo(i1 [[Q]]) +; CHECK-NEXT: [[A:%.*]] = extractvalue [[TMP0]] %X, 0 +; CHECK-NEXT: [[B:%.*]] = extractvalue [[TMP0]] %X, 1 +; CHECK-NEXT: [[C:%.*]] = extractvalue [[TMP0]] undef, 0 +; CHECK-NEXT: [[D:%.*]] = extractvalue [[TMP0]] undef, 1 +; CHECK-NEXT: [[M:%.*]] = add i32 [[A]], [[C]] +; CHECK-NEXT: [[N:%.*]] = add i32 [[B]], [[D]] +; CHECK-NEXT: ret [[TMP0]] %X +; + %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 +;; Check that the second return values didn't get propagated + %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,66 @@ +; 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=2 < %s | FileCheck %s + +define internal i32 @testf(i1 %c) { +; CHECK-LABEL: define {{[^@]+}}@testf +; CHECK-SAME: (i1 [[C:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C]], label [[IF_COND:%.*]], label [[IF_END:%.*]] +; CHECK: if.cond: +; CHECK-NEXT: br i1 undef, label [[IF_THEN:%.*]], label [[IF_END]] +; CHECK: if.then: +; CHECK-NEXT: unreachable +; CHECK: if.end: +; CHECK-NEXT: ret i32 10 +; +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 +} + +define internal i32 @test1(i1 %c) { +; CHECK-LABEL: define {{[^@]+}}@test1 +; CHECK-SAME: (i1 [[C:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[IF_THEN:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[CALL:%.*]] = call i32 @testf(i1 [[C]]) +; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 10, 10 +; CHECK-NEXT: br i1 [[RES]], label [[RET1:%.*]], label [[RET2:%.*]] +; CHECK: ret1: +; CHECK-NEXT: ret i32 99 +; CHECK: ret2: +; CHECK-NEXT: ret i32 0 +; +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 +} + +define i32 @main(i1 %c) { +; CHECK-LABEL: define {{[^@]+}}@main +; CHECK-SAME: (i1 [[C:%.*]]) +; CHECK-NEXT: [[RES:%.*]] = call i32 @test1(i1 [[C]]) +; CHECK-NEXT: ret i32 [[RES]] +; + %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,53 @@ +; 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 +; +; #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) { +; CHECK-LABEL: define {{[^@]+}}@callee +; CHECK-SAME: (i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) [[THREAD_LOCAL_PTR:%.*]], i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) [[SHARED_PTR:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP:%.*]] = load i32, i32* [[THREAD_LOCAL_PTR]], align 4 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @gsh, align 4 +; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP]], [[TMP1]] +; CHECK-NEXT: ret i32 [[ADD]] +; +entry: + %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() { +; CHECK-LABEL: define {{[^@]+}}@caller() +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @broker(i32* nonnull align 4 dereferenceable(4) @gtl, i32 (i32*, i32*)* nonnull @callee, i32* nonnull align 4 dereferenceable(4) @gsh) +; CHECK-NEXT: ret void +; +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}