Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SimplifyCFG] Deduce paths unreachable if they cause div/rem UB #109008

Closed
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions llvm/lib/Transforms/Utils/SimplifyCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7864,10 +7864,10 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
if (I->use_empty())
return false;

if (C->isNullValue() || isa<UndefValue>(C)) {
if (C->isNullValue() || isa<UndefValue>(C) || C->isAllOnesValue()) {
// Only look at the first use we can handle, avoid hurting compile time with
// long uselists
auto FindUse = llvm::find_if(I->users(), [](auto *U) {
auto FindUse = llvm::find_if(I->users(), [C](auto *U) {
auto *Use = cast<Instruction>(U);
// Change this list when we want to add new instructions.
switch (Use->getOpcode()) {
Expand All @@ -7881,7 +7881,13 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
case Instruction::Call:
case Instruction::CallBr:
case Instruction::Invoke:
return true;
return C->isNullValue() || isa<UndefValue>(C);
case Instruction::UDiv:
case Instruction::URem:
return C->isNullValue();
case Instruction::SDiv:
case Instruction::SRem:
return C->isNullValue() || C->isAllOnesValue();
}
});
if (FindUse == I->user_end())
Expand Down Expand Up @@ -7982,6 +7988,17 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
}
}
}
if (match(Use, m_BinOp(m_Value(), m_Specific(I)))) {
// Immediate UB to divide by zero
if (Use->getOpcode() == Instruction::UDiv ||
Use->getOpcode() == Instruction::URem)
return C->isNullValue();
// Immediate UB to signed-divide INT_MIN by -1
if (Use->getOpcode() == Instruction::SDiv ||
Use->getOpcode() == Instruction::SRem)
return C->isNullValue() ||
(C->isAllOnesValue() && match(Use->getOperand(0), m_SignMask()));
}
}
return false;
}
Expand Down
261 changes: 261 additions & 0 deletions llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,267 @@ bb5: ; preds = %bb3, %bb
ret i32 %i7
}

declare void @side.effect()
declare i8 @get.i8()

define i8 @udiv_by_zero(i8 noundef %x, i8 noundef %i, i8 noundef %v) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the noundefs relevant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix before pushing.

; CHECK-LABEL: @udiv_by_zero(
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i8 [[I:%.*]], label [[SW_DEFAULT:%.*]] [
; CHECK-NEXT: i8 9, label [[SW_BB2:%.*]]
; CHECK-NEXT: i8 2, label [[RETURN:%.*]]
; CHECK-NEXT: ]
; CHECK: sw.bb2:
; CHECK-NEXT: br label [[RETURN]]
; CHECK: sw.default:
; CHECK-NEXT: br label [[RETURN]]
; CHECK: return:
; CHECK-NEXT: [[Y:%.*]] = phi i8 [ 9, [[SW_BB2]] ], [ [[V:%.*]], [[SW_DEFAULT]] ], [ 2, [[ENTRY:%.*]] ]
; CHECK-NEXT: [[R:%.*]] = udiv i8 [[X:%.*]], [[Y]]
; CHECK-NEXT: ret i8 [[R]]
;
entry:
switch i8 %i, label %sw.default [
i8 0, label %sw.bb0
i8 2, label %sw.bb1
i8 9, label %sw.bb2
]

sw.bb0:
br label %return

sw.bb1:
br label %return
sw.bb2:
br label %return
sw.default:
br label %return

return:
%y = phi i8 [ 0, %sw.bb0 ], [ 2, %sw.bb1 ], [ 9, %sw.bb2 ], [ %v, %sw.default ]
%r = udiv i8 %x, %y
ret i8 %r
}

define i8 @urem_by_zero(i8 noundef %x, i8 noundef %i, i8 noundef %v) {
; CHECK-LABEL: @urem_by_zero(
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i8 [[I:%.*]], label [[SW_DEFAULT:%.*]] [
; CHECK-NEXT: i8 0, label [[RETURN:%.*]]
; CHECK-NEXT: i8 2, label [[SW_BB1:%.*]]
; CHECK-NEXT: i8 9, label [[SW_BB2:%.*]]
; CHECK-NEXT: ]
; CHECK: sw.bb1:
; CHECK-NEXT: br label [[RETURN]]
; CHECK: sw.bb2:
; CHECK-NEXT: br label [[RETURN]]
; CHECK: sw.default:
; CHECK-NEXT: unreachable
; CHECK: return:
; CHECK-NEXT: [[Y:%.*]] = phi i8 [ 2, [[SW_BB1]] ], [ 9, [[SW_BB2]] ], [ [[V:%.*]], [[ENTRY:%.*]] ]
; CHECK-NEXT: [[R:%.*]] = urem i8 [[X:%.*]], [[Y]]
; CHECK-NEXT: ret i8 [[R]]
;
entry:
switch i8 %i, label %sw.default [
i8 0, label %sw.bb0
i8 2, label %sw.bb1
i8 9, label %sw.bb2
]

sw.bb0:
br label %return

sw.bb1:
br label %return
sw.bb2:
br label %return
sw.default:
br label %return

return:
%y = phi i8 [ %v, %sw.bb0 ], [ 2, %sw.bb1 ], [ 9, %sw.bb2 ], [ 0, %sw.default ]
%r = urem i8 %x, %y
ret i8 %r
}

define i8 @udiv_of_zero_okay(i8 noundef %x, i8 noundef %i, i8 noundef %v) {
; CHECK-LABEL: @udiv_of_zero_okay(
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i8 [[I:%.*]], label [[SW_DEFAULT:%.*]] [
; CHECK-NEXT: i8 0, label [[RETURN:%.*]]
; CHECK-NEXT: i8 2, label [[SW_BB1:%.*]]
; CHECK-NEXT: i8 9, label [[SW_BB2:%.*]]
; CHECK-NEXT: ]
; CHECK: sw.bb1:
; CHECK-NEXT: br label [[RETURN]]
; CHECK: sw.bb2:
; CHECK-NEXT: br label [[RETURN]]
; CHECK: sw.default:
; CHECK-NEXT: br label [[RETURN]]
; CHECK: return:
; CHECK-NEXT: [[Y:%.*]] = phi i8 [ 2, [[SW_BB1]] ], [ 9, [[SW_BB2]] ], [ [[V:%.*]], [[SW_DEFAULT]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT: [[R:%.*]] = udiv i8 [[Y]], [[X:%.*]]
; CHECK-NEXT: ret i8 [[R]]
;
entry:
switch i8 %i, label %sw.default [
i8 0, label %sw.bb0
i8 2, label %sw.bb1
i8 9, label %sw.bb2
]

sw.bb0:
br label %return

sw.bb1:
br label %return
sw.bb2:
br label %return
sw.default:
br label %return

return:
%y = phi i8 [ 0, %sw.bb0 ], [ 2, %sw.bb1 ], [ 9, %sw.bb2 ], [ %v, %sw.default ]
%r = udiv i8 %y, %x
ret i8 %r
}

define i8 @srem_by_zero(i8 noundef %x, i8 noundef %i) {
; CHECK-LABEL: @srem_by_zero(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[I:%.*]], 9
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: call void @side.effect()
; CHECK-NEXT: unreachable
; CHECK: if.else:
; CHECK-NEXT: [[V:%.*]] = call i8 @get.i8()
; CHECK-NEXT: [[R:%.*]] = srem i8 [[X:%.*]], [[V]]
; CHECK-NEXT: ret i8 [[R]]
;
entry:
%cmp = icmp ult i8 %i, 9
br i1 %cmp, label %if.then, label %if.else

if.then:
call void @side.effect()
br label %if.end

if.else:
%v = call i8 @get.i8()
br label %if.end

if.end:
%y = phi i8 [ 0, %if.then ], [ %v, %if.else ]
%r = srem i8 %x, %y
ret i8 %r
}

define i8 @srem_no_overflow_okay(i8 noundef %i) {
; CHECK-LABEL: @srem_no_overflow_okay(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[I:%.*]], 9
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: call void @side.effect()
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: [[V:%.*]] = call i8 @get.i8()
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[Y:%.*]] = phi i8 [ -1, [[IF_THEN]] ], [ [[V]], [[IF_ELSE]] ]
; CHECK-NEXT: [[R:%.*]] = srem i8 [[Y]], -128
; CHECK-NEXT: ret i8 [[R]]
;
entry:
%cmp = icmp ult i8 %i, 9
br i1 %cmp, label %if.then, label %if.else

if.then:
call void @side.effect()
br label %if.end

if.else:
%v = call i8 @get.i8()
br label %if.end

if.end:
%y = phi i8 [ -1, %if.then ], [ %v, %if.else ]
%r = srem i8 %y, 128
ret i8 %r
}

define i8 @sdiv_overflow_ub(i8 noundef %i) {
; CHECK-LABEL: @sdiv_overflow_ub(
; CHECK-NEXT: entry:
; CHECK-NEXT: switch i8 [[I:%.*]], label [[SW_DEFAULT:%.*]] [
; CHECK-NEXT: i8 0, label [[RETURN:%.*]]
; CHECK-NEXT: i8 2, label [[SW_BB1:%.*]]
; CHECK-NEXT: ]
; CHECK: sw.bb1:
; CHECK-NEXT: [[V:%.*]] = call i8 @get.i8()
; CHECK-NEXT: br label [[RETURN]]
; CHECK: sw.default:
; CHECK-NEXT: unreachable
; CHECK: return:
; CHECK-NEXT: [[Y:%.*]] = phi i8 [ [[V]], [[SW_BB1]] ], [ 4, [[ENTRY:%.*]] ]
; CHECK-NEXT: [[R:%.*]] = sdiv i8 -128, [[Y]]
; CHECK-NEXT: ret i8 [[R]]
;
entry:
switch i8 %i, label %sw.default [
i8 0, label %sw.bb0
i8 2, label %sw.bb1
i8 9, label %sw.bb2
]

sw.bb0:
br label %return
sw.bb1:
%v = call i8 @get.i8()
br label %return
sw.bb2:
br label %return
sw.default:
unreachable

return:
%y = phi i8 [ 4, %sw.bb0 ], [ %v, %sw.bb1 ], [ -1, %sw.bb2 ]
%r = sdiv i8 128, %y
ret i8 %r
}

define i8 @sdiv_overflow_ub_2x(i8 noundef %i) {
; CHECK-LABEL: @sdiv_overflow_ub_2x(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[V:%.*]] = call i8 @get.i8()
; CHECK-NEXT: [[R:%.*]] = sdiv i8 -128, [[V]]
; CHECK-NEXT: ret i8 [[R]]
;
entry:
switch i8 %i, label %sw.default [
i8 0, label %sw.bb0
i8 2, label %sw.bb1
i8 9, label %sw.bb2
]

sw.bb0:
br label %return
sw.bb1:
%v = call i8 @get.i8()
br label %return
sw.bb2:
br label %return
sw.default:
unreachable

return:
%y = phi i8 [ 0, %sw.bb0 ], [ %v, %sw.bb1 ], [ -1, %sw.bb2 ]
%r = sdiv i8 128, %y
ret i8 %r
}

attributes #0 = { null_pointer_is_valid }
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
Expand Down
Loading