Currently, SROA is CFG-preserving. Not doing so does not affect any pipeline test. (???) Internally, SROA requires Dominator Tree, and uses it solely for the final `-mem2reg` call. By design, we can't really SROA alloca if their address escapes somehow, but we have logic to deal with `load` of `select`/`PHI`, where at least one of the possible addresses prevents promotion, by speculating the `load`s and `select`ing between loaded values. As one would expect, that requires ensuring that the speculation is actually legal. Even ignoring complexity bailouts, that logic does not deal with everything, e.g. `isSafeToLoadUnconditionally()` does not recurse into hands of `select`. There can also be cases where the load is genuinely non-speculate. So if we can't prove that the load can be speculated, unfold the select, produce two-entry phi node, and perform predicated load. Now, that transformation must obviously update Dominator Tree, since we require it later on. Doing so is trivial. Additionally, we don't want to do this for the final SROA invocation (D136806). In the end, this ends up having negative (!) compile-time cost: https://llvm-compile-time-tracker.com/compare.php?from=c6d7e80ec4c17a415673b1cfd25924f98ac83608&to=ddf9600365093ea50d7e278696cbfa01641c959d&stat=instructions:u Though indeed, this only deals with `select`s, `PHI`s are still using speculation. Should we update some more analysis? Reviewed By: arsenm Differential Revision: https://reviews.llvm.org/D138238 This reverts commit739611870d, and recommits03e6d9d9d1with a fixed assertion - we should check that DTU is there, not just assert false...
419 lines
22 KiB
LLVM
419 lines
22 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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
!0 = !{!"branch_weights", i32 1, i32 99}
|
|
|
|
; 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)
|