
Even if memory is valid from `llvm` point of view, e.g. local alloca, sanitizers have API for user specific memory annotations. These annotations can be used to track size of the local object, e.g. inline vectors may prevent accesses beyond the current vector size. So valid programs should not access those parts of alloca before checking preconditions. Fixes #100639.
873 lines
28 KiB
LLVM
873 lines
28 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
|
|
; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
|
|
target datalayout = "e-p:64:64:64-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"
|
|
|
|
define i32 @test1() {
|
|
; CHECK-LABEL: @test1(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp sle i32 0, 1
|
|
; CHECK-NEXT: br i1 [[COND]], label [[THEN:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: then:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[PHI_SROA_SPECULATED:%.*]] = phi i32 [ 1, [[THEN]] ], [ 0, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: ret i32 [[PHI_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%a = alloca [2 x i32]
|
|
|
|
%a1 = getelementptr [2 x i32], ptr %a, i64 0, i32 1
|
|
store i32 0, ptr %a
|
|
store i32 1, ptr %a1
|
|
%v0 = load i32, ptr %a
|
|
%v1 = load i32, ptr %a1
|
|
|
|
%cond = icmp sle i32 %v0, %v1
|
|
br i1 %cond, label %then, label %exit
|
|
|
|
then:
|
|
br label %exit
|
|
|
|
exit:
|
|
%phi = phi ptr [ %a1, %then ], [ %a, %entry ]
|
|
|
|
%result = load i32, ptr %phi
|
|
ret i32 %result
|
|
}
|
|
|
|
define i32 @test2() {
|
|
; CHECK-LABEL: @test2(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp sle i32 0, 1
|
|
; CHECK-NEXT: [[RESULT_SROA_SPECULATED:%.*]] = select i1 [[COND]], i32 1, i32 0
|
|
; CHECK-NEXT: ret i32 [[RESULT_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%a = alloca [2 x i32]
|
|
|
|
%a1 = getelementptr [2 x i32], ptr %a, i64 0, i32 1
|
|
store i32 0, ptr %a
|
|
store i32 1, ptr %a1
|
|
%v0 = load i32, ptr %a
|
|
%v1 = load i32, ptr %a1
|
|
|
|
%cond = icmp sle i32 %v0, %v1
|
|
%select = select i1 %cond, ptr %a1, ptr %a
|
|
|
|
%result = load i32, ptr %select
|
|
ret i32 %result
|
|
}
|
|
|
|
define float @test2_bitcast() {
|
|
; CHECK-LABEL: @test2_bitcast(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp sle i32 0, 1
|
|
; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32 1 to float
|
|
; CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 0 to float
|
|
; CHECK-NEXT: [[RESULT_SROA_SPECULATED:%.*]] = select i1 [[COND]], float [[TMP0]], float [[TMP1]]
|
|
; CHECK-NEXT: ret float [[RESULT_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%a = alloca [2 x i32]
|
|
%a1 = getelementptr [2 x i32], ptr %a, i64 0, i32 1
|
|
store i32 0, ptr %a
|
|
store i32 1, ptr %a1
|
|
%v0 = load i32, ptr %a
|
|
%v1 = load i32, ptr %a1
|
|
%cond = icmp sle i32 %v0, %v1
|
|
%select = select i1 %cond, ptr %a1, ptr %a
|
|
%result = load float, ptr %select
|
|
ret float %result
|
|
}
|
|
|
|
define i32 @test2_addrspacecast() {
|
|
; CHECK-LABEL: @test2_addrspacecast(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[A_SROA_0:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: [[A_SROA_3:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: store i32 0, ptr [[A_SROA_0]], align 4
|
|
; CHECK-NEXT: store i32 1, ptr [[A_SROA_3]], align 4
|
|
; CHECK-NEXT: [[A_SROA_0_0_A_SROA_0_0_V0:%.*]] = load i32, ptr [[A_SROA_0]], align 4
|
|
; CHECK-NEXT: [[A_SROA_3_0_A_SROA_3_4_V1:%.*]] = load i32, ptr [[A_SROA_3]], align 4
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp sle i32 [[A_SROA_0_0_A_SROA_0_0_V0]], [[A_SROA_3_0_A_SROA_3_4_V1]]
|
|
; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], ptr [[A_SROA_3]], ptr [[A_SROA_0]]
|
|
; CHECK-NEXT: [[SELECT_ASC:%.*]] = addrspacecast ptr [[SELECT]] to ptr addrspace(1)
|
|
; CHECK-NEXT: [[RESULT:%.*]] = load i32, ptr addrspace(1) [[SELECT_ASC]], align 4
|
|
; CHECK-NEXT: ret i32 [[RESULT]]
|
|
;
|
|
entry:
|
|
%a = alloca [2 x i32]
|
|
%a1 = getelementptr [2 x i32], ptr %a, i64 0, i32 1
|
|
store i32 0, ptr %a
|
|
store i32 1, ptr %a1
|
|
%v0 = load i32, ptr %a
|
|
%v1 = load i32, ptr %a1
|
|
%cond = icmp sle i32 %v0, %v1
|
|
%select = select i1 %cond, ptr %a1, ptr %a
|
|
%select.asc = addrspacecast ptr %select to ptr addrspace(1)
|
|
%result = load i32, ptr addrspace(1) %select.asc
|
|
ret i32 %result
|
|
}
|
|
|
|
define i32 @test3(i32 %x) {
|
|
; CHECK-LABEL: @test3(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: switch i32 [[X:%.*]], label [[BB0:%.*]] [
|
|
; CHECK-NEXT: i32 1, label [[BB1:%.*]]
|
|
; CHECK-NEXT: i32 2, label [[BB2:%.*]]
|
|
; CHECK-NEXT: i32 3, label [[BB3:%.*]]
|
|
; CHECK-NEXT: i32 4, label [[BB4:%.*]]
|
|
; CHECK-NEXT: i32 5, label [[BB5:%.*]]
|
|
; CHECK-NEXT: i32 6, label [[BB6:%.*]]
|
|
; CHECK-NEXT: i32 7, label [[BB7:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: bb0:
|
|
; CHECK-NEXT: br label [[EXIT:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: bb3:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: bb4:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: bb5:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: bb6:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: bb7:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[PHI_SROA_SPECULATED:%.*]] = phi i32 [ 1, [[BB0]] ], [ 0, [[BB1]] ], [ 0, [[BB2]] ], [ 1, [[BB3]] ], [ 1, [[BB4]] ], [ 0, [[BB5]] ], [ 0, [[BB6]] ], [ 1, [[BB7]] ]
|
|
; CHECK-NEXT: ret i32 [[PHI_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%a = alloca [2 x i32]
|
|
|
|
; Note that we build redundant GEPs here to ensure that having different GEPs
|
|
; into the same alloca partation continues to work with PHI speculation. This
|
|
; was the underlying cause of PR13926.
|
|
%a1 = getelementptr [2 x i32], ptr %a, i64 0, i32 1
|
|
%a1b = getelementptr [2 x i32], ptr %a, i64 0, i32 1
|
|
store i32 0, ptr %a
|
|
store i32 1, ptr %a1
|
|
|
|
switch i32 %x, label %bb0 [ i32 1, label %bb1
|
|
i32 2, label %bb2
|
|
i32 3, label %bb3
|
|
i32 4, label %bb4
|
|
i32 5, label %bb5
|
|
i32 6, label %bb6
|
|
i32 7, label %bb7 ]
|
|
|
|
bb0:
|
|
br label %exit
|
|
bb1:
|
|
br label %exit
|
|
bb2:
|
|
br label %exit
|
|
bb3:
|
|
br label %exit
|
|
bb4:
|
|
br label %exit
|
|
bb5:
|
|
br label %exit
|
|
bb6:
|
|
br label %exit
|
|
bb7:
|
|
br label %exit
|
|
|
|
exit:
|
|
%phi = phi ptr [ %a1, %bb0 ], [ %a, %bb1 ], [ %a, %bb2 ], [ %a1, %bb3 ],
|
|
[ %a1b, %bb4 ], [ %a, %bb5 ], [ %a, %bb6 ], [ %a1b, %bb7 ]
|
|
|
|
%result = load i32, ptr %phi
|
|
ret i32 %result
|
|
}
|
|
|
|
define i32 @test4() {
|
|
; CHECK-LABEL: @test4(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
entry:
|
|
%a = alloca [2 x i32]
|
|
|
|
%a1 = getelementptr [2 x i32], ptr %a, i64 0, i32 1
|
|
store i32 0, ptr %a
|
|
store i32 1, ptr %a1
|
|
%v0 = load i32, ptr %a
|
|
%v1 = load i32, ptr %a1
|
|
|
|
%cond = icmp sle i32 %v0, %v1
|
|
%select = select i1 %cond, ptr %a, ptr %a
|
|
|
|
%result = load i32, ptr %select
|
|
ret i32 %result
|
|
}
|
|
|
|
define i32 @test5(ptr %b) {
|
|
; CHECK-LABEL: @test5(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
%a = alloca [2 x i32]
|
|
|
|
%a1 = getelementptr [2 x i32], ptr %a, i64 0, i32 1
|
|
store i32 1, ptr %a1
|
|
|
|
%select = select i1 true, ptr %a1, ptr %b
|
|
|
|
%result = load i32, ptr %select
|
|
|
|
ret i32 %result
|
|
}
|
|
|
|
declare void @f(ptr, ptr)
|
|
|
|
define i32 @test6(ptr %b) {
|
|
; CHECK-LABEL: @test6(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[SELECT2:%.*]] = select i1 false, ptr poison, ptr [[B:%.*]]
|
|
; CHECK-NEXT: [[SELECT3:%.*]] = select i1 false, ptr poison, ptr [[B]]
|
|
; CHECK-NEXT: call void @f(ptr [[SELECT2]], ptr [[SELECT3]])
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
%a = alloca [2 x i32]
|
|
%c = alloca i32
|
|
|
|
%a1 = getelementptr [2 x i32], ptr %a, i64 0, i32 1
|
|
store i32 1, ptr %a1
|
|
|
|
%select = select i1 true, ptr %a1, ptr %b
|
|
%select2 = select i1 false, ptr %a1, ptr %b
|
|
%select3 = select i1 false, ptr %c, ptr %b
|
|
|
|
; Note, this would potentially escape the alloca pointer except for the
|
|
; constant folding of the select.
|
|
call void @f(ptr %select2, ptr %select3)
|
|
|
|
|
|
%result = load i32, ptr %select
|
|
|
|
%dead = load i32, ptr %c
|
|
|
|
ret i32 %result
|
|
}
|
|
|
|
define i32 @test7(i1 %c1) {
|
|
; CHECK-LABEL: @test7(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[C1:%.*]], label [[GOOD:%.*]], label [[BAD:%.*]]
|
|
; CHECK: good:
|
|
; CHECK-NEXT: br label [[EXIT:%.*]]
|
|
; CHECK: bad:
|
|
; CHECK-NEXT: [[P_SROA_SPECULATE_LOAD_BAD:%.*]] = load i32, ptr poison, align 4
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[P_SROA_SPECULATED:%.*]] = phi i32 [ 0, [[GOOD]] ], [ [[P_SROA_SPECULATE_LOAD_BAD]], [[BAD]] ]
|
|
; CHECK-NEXT: ret i32 [[P_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%X = alloca i32
|
|
br i1 %c1, label %good, label %bad
|
|
|
|
good:
|
|
store i32 0, ptr %X
|
|
br label %exit
|
|
|
|
bad:
|
|
%Y2 = getelementptr i32, ptr %X, i64 1
|
|
store i32 0, ptr %Y2
|
|
br label %exit
|
|
|
|
exit:
|
|
%P = phi ptr [ %X, %good ], [ %Y2, %bad ]
|
|
%Z2 = load i32, ptr %P
|
|
ret i32 %Z2
|
|
}
|
|
|
|
define i32 @test8(i32 %b, ptr %ptr) {
|
|
; Ensure that we rewrite allocas to the used type when that use is hidden by
|
|
; a PHI that can be speculated.
|
|
;
|
|
; CHECK-LABEL: @test8(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[TEST:%.*]] = icmp ne i32 [[B:%.*]], 0
|
|
; CHECK-NEXT: br i1 [[TEST]], label [[THEN:%.*]], label [[ELSE:%.*]]
|
|
; CHECK: then:
|
|
; CHECK-NEXT: [[PHI_SROA_SPECULATE_LOAD_THEN:%.*]] = load i32, ptr [[PTR:%.*]], align 4
|
|
; CHECK-NEXT: br label [[EXIT:%.*]]
|
|
; CHECK: else:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[PHI_SROA_SPECULATED:%.*]] = phi i32 [ undef, [[ELSE]] ], [ [[PHI_SROA_SPECULATE_LOAD_THEN]], [[THEN]] ]
|
|
; CHECK-NEXT: ret i32 [[PHI_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%f = alloca float
|
|
%test = icmp ne i32 %b, 0
|
|
br i1 %test, label %then, label %else
|
|
|
|
then:
|
|
br label %exit
|
|
|
|
else:
|
|
br label %exit
|
|
|
|
exit:
|
|
%phi = phi ptr [ %f, %else ], [ %ptr, %then ]
|
|
%loaded = load i32, ptr %phi, align 4
|
|
ret i32 %loaded
|
|
}
|
|
|
|
define i32 @test9(i32 %b, ptr %ptr) {
|
|
; Same as @test8 but for a select rather than a PHI node.
|
|
;
|
|
; CHECK-LABEL: @test9(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
|
|
; CHECK-NEXT: [[TEST:%.*]] = icmp ne i32 [[B:%.*]], 0
|
|
; CHECK-NEXT: [[LOADED_SROA_SPECULATE_LOAD_FALSE:%.*]] = load i32, ptr [[PTR]], align 4
|
|
; CHECK-NEXT: [[LOADED_SROA_SPECULATED:%.*]] = select i1 [[TEST]], i32 undef, i32 [[LOADED_SROA_SPECULATE_LOAD_FALSE]]
|
|
; CHECK-NEXT: ret i32 [[LOADED_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%f = alloca float
|
|
store i32 0, ptr %ptr
|
|
%test = icmp ne i32 %b, 0
|
|
%select = select i1 %test, ptr %f, ptr %ptr
|
|
%loaded = load i32, ptr %select, align 4
|
|
ret i32 %loaded
|
|
}
|
|
|
|
; We should not unconditionally load with sanitizers.
|
|
define i32 @test9_asan(i32 %b, ptr %ptr) sanitize_address {
|
|
; Same as @test8 but for a select rather than a PHI node.
|
|
;
|
|
; CHECK-PRESERVE-CFG-LABEL: @test9_asan(
|
|
; CHECK-PRESERVE-CFG-NEXT: entry:
|
|
; CHECK-PRESERVE-CFG-NEXT: [[F:%.*]] = alloca float, align 4
|
|
; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
|
|
; CHECK-PRESERVE-CFG-NEXT: [[TEST:%.*]] = icmp ne i32 [[B:%.*]], 0
|
|
; CHECK-PRESERVE-CFG-NEXT: [[SELECT:%.*]] = select i1 [[TEST]], ptr [[F]], ptr [[PTR]]
|
|
; CHECK-PRESERVE-CFG-NEXT: [[LOADED:%.*]] = load i32, ptr [[SELECT]], align 4
|
|
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[LOADED]]
|
|
;
|
|
; CHECK-MODIFY-CFG-LABEL: @test9_asan(
|
|
; CHECK-MODIFY-CFG-NEXT: entry:
|
|
; CHECK-MODIFY-CFG-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
|
|
; CHECK-MODIFY-CFG-NEXT: [[TEST:%.*]] = icmp ne i32 [[B:%.*]], 0
|
|
; CHECK-MODIFY-CFG-NEXT: [[LOADED_ELSE_VAL:%.*]] = load i32, ptr [[PTR]], align 4
|
|
; CHECK-MODIFY-CFG-NEXT: br i1 [[TEST]], label [[ENTRY_THEN:%.*]], label [[ENTRY_CONT:%.*]]
|
|
; CHECK-MODIFY-CFG: entry.then:
|
|
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
|
|
; CHECK-MODIFY-CFG: entry.cont:
|
|
; CHECK-MODIFY-CFG-NEXT: [[LOADED:%.*]] = phi i32 [ undef, [[ENTRY_THEN]] ], [ [[LOADED_ELSE_VAL]], [[ENTRY:%.*]] ]
|
|
; CHECK-MODIFY-CFG-NEXT: ret i32 [[LOADED]]
|
|
;
|
|
entry:
|
|
%f = alloca float
|
|
store i32 0, ptr %ptr
|
|
%test = icmp ne i32 %b, 0
|
|
%select = select i1 %test, ptr %f, ptr %ptr
|
|
%loaded = load i32, ptr %select, align 4
|
|
ret i32 %loaded
|
|
}
|
|
|
|
define float @test10(i32 %b, ptr %ptr) {
|
|
; Don't try to promote allocas which are not elligible for it even after
|
|
; rewriting due to the necessity of inserting bitcasts when speculating a PHI
|
|
; node.
|
|
;
|
|
; CHECK-LABEL: @test10(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[F:%.*]] = alloca double, align 8
|
|
; CHECK-NEXT: store double 0.000000e+00, ptr [[F]], align 8
|
|
; CHECK-NEXT: [[TEST:%.*]] = icmp ne i32 [[B:%.*]], 0
|
|
; CHECK-NEXT: br i1 [[TEST]], label [[THEN:%.*]], label [[ELSE:%.*]]
|
|
; CHECK: then:
|
|
; CHECK-NEXT: [[PHI_SROA_SPECULATE_LOAD_THEN:%.*]] = load float, ptr [[PTR:%.*]], align 4
|
|
; CHECK-NEXT: br label [[EXIT:%.*]]
|
|
; CHECK: else:
|
|
; CHECK-NEXT: [[F_0_PHI_SROA_SPECULATE_LOAD_ELSE:%.*]] = load float, ptr [[F]], align 8
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[PHI_SROA_SPECULATED:%.*]] = phi float [ [[F_0_PHI_SROA_SPECULATE_LOAD_ELSE]], [[ELSE]] ], [ [[PHI_SROA_SPECULATE_LOAD_THEN]], [[THEN]] ]
|
|
; CHECK-NEXT: ret float [[PHI_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%f = alloca double
|
|
store double 0.0, ptr %f
|
|
%test = icmp ne i32 %b, 0
|
|
br i1 %test, label %then, label %else
|
|
|
|
then:
|
|
br label %exit
|
|
|
|
else:
|
|
br label %exit
|
|
|
|
exit:
|
|
%phi = phi ptr [ %f, %else ], [ %ptr, %then ]
|
|
%loaded = load float, ptr %phi, align 4
|
|
ret float %loaded
|
|
}
|
|
|
|
define float @test11(i32 %b, ptr %ptr) {
|
|
; Same as @test10 but for a select rather than a PHI node.
|
|
;
|
|
; CHECK-LABEL: @test11(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[F:%.*]] = alloca double, align 8
|
|
; CHECK-NEXT: store double 0.000000e+00, ptr [[F]], align 8
|
|
; CHECK-NEXT: store float 0.000000e+00, ptr [[PTR:%.*]], align 4
|
|
; CHECK-NEXT: [[TEST:%.*]] = icmp ne i32 [[B:%.*]], 0
|
|
; CHECK-NEXT: [[F_0_LOADED_SROA_SPECULATE_LOAD_TRUE:%.*]] = load float, ptr [[F]], align 8
|
|
; CHECK-NEXT: [[LOADED_SROA_SPECULATE_LOAD_FALSE:%.*]] = load float, ptr [[PTR]], align 4
|
|
; CHECK-NEXT: [[LOADED_SROA_SPECULATED:%.*]] = select i1 [[TEST]], float [[F_0_LOADED_SROA_SPECULATE_LOAD_TRUE]], float [[LOADED_SROA_SPECULATE_LOAD_FALSE]]
|
|
; CHECK-NEXT: ret float [[LOADED_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%f = alloca double
|
|
store double 0.0, ptr %f
|
|
store float 0.0, ptr %ptr
|
|
%test = icmp ne i32 %b, 0
|
|
%select = select i1 %test, ptr %f, ptr %ptr
|
|
%loaded = load float, ptr %select, align 4
|
|
ret float %loaded
|
|
}
|
|
|
|
define i32 @test12(i32 %x, ptr %p, i1 %c1) {
|
|
; Ensure we don't crash or fail to nuke dead selects of allocas if no load is
|
|
; never found.
|
|
;
|
|
; CHECK-LABEL: @test12(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: ret i32 [[X:%.*]]
|
|
;
|
|
entry:
|
|
%a = alloca i32
|
|
store i32 %x, ptr %a
|
|
%dead = select i1 %c1, ptr %a, ptr %p
|
|
%load = load i32, ptr %a
|
|
ret i32 %load
|
|
}
|
|
|
|
define i32 @test13(i32 %x, ptr %p, i1 %c1) {
|
|
; Ensure we don't crash or fail to nuke dead phis of allocas if no load is ever
|
|
; found.
|
|
;
|
|
; CHECK-LABEL: @test13(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: br i1 [[C1:%.*]], label [[LOOP]], label [[EXIT:%.*]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret i32 [[X:%.*]]
|
|
;
|
|
entry:
|
|
%a = alloca i32
|
|
store i32 %x, ptr %a
|
|
br label %loop
|
|
|
|
loop:
|
|
%phi = phi ptr [ %p, %entry ], [ %a, %loop ]
|
|
br i1 %c1, label %loop, label %exit
|
|
|
|
exit:
|
|
%load = load i32, ptr %a
|
|
ret i32 %load
|
|
}
|
|
|
|
define i32 @test14(i1 %b1, i1 %b2, ptr %ptr) {
|
|
; Check for problems when there are both selects and phis and one is
|
|
; speculatable toward promotion but the other is not. That should block all of
|
|
; the speculation.
|
|
;
|
|
; CHECK-LABEL: @test14(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[F:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: [[G:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: store i32 0, ptr [[F]], align 4
|
|
; CHECK-NEXT: store i32 0, ptr [[G]], align 4
|
|
; CHECK-NEXT: [[F_SELECT:%.*]] = select i1 [[B1:%.*]], ptr [[F]], ptr [[PTR:%.*]]
|
|
; CHECK-NEXT: br i1 [[B2:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]]
|
|
; CHECK: then:
|
|
; CHECK-NEXT: br label [[EXIT:%.*]]
|
|
; CHECK: else:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[F_PHI:%.*]] = phi ptr [ [[F]], [[THEN]] ], [ [[F_SELECT]], [[ELSE]] ]
|
|
; CHECK-NEXT: [[G_PHI:%.*]] = phi ptr [ [[G]], [[THEN]] ], [ [[PTR]], [[ELSE]] ]
|
|
; CHECK-NEXT: [[F_LOADED:%.*]] = load i32, ptr [[F_PHI]], align 4
|
|
; CHECK-NEXT: [[G_SELECT:%.*]] = select i1 [[B1]], ptr [[G]], ptr [[G_PHI]]
|
|
; CHECK-NEXT: [[G_LOADED:%.*]] = load i32, ptr [[G_SELECT]], align 4
|
|
; CHECK-NEXT: [[RESULT:%.*]] = add i32 [[F_LOADED]], [[G_LOADED]]
|
|
; CHECK-NEXT: ret i32 [[RESULT]]
|
|
;
|
|
entry:
|
|
%f = alloca i32
|
|
%g = alloca i32
|
|
store i32 0, ptr %f
|
|
store i32 0, ptr %g
|
|
%f.select = select i1 %b1, ptr %f, ptr %ptr
|
|
br i1 %b2, label %then, label %else
|
|
|
|
then:
|
|
br label %exit
|
|
|
|
else:
|
|
br label %exit
|
|
|
|
exit:
|
|
%f.phi = phi ptr [ %f, %then ], [ %f.select, %else ]
|
|
%g.phi = phi ptr [ %g, %then ], [ %ptr, %else ]
|
|
%f.loaded = load i32, ptr %f.phi
|
|
%g.select = select i1 %b1, ptr %g, ptr %g.phi
|
|
%g.loaded = load i32, ptr %g.select
|
|
%result = add i32 %f.loaded, %g.loaded
|
|
ret i32 %result
|
|
}
|
|
|
|
define void @PR13905(i1 %c1, i1 %c2, i1 %c3) {
|
|
; Check a pattern where we have a chain of dead phi nodes to ensure they are
|
|
; deleted and promotion can proceed.
|
|
;
|
|
; CHECK-LABEL: @PR13905(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[C1:%.*]], label [[LOOP1:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: loop1:
|
|
; CHECK-NEXT: br i1 [[C2:%.*]], label [[LOOP1]], label [[LOOP2:%.*]]
|
|
; CHECK: loop2:
|
|
; CHECK-NEXT: br i1 [[C3:%.*]], label [[LOOP1]], label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[PHI2:%.*]] = phi ptr [ poison, [[LOOP2]] ], [ null, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%h = alloca i32
|
|
store i32 0, ptr %h
|
|
br i1 %c1, label %loop1, label %exit
|
|
|
|
loop1:
|
|
%phi1 = phi ptr [ null, %entry ], [ %h, %loop1 ], [ %h, %loop2 ]
|
|
br i1 %c2, label %loop1, label %loop2
|
|
|
|
loop2:
|
|
br i1 %c3, label %loop1, label %exit
|
|
|
|
exit:
|
|
%phi2 = phi ptr [ %phi1, %loop2 ], [ null, %entry ]
|
|
ret void
|
|
}
|
|
|
|
define i32 @PR13906(i1 %c1, i1 %c2) {
|
|
; Another pattern which can lead to crashes due to failing to clear out dead
|
|
; PHI nodes or select nodes. This triggers subtly differently from the above
|
|
; cases because the PHI node is (recursively) alive, but the select is dead.
|
|
;
|
|
; CHECK-LABEL: @PR13906(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[FOR_COND:%.*]]
|
|
; CHECK: for.cond:
|
|
; CHECK-NEXT: br i1 [[C1:%.*]], label [[IF_THEN:%.*]], label [[FOR_COND]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: br label [[FOR_COND]]
|
|
;
|
|
entry:
|
|
%c = alloca i32
|
|
store i32 0, ptr %c
|
|
br label %for.cond
|
|
|
|
for.cond:
|
|
%d.0 = phi ptr [ poison, %entry ], [ %c, %if.then ], [ %d.0, %for.cond ]
|
|
br i1 %c1, label %if.then, label %for.cond
|
|
|
|
if.then:
|
|
%tmpcast.d.0 = select i1 %c2, ptr %c, ptr %d.0
|
|
br label %for.cond
|
|
}
|
|
|
|
define i64 @PR14132(i1 %flag) {
|
|
; Here we form a PHI-node by promoting the pointer alloca first, and then in
|
|
; order to promote the other two allocas, we speculate the load of the
|
|
; now-phi-node-pointer. In doing so we end up loading a 64-bit value from an i8
|
|
; alloca. While this is a bit dubious, we were asserting on trying to
|
|
; rewrite it. The trick is that the code using the value may carefully take
|
|
; steps to only use the not-undef bits, and so we need to at least loosely
|
|
; support this..
|
|
; CHECK-LABEL: @PR14132(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[B_0_LOAD_EXT:%.*]] = zext i8 1 to i64
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[PTR_0_SROA_SPECULATED:%.*]] = phi i64 [ [[B_0_LOAD_EXT]], [[IF_THEN]] ], [ 0, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: ret i64 [[PTR_0_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%a = alloca i64, align 8
|
|
%b = alloca i8, align 8
|
|
%ptr = alloca ptr, align 8
|
|
|
|
store i64 0, ptr %a, align 8
|
|
store i8 1, ptr %b, align 8
|
|
store ptr %a, ptr %ptr, align 8
|
|
br i1 %flag, label %if.then, label %if.end
|
|
|
|
if.then:
|
|
store ptr %b, ptr %ptr, align 8
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%tmp = load ptr, ptr %ptr, align 8
|
|
%result = load i64, ptr %tmp, align 8
|
|
|
|
ret i64 %result
|
|
}
|
|
|
|
define float @PR16687(i64 %x, i1 %flag) {
|
|
; Check that even when we try to speculate the same phi twice (in two slices)
|
|
; on an otherwise promotable construct, we don't get ahead of ourselves and try
|
|
; to promote one of the slices prior to speculating it.
|
|
; CHECK-LABEL: @PR16687(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[A_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i64 [[X:%.*]] to i32
|
|
; CHECK-NEXT: [[A_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i64 [[X]], 32
|
|
; CHECK-NEXT: [[A_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i64 [[A_SROA_2_0_EXTRACT_SHIFT]] to i32
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]]
|
|
; CHECK: then:
|
|
; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32 [[A_SROA_0_0_EXTRACT_TRUNC]] to float
|
|
; CHECK-NEXT: br label [[END:%.*]]
|
|
; CHECK: else:
|
|
; CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 [[A_SROA_2_0_EXTRACT_TRUNC]] to float
|
|
; CHECK-NEXT: br label [[END]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: [[A_PHI_F_SROA_SPECULATED:%.*]] = phi float [ [[TMP0]], [[THEN]] ], [ [[TMP1]], [[ELSE]] ]
|
|
; CHECK-NEXT: ret float [[A_PHI_F_SROA_SPECULATED]]
|
|
;
|
|
entry:
|
|
%a = alloca i64, align 8
|
|
store i64 %x, ptr %a
|
|
br i1 %flag, label %then, label %else
|
|
|
|
then:
|
|
br label %end
|
|
|
|
else:
|
|
%a.raw.4 = getelementptr i8, ptr %a, i64 4
|
|
br label %end
|
|
|
|
end:
|
|
%a.phi.f = phi ptr [ %a, %then ], [ %a.raw.4, %else ]
|
|
%f = load float, ptr %a.phi.f
|
|
ret float %f
|
|
}
|
|
|
|
; Verifies we fixed PR20425. We should be able to promote all alloca's to
|
|
; registers in this test.
|
|
;
|
|
; %0 = slice
|
|
; %1 = slice
|
|
; %2 = phi(%0, %1) // == slice
|
|
define float @simplify_phi_nodes_that_equal_slice(i1 %cond, ptr %temp) {
|
|
; CHECK-LABEL: @simplify_phi_nodes_that_equal_slice(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[COND:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]]
|
|
; CHECK: then:
|
|
; CHECK-NEXT: br label [[MERGE:%.*]]
|
|
; CHECK: else:
|
|
; CHECK-NEXT: br label [[MERGE]]
|
|
; CHECK: merge:
|
|
; CHECK-NEXT: [[ARR_SROA_0_0:%.*]] = phi float [ 1.000000e+00, [[THEN]] ], [ 2.000000e+00, [[ELSE]] ]
|
|
; CHECK-NEXT: store float 0.000000e+00, ptr [[TEMP:%.*]], align 4
|
|
; CHECK-NEXT: ret float [[ARR_SROA_0_0]]
|
|
;
|
|
entry:
|
|
%arr = alloca [4 x float], align 4
|
|
br i1 %cond, label %then, label %else
|
|
|
|
then:
|
|
%0 = getelementptr inbounds [4 x float], ptr %arr, i64 0, i64 3
|
|
store float 1.000000e+00, ptr %0, align 4
|
|
br label %merge
|
|
|
|
else:
|
|
%1 = getelementptr inbounds [4 x float], ptr %arr, i64 0, i64 3
|
|
store float 2.000000e+00, ptr %1, align 4
|
|
br label %merge
|
|
|
|
merge:
|
|
%2 = phi ptr [ %0, %then ], [ %1, %else ]
|
|
store float 0.000000e+00, ptr %temp, align 4
|
|
%3 = load float, ptr %2, align 4
|
|
ret float %3
|
|
}
|
|
|
|
; A slightly complicated example for PR20425.
|
|
;
|
|
; %0 = slice
|
|
; %1 = phi(%0) // == slice
|
|
; %2 = slice
|
|
; %3 = phi(%1, %2) // == slice
|
|
define float @simplify_phi_nodes_that_equal_slice_2(i1 %cond, ptr %temp) {
|
|
; CHECK-LABEL: @simplify_phi_nodes_that_equal_slice_2(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[COND:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]]
|
|
; CHECK: then:
|
|
; CHECK-NEXT: br label [[THEN2:%.*]]
|
|
; CHECK: then2:
|
|
; CHECK-NEXT: br label [[MERGE:%.*]]
|
|
; CHECK: else:
|
|
; CHECK-NEXT: br label [[MERGE]]
|
|
; CHECK: merge:
|
|
; CHECK-NEXT: [[ARR_SROA_0_0:%.*]] = phi float [ 2.000000e+00, [[THEN2]] ], [ 3.000000e+00, [[ELSE]] ]
|
|
; CHECK-NEXT: store float 0.000000e+00, ptr [[TEMP:%.*]], align 4
|
|
; CHECK-NEXT: ret float [[ARR_SROA_0_0]]
|
|
;
|
|
entry:
|
|
%arr = alloca [4 x float], align 4
|
|
br i1 %cond, label %then, label %else
|
|
|
|
then:
|
|
%0 = getelementptr inbounds [4 x float], ptr %arr, i64 0, i64 3
|
|
store float 1.000000e+00, ptr %0, align 4
|
|
br label %then2
|
|
|
|
then2:
|
|
%1 = phi ptr [ %0, %then ]
|
|
store float 2.000000e+00, ptr %1, align 4
|
|
br label %merge
|
|
|
|
else:
|
|
%2 = getelementptr inbounds [4 x float], ptr %arr, i64 0, i64 3
|
|
store float 3.000000e+00, ptr %2, align 4
|
|
br label %merge
|
|
|
|
merge:
|
|
%3 = phi ptr [ %1, %then2 ], [ %2, %else ]
|
|
store float 0.000000e+00, ptr %temp, align 4
|
|
%4 = load float, ptr %3, align 4
|
|
ret float %4
|
|
}
|
|
|
|
%struct.S = type { i32 }
|
|
|
|
; Verifies we fixed PR20822. We have a foldable PHI feeding a speculatable PHI
|
|
; which requires the rewriting of the speculated PHI to handle insertion
|
|
; when the incoming pointer is itself from a PHI node. We would previously
|
|
; insert a bitcast instruction *before* a PHI, producing an invalid module;
|
|
; make sure we insert *after* the first non-PHI instruction.
|
|
define void @PR20822(i1 %c1, i1 %c2, ptr %ptr) {
|
|
; CHECK-LABEL: @PR20822(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[F_SROA_0:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: [[F1_SROA_GEP:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], ptr [[PTR:%.*]], i32 0, i32 0
|
|
; CHECK-NEXT: br i1 [[C1:%.*]], label [[IF_END:%.*]], label [[FOR_COND:%.*]]
|
|
; CHECK: for.cond:
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ poison, [[ENTRY:%.*]] ], [ poison, [[FOR_COND]] ]
|
|
; CHECK-NEXT: br i1 [[C2:%.*]], label [[IF_THEN5:%.*]], label [[IF_THEN2:%.*]]
|
|
; CHECK: if.then2:
|
|
; CHECK-NEXT: br label [[IF_THEN5]]
|
|
; CHECK: if.then5:
|
|
; CHECK-NEXT: [[F1_SROA_PHI:%.*]] = phi ptr [ [[F1_SROA_GEP]], [[IF_THEN2]] ], [ [[F_SROA_0]], [[IF_END]] ]
|
|
; CHECK-NEXT: store i32 0, ptr [[F1_SROA_PHI]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%f = alloca %struct.S, align 4
|
|
br i1 %c1, label %if.end, label %for.cond
|
|
|
|
for.cond: ; preds = %for.cond, %entry
|
|
br label %if.end
|
|
|
|
if.end: ; preds = %for.cond, %entry
|
|
%f2 = phi ptr [ %f, %entry ], [ %f, %for.cond ]
|
|
phi i32 [ poison, %entry ], [ poison, %for.cond ]
|
|
br i1 %c2, label %if.then5, label %if.then2
|
|
|
|
if.then2: ; preds = %if.end
|
|
br label %if.then5
|
|
|
|
if.then5: ; preds = %if.then2, %if.end
|
|
%f1 = phi ptr [ %ptr, %if.then2 ], [ %f2, %if.end ]
|
|
store %struct.S zeroinitializer, ptr %f1, align 4
|
|
ret void
|
|
}
|
|
|
|
define i32 @phi_align(ptr %z) {
|
|
; CHECK-LABEL: @phi_align(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[A_SROA_0:%.*]] = alloca [7 x i8], align 1
|
|
; CHECK-NEXT: [[A_SROA_0_3_A1X_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 3
|
|
; CHECK-NEXT: store i32 0, ptr [[A_SROA_0]], align 1
|
|
; CHECK-NEXT: [[A_SROA_0_3_A1X_SROA_IDX3:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 3
|
|
; CHECK-NEXT: store i32 1, ptr [[A_SROA_0_3_A1X_SROA_IDX3]], align 1
|
|
; CHECK-NEXT: [[A_SROA_0_0_A_SROA_0_1_V0:%.*]] = load i32, ptr [[A_SROA_0]], align 1
|
|
; CHECK-NEXT: [[A_SROA_0_3_A1X_SROA_IDX4:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 3
|
|
; CHECK-NEXT: [[A_SROA_0_3_A_SROA_0_4_V1:%.*]] = load i32, ptr [[A_SROA_0_3_A1X_SROA_IDX4]], align 1
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp sle i32 [[A_SROA_0_0_A_SROA_0_1_V0]], [[A_SROA_0_3_A_SROA_0_4_V1]]
|
|
; CHECK-NEXT: br i1 [[COND]], label [[THEN:%.*]], label [[EXIT:%.*]]
|
|
; CHECK: then:
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[A_SROA_0_3_A1X_SROA_IDX]], [[THEN]] ], [ [[Z:%.*]], [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: [[RESULT:%.*]] = load i32, ptr [[PHI]], align 1
|
|
; CHECK-NEXT: ret i32 [[RESULT]]
|
|
;
|
|
entry:
|
|
%a = alloca [8 x i8], align 8
|
|
|
|
%a0x = getelementptr [8 x i8], ptr %a, i64 0, i32 1
|
|
%a1x = getelementptr [8 x i8], ptr %a, i64 0, i32 4
|
|
store i32 0, ptr %a0x, align 1
|
|
store i32 1, ptr %a1x, align 4
|
|
%v0 = load i32, ptr %a0x, align 1
|
|
%v1 = load i32, ptr %a1x, align 4
|
|
%cond = icmp sle i32 %v0, %v1
|
|
br i1 %cond, label %then, label %exit
|
|
|
|
then:
|
|
br label %exit
|
|
|
|
exit:
|
|
%phi = phi ptr [ %a1x, %then ], [ %z, %entry ]
|
|
%result = load i32, ptr %phi, align 4
|
|
ret i32 %result
|
|
}
|
|
|
|
; Don't speculate a load based on an earlier volatile operation.
|
|
define i8 @volatile_select(ptr %p, i1 %b) {
|
|
; CHECK-PRESERVE-CFG-LABEL: @volatile_select(
|
|
; CHECK-PRESERVE-CFG-NEXT: [[P2:%.*]] = alloca i8, align 1
|
|
; CHECK-PRESERVE-CFG-NEXT: store i8 0, ptr [[P2]], align 1
|
|
; CHECK-PRESERVE-CFG-NEXT: store volatile i8 0, ptr [[P:%.*]], align 1
|
|
; CHECK-PRESERVE-CFG-NEXT: [[PX:%.*]] = select i1 [[B:%.*]], ptr [[P]], ptr [[P2]]
|
|
; CHECK-PRESERVE-CFG-NEXT: [[V2:%.*]] = load i8, ptr [[PX]], align 1
|
|
; CHECK-PRESERVE-CFG-NEXT: ret i8 [[V2]]
|
|
;
|
|
; CHECK-MODIFY-CFG-LABEL: @volatile_select(
|
|
; CHECK-MODIFY-CFG-NEXT: store volatile i8 0, ptr [[P:%.*]], align 1
|
|
; CHECK-MODIFY-CFG-NEXT: br i1 [[B:%.*]], label [[DOTTHEN:%.*]], label [[DOTCONT:%.*]]
|
|
; CHECK-MODIFY-CFG: .then:
|
|
; CHECK-MODIFY-CFG-NEXT: [[V2_THEN_VAL:%.*]] = load i8, ptr [[P]], align 1
|
|
; CHECK-MODIFY-CFG-NEXT: br label [[DOTCONT]]
|
|
; CHECK-MODIFY-CFG: .cont:
|
|
; CHECK-MODIFY-CFG-NEXT: [[V2:%.*]] = phi i8 [ [[V2_THEN_VAL]], [[DOTTHEN]] ], [ 0, [[TMP0:%.*]] ]
|
|
; CHECK-MODIFY-CFG-NEXT: ret i8 [[V2]]
|
|
;
|
|
%p2 = alloca i8
|
|
store i8 0, ptr %p2
|
|
store volatile i8 0, ptr %p
|
|
%px = select i1 %b, ptr %p, ptr %p2
|
|
%v2 = load i8, ptr %px
|
|
ret i8 %v2
|
|
}
|