; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -passes='sroa<preserve-cfg>' -S -o - < %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
; RUN: opt -passes='sroa<modify-cfg>' -S -o - < %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
%t = type { i32, i32 }
declare ptr @llvm.launder.invariant.group.p0(ptr %a)
declare ptr @llvm.strip.invariant.group.p0(ptr %a)
declare void @h(i32 %a)
declare i32 @somevalue()
define void @f() {
; CHECK-LABEL: @f(
; CHECK-NEXT: [[SV1:%.*]] = call i32 @somevalue()
; CHECK-NEXT: [[SV2:%.*]] = call i32 @somevalue()
; CHECK-NEXT: call void @h(i32 [[SV1]])
; CHECK-NEXT: call void @h(i32 [[SV2]])
; CHECK-NEXT: ret void
;
%a = alloca %t
%a1_i8_inv = call ptr @llvm.launder.invariant.group.p0(ptr %a)
%a2 = getelementptr inbounds %t, ptr %a, i32 0, i32 1
%sv1 = call i32 @somevalue()
%sv2 = call i32 @somevalue()
store i32 %sv1, ptr %a1_i8_inv, !invariant.group !0
store i32 %sv2, ptr %a2
%v1 = load i32, ptr %a1_i8_inv, !invariant.group !0
%v2 = load i32, ptr %a2
call void @h(i32 %v1)
call void @h(i32 %v2)
ret void
}
define void @g() {
; CHECK-LABEL: @g(
; CHECK-NEXT: [[A:%.*]] = alloca [[T:%.*]], align 8
; CHECK-NEXT: [[A1_I8_INV:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[A]])
; CHECK-NEXT: [[A2:%.*]] = getelementptr inbounds [[T]], ptr [[A]], i32 0, i32 1
; CHECK-NEXT: [[SV1:%.*]] = call i32 @somevalue()
; CHECK-NEXT: [[SV2:%.*]] = call i32 @somevalue()
; CHECK-NEXT: store i32 [[SV1]], ptr [[A1_I8_INV]], align 4, !invariant.group [[META0:![0-9]+]]
; CHECK-NEXT: store i32 [[SV2]], ptr [[A2]], align 4
; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[A1_I8_INV]], align 4, !invariant.group [[META0]]
; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[A2]], align 4
; CHECK-NEXT: call void @h(i32 [[V1]])
; CHECK-NEXT: call void @h(i32 [[V2]])
; CHECK-NEXT: [[A1_STRIPPED:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[A]])
; CHECK-NEXT: [[A1_INT:%.*]] = ptrtoint ptr [[A1_STRIPPED]] to i32
; CHECK-NEXT: call void @h(i32 [[A1_INT]])
; CHECK-NEXT: ret void
;
%a = alloca %t
%a1_i8_inv = call ptr @llvm.launder.invariant.group.p0(ptr %a)
%a2 = getelementptr inbounds %t, ptr %a, i32 0, i32 1
%sv1 = call i32 @somevalue()
%sv2 = call i32 @somevalue()
store i32 %sv1, ptr %a1_i8_inv, !invariant.group !0
store i32 %sv2, ptr %a2
%v1 = load i32, ptr %a1_i8_inv, !invariant.group !0
%v2 = load i32, ptr %a2
call void @h(i32 %v1)
call void @h(i32 %v2)
%a1_stripped = call ptr @llvm.strip.invariant.group.p0(ptr %a)
%a1_int = ptrtoint ptr %a1_stripped to i32
call void @h(i32 %a1_int)
ret void
}
define void @store_and_launder() {
; CHECK-LABEL: @store_and_launder(
; CHECK-NEXT: ret void
;
%valptr = alloca i32, align 4
store i32 0, ptr %valptr, align 4
%barr = call ptr @llvm.launder.invariant.group.p0(ptr %valptr)
ret void
}
define i32 @launder_and_load() {
; CHECK-LABEL: @launder_and_load(
; CHECK-NEXT: ret i32 undef
;
%valptr = alloca i32, align 4
%barr = call ptr @llvm.launder.invariant.group.p0(ptr %valptr)
%v2 = load i32, ptr %valptr
ret i32 %v2
}
define void @launder_and_ptr_arith() {
; CHECK-LABEL: @launder_and_ptr_arith(
; CHECK-NEXT: ret void
;
%valptr = alloca i32, align 4
%barr = call ptr @llvm.launder.invariant.group.p0(ptr %valptr)
%a2 = getelementptr inbounds i32, ptr %valptr, i32 0
ret void
}
define void @partial_use_of_alloca() {
; CHECK-LABEL: @partial_use_of_alloca(
; CHECK-NEXT: [[VALPTR:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i32 0, ptr [[VALPTR]], align 4
; CHECK-NEXT: [[BARR:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[VALPTR]])
; CHECK-NEXT: [[LOAD_VAL:%.*]] = load i32, ptr [[VALPTR]], align 4
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[LOAD_VAL]], 0
; CHECK-NEXT: br i1 [[COND]], label [[USE_ALLOCA:%.*]], label [[END:%.*]]
; CHECK: use_alloca:
; CHECK-NEXT: call void @use(ptr nonnull [[VALPTR]])
; CHECK-NEXT: br label [[END]]
; CHECK: end:
; CHECK-NEXT: ret void
;
%valptr = alloca i32, align 4
store i32 0, ptr %valptr, align 4
%barr = call ptr @llvm.launder.invariant.group.p0(ptr %valptr)
%load_val = load i32, ptr %valptr, align 4
%cond = icmp eq i32 %load_val, 0
br i1 %cond, label %use_alloca, label %end
use_alloca:
call void @use(ptr nonnull %valptr)
br label %end
end:
ret void
}
define void @partial_promotion_of_alloca() {
; CHECK-LABEL: @partial_promotion_of_alloca(
; CHECK-NEXT: [[STRUCT_PTR_SROA_2:%.*]] = alloca i32, align 4
; CHECK-NEXT: store volatile i32 0, ptr [[STRUCT_PTR_SROA_2]], align 4
; CHECK-NEXT: [[STRUCT_PTR_SROA_2_0_STRUCT_PTR_SROA_2_4_LOAD_VAL:%.*]] = load volatile i32, ptr [[STRUCT_PTR_SROA_2]], align 4
; CHECK-NEXT: ret void
;
%struct_ptr = alloca %t, align 4
%field_ptr = getelementptr inbounds %t, ptr %struct_ptr, i32 0, i32 0
store i32 0, ptr %field_ptr, align 4
%volatile_field_ptr = getelementptr inbounds %t, ptr %struct_ptr, i32 0, i32 1
store volatile i32 0, ptr %volatile_field_ptr, align 4, !invariant.group !0
%barr = call ptr @llvm.launder.invariant.group.p0(ptr %struct_ptr)
%load_val = load volatile i32, ptr %volatile_field_ptr, align 4, !invariant.group !0
ret void
}
declare void @use(ptr)
!0 = !{}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CHECK-MODIFY-CFG: {{.*}}
; CHECK-PRESERVE-CFG: {{.*}}