Skip to content

Commit 248fcab

Browse files
dtcxzywnikic
andauthored
[InstCombine] Do not use operand info in replaceInInstruction (#99492)
Consider the following case: ``` %cmp = icmp eq ptr %p, null %load = load i32, ptr %p, align 4 %sel = select i1 %cmp, i32 %load, i32 0 ``` `foldSelectValueEquivalence` converts `load i32, ptr %p, align 4` into `load i32, ptr null, align 4`, which causes immediate UB. `%load` is speculatable, but it doesn't hold after operand substitution. This patch introduces a new helper `isSafeToSpeculativelyExecuteWithVariableReplaced`. It ignores operand info in these instructions since their operands will be replaced later. Fixes #99436. --------- Co-authored-by: Nikita Popov <[email protected]>
1 parent 76321b9 commit 248fcab

File tree

5 files changed

+60
-14
lines changed

5 files changed

+60
-14
lines changed

llvm/include/llvm/Analysis/ValueTracking.h

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -822,15 +822,25 @@ bool isSafeToSpeculativelyExecute(const Instruction *I,
822822
const Instruction *CtxI = nullptr,
823823
AssumptionCache *AC = nullptr,
824824
const DominatorTree *DT = nullptr,
825-
const TargetLibraryInfo *TLI = nullptr);
825+
const TargetLibraryInfo *TLI = nullptr,
826+
bool UseVariableInfo = true);
827+
828+
inline bool isSafeToSpeculativelyExecute(const Instruction *I,
829+
BasicBlock::iterator CtxI,
830+
AssumptionCache *AC = nullptr,
831+
const DominatorTree *DT = nullptr,
832+
const TargetLibraryInfo *TLI = nullptr,
833+
bool UseVariableInfo = true) {
834+
// Take an iterator, and unwrap it into an Instruction *.
835+
return isSafeToSpeculativelyExecute(I, &*CtxI, AC, DT, TLI, UseVariableInfo);
836+
}
826837

838+
/// Don't use information from its non-constant operands. This helper is used
839+
/// when its operands are going to be replaced.
827840
inline bool
828-
isSafeToSpeculativelyExecute(const Instruction *I, BasicBlock::iterator CtxI,
829-
AssumptionCache *AC = nullptr,
830-
const DominatorTree *DT = nullptr,
831-
const TargetLibraryInfo *TLI = nullptr) {
832-
// Take an iterator, and unwrap it into an Instruction *.
833-
return isSafeToSpeculativelyExecute(I, &*CtxI, AC, DT, TLI);
841+
isSafeToSpeculativelyExecuteWithVariableReplaced(const Instruction *I) {
842+
return isSafeToSpeculativelyExecute(I, nullptr, nullptr, nullptr, nullptr,
843+
/*UseVariableInfo=*/false);
834844
}
835845

836846
/// This returns the same result as isSafeToSpeculativelyExecute if Opcode is
@@ -853,7 +863,7 @@ isSafeToSpeculativelyExecute(const Instruction *I, BasicBlock::iterator CtxI,
853863
bool isSafeToSpeculativelyExecuteWithOpcode(
854864
unsigned Opcode, const Instruction *Inst, const Instruction *CtxI = nullptr,
855865
AssumptionCache *AC = nullptr, const DominatorTree *DT = nullptr,
856-
const TargetLibraryInfo *TLI = nullptr);
866+
const TargetLibraryInfo *TLI = nullptr, bool UseVariableInfo = true);
857867

858868
/// Returns true if the result or effects of the given instructions \p I
859869
/// depend values not reachable through the def use graph.

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6771,15 +6771,16 @@ bool llvm::isSafeToSpeculativelyExecute(const Instruction *Inst,
67716771
const Instruction *CtxI,
67726772
AssumptionCache *AC,
67736773
const DominatorTree *DT,
6774-
const TargetLibraryInfo *TLI) {
6774+
const TargetLibraryInfo *TLI,
6775+
bool UseVariableInfo) {
67756776
return isSafeToSpeculativelyExecuteWithOpcode(Inst->getOpcode(), Inst, CtxI,
6776-
AC, DT, TLI);
6777+
AC, DT, TLI, UseVariableInfo);
67776778
}
67786779

67796780
bool llvm::isSafeToSpeculativelyExecuteWithOpcode(
67806781
unsigned Opcode, const Instruction *Inst, const Instruction *CtxI,
6781-
AssumptionCache *AC, const DominatorTree *DT,
6782-
const TargetLibraryInfo *TLI) {
6782+
AssumptionCache *AC, const DominatorTree *DT, const TargetLibraryInfo *TLI,
6783+
bool UseVariableInfo) {
67836784
#ifndef NDEBUG
67846785
if (Inst->getOpcode() != Opcode) {
67856786
// Check that the operands are actually compatible with the Opcode override.
@@ -6831,6 +6832,9 @@ bool llvm::isSafeToSpeculativelyExecuteWithOpcode(
68316832
return false;
68326833
}
68336834
case Instruction::Load: {
6835+
if (!UseVariableInfo)
6836+
return false;
6837+
68346838
const LoadInst *LI = dyn_cast<LoadInst>(Inst);
68356839
if (!LI)
68366840
return false;

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,8 +1247,11 @@ bool InstCombinerImpl::replaceInInstruction(Value *V, Value *Old, Value *New,
12471247
if (Depth == 2)
12481248
return false;
12491249

1250+
assert(!isa<Constant>(Old) && "Only replace non-constant values");
1251+
12501252
auto *I = dyn_cast<Instruction>(V);
1251-
if (!I || !I->hasOneUse() || !isSafeToSpeculativelyExecute(I))
1253+
if (!I || !I->hasOneUse() ||
1254+
!isSafeToSpeculativelyExecuteWithVariableReplaced(I))
12521255
return false;
12531256

12541257
bool Changed = false;
@@ -1331,7 +1334,7 @@ Instruction *InstCombinerImpl::foldSelectValueEquivalence(SelectInst &Sel,
13311334
// profitability is not clear for other cases.
13321335
// FIXME: Support vectors.
13331336
if (OldOp == CmpLHS && match(NewOp, m_ImmConstant()) &&
1334-
!match(OldOp, m_ImmConstant()) && !Cmp.getType()->isVectorTy() &&
1337+
!match(OldOp, m_Constant()) && !Cmp.getType()->isVectorTy() &&
13351338
isGuaranteedNotToBeUndef(NewOp, SQ.AC, &Sel, &DT))
13361339
if (replaceInInstruction(TrueVal, OldOp, NewOp))
13371340
return &Sel;

llvm/test/Transforms/InstCombine/select-binop-cmp.ll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,19 @@ define i32 @select_replace_call_speculatable(i32 %x, i32 %y) {
13151315
ret i32 %s
13161316
}
13171317

1318+
define i32 @select_replace_call_speculatable_intrinsic(i32 %x, i32 %y) {
1319+
; CHECK-LABEL: @select_replace_call_speculatable_intrinsic(
1320+
; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[X:%.*]], 0
1321+
; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.smax.i32(i32 [[Y:%.*]], i32 0)
1322+
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i32 [[CALL]], i32 [[Y]]
1323+
; CHECK-NEXT: ret i32 [[S]]
1324+
;
1325+
%c = icmp eq i32 %x, 0
1326+
%call = call i32 @llvm.smax.i32(i32 %x, i32 %y)
1327+
%s = select i1 %c, i32 %call, i32 %y
1328+
ret i32 %s
1329+
}
1330+
13181331
; We can't replace the call arguments, as the call is not speculatable. We
13191332
; may end up changing side-effects or causing undefined behavior.
13201333
define i32 @select_replace_call_non_speculatable(i32 %x, i32 %y) {

llvm/test/Transforms/InstCombine/select.ll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4713,3 +4713,19 @@ define i8 @select_knownbits_simplify_missing_noundef(i8 %x) {
47134713
%res = select i1 %cmp, i8 %and, i8 0
47144714
ret i8 %res
47154715
}
4716+
4717+
@g_ext = external global i8
4718+
4719+
; Make sure we don't replace %ptr with @g_ext, which may cause the load to trigger UB.
4720+
define i32 @pr99436(ptr align 4 dereferenceable(4) %ptr) {
4721+
; CHECK-LABEL: @pr99436(
4722+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[PTR:%.*]], @g_ext
4723+
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[PTR]], align 4
4724+
; CHECK-NEXT: [[RET:%.*]] = select i1 [[CMP]], i32 [[VAL]], i32 0
4725+
; CHECK-NEXT: ret i32 [[RET]]
4726+
;
4727+
%cmp = icmp eq ptr %ptr, @g_ext
4728+
%val = load i32, ptr %ptr, align 4
4729+
%ret = select i1 %cmp, i32 %val, i32 0
4730+
ret i32 %ret
4731+
}

0 commit comments

Comments
 (0)