; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt %s -passes=ipsccp -S | FileCheck %s

declare i1 @cond()
declare void @use(i1)

define internal {i64, i64} @struct1() {
; CHECK-LABEL: @struct1(
; CHECK-NEXT:    [[C:%.*]] = call i1 @cond()
; CHECK-NEXT:    br i1 [[C]], label [[TRUE:%.*]], label [[FALSE:%.*]]
; CHECK:       true:
; CHECK-NEXT:    br label [[EXIT:%.*]]
; CHECK:       false:
; CHECK-NEXT:    br label [[EXIT]]
; CHECK:       exit:
; CHECK-NEXT:    [[R:%.*]] = phi { i64, i64 } [ { i64 20, i64 200 }, [[TRUE]] ], [ { i64 30, i64 300 }, [[FALSE]] ]
; CHECK-NEXT:    ret { i64, i64 } [[R]]
;
  %c = call i1 @cond()
  br i1 %c, label %true, label %false

true:
  %s.1 = insertvalue {i64, i64} undef, i64 20, 0
  %s.2 = insertvalue {i64, i64} %s.1, i64 200, 1
  br label %exit

false:
  %s.3 = insertvalue {i64, i64} poison, i64 30, 0
  %s.4 = insertvalue {i64, i64} %s.3, i64 300, 1
  br label %exit

exit:
  %r = phi {i64, i64} [ %s.2, %true], [ %s.4, %false ]
  ret {i64, i64} %r
}

define void @struct1_caller() {
; CHECK-LABEL: @struct1_caller(
; CHECK-NEXT:    [[S:%.*]] = call { i64, i64 } @struct1()
; CHECK-NEXT:    [[V1:%.*]] = extractvalue { i64, i64 } [[S]], 0
; CHECK-NEXT:    [[V2:%.*]] = extractvalue { i64, i64 } [[S]], 1
; CHECK-NEXT:    call void @use(i1 true)
; CHECK-NEXT:    call void @use(i1 true)
; CHECK-NEXT:    [[T_3:%.*]] = icmp eq i64 [[V1]], 20
; CHECK-NEXT:    call void @use(i1 [[T_3]])
; CHECK-NEXT:    call void @use(i1 true)
; CHECK-NEXT:    call void @use(i1 true)
; CHECK-NEXT:    [[T_6:%.*]] = icmp eq i64 [[V2]], 300
; CHECK-NEXT:    call void @use(i1 [[T_6]])
; CHECK-NEXT:    ret void
;
  %s = call {i64, i64} @struct1()
  %v1 = extractvalue {i64, i64} %s, 0
  %v2 = extractvalue {i64, i64} %s, 1

  %t.1 = icmp ne i64 %v1, 10
  call void @use(i1 %t.1)
  %t.2 = icmp ult i64 %v1, 100
  call void @use(i1 %t.2)
  %t.3 = icmp eq i64 %v1, 20
  call void @use(i1 %t.3)
  %t.4 = icmp ne i64 %v2, 0
  call void @use(i1 %t.4)
  %t.5 = icmp ult i64 %v2, 301
  call void @use(i1 %t.5)
  %t.6 = icmp eq i64 %v2, 300
  call void @use(i1 %t.6)

  ret void
}

define internal {i64, i64} @struct2() {
; CHECK-LABEL: @struct2(
; CHECK-NEXT:    [[C:%.*]] = call i1 @cond()
; CHECK-NEXT:    br i1 [[C]], label [[TRUE:%.*]], label [[FALSE:%.*]]
; CHECK:       true:
; CHECK-NEXT:    br label [[EXIT:%.*]]
; CHECK:       false:
; CHECK-NEXT:    br label [[EXIT]]
; CHECK:       exit:
; CHECK-NEXT:    [[V1:%.*]] = phi i64 [ 20, [[TRUE]] ], [ 30, [[FALSE]] ]
; CHECK-NEXT:    [[V2:%.*]] = phi i64 [ 200, [[TRUE]] ], [ 300, [[FALSE]] ]
; CHECK-NEXT:    [[S_1:%.*]] = insertvalue { i64, i64 } poison, i64 [[V1]], 0
; CHECK-NEXT:    [[S_2:%.*]] = insertvalue { i64, i64 } [[S_1]], i64 [[V2]], 1
; CHECK-NEXT:    ret { i64, i64 } [[S_2]]
;
  %c = call i1 @cond()
  br i1 %c, label %true, label %false

true:
  br label %exit

false:
  br label %exit

exit:
  %v1 = phi i64 [ 20, %true ], [ 30, %false ]
  %v2 = phi i64 [ 200, %true ], [ 300, %false ]
  %s.1 = insertvalue {i64, i64} poison, i64 %v1, 0
  %s.2 = insertvalue {i64, i64} %s.1, i64 %v2, 1
  ret {i64, i64} %s.2
}

define void @struct2_caller() {
; CHECK-LABEL: @struct2_caller(
; CHECK-NEXT:    [[S:%.*]] = call { i64, i64 } @struct2()
; CHECK-NEXT:    [[V1:%.*]] = extractvalue { i64, i64 } [[S]], 0
; CHECK-NEXT:    [[V2:%.*]] = extractvalue { i64, i64 } [[S]], 1
; CHECK-NEXT:    call void @use(i1 true)
; CHECK-NEXT:    call void @use(i1 true)
; CHECK-NEXT:    call void @use(i1 true)
; CHECK-NEXT:    call void @use(i1 true)
; CHECK-NEXT:    call void @use(i1 false)
; CHECK-NEXT:    call void @use(i1 false)
; CHECK-NEXT:    call void @use(i1 false)
; CHECK-NEXT:    call void @use(i1 false)
; CHECK-NEXT:    [[C_1:%.*]] = icmp eq i64 [[V1]], 25
; CHECK-NEXT:    call void @use(i1 [[C_1]])
; CHECK-NEXT:    [[C_2:%.*]] = icmp ult i64 [[V1]], 25
; CHECK-NEXT:    call void @use(i1 [[C_2]])
; CHECK-NEXT:    [[C_3:%.*]] = icmp eq i64 [[V2]], 250
; CHECK-NEXT:    call void @use(i1 [[C_3]])
; CHECK-NEXT:    [[C_4:%.*]] = icmp ugt i64 [[V2]], 250
; CHECK-NEXT:    call void @use(i1 [[C_4]])
; CHECK-NEXT:    ret void
;
  %s = call {i64, i64} @struct2()
  %v1 = extractvalue {i64, i64} %s, 0
  %v2 = extractvalue {i64, i64} %s, 1

  %t.1 = icmp ne i64 %v1, 10
  call void @use(i1 %t.1)
  %t.2 = icmp ult i64 %v1, 100
  call void @use(i1 %t.2)
  %t.3 = icmp ne i64 %v2, 0
  call void @use(i1 %t.3)
  %t.4 = icmp ult i64 %v2, 301
  call void @use(i1 %t.4)

  %f.1 = icmp eq i64 %v1, 10
  call void @use(i1 %f.1)
  %f.2 = icmp ult i64 %v1, 19
  call void @use(i1 %f.2)
  %f.3 = icmp eq i64 %v2, 50
  call void @use(i1 %f.3)
  %f.4 = icmp ugt i64 %v2, 301
  call void @use(i1 %f.4)

  %c.1 = icmp eq i64 %v1, 25
  call void @use(i1 %c.1)
  %c.2 = icmp ult i64 %v1, 25
  call void @use(i1 %c.2)
  %c.3 = icmp eq i64 %v2, 250
  call void @use(i1 %c.3)
  %c.4 = icmp ugt i64 %v2, 250
  call void @use(i1 %c.4)

  ret void
}

%"phi_type" =  type {i64, i64}

define internal %"phi_type" @test(i32 %input) {
; CHECK-LABEL: @test(
; CHECK-NEXT:    br label [[COND_TRUE_I:%.*]]
; CHECK:       cond.true.i:
; CHECK-NEXT:    br label [[COND_END_I:%.*]]
; CHECK:       cond.end.i:
; CHECK-NEXT:    ret [[PHI_TYPE:%.*]] poison
;
  %cmp.cond = icmp eq i32 %input, 1
  br i1 %cmp.cond, label %cond.true.i, label %cond.false.i

cond.true.i:
  %r1.tmp = insertvalue %"phi_type" poison, i64 1, 0
  %r1.tmp.2 = insertvalue %"phi_type" %r1.tmp, i64 2, 1
  br label %cond.end.i

cond.false.i:
  %r2.tmp = insertvalue %"phi_type" poison, i64 3, 0
  %r2.tmp.2 = insertvalue %"phi_type" %r2.tmp, i64 4, 1
  br label %cond.end.i

cond.end.i:
  %retval = phi %"phi_type" [ %r1.tmp.2, %cond.true.i ], [ %r2.tmp.2, %cond.false.i ]
  ret %"phi_type" %retval
}

define %"phi_type" @test2() {
; CHECK-LABEL: @test2(
; CHECK-NEXT:    [[CALL_1:%.*]] = tail call fastcc [[PHI_TYPE:%.*]] @[[TEST:[a-zA-Z0-9_$\"\\.-]*[a-zA-Z_$\"\\.-][a-zA-Z0-9_$\"\\.-]*]](i32 noundef 1)
; CHECK-NEXT:    ret [[PHI_TYPE]] { i64 1, i64 2 }
;
  %call.1 = tail call fastcc noundef %"phi_type" @test(i32 noundef 1)
  ret %"phi_type" %call.1
}
