[llvm] 3da7efe - [Attributor] Reuse the IPConstantProp tests for the Attributor

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 13 20:04:19 PST 2019


Author: Johannes Doerfert
Date: 2019-12-13T22:03:26-06:00
New Revision: 3da7efedaa77f8fd8d40294c9e79716003517e6d

URL: https://github.com/llvm/llvm-project/commit/3da7efedaa77f8fd8d40294c9e79716003517e6d
DIFF: https://github.com/llvm/llvm-project/commit/3da7efedaa77f8fd8d40294c9e79716003517e6d.diff

LOG: [Attributor] Reuse the IPConstantProp tests for the Attributor

The Attributor can, to some degree, do what IPConstantProp does. We can
consequently use the corner cases already collected and tested for in
the IPConstantProp tests to improve Attributor test coverage.

This exposed various bugs fixed in previous Attributor patches.

Not all functionality of IPConstantProp is available in AAValueSimplify
and AAIsDead so some tests show that we cannot perform the expected
constant propagation.

Reviewers: fhahn, efriedma, mssimpso, davide

Subscribers: bollu, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D69748

Added: 
    llvm/test/Transforms/Attributor/IPConstantProp/2008-06-09-WeakProp.ll
    llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll
    llvm/test/Transforms/Attributor/IPConstantProp/PR16052.ll
    llvm/test/Transforms/Attributor/IPConstantProp/PR26044.ll
    llvm/test/Transforms/Attributor/IPConstantProp/PR43857.ll
    llvm/test/Transforms/Attributor/IPConstantProp/arg-count-mismatch.ll
    llvm/test/Transforms/Attributor/IPConstantProp/arg-type-mismatch.ll
    llvm/test/Transforms/Attributor/IPConstantProp/comdat-ipo.ll
    llvm/test/Transforms/Attributor/IPConstantProp/dangling-block-address.ll
    llvm/test/Transforms/Attributor/IPConstantProp/deadarg.ll
    llvm/test/Transforms/Attributor/IPConstantProp/fp-bc-icmp-const-fold.ll
    llvm/test/Transforms/Attributor/IPConstantProp/global.ll
    llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll
    llvm/test/Transforms/Attributor/IPConstantProp/musttail-call.ll
    llvm/test/Transforms/Attributor/IPConstantProp/naked-return.ll
    llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll
    llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll
    llvm/test/Transforms/Attributor/IPConstantProp/recursion.ll
    llvm/test/Transforms/Attributor/IPConstantProp/remove-call-inst.ll
    llvm/test/Transforms/Attributor/IPConstantProp/return-argument.ll
    llvm/test/Transforms/Attributor/IPConstantProp/return-constant.ll
    llvm/test/Transforms/Attributor/IPConstantProp/return-constants.ll
    llvm/test/Transforms/Attributor/IPConstantProp/solve-after-each-resolving-undefs-for-function.ll
    llvm/test/Transforms/Attributor/IPConstantProp/thread_local_acs.ll

Modified: 
    

Removed: 
    


################################################################################
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
index 000000000000..d05e3b113191
--- /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
index 000000000000..fa3b038f7a99
--- /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 }
+ at 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 ; <i32*> [#uses=1]
+  store i32 99, i32* %0, align 4
+  %1 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 0 ; <i8*> [#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 ; <i32*> [#uses=1]
+  %1 = load i32, i32* %0
+  %2 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 0 ; <i8*> [#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 ; <i32*> [#uses=1]
+  %1 = load i32, i32* %0
+  %2 = getelementptr %struct.MYstr, %struct.MYstr* %u, i32 0, i32 0 ; <i8*> [#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
index 000000000000..6ec7dde13f25
--- /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
index 000000000000..a837b962a883
--- /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
index 000000000000..82c404ea2887
--- /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
index 000000000000..cd8a9b4baa66
--- /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
index 000000000000..e7f8705675bb
--- /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
index 000000000000..2f50a9c3d203
--- /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
index 000000000000..679067650870
--- /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*)]
+
+ at code = global [5 x i32] [i32 0, i32 0, i32 0, i32 0, i32 1], align 4 ; <[5 x i32]*> [#uses=0]
+ at 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                        ; <i32*> [#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               ; <i32> [#uses=1]
+  br label %indirectgoto
+
+end:                                              ; preds = %indirectgoto
+  ret void
+
+indirectgoto:                                     ; preds = %lab0, %entry
+  %indvar = phi i32 [ %indvar.next, %lab0 ], [ 0, %entry ] ; <i32> [#uses=2]
+  %pc.addr.0 = getelementptr i32, i32* %pc, i32 %indvar ; <i32*> [#uses=1]
+  %tmp1.pn = load i32, i32* %pc.addr.0                 ; <i32> [#uses=1]
+  %indirect.goto.dest.in = getelementptr inbounds [2 x i8*], [2 x i8*]* @bar.l, i32 0, i32 %tmp1.pn ; <i8**> [#uses=1]
+  %indirect.goto.dest = load i8*, i8** %indirect.goto.dest.in ; <i8*> [#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
index 000000000000..c84f4dee2487
--- /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
index 000000000000..d37e7ab49dc5
--- /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
index 000000000000..7b186f05976e
--- /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
+
+ at _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
index 000000000000..ddb0e4430210
--- /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
index 000000000000..19e52931f583
--- /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
index 000000000000..e751fa3cada1
--- /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
index 000000000000..e96311d71756
--- /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* }
+
+ at .str = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
+ at 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
+ at 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
index 000000000000..567455f4fd54
--- /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 <pthread.h>
+;
+;    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] }
+
+ at 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
index 000000000000..b9fd0468d380
--- /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 )            ; <i32> [#uses=1]
+  %Z = add i32 %Y, 1              ; <i32> [#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 )         ; <i32>: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
index 000000000000..3e0b575f94f5
--- /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
index 000000000000..f89da0304d7e
--- /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 )             ; <i32> [#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
index 000000000000..e110c0a65c95
--- /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 )             ; <i32> [#uses=1]
+  %Y = icmp ne i32 %X, 0          ; <i1> [#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             ; <i32> [#uses=1]
+OK:
+  %Y = icmp ne i32 %X, 0          ; <i1> [#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
index 000000000000..a2be8c073b18
--- /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
index 000000000000..ed6f90a7810a
--- /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
index 000000000000..2260e88812df
--- /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 <threads.h>
+;    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(&gtl, callee, &gsh);
+;    }
+;
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+ at gtl = dso_local thread_local global i32 0, align 4
+ at 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}


        


More information about the llvm-commits mailing list