; This file is a part of Julia. License is MIT: https://julialang.org/license

; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame,FinalLowerGC)' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE


declare void @boxed_simple({} addrspace(10)*, {} addrspace(10)*)
declare {} addrspace(10)* @jl_box_int64(i64)
declare {}*** @julia.ptls_states()
declare {}*** @julia.get_pgcstack()
declare void @jl_safepoint()
declare {} addrspace(10)* @jl_apply_generic({} addrspace(10)*, {} addrspace(10)**, i32)

define void @simple(i64 %a, i64 %b) {
top:
; CHECK-LABEL: @simple
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
; OPAQUE: call ptr addrspace(10) @jl_box_int64
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)

; OPAQUE: [[GEP0:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 [[GEPSLOT0:[0-9]+]]
; OPAQUE-NEXT: store ptr addrspace(10) %aboxed, ptr [[GEP0]]
    %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b)
; CHECK-NEXT: %bboxed =
; Make sure the same gc slot isn't re-used


; OPAQUE-NOT: getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 [[GEPSLOT0]]
; OPAQUE: [[GEP1:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 [[GEPSLOT1:[0-9]+]]
; OPAQUE-NEXT: store ptr addrspace(10) %bboxed, ptr [[GEP1]]

; CHECK-NEXT: call void @boxed_simple
    call void @boxed_simple({} addrspace(10)* %aboxed,
                            {} addrspace(10)* %bboxed)
    ret void
}

define void @leftover_alloca({} addrspace(10)* %a) {
; If this pass encounters an alloca, it'll just sink it into the gcframe,
; relying on mem2reg to catch simple cases such as this earlier
; CHECK-LABEL: @leftover_alloca
; OPAQUE: %var = getelementptr inbounds ptr addrspace(10), ptr %gcframe
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %var = alloca {} addrspace(10)*
    store {} addrspace(10)* %a, {} addrspace(10)** %var
    %b = load {} addrspace(10)*, {} addrspace(10)** %var
    call void @boxed_simple({} addrspace(10)* %a,
                            {} addrspace(10)* %b)
    ret void
}

declare {{} addrspace(10)*, i8} @union_ret()
declare void @union_arg({{} addrspace(10)*, i8})

define void @simple_union() {
; CHECK-LABEL: @simple_union
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
; OPAQUE: %a = call { ptr addrspace(10), i8 } @union_ret()
    %a = call { {} addrspace(10)*, i8 } @union_ret()

; OPAQUE: [[GEP0:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 [[GEPSLOT0:[0-9]+]]
; OPAQUE-NEXT: [[EXTRACT:%.*]] = extractvalue { ptr addrspace(10), i8 } %a, 0
; OPAQUE-NEXT: store ptr addrspace(10) [[EXTRACT]], ptr [[GEP0]]
    call void @union_arg({{} addrspace(10)*, i8} %a)
    ret void
}

declare void @one_arg_boxed({} addrspace(10)*)

define void @select_simple(i64 %a, i64 %b) {
; CHECK-LABEL: @select_simple
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
    %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b)
    %cmp = icmp eq i64 %a, %b
    %selectb = select i1 %cmp, {} addrspace(10)* %aboxed, {} addrspace(10)* %bboxed
    call void @one_arg_boxed({} addrspace(10)* %selectb)
    ret void
}

define void @phi_simple(i64 %a, i64 %b) {
top:
; CHECK-LABEL: @phi_simple
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %cmp = icmp eq i64 %a, %b
    br i1 %cmp, label %alabel, label %blabel
alabel:
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
    br label %common
blabel:
    %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b)
    br label %common
common:
    %phi = phi {} addrspace(10)* [ %aboxed, %alabel ], [ %bboxed, %blabel ]

; OPAQUE:  [[GEP:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 2
; OPAQUE:  store ptr addrspace(10) %phi, ptr [[GEP]]
    call void @one_arg_boxed({} addrspace(10)* %phi)
    ret void
}

declare void @one_arg_decayed(i64 addrspace(12)*)

define void @select_lift(i64 %a, i64 %b) {
; CHECK-LABEL: @select_lift
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
    %adecayed = addrspacecast {} addrspace(10)* %aboxed to i64 addrspace(12)*
    %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b)
    %bdecayed = addrspacecast {} addrspace(10)* %bboxed to i64 addrspace(12)*
    %cmp = icmp eq i64 %a, %b
; OPAQUE: %gclift = select i1 %cmp, ptr addrspace(10) %aboxed, ptr addrspace(10) %bboxed
    %selectb = select i1 %cmp, i64 addrspace(12)* %adecayed, i64 addrspace(12)* %bdecayed
    call void @one_arg_decayed(i64 addrspace(12)* %selectb)
    ret void
}

define void @phi_lift(i64 %a, i64 %b) {
top:
; CHECK-LABEL: @phi_lift
; OPAQUE: %gclift = phi ptr addrspace(10) [ %aboxed, %alabel ], [ %bboxed, %blabel ], [ %gclift, %common ]
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %cmp = icmp eq i64 %a, %b
    br i1 %cmp, label %alabel, label %blabel
alabel:
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
    %adecayed = addrspacecast {} addrspace(10)* %aboxed to i64 addrspace(12)*
    br label %common
blabel:
    %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b)
    %bdecayed = addrspacecast {} addrspace(10)* %bboxed to i64 addrspace(12)*
    br label %common
common:
    %phi = phi i64 addrspace(12)* [ %adecayed, %alabel ], [ %bdecayed, %blabel ], [ %phi, %common ]
    call void @one_arg_decayed(i64 addrspace(12)* %phi)
    br label %common
}


define void @phi_lift_union(i64 %a, i64 %b) {
top:
; CHECK-LABEL: @phi_lift_union
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %cmp = icmp eq i64 %a, %b
    br i1 %cmp, label %alabel, label %blabel
alabel:
    %u = call { {} addrspace(10)*, i8 } @union_ret()
; OPAQUE: %aboxed = extractvalue { ptr addrspace(10), i8 } %u, 0
    %aboxed = extractvalue { {} addrspace(10)*, i8 } %u, 0
    %adecayed = addrspacecast {} addrspace(10)* %aboxed to i64 addrspace(12)*
; CHECK: br label %common
    br label %common
blabel:
    %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b)
    %bdecayed = addrspacecast {} addrspace(10)* %bboxed to i64 addrspace(12)*
    br label %common
common:
; OPAQUE: %gclift = phi ptr addrspace(10) [ %aboxed, %alabel ], [ %bboxed, %blabel ]
    %phi = phi i64 addrspace(12)* [ %adecayed, %alabel ], [ %bdecayed, %blabel ]
    call void @one_arg_decayed(i64 addrspace(12)* %phi)
    ret void
}

define void @live_if_live_out(i64 %a, i64 %b) {
; CHECK-LABEL: @live_if_live_out
top:
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
; The failure case is failing to realize that `aboxed` is live across the first
; one_arg_boxed safepoint and putting bboxed in the same root slot
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
    %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b)
    call void @one_arg_boxed({} addrspace(10)* %bboxed)
    br label %succ
succ:
    call void @one_arg_boxed({} addrspace(10)* %aboxed)
    ret void
}

; A ret is a use - make sure the value is kept alive for any intervening
; safepoint
define {} addrspace(10)* @ret_use(i64 %a, i64 %b) {
; CHECK-LABEL: @ret_use
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
; OPAQUE: store ptr addrspace(10) %aboxed
    %bboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %b)
    ret {} addrspace(10)* %aboxed
}

define {{} addrspace(10)*, i8} @ret_use_struct() {
; CHECK-LABEL: @ret_use_struct
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
; OPAQUE: %aunion = call { ptr addrspace(10), i8 } @union_ret()
    %aunion = call { {} addrspace(10)*, i8 } @union_ret()

; OPAQUE-DAG: [[GEP0:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 [[GEPSLOT0:[0-9]+]]
; OPAQUE-DAG: [[EXTRACT:%.*]] = extractvalue { ptr addrspace(10), i8 } %aunion, 0
; OPAQUE-NEXT: store ptr addrspace(10) [[EXTRACT]], ptr [[GEP0]]

; CHECK-NEXT: call void @jl_safepoint()
    call void @jl_safepoint()
    ret {{} addrspace(10)*, i8} %aunion
}


define i8 @nosafepoint({} addrspace(10)* dereferenceable(16)) {
; CHECK-LABEL: @nosafepoint
; CHECK-NOT: %gcframe
top:
  %pgcstack = call {}*** @julia.get_pgcstack()
  %1 = call {}*** @julia.ptls_states()
  %2 = bitcast {}*** %1 to {} addrspace(10)**
  %3 = getelementptr {} addrspace(10)*, {} addrspace(10)** %2, i64 3
  %4 = bitcast {} addrspace(10)** %3 to i64**
  %5 = load i64*, i64** %4
  %6 = bitcast {} addrspace(10)* %0 to i8 addrspace(10)*
  %7 = addrspacecast i8 addrspace(10)* %6 to i8 addrspace(11)*
  %8 = getelementptr i8, i8 addrspace(11)* %7, i64 0
  %9 = load i8, i8 addrspace(11)* %8
  %10 = trunc i8 %9 to i1
  %11 = zext i1 %10 to i8
  %12 = xor i8 %11, 1
  ret i8 %12
}

define void @global_ref() {
; CHECK-LABEL: @global_ref
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %loaded = load {} addrspace(10)*, {} addrspace(10)** getelementptr ({} addrspace(10)*, {} addrspace(10)** inttoptr (i64 140540744325952 to {} addrspace(10)**), i64 1)
; OPAQUE: store ptr addrspace(10) %loaded, ptr
    call void @one_arg_boxed({} addrspace(10)* %loaded)
    ret void
}

define {} addrspace(10)* @no_redundant_rerooting(i64 %a, i1 %cond) {
; CHECK-LABEL: @no_redundant_rerooting
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
; OPAQUE: store ptr addrspace(10) %aboxed
; CHECK-NEXT: call void @jl_safepoint()
    call void @jl_safepoint()
    br i1 %cond, label %blocka, label %blockb
blocka:
; CHECK-NOT: call void @jl_safepoint()
; CHECK: call void @jl_safepoint()
    call void @jl_safepoint()
    ret {} addrspace(10)* %aboxed
blockb:
; CHECK-NOT: call void @jl_safepoint()
; CHECK: call void @jl_safepoint()
    call void @jl_safepoint()
    ret {} addrspace(10)* %aboxed
}

declare void @llvm.memcpy.p064.p10i8.i64(i64*, i8 addrspace(10)*, i64, i32, i1)

define void @memcpy_use(i64 %a, i64 *%aptr) {
; CHECK-LABEL: @memcpy_use
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
; OPAQUE: store ptr addrspace(10) %aboxed
    call void @jl_safepoint()
    %acast = bitcast {} addrspace(10)* %aboxed to i8 addrspace(10)*
    call void @llvm.memcpy.p064.p10i8.i64(i64* %aptr, i8 addrspace(10)* %acast, i64 8, i32 1, i1 false)
    ret void
}

declare token @llvm.julia.gc_preserve_begin(...)
declare void @llvm.julia.gc_preserve_end(token)

define void @gc_preserve(i64 %a) {
; CHECK-LABEL: @gc_preserve
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
; OPAQUE: store ptr addrspace(10) %aboxed
    call void @jl_safepoint()
    %tok = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* %aboxed)
    %aboxed2 = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
; OPAQUE: store ptr addrspace(10) %aboxed2
    call void @jl_safepoint()
    call void @llvm.julia.gc_preserve_end(token %tok)
    %aboxed3 = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
; OPAQUE: store ptr addrspace(10) %aboxed3
    call void @jl_safepoint()
    call void @one_arg_boxed({} addrspace(10)* %aboxed2)
    call void @one_arg_boxed({} addrspace(10)* %aboxed3)
    ret void
}

define void @gc_preserve_vec([2 x <2 x {} addrspace(10)*>] addrspace(11)* nocapture nonnull readonly dereferenceable(16)) {
; CHECK-LABEL: @gc_preserve_vec
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 6
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %v = load [2 x <2 x {} addrspace(10)*>], [2 x <2 x {} addrspace(10)*>] addrspace(11)* %0, align 8

; OPAQUE-DAG: [[EXTRACT11:%.*]] = extractvalue [2 x <2 x ptr addrspace(10)>] %v, 0
; OPAQUE-DAG: [[EXTRACT12:%.*]] = extractvalue [2 x <2 x ptr addrspace(10)>] %v, 0
; OPAQUE-DAG: [[EXTRACT21:%.*]] = extractvalue [2 x <2 x ptr addrspace(10)>] %v, 1
; OPAQUE-DAG: [[EXTRACT22:%.*]] = extractvalue [2 x <2 x ptr addrspace(10)>] %v, 1
; OPAQUE-DAG: [[V11:%.*]] = extractelement <2 x ptr addrspace(10)> [[EXTRACT11]], i32 0
; OPAQUE-DAG: [[V12:%.*]] = extractelement <2 x ptr addrspace(10)> [[EXTRACT12]], i32 1
; OPAQUE-DAG: [[V21:%.*]] = extractelement <2 x ptr addrspace(10)> [[EXTRACT21]], i32 0
; OPAQUE-DAG: [[V22:%.*]] = extractelement <2 x ptr addrspace(10)> [[EXTRACT22]], i32 1
; OPAQUE-DAG: store ptr addrspace(10) [[V11]]
; OPAQUE-DAG: store ptr addrspace(10) [[V12]]
; OPAQUE-DAG: store ptr addrspace(10) [[V21]]
; OPAQUE-DAG: store ptr addrspace(10) [[V22]]
    %tok = call token (...) @llvm.julia.gc_preserve_begin([2 x <2 x {} addrspace(10)*>] %v, i64 addrspace(10)* null, {}*** %ptls)
    call void @jl_safepoint()
    ret void
}


@gv1 = external global {}*
@gv2 = external global {} addrspace(10)*

define {} addrspace(10)* @gv_const() {
; CHECK-LABEL: @gv_const
; CHECK-NOT: %gcframe
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %v10 = load {}*, {}** @gv1, !tbaa !2
    %v1 = addrspacecast {}* %v10 to {} addrspace(10)*
    %v2 = load {} addrspace(10)*, {} addrspace(10)** @gv2, !tbaa !2
    call void @jl_safepoint()
    call void @one_arg_boxed({} addrspace(10)* %v1)
    call void @one_arg_boxed({} addrspace(10)* %v2)
    ret {} addrspace(10)* %v1
}

define {} addrspace(10)* @vec_jlcallarg({} addrspace(10)*, {} addrspace(10)**, i32) {
; CHECK-LABEL: @vec_jlcallarg
; CHECK-NOT: %gcframe
  %pgcstack = call {}*** @julia.get_pgcstack()
  %v4 = call {}*** @julia.ptls_states()
  %v5 = bitcast {} addrspace(10)** %1 to <2 x {} addrspace(10)*>*
  %v6 = load <2 x {} addrspace(10)*>, <2 x {} addrspace(10)*>* %v5, align 8
  %v7 = extractelement <2 x {} addrspace(10)*> %v6, i32 0
  ret {} addrspace(10)* %v7
}

declare {} addrspace(10) *@alloc()

define {} addrspace(10)* @vec_loadobj() {
; CHECK-LABEL: @vec_loadobj
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
  %pgcstack = call {}*** @julia.get_pgcstack()
  %v4 = call {}*** @julia.ptls_states()
  %obj = call {} addrspace(10) *@alloc()
  %v1 = bitcast {} addrspace(10) * %obj to {} addrspace(10)* addrspace(10)*
  %v5 = bitcast {} addrspace(10)* addrspace(10)* %v1 to <2 x {} addrspace(10)*> addrspace(10)*
  %v6 = load <2 x {} addrspace(10)*>, <2 x {} addrspace(10)*> addrspace(10)* %v5, align 8
  %obj2 = call {} addrspace(10) *@alloc()
  %v7 = extractelement <2 x {} addrspace(10)*> %v6, i32 0
  ret {} addrspace(10)* %v7
}

define {} addrspace(10)* @vec_gep() {
; CHECK-LABEL: @vec_gep
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
  %pgcstack = call {}*** @julia.get_pgcstack()
  %v4 = call {}*** @julia.ptls_states()
  %obj = call {} addrspace(10) *@alloc()
  %obj1 = bitcast {} addrspace(10) * %obj to {} addrspace(10)* addrspace(10)*
  %v1 = getelementptr {} addrspace(10)*, {} addrspace(10)* addrspace(10)* %obj1, <2 x i32> < i32 0, i32 0 >
  call void @jl_safepoint()
  %obj2 = extractelement <2 x {} addrspace(10)* addrspace(10)*> %v1, i32 0
  %obj3 = bitcast {} addrspace(10)* addrspace(10)* %obj2 to {} addrspace(10)*
  ret {} addrspace(10)* %obj3
}

declare i1 @check_property({} addrspace(10)* %val)
define void @loopyness(i1 %cond1, {} addrspace(10) *%arg) {
; CHECK-LABEL: @loopyness
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    br label %header

header:
    %phi = phi {} addrspace(10)* [null, %top], [%obj, %latch]
    br i1 %cond1, label %a, label %latch

a:
; This needs a store
; CHECK-LABEL: a:

; OPAQUE:  [[GEP1:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 [[GEPSLOT0:[0-9]+]]
; OPAQUE:  store ptr addrspace(10) %phi, ptr [[GEP1]]
    call void @one_arg_boxed({} addrspace(10)* %phi)
    br label %latch

latch:
; This as well in case we went the other path

; OPAQUE:  [[GEP2:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 [[GEPSLOT0]]
; OPAQUE:  store ptr addrspace(10) %phi, ptr [[GEP2]]
    %obj = call {} addrspace(10)* @alloc()
    %cond = call i1 @check_property({} addrspace(10)* %phi)
    br i1 %cond, label %exit, label %header

exit:
    ret void
}

define {} addrspace(10)* @phi_union(i1 %cond) {
; CHECK-LABEL: @phi_union
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
top:
  %pgcstack = call {}*** @julia.get_pgcstack()
  %ptls = call {}*** @julia.ptls_states()
  br i1 %cond, label %a, label %b

a:
  %obj = call {} addrspace(10) *@alloc()
  %aobj = insertvalue {{} addrspace(10)*, i8} undef, {} addrspace(10)* %obj, 0
  %aunion = insertvalue {{} addrspace(10)*, i8} undef, i8 -126, 1
  br label %join

b:
  %bunion = call {{} addrspace(10)*, i8} @union_ret()
  br label %join

join:
  %phi = phi {{} addrspace(10)*, i8} [%aunion, %a], [%bunion, %b]
  call void @jl_safepoint()
  %rval = extractvalue { {} addrspace(10)*, i8 } %phi, 0
  ret {} addrspace(10)* %rval
}

define {} addrspace(10)* @select_union(i1 %cond) {
; CHECK-LABEL: @select_union
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3
top:
  %pgcstack = call {}*** @julia.get_pgcstack()
  %ptls = call {}*** @julia.ptls_states()
  %obj = call {} addrspace(10) *@alloc()
  %aobj = insertvalue {{} addrspace(10)*, i8} undef, {} addrspace(10)* %obj, 0
  %aunion = insertvalue {{} addrspace(10)*, i8} undef, i8 -126, 1
  %bunion = call {{} addrspace(10)*, i8} @union_ret()
  %select = select i1 %cond, {{} addrspace(10)*, i8} %aunion, {{} addrspace(10)*, i8} %bunion
  call void @jl_safepoint()
  %rval = extractvalue { {} addrspace(10)*, i8 } %select, 0
  ret {} addrspace(10)* %rval
}

define i8 @simple_arrayptr() {
; CHECK-LABEL: @simple_arrayptr
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
top:
   %pgcstack = call {}*** @julia.get_pgcstack()
   %ptls = call {}*** @julia.ptls_states()
   %obj1 = call {} addrspace(10) *@alloc()
   %obj2 = call {} addrspace(10) *@alloc()
   %decayed = addrspacecast {} addrspace(10) *%obj1 to {} addrspace(11) *
   %arrayptrptr = bitcast {} addrspace(11) *%decayed to i8 addrspace(13)* addrspace(11)*
   %arrayptr = load i8 addrspace(13)*, i8 addrspace(13)* addrspace(11)* %arrayptrptr
   call void @jl_safepoint()
   call void @one_arg_boxed({} addrspace(10) *%obj2)
   %val = load i8, i8 addrspace(13)* %arrayptr
   ret i8 %val
}

define {} addrspace(10)* @vecstoreload(<2 x {} addrspace(10)*> *%arg) {
; CHECK-LABEL: @vecstoreload
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %loaded = load <2 x {} addrspace(10)*>, <2 x {} addrspace(10)*> *%arg
    call void @jl_safepoint()
    %obj = call {} addrspace(10) *@alloc()
    %casted = bitcast {} addrspace(10)* %obj to <2 x {} addrspace(10)*> addrspace(10)*
    store <2 x {} addrspace(10)*> %loaded, <2 x {} addrspace(10)*> addrspace(10)* %casted
    ret {} addrspace(10)* %obj
}

define void @vecphi(i1 %cond, <2 x {} addrspace(10)*> *%arg) {
; CHECK-LABEL: @vecphi
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    br i1 %cond, label %A, label %B

A:
    br label %common

B:
    %loaded = load <2 x {} addrspace(10)*>, <2 x {} addrspace(10)*> *%arg
    call void @jl_safepoint()
    br label %common

common:
    %phi = phi <2 x {} addrspace(10)*> [ zeroinitializer, %A ], [ %loaded, %B ]
    call void @jl_safepoint()
    %el1 = extractelement <2 x {} addrspace(10)*> %phi, i32 0
    %el2 = extractelement <2 x {} addrspace(10)*> %phi, i32 1
    call void @one_arg_boxed({} addrspace(10)* %el1)
    call void @one_arg_boxed({} addrspace(10)* %el2)
    unreachable
}

define i8 @phi_arrayptr(i1 %cond) {
; CHECK-LABEL: @phi_arrayptr
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    br i1 %cond, label %A, label %B

A:
    %obj1 = call {} addrspace(10) *@alloc()
    %obj2 = call {} addrspace(10) *@alloc()
    %decayed1 = addrspacecast {} addrspace(10) *%obj1 to {} addrspace(11) *
    %arrayptrptr1 = bitcast {} addrspace(11) *%decayed1 to i8 addrspace(13)* addrspace(11)*
    %arrayptr1 = load i8 addrspace(13)*, i8 addrspace(13)* addrspace(11)* %arrayptrptr1
    %decayed2 = addrspacecast {} addrspace(10) *%obj2 to {} addrspace(11) *
    %arrayptrptr2 = bitcast {} addrspace(11) *%decayed2 to i8 addrspace(13)* addrspace(11)*
    %arrayptr2 = load i8 addrspace(13)*, i8 addrspace(13)* addrspace(11)* %arrayptrptr2
    %insert1 = insertelement <2 x i8 addrspace(13)*> undef, i8 addrspace(13)* %arrayptr1, i32 0
    %insert2 = insertelement <2 x i8 addrspace(13)*> %insert1, i8 addrspace(13)* %arrayptr2, i32 1
    call void @jl_safepoint()
    br label %common

B:
    br label %common

common:
; CHECK: %gclift
; CHECK: %gclift1
; CHECK-NOT: %gclift2
    %phi = phi <2 x i8 addrspace(13)*> [ %insert2, %A ], [ zeroinitializer, %B ]
    call void @jl_safepoint()
    %el1 = extractelement <2 x i8 addrspace(13)*> %phi, i32 0
    %el2 = extractelement <2 x i8 addrspace(13)*> %phi, i32 1
    %l1 = load i8, i8 addrspace(13)* %el1
    %l2 = load i8, i8 addrspace(13)* %el2
    %add = add i8 %l1, %l2
    ret i8 %add
}

define void @vecselect(i1 %cond, <2 x {} addrspace(10)*> *%arg) {
; CHECK-LABEL: @vecselect
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %loaded = load <2 x {} addrspace(10)*>, <2 x {} addrspace(10)*> *%arg
    call void @jl_safepoint()
    %select = select i1 %cond, <2 x {} addrspace(10)*> zeroinitializer, <2 x {} addrspace(10)*> %loaded
    call void @jl_safepoint()
    %el1 = extractelement <2 x {} addrspace(10)*> %select, i32 0
    %el2 = extractelement <2 x {} addrspace(10)*> %select, i32 1
    call void @one_arg_boxed({} addrspace(10)* %el1)
    call void @one_arg_boxed({} addrspace(10)* %el2)
    unreachable
}

define void @vecselect_lift(i1 %cond, <2 x {} addrspace(10)*> *%arg) {
; CHECK-LABEL: @vecselect_lift
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %loaded = load <2 x {} addrspace(10)*>, <2 x {} addrspace(10)*> *%arg
    %decayed = addrspacecast <2 x {} addrspace(10)*> %loaded to <2 x i64 addrspace(12)*>
    call void @jl_safepoint()
; OPAQUE: %gclift = select i1 %cond, ptr addrspace(10) null, ptr addrspace(10) %{{[0-9]+}}
    %select = select i1 %cond, <2 x i64 addrspace(12)*> zeroinitializer, <2 x i64 addrspace(12)*> %decayed
    call void @jl_safepoint()
    %el1 = extractelement <2 x i64 addrspace(12)*> %select, i32 0
    %el2 = extractelement <2 x i64 addrspace(12)*> %select, i32 1
    call void @one_arg_decayed(i64 addrspace(12)* %el1)
    call void @one_arg_decayed(i64 addrspace(12)* %el2)
    unreachable
}

define void @vecvecselect_lift(<2 x i1> %cond, <2 x {} addrspace(10)*> *%arg) {
; CHECK-LABEL: @vecvecselect_lift
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %loaded = load <2 x {} addrspace(10)*>, <2 x {} addrspace(10)*> *%arg
    %decayed = addrspacecast <2 x {} addrspace(10)*> %loaded to <2 x i64 addrspace(12)*>
    call void @jl_safepoint()
; OPAQUE: %gclift = select i1 %{{[0-9]+}}, ptr addrspace(10) null, ptr addrspace(10) %{{[0-9]+}}
    %select = select <2 x i1> %cond, <2 x i64 addrspace(12)*> zeroinitializer, <2 x i64 addrspace(12)*> %decayed
    call void @jl_safepoint()
    %el1 = extractelement <2 x i64 addrspace(12)*> %select, i32 0
    %el2 = extractelement <2 x i64 addrspace(12)*> %select, i32 1
    call void @one_arg_decayed(i64 addrspace(12)* %el1)
    call void @one_arg_decayed(i64 addrspace(12)* %el2)
    unreachable
}

define void @vecscalarselect_lift(<2 x i1> %cond, i64 %a) {
; CHECK-LABEL: @vecscalarselect_lift
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
    %adecayed = addrspacecast {} addrspace(10)* %aboxed to i64 addrspace(12)*
    %avec = getelementptr i64, i64 addrspace(12)*  %adecayed, <2 x i32> zeroinitializer
    call void @jl_safepoint()
; OPAQUE: %gclift = select i1 %{{[0-9]+}}, ptr addrspace(10) null, ptr addrspace(10) %aboxed
    %select = select <2 x i1> %cond, <2 x i64 addrspace(12)*> zeroinitializer, <2 x i64 addrspace(12)*> %avec
    call void @jl_safepoint()
    %el1 = extractelement <2 x i64 addrspace(12)*> %select, i32 0
    %el2 = extractelement <2 x i64 addrspace(12)*> %select, i32 1
    call void @one_arg_decayed(i64 addrspace(12)* %el1)
    call void @one_arg_decayed(i64 addrspace(12)* %el2)
    unreachable
}

define void @scalarvecselect_lift(i1 %cond, i64 %a) {
; CHECK-LABEL: @scalarvecselect_lift
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %aboxed = call {} addrspace(10)* @jl_box_int64(i64 signext %a)
    %adecayed = addrspacecast {} addrspace(10)* %aboxed to i64 addrspace(12)*
    %avec = getelementptr i64, i64 addrspace(12)*  %adecayed, <2 x i32> zeroinitializer
    call void @jl_safepoint()
; OPAQUE: %gclift = select i1 %cond, ptr addrspace(10) null, ptr addrspace(10) %aboxed
    %select = select i1 %cond, <2 x i64 addrspace(12)*> zeroinitializer, <2 x i64 addrspace(12)*> %avec
    call void @jl_safepoint()
    %el1 = extractelement <2 x i64 addrspace(12)*> %select, i32 0
    %el2 = extractelement <2 x i64 addrspace(12)*> %select, i32 1
    call void @one_arg_decayed(i64 addrspace(12)* %el1)
    call void @one_arg_decayed(i64 addrspace(12)* %el2)
    unreachable
}

define i8 @select_arrayptr(i1 %cond) {
; CHECK-LABEL: @select_arrayptr
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %obj1 = call {} addrspace(10) *@alloc()
    %obj2 = call {} addrspace(10) *@alloc()
    %decayed1 = addrspacecast {} addrspace(10) *%obj1 to {} addrspace(11) *
    %arrayptrptr1 = bitcast {} addrspace(11) *%decayed1 to i8 addrspace(13)* addrspace(11)*
    %arrayptr1 = load i8 addrspace(13)*, i8 addrspace(13)* addrspace(11)* %arrayptrptr1
    %decayed2 = addrspacecast {} addrspace(10) *%obj2 to {} addrspace(11) *
    %arrayptrptr2 = bitcast {} addrspace(11) *%decayed2 to i8 addrspace(13)* addrspace(11)*
    %arrayptr2 = load i8 addrspace(13)*, i8 addrspace(13)* addrspace(11)* %arrayptrptr2
    %insert1 = insertelement <2 x i8 addrspace(13)*> undef, i8 addrspace(13)* %arrayptr1, i32 0
    %insert2 = insertelement <2 x i8 addrspace(13)*> %insert1, i8 addrspace(13)* %arrayptr2, i32 1
    call void @jl_safepoint()
    %select = select i1 %cond, <2 x i8 addrspace(13)*> %insert2, <2 x i8 addrspace(13)*> zeroinitializer
    call void @jl_safepoint()
    %el1 = extractelement <2 x i8 addrspace(13)*> %select, i32 0
    %el2 = extractelement <2 x i8 addrspace(13)*> %select, i32 1
    %l1 = load i8, i8 addrspace(13)* %el1
    %l2 = load i8, i8 addrspace(13)* %el2
    %add = add i8 %l1, %l2
    ret i8 %add
}

define i8 @vector_arrayptrs() {
; CHECK-LABEL: @vector_arrayptrs
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3


; OPAQUE: [[GEP0:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 2
; OPAQUE: store ptr addrspace(10) %obj1, ptr [[GEP0]]
;
top:
   %pgcstack = call {}*** @julia.get_pgcstack()
   %ptls = call {}*** @julia.ptls_states()
   %obj1 = call {} addrspace(10) *@alloc()
   %decayed = addrspacecast {} addrspace(10) *%obj1 to {} addrspace(11) *
   %arrayptrptr = bitcast {} addrspace(11) *%decayed to <2 x i8 addrspace(13)*> addrspace(11)*
   %arrayptrs = load <2 x i8 addrspace(13)*>, <2 x i8 addrspace(13)*> addrspace(11)* %arrayptrptr, align 16
   %arrayptr = extractelement <2 x i8 addrspace(13)*> %arrayptrs, i32 0
   call void @jl_safepoint()
   %val = load i8, i8 addrspace(13)* %arrayptr
   ret i8 %val
}

declare <2 x i8 addrspace(13)*> @llvm.masked.load.v2p13i8.p11v2p13i8 (<2 x i8 addrspace(13)*> addrspace(11)*, i32, <2 x i1>, <2 x i8 addrspace(13)*>)

define i8 @masked_arrayptrs() {
; CHECK-LABEL: @masked_arrayptrs
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3


; OPAQUE: %arrayptrs = call <2 x ptr addrspace(13)> @llvm.masked.load.v2p13.p11(ptr addrspace(11) %arrayptrptr, i32 16, <2 x i1> <i1 true, i1 false>, <2 x ptr addrspace(13)> zeroinitializer)
; OPAQUE: [[GEP0:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 2
; OPAQUE: store ptr addrspace(10) %obj1, ptr [[GEP0]]
;
top:
   %pgcstack = call {}*** @julia.get_pgcstack()
   %ptls = call {}*** @julia.ptls_states()
   %obj1 = call {} addrspace(10) *@alloc()
   %decayed = addrspacecast {} addrspace(10) *%obj1 to {} addrspace(11) *
   %arrayptrptr = bitcast {} addrspace(11) *%decayed to <2 x i8 addrspace(13)*> addrspace(11)*
   %arrayptrs = call <2 x i8 addrspace(13)*> @llvm.masked.load.v2p13i8.p11v2p13i8(<2 x i8 addrspace(13)*> addrspace(11)* %arrayptrptr, i32 16, <2 x i1> <i1 true, i1 false>, <2 x i8 addrspace(13)*> undef)
   %arrayptr = extractelement <2 x i8 addrspace(13)*> %arrayptrs, i32 0
   call void @jl_safepoint()
   %val = load i8, i8 addrspace(13)* %arrayptr
   ret i8 %val
}

declare <2 x i8 addrspace(13)*> @llvm.masked.gather.v2p13i8.v2p11p13i8 (<2 x i8 addrspace(13)* addrspace(11)*>, i32, <2 x i1>, <2 x i8 addrspace(13)*>)

define i8 @gather_arrayptrs() {
; CHECK-LABEL: @gather_arrayptrs
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3


; OPAQUE: %arrayptrs = call <2 x ptr addrspace(13)> @llvm.masked.gather.v2p13.v2p11(<2 x ptr addrspace(11)> %arrayptrptrs, i32 16, <2 x i1> <i1 true, i1 false>, <2 x ptr addrspace(13)> zeroinitializer)
; OPAQUE: [[GEP0:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 2
; OPAQUE: store ptr addrspace(10) %obj1, ptr [[GEP0]]
;
top:
   %pgcstack = call {}*** @julia.get_pgcstack()
   %ptls = call {}*** @julia.ptls_states()
   %obj1 = call {} addrspace(10) *@alloc()
   %decayed = addrspacecast {} addrspace(10) *%obj1 to {} addrspace(11)*
   %arrayptrptr = bitcast {} addrspace(11) *%decayed to i8 addrspace(13)* addrspace(11)*
   %arrayptrptrs = insertelement <2 x i8 addrspace(13)* addrspace(11)*> zeroinitializer, i8 addrspace(13)* addrspace(11)* %arrayptrptr, i32 0
   %arrayptrs = call <2 x i8 addrspace(13)*> @llvm.masked.gather.v2p13i8.v2p11p13i8(<2 x i8 addrspace(13)* addrspace(11)*> %arrayptrptrs, i32 16, <2 x i1> <i1 true, i1 false>, <2 x i8 addrspace(13)*> undef)
   %arrayptr = extractelement <2 x i8 addrspace(13)*> %arrayptrs, i32 0
   call void @jl_safepoint()
   %val = load i8, i8 addrspace(13)* %arrayptr
   ret i8 %val
}

define i8 @gather_arrayptrs_alltrue() {
; CHECK-LABEL: @gather_arrayptrs
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3


; OPAQUE: %arrayptrs = call <2 x ptr addrspace(13)> @llvm.masked.gather.v2p13.v2p11(<2 x ptr addrspace(11)> %arrayptrptrs, i32 16, <2 x i1> <i1 true, i1 true>, <2 x ptr addrspace(13)> zeroinitializer)
; OPAQUE: [[GEP0:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 2
; OPAQUE: store ptr addrspace(10) %obj1, ptr [[GEP0]]
;
top:
   %pgcstack = call {}*** @julia.get_pgcstack()
   %ptls = call {}*** @julia.ptls_states()
   %obj1 = call {} addrspace(10) *@alloc()
   %decayed = addrspacecast {} addrspace(10) *%obj1 to {} addrspace(11)*
   %arrayptrptr = bitcast {} addrspace(11) *%decayed to i8 addrspace(13)* addrspace(11)*
   %arrayptrptrs = insertelement <2 x i8 addrspace(13)* addrspace(11)*> zeroinitializer, i8 addrspace(13)* addrspace(11)* %arrayptrptr, i32 0
   %arrayptrs = call <2 x i8 addrspace(13)*> @llvm.masked.gather.v2p13i8.v2p11p13i8(<2 x i8 addrspace(13)* addrspace(11)*> %arrayptrptrs, i32 16, <2 x i1> <i1 true, i1 true>, <2 x i8 addrspace(13)*> zeroinitializer)
   %arrayptr = extractelement <2 x i8 addrspace(13)*> %arrayptrs, i32 0
   call void @jl_safepoint()
   %val = load i8, i8 addrspace(13)* %arrayptr
   ret i8 %val
}

define i8 @lost_select_decayed(i1 %arg1) {
; CHECK-LABEL: @lost_select_decayed
; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 3


; OPAQUE: [[GEP0:%.*]] = getelementptr inbounds ptr addrspace(10), ptr %gcframe, i32 2
; OPAQUE: store ptr addrspace(10) [[SOMETHING:%.*]], ptr [[GEP0]]
top:
    %pgcstack = call {}*** @julia.get_pgcstack()
    %ptls = call {}*** @julia.ptls_states()
    %obj1 = call {} addrspace(10) *@alloc()
    %decayed = addrspacecast {} addrspace(10) *%obj1 to {} addrspace(11)*
    %selected = select i1 %arg1, {} addrspace(11)* null, {} addrspace(11)* %decayed
    %casted = bitcast {} addrspace(11)* %selected to i8 addrspace(11)*
    call void @jl_safepoint()
    %val = load i8, i8 addrspace(11)* %casted
    ret i8 %val
}

!0 = !{!"jtbaa"}
!1 = !{!"jtbaa_const", !0, i64 0}
!2 = !{!1, !1, i64 0, i64 1}
