llvm/llvm/test/Transforms/Attributor/value-simplify-reachability.ll

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-annotate-decl-cs  -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC

@GInt1 = internal global i32 undef, align 4
@GInt2 = internal global i32 zeroinitializer, align 4
@GInt3 = internal global i32 undef, align 4
@GInt4 = internal global i32 zeroinitializer, align 4
@GInt5 = internal global i32 undef, align 4

declare void @llvm.assume(i1)
declare void @useI32(i32) nosync nocallback
declare void @free(ptr) allockind("free") "alloc-family"="malloc"
declare noalias ptr @calloc(i64, i64) allockind("alloc,zeroed") allocsize(0, 1) "alloc-family"="malloc"

;.
; CHECK: @GInt1 = internal global i32 undef, align 4
; CHECK: @GInt2 = internal global i32 0, align 4
; CHECK: @GInt3 = internal global i32 undef, align 4
; CHECK: @GInt4 = internal global i32 0, align 4
; CHECK: @GInt5 = internal global i32 undef, align 4
; CHECK: @B = global i32 0
;.
define internal void @write1ToGInt1() {
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
; CHECK-LABEL: define {{[^@]+}}@write1ToGInt1
; CHECK-SAME: () #[[ATTR4:[0-9]+]] {
; CHECK-NEXT:    store i32 1, ptr @GInt1, align 4
; CHECK-NEXT:    ret void
;
  store i32 1, ptr @GInt1
  ret void
}

define internal void @write1ToGInt2() {
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
; CHECK-LABEL: define {{[^@]+}}@write1ToGInt2
; CHECK-SAME: () #[[ATTR4]] {
; CHECK-NEXT:    store i32 1, ptr @GInt2, align 4
; CHECK-NEXT:    ret void
;
  store i32 1, ptr @GInt2
  ret void
}

define void @entry1(i1 %c, i32 %v) {
; TUNIT: Function Attrs: norecurse nosync
; TUNIT-LABEL: define {{[^@]+}}@entry1
; TUNIT-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5:[0-9]+]] {
; TUNIT-NEXT:    [[L0:%.*]] = load i32, ptr @GInt1, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L0]])
; TUNIT-NEXT:    call void @write1ToGInt1() #[[ATTR12:[0-9]+]]
; TUNIT-NEXT:    [[L1:%.*]] = load i32, ptr @GInt1, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L1]])
; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; TUNIT:       T:
; TUNIT-NEXT:    store i32 [[V]], ptr @GInt1, align 4
; TUNIT-NEXT:    [[L2:%.*]] = load i32, ptr @GInt1, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L2]])
; TUNIT-NEXT:    br label [[F]]
; TUNIT:       F:
; TUNIT-NEXT:    [[L3:%.*]] = load i32, ptr @GInt1, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L3]])
; TUNIT-NEXT:    call void @write1ToGInt1() #[[ATTR12]]
; TUNIT-NEXT:    [[L4:%.*]] = load i32, ptr @GInt1, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L4]])
; TUNIT-NEXT:    ret void
;
; CGSCC: Function Attrs: nosync
; CGSCC-LABEL: define {{[^@]+}}@entry1
; CGSCC-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5:[0-9]+]] {
; CGSCC-NEXT:    [[L0:%.*]] = load i32, ptr @GInt1, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L0]])
; CGSCC-NEXT:    call void @write1ToGInt1() #[[ATTR13:[0-9]+]]
; CGSCC-NEXT:    [[L1:%.*]] = load i32, ptr @GInt1, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L1]])
; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; CGSCC:       T:
; CGSCC-NEXT:    store i32 [[V]], ptr @GInt1, align 4
; CGSCC-NEXT:    [[L2:%.*]] = load i32, ptr @GInt1, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L2]])
; CGSCC-NEXT:    br label [[F]]
; CGSCC:       F:
; CGSCC-NEXT:    [[L3:%.*]] = load i32, ptr @GInt1, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L3]])
; CGSCC-NEXT:    call void @write1ToGInt1() #[[ATTR13]]
; CGSCC-NEXT:    [[L4:%.*]] = load i32, ptr @GInt1, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L4]])
; CGSCC-NEXT:    ret void
;
  %l0 = load i32, ptr @GInt1
  call void @useI32(i32 %l0)
  call void @write1ToGInt1();
  %l1 = load i32, ptr @GInt1
  call void @useI32(i32 %l1)
  br i1 %c, label %T, label %F
T:
  store i32 %v, ptr @GInt1
  %l2 = load i32, ptr @GInt1
  call void @useI32(i32 %l2)
  br label %F
F:
  %l3 = load i32, ptr @GInt1
  call void @useI32(i32 %l3)
  call void @write1ToGInt1();
  %l4 = load i32, ptr @GInt1
  call void @useI32(i32 %l4)
  ret void
}

define void @entry2(i1 %c, i32 %v) {
; TUNIT: Function Attrs: norecurse nosync
; TUNIT-LABEL: define {{[^@]+}}@entry2
; TUNIT-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5]] {
; TUNIT-NEXT:    [[L0:%.*]] = load i32, ptr @GInt2, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L0]])
; TUNIT-NEXT:    call void @write1ToGInt2() #[[ATTR12]]
; TUNIT-NEXT:    [[L1:%.*]] = load i32, ptr @GInt2, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L1]])
; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; TUNIT:       T:
; TUNIT-NEXT:    store i32 [[V]], ptr @GInt2, align 4
; TUNIT-NEXT:    [[L2:%.*]] = load i32, ptr @GInt2, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L2]])
; TUNIT-NEXT:    br label [[F]]
; TUNIT:       F:
; TUNIT-NEXT:    [[L3:%.*]] = load i32, ptr @GInt2, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L3]])
; TUNIT-NEXT:    call void @write1ToGInt2() #[[ATTR12]]
; TUNIT-NEXT:    [[L4:%.*]] = load i32, ptr @GInt2, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L4]])
; TUNIT-NEXT:    ret void
;
; CGSCC: Function Attrs: nosync
; CGSCC-LABEL: define {{[^@]+}}@entry2
; CGSCC-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5]] {
; CGSCC-NEXT:    [[L0:%.*]] = load i32, ptr @GInt2, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L0]])
; CGSCC-NEXT:    call void @write1ToGInt2() #[[ATTR13]]
; CGSCC-NEXT:    [[L1:%.*]] = load i32, ptr @GInt2, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L1]])
; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; CGSCC:       T:
; CGSCC-NEXT:    store i32 [[V]], ptr @GInt2, align 4
; CGSCC-NEXT:    [[L2:%.*]] = load i32, ptr @GInt2, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L2]])
; CGSCC-NEXT:    br label [[F]]
; CGSCC:       F:
; CGSCC-NEXT:    [[L3:%.*]] = load i32, ptr @GInt2, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L3]])
; CGSCC-NEXT:    call void @write1ToGInt2() #[[ATTR13]]
; CGSCC-NEXT:    [[L4:%.*]] = load i32, ptr @GInt2, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L4]])
; CGSCC-NEXT:    ret void
;
  %l0 = load i32, ptr @GInt2
  call void @useI32(i32 %l0)
  call void @write1ToGInt2();
  %l1 = load i32, ptr @GInt2
  call void @useI32(i32 %l1)
  br i1 %c, label %T, label %F
T:
  store i32 %v, ptr @GInt2
  %l2 = load i32, ptr @GInt2
  call void @useI32(i32 %l2)
  br label %F
F:
  %l3 = load i32, ptr @GInt2
  call void @useI32(i32 %l3)
  call void @write1ToGInt2();
  %l4 = load i32, ptr @GInt2
  call void @useI32(i32 %l4)
  ret void
}
define void @entry3(i1 %c, i32 %v) {
; TUNIT: Function Attrs: norecurse nosync
; TUNIT-LABEL: define {{[^@]+}}@entry3
; TUNIT-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5]] {
; TUNIT-NEXT:    call void @useI32(i32 1)
; TUNIT-NEXT:    store i32 1, ptr @GInt3, align 4
; TUNIT-NEXT:    call void @useI32(i32 noundef 1)
; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; TUNIT:       T:
; TUNIT-NEXT:    store i32 [[V]], ptr @GInt3, align 4
; TUNIT-NEXT:    [[L2:%.*]] = load i32, ptr @GInt3, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L2]])
; TUNIT-NEXT:    br label [[F]]
; TUNIT:       F:
; TUNIT-NEXT:    [[L3:%.*]] = load i32, ptr @GInt3, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L3]])
; TUNIT-NEXT:    store i32 1, ptr @GInt3, align 4
; TUNIT-NEXT:    call void @useI32(i32 noundef 1)
; TUNIT-NEXT:    ret void
;
; CGSCC: Function Attrs: norecurse nosync
; CGSCC-LABEL: define {{[^@]+}}@entry3
; CGSCC-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR6:[0-9]+]] {
; CGSCC-NEXT:    call void @useI32(i32 1)
; CGSCC-NEXT:    store i32 1, ptr @GInt3, align 4
; CGSCC-NEXT:    call void @useI32(i32 noundef 1)
; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; CGSCC:       T:
; CGSCC-NEXT:    store i32 [[V]], ptr @GInt3, align 4
; CGSCC-NEXT:    [[L2:%.*]] = load i32, ptr @GInt3, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L2]])
; CGSCC-NEXT:    br label [[F]]
; CGSCC:       F:
; CGSCC-NEXT:    [[L3:%.*]] = load i32, ptr @GInt3, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L3]])
; CGSCC-NEXT:    store i32 1, ptr @GInt3, align 4
; CGSCC-NEXT:    call void @useI32(i32 noundef 1)
; CGSCC-NEXT:    ret void
;
  %l0 = load i32, ptr @GInt3
  call void @useI32(i32 %l0)
  store i32 1, ptr @GInt3
  %l1 = load i32, ptr @GInt3
  call void @useI32(i32 %l1)
  br i1 %c, label %T, label %F
T:
  store i32 %v, ptr @GInt3
  %l2 = load i32, ptr @GInt3
  call void @useI32(i32 %l2)
  br label %F
F:
  %l3 = load i32, ptr @GInt3
  call void @useI32(i32 %l3)
  store i32 1, ptr @GInt3
  %l4 = load i32, ptr @GInt3
  call void @useI32(i32 %l4)
  ret void
}

define void @entry4(i1 %c, i32 %v) {
; TUNIT: Function Attrs: norecurse nosync
; TUNIT-LABEL: define {{[^@]+}}@entry4
; TUNIT-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5]] {
; TUNIT-NEXT:    [[L0:%.*]] = load i32, ptr @GInt4, align 4
; TUNIT-NEXT:    call void @useI32(i32 noundef [[L0]])
; TUNIT-NEXT:    store i32 1, ptr @GInt4, align 4
; TUNIT-NEXT:    call void @useI32(i32 noundef 1)
; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; TUNIT:       T:
; TUNIT-NEXT:    store i32 [[V]], ptr @GInt4, align 4
; TUNIT-NEXT:    [[L2:%.*]] = load i32, ptr @GInt4, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L2]])
; TUNIT-NEXT:    br label [[F]]
; TUNIT:       F:
; TUNIT-NEXT:    [[L3:%.*]] = load i32, ptr @GInt4, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L3]])
; TUNIT-NEXT:    store i32 1, ptr @GInt4, align 4
; TUNIT-NEXT:    call void @useI32(i32 noundef 1)
; TUNIT-NEXT:    ret void
;
; CGSCC: Function Attrs: norecurse nosync
; CGSCC-LABEL: define {{[^@]+}}@entry4
; CGSCC-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR6]] {
; CGSCC-NEXT:    [[L0:%.*]] = load i32, ptr @GInt4, align 4
; CGSCC-NEXT:    call void @useI32(i32 noundef [[L0]])
; CGSCC-NEXT:    store i32 1, ptr @GInt4, align 4
; CGSCC-NEXT:    call void @useI32(i32 noundef 1)
; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; CGSCC:       T:
; CGSCC-NEXT:    store i32 [[V]], ptr @GInt4, align 4
; CGSCC-NEXT:    [[L2:%.*]] = load i32, ptr @GInt4, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L2]])
; CGSCC-NEXT:    br label [[F]]
; CGSCC:       F:
; CGSCC-NEXT:    [[L3:%.*]] = load i32, ptr @GInt4, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L3]])
; CGSCC-NEXT:    store i32 1, ptr @GInt4, align 4
; CGSCC-NEXT:    call void @useI32(i32 noundef 1)
; CGSCC-NEXT:    ret void
;
  %l0 = load i32, ptr @GInt4
  call void @useI32(i32 %l0)
  store i32 1, ptr @GInt4
  %l1 = load i32, ptr @GInt4
  call void @useI32(i32 %l1)
  br i1 %c, label %T, label %F
T:
  store i32 %v, ptr @GInt4
  %l2 = load i32, ptr @GInt4
  call void @useI32(i32 %l2)
  br label %F
F:
  %l3 = load i32, ptr @GInt4
  call void @useI32(i32 %l3)
  store i32 1, ptr @GInt4
  %l4 = load i32, ptr @GInt4
  call void @useI32(i32 %l4)
  ret void
}

; TODO: In this test we can replace %l0, in the others above we cannot.
define void @entry5(i1 %c, i32 %v) {
; TUNIT: Function Attrs: norecurse nosync
; TUNIT-LABEL: define {{[^@]+}}@entry5
; TUNIT-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR5]] {
; TUNIT-NEXT:    call void @useI32(i32 1)
; TUNIT-NEXT:    store i32 1, ptr @GInt5, align 4
; TUNIT-NEXT:    call void @useI32(i32 noundef 1) #[[ATTR6:[0-9]+]]
; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; TUNIT:       T:
; TUNIT-NEXT:    store i32 [[V]], ptr @GInt5, align 4
; TUNIT-NEXT:    [[L2:%.*]] = load i32, ptr @GInt5, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L2]]) #[[ATTR6]]
; TUNIT-NEXT:    br label [[F]]
; TUNIT:       F:
; TUNIT-NEXT:    [[L3:%.*]] = load i32, ptr @GInt5, align 4
; TUNIT-NEXT:    call void @useI32(i32 [[L3]]) #[[ATTR6]]
; TUNIT-NEXT:    store i32 1, ptr @GInt5, align 4
; TUNIT-NEXT:    call void @useI32(i32 noundef 1) #[[ATTR6]]
; TUNIT-NEXT:    ret void
;
; CGSCC: Function Attrs: norecurse nosync
; CGSCC-LABEL: define {{[^@]+}}@entry5
; CGSCC-SAME: (i1 [[C:%.*]], i32 [[V:%.*]]) #[[ATTR6]] {
; CGSCC-NEXT:    call void @useI32(i32 1)
; CGSCC-NEXT:    store i32 1, ptr @GInt5, align 4
; CGSCC-NEXT:    call void @useI32(i32 noundef 1) #[[ATTR7:[0-9]+]]
; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; CGSCC:       T:
; CGSCC-NEXT:    store i32 [[V]], ptr @GInt5, align 4
; CGSCC-NEXT:    [[L2:%.*]] = load i32, ptr @GInt5, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L2]]) #[[ATTR7]]
; CGSCC-NEXT:    br label [[F]]
; CGSCC:       F:
; CGSCC-NEXT:    [[L3:%.*]] = load i32, ptr @GInt5, align 4
; CGSCC-NEXT:    call void @useI32(i32 [[L3]]) #[[ATTR7]]
; CGSCC-NEXT:    store i32 1, ptr @GInt5, align 4
; CGSCC-NEXT:    call void @useI32(i32 noundef 1) #[[ATTR7]]
; CGSCC-NEXT:    ret void
;
  %l0 = load i32, ptr @GInt5
  call void @useI32(i32 %l0)
  store i32 1, ptr @GInt5
  %l1 = load i32, ptr @GInt5
  call void @useI32(i32 %l1) nocallback
  br i1 %c, label %T, label %F
T:
  store i32 %v, ptr @GInt5
  %l2 = load i32, ptr @GInt5
  call void @useI32(i32 %l2) nocallback
  br label %F
F:
  %l3 = load i32, ptr @GInt5
  call void @useI32(i32 %l3) nocallback
  store i32 1, ptr @GInt5
  %l4 = load i32, ptr @GInt5
  call void @useI32(i32 %l4) nocallback
  ret void
}


declare void @use_4_i8(i8, i8, i8, i8) nocallback

define void @exclusion_set1(i1 %c1, i1 %c2, i1 %c3) {
; CHECK-LABEL: define {{[^@]+}}@exclusion_set1
; CHECK-SAME: (i1 [[C1:%.*]], i1 [[C2:%.*]], i1 [[C3:%.*]]) {
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[CALL_H2S:%.*]] = alloca i8, i64 4, align 1
; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[CALL_H2S]], i8 0, i64 4, i1 false)
; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr inbounds i8, ptr [[CALL_H2S]], i64 1
; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr inbounds i8, ptr [[CALL_H2S]], i64 2
; CHECK-NEXT:    [[GEP3:%.*]] = getelementptr inbounds i8, ptr [[CALL_H2S]], i64 3
; CHECK-NEXT:    [[L0_A:%.*]] = load i8, ptr [[CALL_H2S]], align 1
; CHECK-NEXT:    [[L1_A:%.*]] = load i8, ptr [[GEP1]], align 1
; CHECK-NEXT:    [[L2_A:%.*]] = load i8, ptr [[GEP2]], align 1
; CHECK-NEXT:    [[L3_A:%.*]] = load i8, ptr [[GEP3]], align 1
; CHECK-NEXT:    call void @use_4_i8(i8 noundef [[L0_A]], i8 noundef [[L1_A]], i8 noundef [[L2_A]], i8 noundef [[L3_A]])
; CHECK-NEXT:    store i8 1, ptr [[CALL_H2S]], align 4
; CHECK-NEXT:    [[L1_B:%.*]] = load i8, ptr [[GEP1]], align 1
; CHECK-NEXT:    [[L2_B:%.*]] = load i8, ptr [[GEP2]], align 1
; CHECK-NEXT:    [[L3_B:%.*]] = load i8, ptr [[GEP3]], align 1
; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef [[L1_B]], i8 noundef [[L2_B]], i8 noundef [[L3_B]])
; CHECK-NEXT:    br i1 [[C1]], label [[IF_MERGE1:%.*]], label [[IF_THEN:%.*]]
; CHECK:       if.then:
; CHECK-NEXT:    [[L1_C:%.*]] = load i8, ptr [[GEP1]], align 1
; CHECK-NEXT:    [[L2_C:%.*]] = load i8, ptr [[GEP2]], align 1
; CHECK-NEXT:    [[L3_C:%.*]] = load i8, ptr [[GEP3]], align 1
; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef [[L1_C]], i8 noundef [[L2_C]], i8 noundef [[L3_C]])
; CHECK-NEXT:    store i8 2, ptr [[GEP1]], align 4
; CHECK-NEXT:    [[L2_D:%.*]] = load i8, ptr [[GEP2]], align 1
; CHECK-NEXT:    [[L3_D:%.*]] = load i8, ptr [[GEP3]], align 1
; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef [[L2_D]], i8 noundef [[L3_D]])
; CHECK-NEXT:    br i1 [[C1]], label [[IF_MERGE1]], label [[IF_THEN2:%.*]]
; CHECK:       if.then2:
; CHECK-NEXT:    [[L2_E:%.*]] = load i8, ptr [[GEP2]], align 1
; CHECK-NEXT:    [[L3_E:%.*]] = load i8, ptr [[GEP3]], align 1
; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef [[L2_E]], i8 noundef [[L3_E]])
; CHECK-NEXT:    store i8 3, ptr [[GEP2]], align 4
; CHECK-NEXT:    [[L3_F:%.*]] = load i8, ptr [[GEP3]], align 1
; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 noundef [[L3_F]])
; CHECK-NEXT:    br i1 [[C2]], label [[IF_MERGE2:%.*]], label [[IF_THEN3:%.*]]
; CHECK:       if.merge1:
; CHECK-NEXT:    [[L1_G:%.*]] = load i8, ptr [[GEP1]], align 1
; CHECK-NEXT:    [[L2_G:%.*]] = load i8, ptr [[GEP2]], align 1
; CHECK-NEXT:    [[L3_G:%.*]] = load i8, ptr [[GEP3]], align 1
; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef [[L1_G]], i8 noundef [[L2_G]], i8 noundef [[L3_G]])
; CHECK-NEXT:    br label [[IF_MERGE2]]
; CHECK:       if.merge2:
; CHECK-NEXT:    [[L1_H:%.*]] = load i8, ptr [[GEP1]], align 1
; CHECK-NEXT:    [[L2_H:%.*]] = load i8, ptr [[GEP2]], align 1
; CHECK-NEXT:    [[L3_H:%.*]] = load i8, ptr [[GEP3]], align 1
; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef [[L1_H]], i8 noundef [[L2_H]], i8 noundef [[L3_H]])
; CHECK-NEXT:    br label [[IF_END:%.*]]
; CHECK:       if.then3:
; CHECK-NEXT:    [[L3_I:%.*]] = load i8, ptr [[GEP3]], align 1
; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 noundef [[L3_I]])
; CHECK-NEXT:    store i8 4, ptr [[GEP3]], align 4
; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 noundef 4)
; CHECK-NEXT:    br label [[IF_END]]
; CHECK:       if.end:
; CHECK-NEXT:    [[L1_K:%.*]] = load i8, ptr [[GEP1]], align 1
; CHECK-NEXT:    [[L2_K:%.*]] = load i8, ptr [[GEP2]], align 1
; CHECK-NEXT:    [[L3_K:%.*]] = load i8, ptr [[GEP3]], align 1
; CHECK-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef [[L1_K]], i8 noundef [[L2_K]], i8 noundef [[L3_K]])
; CHECK-NEXT:    ret void
;
entry:
  %call = call noalias ptr @calloc(i64 1, i64 4) norecurse
  %gep0 = getelementptr inbounds i8, ptr %call, i64 0
  %gep1 = getelementptr inbounds i8, ptr %call, i64 1
  %gep2 = getelementptr inbounds i8, ptr %call, i64 2
  %gep3 = getelementptr inbounds i8, ptr %call, i64 3

  %l0_a = load i8, ptr %gep0
  %l1_a = load i8, ptr %gep1
  %l2_a = load i8, ptr %gep2
  %l3_a = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_a, i8 %l1_a, i8 %l2_a, i8 %l3_a)

  store i8 1, ptr %gep0, align 4

  %l0_b = load i8, ptr %gep0
  %l1_b = load i8, ptr %gep1
  %l2_b = load i8, ptr %gep2
  %l3_b = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_b, i8 %l1_b, i8 %l2_b, i8 %l3_b)

  br i1 %c1, label %if.merge1, label %if.then

if.then:
  %l0_c = load i8, ptr %gep0
  %l1_c = load i8, ptr %gep1
  %l2_c = load i8, ptr %gep2
  %l3_c = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_c, i8 %l1_c, i8 %l2_c, i8 %l3_c)

  store i8 2, ptr %gep1, align 4

  %l0_d = load i8, ptr %gep0
  %l1_d = load i8, ptr %gep1
  %l2_d = load i8, ptr %gep2
  %l3_d = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_d, i8 %l1_d, i8 %l2_d, i8 %l3_d)

  br i1 %c1, label %if.merge1, label %if.then2

if.then2:
  %l0_e = load i8, ptr %gep0
  %l1_e = load i8, ptr %gep1
  %l2_e = load i8, ptr %gep2
  %l3_e = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_e, i8 %l1_e, i8 %l2_e, i8 %l3_e)

  store i8 3, ptr %gep2, align 4

  %l0_f = load i8, ptr %gep0
  %l1_f = load i8, ptr %gep1
  %l2_f = load i8, ptr %gep2
  %l3_f = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_f, i8 %l1_f, i8 %l2_f, i8 %l3_f)

  br i1 %c2, label %if.merge2, label %if.then3

if.merge1:

  %l0_g = load i8, ptr %gep0
  %l1_g = load i8, ptr %gep1
  %l2_g = load i8, ptr %gep2
  %l3_g = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_g, i8 %l1_g, i8 %l2_g, i8 %l3_g)

  br label %if.merge2

if.merge2:

  %l0_h = load i8, ptr %gep0
  %l1_h = load i8, ptr %gep1
  %l2_h = load i8, ptr %gep2
  %l3_h = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_h, i8 %l1_h, i8 %l2_h, i8 %l3_h)

  br label %if.end

if.then3:

  %l0_i = load i8, ptr %gep0
  %l1_i = load i8, ptr %gep1
  %l2_i = load i8, ptr %gep2
  %l3_i = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_i, i8 %l1_i, i8 %l2_i, i8 %l3_i)

  store i8 4, ptr %gep3, align 4

  %l0_j = load i8, ptr %gep0
  %l1_j = load i8, ptr %gep1
  %l2_j = load i8, ptr %gep2
  %l3_j = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_j, i8 %l1_j, i8 %l2_j, i8 %l3_j)

  br label %if.end

if.end:
  %l0_k = load i8, ptr %gep0
  %l1_k = load i8, ptr %gep1
  %l2_k = load i8, ptr %gep2
  %l3_k = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_k, i8 %l1_k, i8 %l2_k, i8 %l3_k)

  call void @free(ptr %call) norecurse
  ret void
}

define void @exclusion_set2(i1 %c1, i1 %c2, i1 %c3) {
; TUNIT: Function Attrs: norecurse
; TUNIT-LABEL: define {{[^@]+}}@exclusion_set2
; TUNIT-SAME: (i1 [[C1:%.*]], i1 [[C2:%.*]], i1 [[C3:%.*]]) #[[ATTR7:[0-9]+]] {
; TUNIT-NEXT:  entry:
; TUNIT-NEXT:    call void @use_4_i8(i8 undef, i8 undef, i8 undef, i8 undef)
; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 undef, i8 undef, i8 undef)
; TUNIT-NEXT:    br i1 [[C1]], label [[IF_MERGE1:%.*]], label [[IF_THEN:%.*]]
; TUNIT:       if.then:
; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 undef, i8 undef, i8 undef)
; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 undef, i8 undef)
; TUNIT-NEXT:    br i1 [[C1]], label [[IF_MERGE1]], label [[IF_THEN2:%.*]]
; TUNIT:       if.then2:
; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 undef, i8 undef)
; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 undef)
; TUNIT-NEXT:    br i1 [[C2]], label [[IF_MERGE2:%.*]], label [[IF_THEN3:%.*]]
; TUNIT:       if.merge1:
; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 undef, i8 undef)
; TUNIT-NEXT:    br label [[IF_MERGE2]]
; TUNIT:       if.merge2:
; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 undef)
; TUNIT-NEXT:    br label [[IF_END:%.*]]
; TUNIT:       if.then3:
; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 undef)
; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 noundef 4)
; TUNIT-NEXT:    br label [[IF_END]]
; TUNIT:       if.end:
; TUNIT-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
; TUNIT-NEXT:    ret void
;
; CGSCC: Function Attrs: norecurse
; CGSCC-LABEL: define {{[^@]+}}@exclusion_set2
; CGSCC-SAME: (i1 [[C1:%.*]], i1 [[C2:%.*]], i1 [[C3:%.*]]) #[[ATTR8:[0-9]+]] {
; CGSCC-NEXT:  entry:
; CGSCC-NEXT:    call void @use_4_i8(i8 undef, i8 undef, i8 undef, i8 undef)
; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 undef, i8 undef, i8 undef)
; CGSCC-NEXT:    br i1 [[C1]], label [[IF_MERGE1:%.*]], label [[IF_THEN:%.*]]
; CGSCC:       if.then:
; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 undef, i8 undef, i8 undef)
; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 undef, i8 undef)
; CGSCC-NEXT:    br i1 [[C1]], label [[IF_MERGE1]], label [[IF_THEN2:%.*]]
; CGSCC:       if.then2:
; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 undef, i8 undef)
; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 undef)
; CGSCC-NEXT:    br i1 [[C2]], label [[IF_MERGE2:%.*]], label [[IF_THEN3:%.*]]
; CGSCC:       if.merge1:
; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 undef, i8 undef)
; CGSCC-NEXT:    br label [[IF_MERGE2]]
; CGSCC:       if.merge2:
; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 undef)
; CGSCC-NEXT:    br label [[IF_END:%.*]]
; CGSCC:       if.then3:
; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 undef)
; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 noundef 2, i8 noundef 3, i8 noundef 4)
; CGSCC-NEXT:    br label [[IF_END]]
; CGSCC:       if.end:
; CGSCC-NEXT:    call void @use_4_i8(i8 noundef 1, i8 2, i8 3, i8 4)
; CGSCC-NEXT:    ret void
;
entry:
  %alloc = alloca i8, i32 4
  %gep0 = getelementptr inbounds i8, ptr %alloc, i64 0
  %gep1 = getelementptr inbounds i8, ptr %alloc, i64 1
  %gep2 = getelementptr inbounds i8, ptr %alloc, i64 2
  %gep3 = getelementptr inbounds i8, ptr %alloc, i64 3

  %l0_a = load i8, ptr %gep0
  %l1_a = load i8, ptr %gep1
  %l2_a = load i8, ptr %gep2
  %l3_a = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_a, i8 %l1_a, i8 %l2_a, i8 %l3_a)

  store i8 1, ptr %gep0, align 4

  %l0_b = load i8, ptr %gep0
  %l1_b = load i8, ptr %gep1
  %l2_b = load i8, ptr %gep2
  %l3_b = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_b, i8 %l1_b, i8 %l2_b, i8 %l3_b)

  br i1 %c1, label %if.merge1, label %if.then

if.then:
  %l0_c = load i8, ptr %gep0
  %l1_c = load i8, ptr %gep1
  %l2_c = load i8, ptr %gep2
  %l3_c = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_c, i8 %l1_c, i8 %l2_c, i8 %l3_c)

  store i8 2, ptr %gep1, align 4

  %l0_d = load i8, ptr %gep0
  %l1_d = load i8, ptr %gep1
  %l2_d = load i8, ptr %gep2
  %l3_d = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_d, i8 %l1_d, i8 %l2_d, i8 %l3_d)

  br i1 %c1, label %if.merge1, label %if.then2

if.then2:
  %l0_e = load i8, ptr %gep0
  %l1_e = load i8, ptr %gep1
  %l2_e = load i8, ptr %gep2
  %l3_e = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_e, i8 %l1_e, i8 %l2_e, i8 %l3_e)

  store i8 3, ptr %gep2, align 4

  %l0_f = load i8, ptr %gep0
  %l1_f = load i8, ptr %gep1
  %l2_f = load i8, ptr %gep2
  %l3_f = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_f, i8 %l1_f, i8 %l2_f, i8 %l3_f)

  br i1 %c2, label %if.merge2, label %if.then3

if.merge1:

  %l0_g = load i8, ptr %gep0
  %l1_g = load i8, ptr %gep1
  %l2_g = load i8, ptr %gep2
  %l3_g = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_g, i8 %l1_g, i8 %l2_g, i8 %l3_g)

  br label %if.merge2

if.merge2:

  %l0_h = load i8, ptr %gep0
  %l1_h = load i8, ptr %gep1
  %l2_h = load i8, ptr %gep2
  %l3_h = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_h, i8 %l1_h, i8 %l2_h, i8 %l3_h)

  br label %if.end

if.then3:

  %l0_i = load i8, ptr %gep0
  %l1_i = load i8, ptr %gep1
  %l2_i = load i8, ptr %gep2
  %l3_i = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_i, i8 %l1_i, i8 %l2_i, i8 %l3_i)

  store i8 4, ptr %gep3, align 4

  %l0_j = load i8, ptr %gep0
  %l1_j = load i8, ptr %gep1
  %l2_j = load i8, ptr %gep2
  %l3_j = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_j, i8 %l1_j, i8 %l2_j, i8 %l3_j)

  br label %if.end

if.end:
  %l0_k = load i8, ptr %gep0
  %l1_k = load i8, ptr %gep1
  %l2_k = load i8, ptr %gep2
  %l3_k = load i8, ptr %gep3
  call void @use_4_i8(i8 %l0_k, i8 %l1_k, i8 %l2_k, i8 %l3_k)

  ret void
}

declare void @usei32(i32) nocallback nosync
define internal void @exclusion_set3_helper(i1 %c, ptr %p) {
; TUNIT: Function Attrs: nosync
; TUNIT-LABEL: define {{[^@]+}}@exclusion_set3_helper
; TUNIT-SAME: (i1 noundef [[C:%.*]], ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR8:[0-9]+]] {
; TUNIT-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; TUNIT:       t:
; TUNIT-NEXT:    store i32 42, ptr [[P]], align 4
; TUNIT-NEXT:    br label [[M:%.*]]
; TUNIT:       f:
; TUNIT-NEXT:    [[L:%.*]] = load i32, ptr [[P]], align 4
; TUNIT-NEXT:    [[ADD:%.*]] = add i32 [[L]], 1
; TUNIT-NEXT:    store i32 [[ADD]], ptr [[P]], align 4
; TUNIT-NEXT:    [[CND:%.*]] = icmp eq i32 [[L]], 100
; TUNIT-NEXT:    br i1 [[CND]], label [[F2:%.*]], label [[F]]
; TUNIT:       f2:
; TUNIT-NEXT:    [[USE1:%.*]] = load i32, ptr [[P]], align 4
; TUNIT-NEXT:    call void @usei32(i32 [[USE1]])
; TUNIT-NEXT:    store i32 77, ptr [[P]], align 4
; TUNIT-NEXT:    call void @exclusion_set3_helper(i1 noundef true, ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[P]]) #[[ATTR8]]
; TUNIT-NEXT:    [[USE2:%.*]] = load i32, ptr [[P]], align 4
; TUNIT-NEXT:    call void @usei32(i32 noundef [[USE2]])
; TUNIT-NEXT:    br label [[T]]
; TUNIT:       m:
; TUNIT-NEXT:    call void @usei32(i32 noundef 42)
; TUNIT-NEXT:    ret void
;
; CGSCC: Function Attrs: nosync
; CGSCC-LABEL: define {{[^@]+}}@exclusion_set3_helper
; CGSCC-SAME: (i1 noundef [[C:%.*]], ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR5]] {
; CGSCC-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
; CGSCC:       t:
; CGSCC-NEXT:    store i32 42, ptr [[P]], align 4
; CGSCC-NEXT:    br label [[M:%.*]]
; CGSCC:       f:
; CGSCC-NEXT:    [[L:%.*]] = load i32, ptr [[P]], align 4
; CGSCC-NEXT:    [[ADD:%.*]] = add i32 [[L]], 1
; CGSCC-NEXT:    store i32 [[ADD]], ptr [[P]], align 4
; CGSCC-NEXT:    [[CND:%.*]] = icmp eq i32 [[L]], 100
; CGSCC-NEXT:    br i1 [[CND]], label [[F2:%.*]], label [[F]]
; CGSCC:       f2:
; CGSCC-NEXT:    [[USE1:%.*]] = load i32, ptr [[P]], align 4
; CGSCC-NEXT:    call void @usei32(i32 [[USE1]])
; CGSCC-NEXT:    store i32 77, ptr [[P]], align 4
; CGSCC-NEXT:    call void @exclusion_set3_helper(i1 noundef true, ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[P]]) #[[ATTR5]]
; CGSCC-NEXT:    [[USE2:%.*]] = load i32, ptr [[P]], align 4
; CGSCC-NEXT:    call void @usei32(i32 [[USE2]])
; CGSCC-NEXT:    br label [[T]]
; CGSCC:       m:
; CGSCC-NEXT:    [[USE3:%.*]] = load i32, ptr [[P]], align 4
; CGSCC-NEXT:    call void @usei32(i32 [[USE3]])
; CGSCC-NEXT:    ret void
;
  br i1 %c, label %t, label %f
t:
  store i32 42, ptr %p
  br label %m
f:
  %l = load i32, ptr %p
  %add = add i32 %l, 1
  store i32 %add, ptr %p
  %cnd = icmp eq i32 %l, 100
  br i1 %cnd, label %f2, label %f
f2:
  %use1 = load i32, ptr %p
  call void @usei32(i32 %use1)
  store i32 77, ptr %p
  call void @exclusion_set3_helper(i1 true, ptr %p)
  %use2 = load i32, ptr %p
  call void @usei32(i32 %use2)
  br label %t
m:
  %use3 = load i32, ptr %p
  call void @usei32(i32 %use3)
  ret void
}

define i32 @exclusion_set3(i1 %c) {
; TUNIT: Function Attrs: norecurse nosync
; TUNIT-LABEL: define {{[^@]+}}@exclusion_set3
; TUNIT-SAME: (i1 [[C:%.*]]) #[[ATTR5]] {
; TUNIT-NEXT:    [[A:%.*]] = alloca i32, align 4
; TUNIT-NEXT:    store i32 3, ptr [[A]], align 4
; TUNIT-NEXT:    call void @exclusion_set3_helper(i1 noundef [[C]], ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[A]]) #[[ATTR8]]
; TUNIT-NEXT:    [[FINAL:%.*]] = load i32, ptr [[A]], align 4
; TUNIT-NEXT:    ret i32 [[FINAL]]
;
; CGSCC: Function Attrs: nosync
; CGSCC-LABEL: define {{[^@]+}}@exclusion_set3
; CGSCC-SAME: (i1 noundef [[C:%.*]]) #[[ATTR5]] {
; CGSCC-NEXT:    [[A:%.*]] = alloca i32, align 4
; CGSCC-NEXT:    store i32 3, ptr [[A]], align 4
; CGSCC-NEXT:    call void @exclusion_set3_helper(i1 noundef [[C]], ptr noalias nocapture nofree noundef nonnull align 4 dereferenceable(4) [[A]])
; CGSCC-NEXT:    [[FINAL:%.*]] = load i32, ptr [[A]], align 4
; CGSCC-NEXT:    ret i32 [[FINAL]]
;
  %a = alloca i32
  store i32 3, ptr %a
  call void @exclusion_set3_helper(i1 %c, ptr %a)
  %final = load i32, ptr %a
  ret i32 %final
}

@B = global i32 0

define internal i32 @readI32(ptr %a) {
; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: none)
; TUNIT-LABEL: define {{[^@]+}}@readI32
; TUNIT-SAME: (i32 [[TMP0:%.*]]) #[[ATTR9:[0-9]+]] {
; TUNIT-NEXT:    [[A_PRIV:%.*]] = alloca i32, align 4
; TUNIT-NEXT:    store i32 [[TMP0]], ptr [[A_PRIV]], align 4
; TUNIT-NEXT:    [[R:%.*]] = load i32, ptr [[A_PRIV]], align 4
; TUNIT-NEXT:    store i32 1, ptr @B, align 4
; TUNIT-NEXT:    ret i32 [[R]]
;
; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@readI32
; CGSCC-SAME: (ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[A:%.*]]) #[[ATTR9:[0-9]+]] {
; CGSCC-NEXT:    [[R:%.*]] = load i32, ptr [[A]], align 4
; CGSCC-NEXT:    store i32 1, ptr @B, align 4
; CGSCC-NEXT:    ret i32 [[R]]
;
  %r = load i32, ptr %a
  store i32 1, ptr @B
  ret i32 %r
}

define internal i32 @broker(ptr %a) {
; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: none)
; TUNIT-LABEL: define {{[^@]+}}@broker
; TUNIT-SAME: (i32 [[TMP0:%.*]]) #[[ATTR9]] {
; TUNIT-NEXT:    [[A_PRIV:%.*]] = alloca i32, align 4
; TUNIT-NEXT:    store i32 [[TMP0]], ptr [[A_PRIV]], align 4
; TUNIT-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A_PRIV]], align 4
; TUNIT-NEXT:    [[R:%.*]] = call i32 @readI32(i32 [[TMP2]]) #[[ATTR13:[0-9]+]]
; TUNIT-NEXT:    ret i32 [[R]]
;
; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, argmem: none)
; CGSCC-LABEL: define {{[^@]+}}@broker
; CGSCC-SAME: (i32 [[TMP0:%.*]]) #[[ATTR10:[0-9]+]] {
; CGSCC-NEXT:    [[A_PRIV:%.*]] = alloca i32, align 4
; CGSCC-NEXT:    store i32 [[TMP0]], ptr [[A_PRIV]], align 4
; CGSCC-NEXT:    [[R:%.*]] = call i32 @readI32(ptr noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[A_PRIV]]) #[[ATTR14:[0-9]+]]
; CGSCC-NEXT:    ret i32 [[R]]
;
  %r = call i32 @readI32(ptr %a)
  ret i32 %r
}

; FIXME: This function should return 1, not 0
define i32 @two_calls() {
; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
; TUNIT-LABEL: define {{[^@]+}}@two_calls
; TUNIT-SAME: () #[[ATTR10:[0-9]+]] {
; TUNIT-NEXT:    [[A:%.*]] = alloca i32, align 4
; TUNIT-NEXT:    store i32 0, ptr [[A]], align 4
; TUNIT-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A]], align 4
; TUNIT-NEXT:    [[D:%.*]] = call i32 @broker(i32 [[TMP1]]) #[[ATTR13]]
; TUNIT-NEXT:    store i32 1, ptr [[A]], align 4
; TUNIT-NEXT:    [[TMP2:%.*]] = load i32, ptr [[A]], align 4
; TUNIT-NEXT:    [[R:%.*]] = call i32 @broker(i32 [[TMP2]]) #[[ATTR13]]
; TUNIT-NEXT:    ret i32 [[R]]
;
; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn
; CGSCC-LABEL: define {{[^@]+}}@two_calls
; CGSCC-SAME: () #[[ATTR11:[0-9]+]] {
; CGSCC-NEXT:    [[A:%.*]] = alloca i32, align 4
; CGSCC-NEXT:    store i32 0, ptr [[A]], align 4
; CGSCC-NEXT:    [[TMP1:%.*]] = load i32, ptr [[A]], align 4
; CGSCC-NEXT:    [[D:%.*]] = call i32 @broker(i32 noundef [[TMP1]]) #[[ATTR14]]
; CGSCC-NEXT:    store i32 1, ptr [[A]], align 4
; CGSCC-NEXT:    [[R:%.*]] = call i32 @broker(i32 noundef 1) #[[ATTR14]]
; CGSCC-NEXT:    ret i32 [[R]]
;
  %a = alloca i32
  store i32 0, ptr %a
  %d = call i32 @broker(ptr %a)
  store i32 1, ptr %a
  %r = call i32 @broker(ptr %a)
  ret i32 %r
}

;.
; TUNIT: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
; TUNIT: attributes #[[ATTR1:[0-9]+]] = { nocallback nosync }
; TUNIT: attributes #[[ATTR2:[0-9]+]] = { allockind("free") "alloc-family"="malloc" }
; TUNIT: attributes #[[ATTR3:[0-9]+]] = { allockind("alloc,zeroed") allocsize(0,1) "alloc-family"="malloc" }
; TUNIT: attributes #[[ATTR4]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(write) }
; TUNIT: attributes #[[ATTR5]] = { norecurse nosync }
; TUNIT: attributes #[[ATTR6]] = { nocallback }
; TUNIT: attributes #[[ATTR7]] = { norecurse }
; TUNIT: attributes #[[ATTR8]] = { nosync }
; TUNIT: attributes #[[ATTR9]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: none) }
; TUNIT: attributes #[[ATTR10]] = { mustprogress nofree norecurse nosync nounwind willreturn }
; TUNIT: attributes #[[ATTR11:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) }
; TUNIT: attributes #[[ATTR12]] = { nosync nounwind memory(write) }
; TUNIT: attributes #[[ATTR13]] = { nofree nosync nounwind willreturn }
;.
; CGSCC: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
; CGSCC: attributes #[[ATTR1:[0-9]+]] = { nocallback nosync }
; CGSCC: attributes #[[ATTR2:[0-9]+]] = { allockind("free") "alloc-family"="malloc" }
; CGSCC: attributes #[[ATTR3:[0-9]+]] = { allockind("alloc,zeroed") allocsize(0,1) "alloc-family"="malloc" }
; CGSCC: attributes #[[ATTR4]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(write) }
; CGSCC: attributes #[[ATTR5]] = { nosync }
; CGSCC: attributes #[[ATTR6]] = { norecurse nosync }
; CGSCC: attributes #[[ATTR7]] = { nocallback }
; CGSCC: attributes #[[ATTR8]] = { norecurse }
; CGSCC: attributes #[[ATTR9]] = { mustprogress nofree norecurse nosync nounwind willreturn }
; CGSCC: attributes #[[ATTR10]] = { mustprogress nofree nosync nounwind willreturn memory(readwrite, argmem: none) }
; CGSCC: attributes #[[ATTR11]] = { mustprogress nofree nosync nounwind willreturn }
; CGSCC: attributes #[[ATTR12:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) }
; CGSCC: attributes #[[ATTR13]] = { nounwind memory(write) }
; CGSCC: attributes #[[ATTR14]] = { nofree nounwind willreturn }
;.