Compiler projects using llvm
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=sroa -S | FileCheck %s

target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"

declare void @llvm.memcpy.p0.p1.i32(ptr nocapture writeonly, ptr addrspace(1) nocapture readonly, i32, i1 immarg) #0
declare void @llvm.memcpy.p1.p0.i32(ptr addrspace(1) nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #0

define i64 @alloca_addrspacecast_bitcast(i64 %X) {
; CHECK-LABEL: @alloca_addrspacecast_bitcast(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    ret i64 [[X:%.*]]
;
entry:
  %A = alloca [8 x i8]
  %A.cast = addrspacecast ptr %A to ptr addrspace(1)
  store i64 %X, ptr addrspace(1) %A.cast
  %Z = load i64, ptr addrspace(1) %A.cast
  ret i64 %Z
}

define i64 @alloca_bitcast_addrspacecast(i64 %X) {
; CHECK-LABEL: @alloca_bitcast_addrspacecast(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    ret i64 [[X:%.*]]
;
entry:
  %A = alloca [8 x i8]
  %B = addrspacecast ptr %A to ptr addrspace(1)
  store i64 %X, ptr addrspace(1) %B
  %Z = load i64, ptr addrspace(1) %B
  ret i64 %Z
}

define i64 @alloca_addrspacecast_gep(i64 %X) {
; CHECK-LABEL: @alloca_addrspacecast_gep(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    ret i64 [[X:%.*]]
;
entry:
  %A.as0 = alloca [256 x i8], align 4

  %gepA.as0 = getelementptr [256 x i8], ptr %A.as0, i16 0, i16 32
  store i64 %X, ptr %gepA.as0, align 4

  %A.as1 = addrspacecast ptr %A.as0 to ptr addrspace(1)
  %gepA.as1 = getelementptr [256 x i8], ptr addrspace(1) %A.as1, i16 0, i16 32
  %Z = load i64, ptr addrspace(1) %gepA.as1, align 4

  ret i64 %Z
}

define i64 @alloca_gep_addrspacecast(i64 %X) {
; CHECK-LABEL: @alloca_gep_addrspacecast(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    ret i64 [[X:%.*]]
;
entry:
  %A.as0 = alloca [256 x i8], align 4

  %gepA.as0 = getelementptr [256 x i8], ptr %A.as0, i16 0, i16 32
  store i64 %X, ptr %gepA.as0, align 4

  %gepA.as1.bc = addrspacecast ptr %gepA.as0 to ptr addrspace(1)
  %Z = load i64, ptr addrspace(1) %gepA.as1.bc, align 4
  ret i64 %Z
}

define i64 @alloca_gep_addrspacecast_gep(i64 %X) {
; CHECK-LABEL: @alloca_gep_addrspacecast_gep(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    ret i64 [[X:%.*]]
;
entry:
  %A.as0 = alloca [256 x i8], align 4

  %gepA.as0 = getelementptr [256 x i8], ptr %A.as0, i16 0, i16 32
  store i64 %X, ptr %gepA.as0, align 4


  %gepB.as0 = getelementptr [256 x i8], ptr %A.as0, i16 0, i16 16
  %gepB.as1 = addrspacecast ptr %gepB.as0 to ptr addrspace(1)
  %gepC.as1 = getelementptr i8, ptr addrspace(1) %gepB.as1, i16 16
  %Z = load i64, ptr addrspace(1) %gepC.as1, align 4

  ret i64 %Z
}

define i64 @getAdjustedPtr_addrspacecast_gep(ptr %x) {
; CHECK-LABEL: @getAdjustedPtr_addrspacecast_gep(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[CAST1:%.*]] = addrspacecast ptr [[X:%.*]] to ptr addrspace(1)
; CHECK-NEXT:    [[A_SROA_0_0_COPYLOAD:%.*]] = load i64, ptr addrspace(1) [[CAST1]], align 1
; CHECK-NEXT:    [[A_SROA_2_0_CAST1_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[CAST1]], i16 8
; CHECK-NEXT:    [[A_SROA_2_0_COPYLOAD:%.*]] = load i64, ptr addrspace(1) [[A_SROA_2_0_CAST1_SROA_IDX]], align 1
; CHECK-NEXT:    ret i64 [[A_SROA_0_0_COPYLOAD]]
;
entry:
  %a = alloca [32 x i8], align 8
  %cast1 = addrspacecast ptr %x to ptr addrspace(1)
  call void @llvm.memcpy.p0.p1.i32(ptr %a, ptr addrspace(1) %cast1, i32 16, i1 false)
  %val = load i64, ptr %a
  ret i64 %val
}

define i64 @getAdjustedPtr_gep_addrspacecast(ptr %x) {
; CHECK-LABEL: @getAdjustedPtr_gep_addrspacecast(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[GEP_X:%.*]] = getelementptr [32 x i8], ptr [[X:%.*]], i32 0, i32 16
; CHECK-NEXT:    [[CAST1:%.*]] = addrspacecast ptr [[GEP_X]] to ptr addrspace(1)
; CHECK-NEXT:    [[A_SROA_0_0_COPYLOAD:%.*]] = load i64, ptr addrspace(1) [[CAST1]], align 1
; CHECK-NEXT:    [[A_SROA_2_0_CAST1_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[CAST1]], i16 8
; CHECK-NEXT:    [[A_SROA_2_0_COPYLOAD:%.*]] = load i64, ptr addrspace(1) [[A_SROA_2_0_CAST1_SROA_IDX]], align 1
; CHECK-NEXT:    ret i64 [[A_SROA_0_0_COPYLOAD]]
;
entry:
  %a = alloca [32 x i8], align 8
  %gep.x = getelementptr [32 x i8], ptr %x, i32 0, i32 16
  %cast1 = addrspacecast ptr %gep.x to ptr addrspace(1)

  call void @llvm.memcpy.p0.p1.i32(ptr %a, ptr addrspace(1) %cast1, i32 16, i1 false)
  %val = load i64, ptr %a
  ret i64 %val
}

define i64 @getAdjustedPtr_gep_addrspacecast_gep(ptr %x) {
; CHECK-LABEL: @getAdjustedPtr_gep_addrspacecast_gep(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[GEP0_X:%.*]] = getelementptr [32 x i8], ptr [[X:%.*]], i32 0, i32 8
; CHECK-NEXT:    [[CAST1:%.*]] = addrspacecast ptr [[GEP0_X]] to ptr addrspace(1)
; CHECK-NEXT:    [[GEP1_X:%.*]] = getelementptr i8, ptr addrspace(1) [[CAST1]], i32 8
; CHECK-NEXT:    [[A_SROA_0_0_COPYLOAD:%.*]] = load i64, ptr addrspace(1) [[GEP1_X]], align 1
; CHECK-NEXT:    [[A_SROA_2_0_GEP1_X_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[GEP1_X]], i16 8
; CHECK-NEXT:    [[A_SROA_2_0_COPYLOAD:%.*]] = load i64, ptr addrspace(1) [[A_SROA_2_0_GEP1_X_SROA_IDX]], align 1
; CHECK-NEXT:    ret i64 [[A_SROA_0_0_COPYLOAD]]
;
entry:
  %a = alloca [32 x i8], align 8
  %gep0.x = getelementptr [32 x i8], ptr %x, i32 0, i32 8
  %cast1 = addrspacecast ptr %gep0.x to ptr addrspace(1)
  %gep1.x = getelementptr i8, ptr addrspace(1) %cast1, i32 8

  call void @llvm.memcpy.p0.p1.i32(ptr %a, ptr addrspace(1) %gep1.x, i32 16, i1 false)
  %val = load i64, ptr %a
  ret i64 %val
}

; Don't change the address space of a volatile operation
define i64 @alloca_addrspacecast_bitcast_volatile_store(i64 %X) {
; CHECK-LABEL: @alloca_addrspacecast_bitcast_volatile_store(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca [8 x i8], align 1
; CHECK-NEXT:    [[A_CAST:%.*]] = addrspacecast ptr [[A]] to ptr addrspace(1)
; CHECK-NEXT:    store volatile i64 [[X:%.*]], ptr addrspace(1) [[A_CAST]], align 4
; CHECK-NEXT:    [[Z:%.*]] = load i64, ptr addrspace(1) [[A_CAST]], align 4
; CHECK-NEXT:    ret i64 [[Z]]
;
entry:
  %A = alloca [8 x i8]
  %A.cast = addrspacecast ptr %A to ptr addrspace(1)
  store volatile i64 %X, ptr addrspace(1) %A.cast
  %Z = load i64, ptr addrspace(1) %A.cast
  ret i64 %Z
}

; Don't change the address space of a volatile operation
define i64 @alloca_addrspacecast_bitcast_volatile_load(i64 %X) {
; CHECK-LABEL: @alloca_addrspacecast_bitcast_volatile_load(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca [8 x i8], align 1
; CHECK-NEXT:    [[A_CAST:%.*]] = addrspacecast ptr [[A]] to ptr addrspace(1)
; CHECK-NEXT:    store i64 [[X:%.*]], ptr addrspace(1) [[A_CAST]], align 4
; CHECK-NEXT:    [[Z:%.*]] = load volatile i64, ptr addrspace(1) [[A_CAST]], align 4
; CHECK-NEXT:    ret i64 [[Z]]
;
entry:
  %A = alloca [8 x i8]
  %A.cast = addrspacecast ptr %A to ptr addrspace(1)
  store i64 %X, ptr addrspace(1) %A.cast
  %Z = load volatile i64, ptr addrspace(1) %A.cast
  ret i64 %Z
}

declare void @llvm.memset.p1.i32(ptr addrspace(1) nocapture, i8, i32, i1) nounwind

; Don't change the address space of a volatile operation
define i32 @volatile_memset() {
; CHECK-LABEL: @volatile_memset(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca [4 x i8], align 1
; CHECK-NEXT:    [[ASC:%.*]] = addrspacecast ptr [[A]] to ptr addrspace(1)
; CHECK-NEXT:    call void @llvm.memset.p1.i32(ptr addrspace(1) [[ASC]], i8 42, i32 4, i1 true)
; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[A]], align 4
; CHECK-NEXT:    ret i32 [[VAL]]
;
entry:
  %a = alloca [4 x i8]
  %asc = addrspacecast ptr %a to ptr addrspace(1)
  call void @llvm.memset.p1.i32(ptr addrspace(1) %asc, i8 42, i32 4, i1 true)
  %val = load i32, ptr %a
  ret i32 %val
}

; Don't change the address space of a volatile operation
define void @volatile_memcpy(ptr %src, ptr %dst) {
; CHECK-LABEL: @volatile_memcpy(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca [4 x i8], align 1
; CHECK-NEXT:    [[ASC:%.*]] = addrspacecast ptr [[A]] to ptr addrspace(1)
; CHECK-NEXT:    call void @llvm.memcpy.p1.p0.i32(ptr addrspace(1) [[ASC]], ptr [[SRC:%.*]], i32 4, i1 true), !tbaa [[TBAA0:![0-9]+]]
; CHECK-NEXT:    call void @llvm.memcpy.p0.p1.i32(ptr [[DST:%.*]], ptr addrspace(1) [[ASC]], i32 4, i1 true), !tbaa [[TBAA3:![0-9]+]]
; CHECK-NEXT:    ret void
;
entry:
  %a = alloca [4 x i8]
  %asc = addrspacecast ptr %a to ptr addrspace(1)
  call void @llvm.memcpy.p1.p0.i32(ptr addrspace(1) %asc, ptr %src, i32 4, i1 true), !tbaa !0
  call void @llvm.memcpy.p0.p1.i32(ptr %dst, ptr addrspace(1) %asc, i32 4, i1 true), !tbaa !3
  ret void
}

define void @select_addrspacecast(i1 %a, i1 %b) {
; CHECK-LABEL: @select_addrspacecast(
; CHECK-NEXT:    ret void
;
  %c = alloca i64, align 8
  %p.0.c = select i1 %a, ptr %c, ptr %c
  %asc = addrspacecast ptr %p.0.c to ptr addrspace(1)

  %cond.in = select i1 %b, ptr addrspace(1) %asc, ptr addrspace(1) %asc
  %cond = load i64, ptr addrspace(1) %cond.in, align 8
  ret void
}

define void @select_addrspacecast_const_op(i1 %a, i1 %b) {
; CHECK-LABEL: @select_addrspacecast_const_op(
; CHECK-NEXT:    [[C:%.*]] = alloca i64, align 8
; CHECK-NEXT:    [[C_0_ASC_SROA_CAST:%.*]] = addrspacecast ptr [[C]] to ptr addrspace(1)
; CHECK-NEXT:    [[COND_IN:%.*]] = select i1 [[B:%.*]], ptr addrspace(1) [[C_0_ASC_SROA_CAST]], ptr addrspace(1) null
; CHECK-NEXT:    [[COND:%.*]] = load i64, ptr addrspace(1) [[COND_IN]], align 8
; CHECK-NEXT:    ret void
;
  %c = alloca i64, align 8
  %p.0.c = select i1 %a, ptr %c, ptr %c
  %asc = addrspacecast ptr %p.0.c to ptr addrspace(1)

  %cond.in = select i1 %b, ptr addrspace(1) %asc, ptr addrspace(1) null
  %cond = load i64, ptr addrspace(1) %cond.in, align 8
  ret void
}

;; If this was external, we wouldn't be able to prove dereferenceability
;; of the location.
@gv = addrspace(1) global i64 zeroinitializer

define void @select_addrspacecast_gv(i1 %a, i1 %b) {
; CHECK-LABEL: @select_addrspacecast_gv(
; CHECK-NEXT:    [[COND_SROA_SPECULATE_LOAD_FALSE:%.*]] = load i64, ptr addrspace(1) @gv, align 8
; CHECK-NEXT:    [[COND_SROA_SPECULATED:%.*]] = select i1 [[B:%.*]], i64 undef, i64 [[COND_SROA_SPECULATE_LOAD_FALSE]]
; CHECK-NEXT:    ret void
;
  %c = alloca i64, align 8
  %p.0.c = select i1 %a, ptr %c, ptr %c
  %asc = addrspacecast ptr %p.0.c to ptr addrspace(1)

  %cond.in = select i1 %b, ptr addrspace(1) %asc, ptr addrspace(1) @gv
  %cond = load i64, ptr addrspace(1) %cond.in, align 8
  ret void
}

define void @select_addrspacecast_gv_constexpr(i1 %a, i1 %b) {
; CHECK-LABEL: @select_addrspacecast_gv_constexpr(
; CHECK-NEXT:    [[COND_SROA_SPECULATE_LOAD_FALSE:%.*]] = load i64, ptr addrspace(2) addrspacecast (ptr addrspace(1) @gv to ptr addrspace(2)), align 8
; CHECK-NEXT:    [[COND_SROA_SPECULATED:%.*]] = select i1 [[B:%.*]], i64 undef, i64 [[COND_SROA_SPECULATE_LOAD_FALSE]]
; CHECK-NEXT:    ret void
;
  %c = alloca i64, align 8
  %p.0.c = select i1 %a, ptr %c, ptr %c
  %asc = addrspacecast ptr %p.0.c to ptr addrspace(2)

  %cond.in = select i1 %b, ptr addrspace(2) %asc, ptr addrspace(2) addrspacecast (ptr addrspace(1) @gv to ptr addrspace(2))
  %cond = load i64, ptr addrspace(2) %cond.in, align 8
  ret void
}

define i8 @select_addrspacecast_i8(i1 %c) {
; CHECK-LABEL: @select_addrspacecast_i8(
; CHECK-NEXT:    [[RET_SROA_SPECULATED:%.*]] = select i1 [[C:%.*]], i8 undef, i8 undef
; CHECK-NEXT:    ret i8 [[RET_SROA_SPECULATED]]
;
  %a = alloca i8
  %b = alloca i8

  %a.ptr = addrspacecast ptr %a to ptr addrspace(1)
  %b.ptr = addrspacecast ptr %b to ptr addrspace(1)

  %ptr = select i1 %c, ptr addrspace(1) %a.ptr, ptr addrspace(1) %b.ptr
  %ret = load i8, ptr addrspace(1) %ptr
  ret i8 %ret
}

!0 = !{!1, !1, i64 0, i64 1}
!1 = !{!2, i64 1, !"type_0"}
!2 = !{!"root"}
!3 = !{!4, !4, i64 0, i64 1}
!4 = !{!2, i64 1, !"type_3"}