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.
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(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)
|