; 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
%st.half = type { half }
; Allow speculateSelectInstLoads to fold load and select
; even if there is an intervening bitcast.
define <2 x i16> @test_load_bitcast_select(i1 %cond1, i1 %cond2) {
; CHECK-LABEL: @test_load_bitcast_select(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = bitcast half 0xHFFFF to i16
; CHECK-NEXT: [[TMP1:%.*]] = bitcast half 0xH0000 to i16
; CHECK-NEXT: [[LD1_SROA_SPECULATED:%.*]] = select i1 [[COND1:%.*]], i16 [[TMP0]], i16 [[TMP1]]
; CHECK-NEXT: [[V1:%.*]] = insertelement <2 x i16> poison, i16 [[LD1_SROA_SPECULATED]], i32 0
; CHECK-NEXT: [[TMP2:%.*]] = bitcast half 0xHFFFF to i16
; CHECK-NEXT: [[TMP3:%.*]] = bitcast half 0xH0000 to i16
; CHECK-NEXT: [[LD2_SROA_SPECULATED:%.*]] = select i1 [[COND2:%.*]], i16 [[TMP2]], i16 [[TMP3]]
; CHECK-NEXT: [[V2:%.*]] = insertelement <2 x i16> [[V1]], i16 [[LD2_SROA_SPECULATED]], i32 1
; CHECK-NEXT: ret <2 x i16> [[V2]]
;
entry:
%true = alloca half, align 2
%false = alloca half, align 2
store half 0xHFFFF, ptr %true, align 2
store half 0xH0000, ptr %false, align 2
%sel1 = select i1 %cond1, ptr %true, ptr %false
%ld1 = load i16, ptr %sel1, align 2
%v1 = insertelement <2 x i16> poison, i16 %ld1, i32 0
%sel2 = select i1 %cond2, ptr %true, ptr %false
%ld2 = load i16, ptr %sel2, align 2
%v2 = insertelement <2 x i16> %v1, i16 %ld2, i32 1
ret <2 x i16> %v2
}
%st.args = type { i32, ptr }
; A bitcasted load and a direct load of select.
define void @test_multiple_loads_select(i1 %cmp) {
; CHECK-LABEL: @test_multiple_loads_select(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ADDR_I8_SROA_SPECULATED:%.*]] = select i1 [[CMP:%.*]], ptr undef, ptr undef
; CHECK-NEXT: call void @foo_i8(ptr [[ADDR_I8_SROA_SPECULATED]])
; CHECK-NEXT: [[ADDR_I32_SROA_SPECULATED:%.*]] = select i1 [[CMP]], ptr undef, ptr undef
; CHECK-NEXT: call void @foo_i32(ptr [[ADDR_I32_SROA_SPECULATED]])
; CHECK-NEXT: ret void
;
entry:
%args = alloca [2 x %st.args], align 16
%arr1 = getelementptr inbounds [2 x %st.args], ptr %args, i64 0, i64 1
%sel = select i1 %cmp, ptr %arr1, ptr %args
%addr = getelementptr inbounds %st.args, ptr %sel, i64 0, i32 1
%addr.i8 = load ptr, ptr %addr, align 8
call void @foo_i8(ptr %addr.i8)
%addr.i32 = load ptr, ptr %addr, align 8
call void @foo_i32 (ptr %addr.i32)
ret void
}
; Sanitizer will break optimization.
define void @test_multiple_loads_select_asan(i1 %cmp) sanitize_address {
; CHECK-PRESERVE-CFG-LABEL: @test_multiple_loads_select_asan(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[ARGS_SROA_0:%.*]] = alloca ptr, align 8
; CHECK-PRESERVE-CFG-NEXT: [[ARGS_SROA_1:%.*]] = alloca ptr, align 8
; CHECK-PRESERVE-CFG-NEXT: [[SEL_SROA_SEL:%.*]] = select i1 [[CMP:%.*]], ptr [[ARGS_SROA_1]], ptr [[ARGS_SROA_0]]
; CHECK-PRESERVE-CFG-NEXT: [[ADDR_I8:%.*]] = load ptr, ptr [[SEL_SROA_SEL]], align 8
; CHECK-PRESERVE-CFG-NEXT: call void @foo_i8(ptr [[ADDR_I8]])
; CHECK-PRESERVE-CFG-NEXT: [[ADDR_I32:%.*]] = load ptr, ptr [[SEL_SROA_SEL]], align 8
; CHECK-PRESERVE-CFG-NEXT: call void @foo_i32(ptr [[ADDR_I32]])
; CHECK-PRESERVE-CFG-NEXT: ret void
;
; CHECK-MODIFY-CFG-LABEL: @test_multiple_loads_select_asan(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: br i1 [[CMP:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_ELSE:%.*]]
; CHECK-MODIFY-CFG: entry.then:
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT:%.*]]
; CHECK-MODIFY-CFG: entry.else:
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
; CHECK-MODIFY-CFG: entry.cont:
; CHECK-MODIFY-CFG-NEXT: [[ADDR_I8:%.*]] = phi ptr [ undef, [[ENTRY_THEN]] ], [ undef, [[ENTRY_ELSE]] ]
; CHECK-MODIFY-CFG-NEXT: call void @foo_i8(ptr [[ADDR_I8]])
; CHECK-MODIFY-CFG-NEXT: br i1 [[CMP]], label [[ENTRY_CONT_THEN:%.*]], label [[ENTRY_CONT_ELSE:%.*]]
; CHECK-MODIFY-CFG: entry.cont.then:
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT_CONT:%.*]]
; CHECK-MODIFY-CFG: entry.cont.else:
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT_CONT]]
; CHECK-MODIFY-CFG: entry.cont.cont:
; CHECK-MODIFY-CFG-NEXT: [[ADDR_I32:%.*]] = phi ptr [ undef, [[ENTRY_CONT_THEN]] ], [ undef, [[ENTRY_CONT_ELSE]] ]
; CHECK-MODIFY-CFG-NEXT: call void @foo_i32(ptr [[ADDR_I32]])
; CHECK-MODIFY-CFG-NEXT: ret void
;
entry:
%args = alloca [2 x %st.args], align 16
%arr1 = getelementptr inbounds [2 x %st.args], ptr %args, i64 0, i64 1
%sel = select i1 %cmp, ptr %arr1, ptr %args
%addr = getelementptr inbounds %st.args, ptr %sel, i64 0, i32 1
%addr.i8 = load ptr, ptr %addr, align 8
call void @foo_i8(ptr %addr.i8)
%addr.i32 = load ptr, ptr %addr, align 8
call void @foo_i32 (ptr %addr.i32)
ret void
}
declare void @foo_i8(ptr)
declare void @foo_i32(ptr)
; Lifetime intrinsics should not prevent dereferenceability inferrence.
define i32 @interfering_lifetime(ptr %data, i64 %indvars.iv) {
; CHECK-LABEL: @interfering_lifetime(
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[DATA:%.*]], i64 [[INDVARS_IV:%.*]]
; CHECK-NEXT: [[I1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
; CHECK-NEXT: [[CMP_I_I:%.*]] = icmp slt i32 [[I1]], 0
; CHECK-NEXT: [[I3_SROA_SPECULATE_LOAD_FALSE:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
; CHECK-NEXT: [[I3_SROA_SPECULATED:%.*]] = select i1 [[CMP_I_I]], i32 0, i32 [[I3_SROA_SPECULATE_LOAD_FALSE]]
; CHECK-NEXT: ret i32 [[I3_SROA_SPECULATED]]
;
%min = alloca i32, align 4
%arrayidx = getelementptr inbounds i32, ptr %data, i64 %indvars.iv
%i1 = load i32, ptr %arrayidx, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr %min)
store i32 0, ptr %min, align 4
%cmp.i.i = icmp slt i32 %i1, 0
%__b.__a.i.i = select i1 %cmp.i.i, ptr %min, ptr %arrayidx
%i3 = load i32, ptr %__b.__a.i.i, align 4
ret i32 %i3
}
; We should recursively evaluate select's.
define i32 @clamp_load_to_constant_range(ptr %data, i64 %indvars.iv) {
; CHECK-PRESERVE-CFG-LABEL: @clamp_load_to_constant_range(
; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: [[MAX:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[DATA:%.*]], i64 [[INDVARS_IV:%.*]]
; CHECK-PRESERVE-CFG-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[MIN]])
; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-PRESERVE-CFG-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[MAX]])
; CHECK-PRESERVE-CFG-NEXT: store i32 4095, ptr [[MAX]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[I1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[CMP_I_I:%.*]] = icmp slt i32 [[I1]], 0
; CHECK-PRESERVE-CFG-NEXT: [[I2:%.*]] = tail call i32 @llvm.smax.i32(i32 [[I1]], i32 0)
; CHECK-PRESERVE-CFG-NEXT: [[__B___A_I_I:%.*]] = select i1 [[CMP_I_I]], ptr [[MIN]], ptr [[ARRAYIDX]]
; CHECK-PRESERVE-CFG-NEXT: [[CMP_I1_I:%.*]] = icmp ugt i32 [[I2]], 4095
; CHECK-PRESERVE-CFG-NEXT: [[__B___A_I2_I:%.*]] = select i1 [[CMP_I1_I]], ptr [[MAX]], ptr [[__B___A_I_I]]
; CHECK-PRESERVE-CFG-NEXT: [[I3:%.*]] = load i32, ptr [[__B___A_I2_I]], align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[I3]]
;
; CHECK-MODIFY-CFG-LABEL: @clamp_load_to_constant_range(
; CHECK-MODIFY-CFG-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[DATA:%.*]], i64 [[INDVARS_IV:%.*]]
; CHECK-MODIFY-CFG-NEXT: [[I1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
; CHECK-MODIFY-CFG-NEXT: [[CMP_I_I:%.*]] = icmp slt i32 [[I1]], 0
; CHECK-MODIFY-CFG-NEXT: [[I2:%.*]] = tail call i32 @llvm.smax.i32(i32 [[I1]], i32 0)
; CHECK-MODIFY-CFG-NEXT: [[CMP_I1_I:%.*]] = icmp ugt i32 [[I2]], 4095
; CHECK-MODIFY-CFG-NEXT: br i1 [[CMP_I1_I]], label [[DOTCONT:%.*]], label [[DOTELSE:%.*]]
; CHECK-MODIFY-CFG: .else:
; CHECK-MODIFY-CFG-NEXT: br i1 [[CMP_I_I]], label [[DOTELSE_CONT:%.*]], label [[DOTELSE_ELSE:%.*]]
; CHECK-MODIFY-CFG: .else.else:
; CHECK-MODIFY-CFG-NEXT: [[I3_ELSE_VAL_ELSE_VAL:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
; CHECK-MODIFY-CFG-NEXT: br label [[DOTELSE_CONT]]
; CHECK-MODIFY-CFG: .else.cont:
; CHECK-MODIFY-CFG-NEXT: [[I3_ELSE_VAL:%.*]] = phi i32 [ 0, [[DOTELSE]] ], [ [[I3_ELSE_VAL_ELSE_VAL]], [[DOTELSE_ELSE]] ]
; CHECK-MODIFY-CFG-NEXT: br label [[DOTCONT]]
; CHECK-MODIFY-CFG: .cont:
; CHECK-MODIFY-CFG-NEXT: [[I3:%.*]] = phi i32 [ 4095, [[TMP0:%.*]] ], [ [[I3_ELSE_VAL]], [[DOTELSE_CONT]] ]
; CHECK-MODIFY-CFG-NEXT: ret i32 [[I3]]
;
%min = alloca i32, align 4
%max = alloca i32, align 4
%arrayidx = getelementptr inbounds i32, ptr %data, i64 %indvars.iv
call void @llvm.lifetime.start.p0(i64 4, ptr %min)
store i32 0, ptr %min, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr %max)
store i32 4095, ptr %max, align 4
%i1 = load i32, ptr %arrayidx, align 4
%cmp.i.i = icmp slt i32 %i1, 0
%i2 = tail call i32 @llvm.smax.i32(i32 %i1, i32 0)
%__b.__a.i.i = select i1 %cmp.i.i, ptr %min, ptr %arrayidx
%cmp.i1.i = icmp ugt i32 %i2, 4095
%__b.__a.i2.i = select i1 %cmp.i1.i, ptr %max, ptr %__b.__a.i.i
%i3 = load i32, ptr %__b.__a.i2.i, align 4
ret i32 %i3
}
define i32 @non_speculatable_load_of_select(i1 %cond, ptr %else.addr) {
; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF0:![0-9]+]]
; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
;
; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: br i1 [[COND:%.*]], label [[ENTRY_CONT:%.*]], label [[ENTRY_ELSE:%.*]], !prof [[PROF0:![0-9]+]]
; CHECK-MODIFY-CFG: entry.else:
; CHECK-MODIFY-CFG-NEXT: [[R_ELSE_VAL:%.*]] = load i32, ptr [[ELSE_ADDR:%.*]], align 4
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
; CHECK-MODIFY-CFG: entry.cont:
; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[R_ELSE_VAL]], [[ENTRY_ELSE]] ]
; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
store i32 0, ptr %min, align 4
%addr = select i1 %cond, ptr %min, ptr %else.addr, !prof !0
%r = load i32, ptr %addr, align 4
ret i32 %r
}
define i32 @non_speculatable_load_of_select_inverted(i1 %cond, ptr %then.addr) {
; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select_inverted(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[MAX:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: store i32 4095, ptr [[MAX]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
;
; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select_inverted(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: br i1 [[COND:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_CONT:%.*]], !prof [[PROF1:![0-9]+]]
; CHECK-MODIFY-CFG: entry.then:
; CHECK-MODIFY-CFG-NEXT: [[R_THEN_VAL:%.*]] = load i32, ptr [[THEN_ADDR:%.*]], align 4
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
; CHECK-MODIFY-CFG: entry.cont:
; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ [[R_THEN_VAL]], [[ENTRY_THEN]] ], [ 4095, [[ENTRY:%.*]] ]
; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%max = alloca i32, align 4
store i32 4095, ptr %max, align 4
%addr = select i1 %cond, ptr %then.addr, ptr %max, !prof !0
%r = load i32, ptr %addr, align 4
ret i32 %r
}
define i32 @non_speculatable_volatile_load_of_select(i1 %cond, ptr %else.addr) {
; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_volatile_load_of_select(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load volatile i32, ptr [[ADDR]], align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
;
; CHECK-MODIFY-CFG-LABEL: @non_speculatable_volatile_load_of_select(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-MODIFY-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-MODIFY-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF1]]
; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = load volatile i32, ptr [[ADDR]], align 4
; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
store i32 0, ptr %min, align 4
%addr = select i1 %cond, ptr %min, ptr %else.addr, !prof !0
%r = load volatile i32, ptr %addr, align 4
ret i32 %r
}
define i32 @non_speculatable_volatile_load_of_select_inverted(i1 %cond, ptr %then.addr) {
; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_volatile_load_of_select_inverted(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[MAX:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: store i32 4095, ptr [[MAX]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load volatile i32, ptr [[ADDR]], align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
;
; CHECK-MODIFY-CFG-LABEL: @non_speculatable_volatile_load_of_select_inverted(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: [[MAX:%.*]] = alloca i32, align 4
; CHECK-MODIFY-CFG-NEXT: store i32 4095, ptr [[MAX]], align 4
; CHECK-MODIFY-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF1]]
; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = load volatile i32, ptr [[ADDR]], align 4
; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%max = alloca i32, align 4
store i32 4095, ptr %max, align 4
%addr = select i1 %cond, ptr %then.addr, ptr %max, !prof !0
%r = load volatile i32, ptr %addr, align 4
ret i32 %r
}
define i32 @non_speculatable_atomic_unord_load_of_select(i1 %cond, ptr %else.addr) {
; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_atomic_unord_load_of_select(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load atomic i32, ptr [[ADDR]] unordered, align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
;
; CHECK-MODIFY-CFG-LABEL: @non_speculatable_atomic_unord_load_of_select(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: br i1 [[COND:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_ELSE:%.*]], !prof [[PROF1]]
; CHECK-MODIFY-CFG: entry.then:
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT:%.*]]
; CHECK-MODIFY-CFG: entry.else:
; CHECK-MODIFY-CFG-NEXT: [[R_ELSE_VAL:%.*]] = load atomic i32, ptr [[ELSE_ADDR:%.*]] unordered, align 4
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
; CHECK-MODIFY-CFG: entry.cont:
; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ 0, [[ENTRY_THEN]] ], [ [[R_ELSE_VAL]], [[ENTRY_ELSE]] ]
; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
store i32 0, ptr %min, align 4
%addr = select i1 %cond, ptr %min, ptr %else.addr, !prof !0
%r = load atomic i32, ptr %addr unordered, align 4
ret i32 %r
}
define i32 @non_speculatable_atomic_unord_load_of_select_inverted(i1 %cond, ptr %then.addr) {
; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_atomic_unord_load_of_select_inverted(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[MAX:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: store i32 4095, ptr [[MAX]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load atomic i32, ptr [[ADDR]] unordered, align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
;
; CHECK-MODIFY-CFG-LABEL: @non_speculatable_atomic_unord_load_of_select_inverted(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: br i1 [[COND:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_ELSE:%.*]], !prof [[PROF1]]
; CHECK-MODIFY-CFG: entry.then:
; CHECK-MODIFY-CFG-NEXT: [[R_THEN_VAL:%.*]] = load atomic i32, ptr [[THEN_ADDR:%.*]] unordered, align 4
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT:%.*]]
; CHECK-MODIFY-CFG: entry.else:
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
; CHECK-MODIFY-CFG: entry.cont:
; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ [[R_THEN_VAL]], [[ENTRY_THEN]] ], [ 4095, [[ENTRY_ELSE]] ]
; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%max = alloca i32, align 4
store i32 4095, ptr %max, align 4
%addr = select i1 %cond, ptr %then.addr, ptr %max, !prof !0
%r = load atomic i32, ptr %addr unordered, align 4
ret i32 %r
}
define i32 @non_speculatable_load_of_select_outer(i1 %cond_inner, i1 %cond_outer, ptr %data_then, ptr %data_else) {
; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select_outer(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[DATA_THEN:%.*]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN]], ptr [[ADDR_DATA]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
;
; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select_outer(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: [[ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[DATA_THEN:%.*]], ptr [[DATA_ELSE:%.*]], !prof [[PROF1]]
; CHECK-MODIFY-CFG-NEXT: br i1 [[COND_OUTER:%.*]], label [[ENTRY_CONT:%.*]], label [[ENTRY_ELSE:%.*]], !prof [[PROF0]]
; CHECK-MODIFY-CFG: entry.else:
; CHECK-MODIFY-CFG-NEXT: [[R_ELSE_VAL:%.*]] = load i32, ptr [[ADDR_DATA]], align 4
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
; CHECK-MODIFY-CFG: entry.cont:
; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[R_ELSE_VAL]], [[ENTRY_ELSE]] ]
; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
store i32 0, ptr %min, align 4
%addr.data = select i1 %cond_inner, ptr %data_then, ptr %data_else, !prof !0
%addr = select i1 %cond_outer, ptr %min, ptr %addr.data, !prof !0
%r = load i32, ptr %addr, align 4
ret i32 %r
}
define i32 @non_speculatable_load_of_select_outer_inverted(i1 %cond_inner, i1 %cond_outer, ptr %data_then, ptr %data_else) {
; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select_outer_inverted(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[DATA_THEN:%.*]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[ADDR_DATA]], ptr [[MIN]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
;
; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select_outer_inverted(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: [[ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[DATA_THEN:%.*]], ptr [[DATA_ELSE:%.*]], !prof [[PROF1]]
; CHECK-MODIFY-CFG-NEXT: br i1 [[COND_OUTER:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_CONT:%.*]], !prof [[PROF1]]
; CHECK-MODIFY-CFG: entry.then:
; CHECK-MODIFY-CFG-NEXT: [[R_THEN_VAL:%.*]] = load i32, ptr [[ADDR_DATA]], align 4
; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
; CHECK-MODIFY-CFG: entry.cont:
; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ [[R_THEN_VAL]], [[ENTRY_THEN]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
store i32 0, ptr %min, align 4
%addr.data = select i1 %cond_inner, ptr %data_then, ptr %data_else, !prof !0
%addr = select i1 %cond_outer, ptr %addr.data, ptr %min, !prof !0
%r = load i32, ptr %addr, align 4
ret i32 %r
}
define i32 @non_speculatable_load_of_select_inner(i1 %cond_inner, i1 %cond_outer, ptr %data_else, ptr %min_else) {
; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select_inner(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[MIN_ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[MIN]], ptr [[MIN_ELSE:%.*]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN_ADDR_DATA]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
;
; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select_inner(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-MODIFY-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-MODIFY-CFG-NEXT: [[MIN_ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[MIN]], ptr [[MIN_ELSE:%.*]], !prof [[PROF1]]
; CHECK-MODIFY-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN_ADDR_DATA]], ptr [[DATA_ELSE:%.*]], !prof [[PROF1]]
; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
store i32 0, ptr %min, align 4
%min.addr.data = select i1 %cond_inner, ptr %min, ptr %min_else, !prof !0
%addr = select i1 %cond_outer, ptr %min.addr.data, ptr %data_else, !prof !0
%r = load i32, ptr %addr, align 4
ret i32 %r
}
define i32 @non_speculatable_load_of_select_inner_inverted(i1 %cond_inner, i1 %cond_outer, ptr %data_else, ptr %min_then) {
; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select_inner_inverted(
; CHECK-PRESERVE-CFG-NEXT: entry:
; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-PRESERVE-CFG-NEXT: [[MIN_ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[MIN_THEN:%.*]], ptr [[MIN]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN_ADDR_DATA]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
;
; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select_inner_inverted(
; CHECK-MODIFY-CFG-NEXT: entry:
; CHECK-MODIFY-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
; CHECK-MODIFY-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
; CHECK-MODIFY-CFG-NEXT: [[MIN_ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[MIN_THEN:%.*]], ptr [[MIN]], !prof [[PROF1]]
; CHECK-MODIFY-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN_ADDR_DATA]], ptr [[DATA_ELSE:%.*]], !prof [[PROF1]]
; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
store i32 0, ptr %min, align 4
%min.addr.data = select i1 %cond_inner, ptr %min_then, ptr %min, !prof !0
%addr = select i1 %cond_outer, ptr %min.addr.data, ptr %data_else, !prof !0
%r = load i32, ptr %addr, align 4
ret i32 %r
}
; When promoting speculative instruction, metadata that may trigger immediate UB should be dropped.
define void @load_of_select_with_noundef_nonnull(ptr %buffer, i1 %b) {
; CHECK-PRESERVE-CFG-LABEL: @load_of_select_with_noundef_nonnull(
; CHECK-PRESERVE-CFG-NEXT: [[UB_PTR:%.*]] = alloca ptr, align 8
; CHECK-PRESERVE-CFG-NEXT: [[SELECT_PTR:%.*]] = select i1 [[B:%.*]], ptr [[BUFFER:%.*]], ptr [[UB_PTR]]
; CHECK-PRESERVE-CFG-NEXT: [[LOAD_PTR:%.*]] = load ptr, ptr [[SELECT_PTR]], align 8, !nonnull [[META1:![0-9]+]], !noundef [[META1]]
; CHECK-PRESERVE-CFG-NEXT: ret void
;
; CHECK-MODIFY-CFG-LABEL: @load_of_select_with_noundef_nonnull(
; CHECK-MODIFY-CFG-NEXT: br i1 [[B:%.*]], label [[DOTTHEN:%.*]], label [[DOTCONT:%.*]]
; CHECK-MODIFY-CFG: .then:
; CHECK-MODIFY-CFG-NEXT: [[LOAD_PTR_THEN_VAL:%.*]] = load ptr, ptr [[BUFFER:%.*]], align 8, !nonnull [[META2:![0-9]+]], !noundef [[META2]]
; CHECK-MODIFY-CFG-NEXT: br label [[DOTCONT]]
; CHECK-MODIFY-CFG: .cont:
; CHECK-MODIFY-CFG-NEXT: [[LOAD_PTR:%.*]] = phi ptr [ [[LOAD_PTR_THEN_VAL]], [[DOTTHEN]] ], [ undef, [[TMP0:%.*]] ]
; CHECK-MODIFY-CFG-NEXT: ret void
;
%ub_ptr = alloca ptr
%select_ptr = select i1 %b, ptr %buffer, ptr %ub_ptr
%load_ptr = load ptr, ptr %select_ptr, !nonnull !1, !noundef !1
ret void
}
!0 = !{!"branch_weights", i32 1, i32 99}
!1 = !{}
; Ensure that the branch metadata is reversed to match the reversals above.
declare void @llvm.lifetime.start.p0(i64, ptr )
declare void @llvm.lifetime.end.p0(i64, ptr)
declare i32 @llvm.smax.i32(i32, i32)