; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -mtriple=x86_64-pc-linux-gnu -stack-protector < %s | FileCheck %s
; Bug 42238: Test some situations missed by old, custom capture tracking.

define void @store_captures() #0 {
; CHECK-LABEL: @store_captures(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[STACKGUARDSLOT:%.*]] = alloca i8*
; CHECK-NEXT:    [[STACKGUARD:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
; CHECK-NEXT:    call void @llvm.stackprotector(i8* [[STACKGUARD]], i8** [[STACKGUARDSLOT]])
; CHECK-NEXT:    [[RETVAL:%.*]] = alloca i32, align 4
; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT:    [[J:%.*]] = alloca i32*, align 8
; CHECK-NEXT:    store i32 0, i32* [[RETVAL]]
; CHECK-NEXT:    [[LOAD:%.*]] = load i32, i32* [[A]], align 4
; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[LOAD]], 1
; CHECK-NEXT:    store i32 [[ADD]], i32* [[A]], align 4
; CHECK-NEXT:    store i32* [[A]], i32** [[J]], align 8
; CHECK-NEXT:    [[STACKGUARD1:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
; CHECK-NEXT:    [[TMP0:%.*]] = load volatile i8*, i8** [[STACKGUARDSLOT]]
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8* [[STACKGUARD1]], [[TMP0]]
; CHECK-NEXT:    br i1 [[TMP1]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0
; CHECK:       SP_return:
; CHECK-NEXT:    ret void
; CHECK:       CallStackCheckFailBlk:
; CHECK-NEXT:    call void @__stack_chk_fail()
; CHECK-NEXT:    unreachable
;
entry:
  %retval = alloca i32, align 4
  %a = alloca i32, align 4
  %j = alloca i32*, align 8
  store i32 0, i32* %retval
  %load = load i32, i32* %a, align 4
  %add = add nsw i32 %load, 1
  store i32 %add, i32* %a, align 4
  store i32* %a, i32** %j, align 8
  ret void
}

define i32* @return_captures() #0 {
; CHECK-LABEL: @return_captures(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[RETVAL:%.*]] = alloca i32, align 4
; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT:    [[J:%.*]] = alloca i32*, align 8
; CHECK-NEXT:    store i32 0, i32* [[RETVAL]]
; CHECK-NEXT:    [[LOAD:%.*]] = load i32, i32* [[A]], align 4
; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[LOAD]], 1
; CHECK-NEXT:    store i32 [[ADD]], i32* [[A]], align 4
; CHECK-NEXT:    ret i32* [[A]]
;
entry:
  %retval = alloca i32, align 4
  %a = alloca i32, align 4
  %j = alloca i32*, align 8
  store i32 0, i32* %retval
  %load = load i32, i32* %a, align 4
  %add = add nsw i32 %load, 1
  store i32 %add, i32* %a, align 4
  ret i32* %a
}

define void @store_addrspacecast_captures() #0 {
; CHECK-LABEL: @store_addrspacecast_captures(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[STACKGUARDSLOT:%.*]] = alloca i8*
; CHECK-NEXT:    [[STACKGUARD:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
; CHECK-NEXT:    call void @llvm.stackprotector(i8* [[STACKGUARD]], i8** [[STACKGUARDSLOT]])
; CHECK-NEXT:    [[RETVAL:%.*]] = alloca i32, align 4
; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT:    [[J:%.*]] = alloca i32 addrspace(1)*, align 8
; CHECK-NEXT:    store i32 0, i32* [[RETVAL]]
; CHECK-NEXT:    [[LOAD:%.*]] = load i32, i32* [[A]], align 4
; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[LOAD]], 1
; CHECK-NEXT:    store i32 [[ADD]], i32* [[A]], align 4
; CHECK-NEXT:    [[A_ADDRSPACECAST:%.*]] = addrspacecast i32* [[A]] to i32 addrspace(1)*
; CHECK-NEXT:    store i32 addrspace(1)* [[A_ADDRSPACECAST]], i32 addrspace(1)** [[J]], align 8
; CHECK-NEXT:    [[STACKGUARD1:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
; CHECK-NEXT:    [[TMP0:%.*]] = load volatile i8*, i8** [[STACKGUARDSLOT]]
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8* [[STACKGUARD1]], [[TMP0]]
; CHECK-NEXT:    br i1 [[TMP1]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0
; CHECK:       SP_return:
; CHECK-NEXT:    ret void
; CHECK:       CallStackCheckFailBlk:
; CHECK-NEXT:    call void @__stack_chk_fail()
; CHECK-NEXT:    unreachable
;
entry:
  %retval = alloca i32, align 4
  %a = alloca i32, align 4
  %j = alloca i32 addrspace(1)*, align 8
  store i32 0, i32* %retval
  %load = load i32, i32* %a, align 4
  %add = add nsw i32 %load, 1
  store i32 %add, i32* %a, align 4
  %a.addrspacecast = addrspacecast i32* %a to i32 addrspace(1)*
  store i32 addrspace(1)* %a.addrspacecast, i32 addrspace(1)** %j, align 8
  ret void
}

define void @cmpxchg_captures() #0 {
; CHECK-LABEL: @cmpxchg_captures(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[STACKGUARDSLOT:%.*]] = alloca i8*
; CHECK-NEXT:    [[STACKGUARD:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
; CHECK-NEXT:    call void @llvm.stackprotector(i8* [[STACKGUARD]], i8** [[STACKGUARDSLOT]])
; CHECK-NEXT:    [[RETVAL:%.*]] = alloca i32, align 4
; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT:    [[J:%.*]] = alloca i32*, align 8
; CHECK-NEXT:    store i32 0, i32* [[RETVAL]]
; CHECK-NEXT:    [[LOAD:%.*]] = load i32, i32* [[A]], align 4
; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[LOAD]], 1
; CHECK-NEXT:    store i32 [[ADD]], i32* [[A]], align 4
; CHECK-NEXT:    [[TMP0:%.*]] = cmpxchg i32** [[J]], i32* [[A]], i32* null seq_cst monotonic
; CHECK-NEXT:    [[STACKGUARD1:%.*]] = load volatile i8*, i8* addrspace(257)* inttoptr (i32 40 to i8* addrspace(257)*)
; CHECK-NEXT:    [[TMP1:%.*]] = load volatile i8*, i8** [[STACKGUARDSLOT]]
; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i8* [[STACKGUARD1]], [[TMP1]]
; CHECK-NEXT:    br i1 [[TMP2]], label [[SP_RETURN:%.*]], label [[CALLSTACKCHECKFAILBLK:%.*]], !prof !0
; CHECK:       SP_return:
; CHECK-NEXT:    ret void
; CHECK:       CallStackCheckFailBlk:
; CHECK-NEXT:    call void @__stack_chk_fail()
; CHECK-NEXT:    unreachable
;
entry:
  %retval = alloca i32, align 4
  %a = alloca i32, align 4
  %j = alloca i32*, align 8
  store i32 0, i32* %retval
  %load = load i32, i32* %a, align 4
  %add = add nsw i32 %load, 1
  store i32 %add, i32* %a, align 4

  cmpxchg i32** %j, i32* %a, i32* null seq_cst monotonic
  ret void
}

attributes #0 = { sspstrong }
