
Now that #149310 has restricted lifetime intrinsics to only work on allocas, we can also drop the explicit size argument. Instead, the size is implied by the alloca. This removes the ability to only mark a prefix of an alloca alive/dead. We never used that capability, so we should remove the need to handle that possibility everywhere (though many key places, including stack coloring, did not actually respect this).
488 lines
25 KiB
LLVM
488 lines
25 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
|
|
|
|
%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(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(ptr [[MIN]])
|
|
; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
|
|
; CHECK-PRESERVE-CFG-NEXT: call void @llvm.lifetime.start.p0(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(ptr %min)
|
|
store i32 0, ptr %min, align 4
|
|
call void @llvm.lifetime.start.p0(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(ptr )
|
|
declare void @llvm.lifetime.end.p0(ptr)
|
|
declare i32 @llvm.smax.i32(i32, i32)
|