[llvm] [SimplifyCFG] Deduce paths unreachable if they cause div/rem UB (PR #109008)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 17 12:16:29 PDT 2024
https://github.com/goldsteinn updated https://github.com/llvm/llvm-project/pull/109008
>From f3e4ff08fc5e0dce1e67c4e5a9622609ca945fc2 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Tue, 17 Sep 2024 10:18:29 -0500
Subject: [PATCH 1/4] [SimplifyCFG] Add tests for deducing paths unreachable if
they cause div/rem UB; NFC
---
.../SimplifyCFG/UnreachableEliminate.ll | 283 ++++++++++++++++++
1 file changed, 283 insertions(+)
diff --git a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
index c4602e72ecbce0..c299f8fabe003b 100644
--- a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
+++ b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
@@ -918,6 +918,289 @@ 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) {
+; CHECK-LABEL: @udiv_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: 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 [[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: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: [[Y:%.*]] = phi i8 [ 2, [[SW_BB1]] ], [ 9, [[SW_BB2]] ], [ 0, [[SW_DEFAULT]] ], [ [[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: 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 [ 0, [[IF_THEN]] ], [ [[V]], [[IF_ELSE]] ]
+; CHECK-NEXT: [[R:%.*]] = srem i8 [[X:%.*]], [[Y]]
+; 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: i8 9, label [[SW_BB2:%.*]]
+; CHECK-NEXT: ]
+; CHECK: sw.bb1:
+; CHECK-NEXT: [[V:%.*]] = call i8 @get.i8()
+; 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 [ [[V]], [[SW_BB1]] ], [ -1, [[SW_BB2]] ], [ 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: 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: [[V:%.*]] = call i8 @get.i8()
+; 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 [ [[V]], [[SW_BB1]] ], [ -1, [[SW_BB2]] ], [ 0, [[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 [ 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) }
>From 99780486c6395e469a4183376c0d1ee925f03de5 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Tue, 17 Sep 2024 09:40:49 -0500
Subject: [PATCH 2/4] [SimplifyCFG] Deduce paths unreachable if they cause
div/rem UB
Same we way mark a path unreachable if it may cause a nullptr
dereference, div/rem by zero or signed div/rem of INT_MIN by -1 cause
immediate UB.
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 23 +++++++++--
.../SimplifyCFG/UnreachableEliminate.ll | 38 ++++---------------
2 files changed, 28 insertions(+), 33 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 5a694b5e7f204b..99970b2a9ead45 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -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()) {
@@ -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())
@@ -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;
}
diff --git a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
index c299f8fabe003b..f3315c31919978 100644
--- a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
+++ b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
@@ -925,18 +925,15 @@ define i8 @udiv_by_zero(i8 noundef %x, i8 noundef %i, i8 noundef %v) {
; CHECK-LABEL: @udiv_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: i8 2, label [[RETURN:%.*]]
; 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: [[Y:%.*]] = phi i8 [ 9, [[SW_BB2]] ], [ [[V:%.*]], [[SW_DEFAULT]] ], [ 2, [[ENTRY:%.*]] ]
; CHECK-NEXT: [[R:%.*]] = udiv i8 [[X:%.*]], [[Y]]
; CHECK-NEXT: ret i8 [[R]]
;
@@ -976,9 +973,9 @@ define i8 @urem_by_zero(i8 noundef %x, i8 noundef %i, i8 noundef %v) {
; CHECK: sw.bb2:
; CHECK-NEXT: br label [[RETURN]]
; CHECK: sw.default:
-; CHECK-NEXT: br label [[RETURN]]
+; CHECK-NEXT: unreachable
; CHECK: return:
-; CHECK-NEXT: [[Y:%.*]] = phi i8 [ 2, [[SW_BB1]] ], [ 9, [[SW_BB2]] ], [ 0, [[SW_DEFAULT]] ], [ [[V:%.*]], [[ENTRY:%.*]] ]
+; 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]]
;
@@ -1054,13 +1051,10 @@ define i8 @srem_by_zero(i8 noundef %x, i8 noundef %i) {
; 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-NEXT: unreachable
; CHECK: if.else:
; CHECK-NEXT: [[V:%.*]] = call i8 @get.i8()
-; CHECK-NEXT: br label [[IF_END]]
-; CHECK: if.end:
-; CHECK-NEXT: [[Y:%.*]] = phi i8 [ 0, [[IF_THEN]] ], [ [[V]], [[IF_ELSE]] ]
-; CHECK-NEXT: [[R:%.*]] = srem i8 [[X:%.*]], [[Y]]
+; CHECK-NEXT: [[R:%.*]] = srem i8 [[X:%.*]], [[V]]
; CHECK-NEXT: ret i8 [[R]]
;
entry:
@@ -1121,17 +1115,14 @@ define i8 @sdiv_overflow_ub(i8 noundef %i) {
; 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: [[V:%.*]] = call i8 @get.i8()
; 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 [ [[V]], [[SW_BB1]] ], [ -1, [[SW_BB2]] ], [ 4, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[Y:%.*]] = phi i8 [ [[V]], [[SW_BB1]] ], [ 4, [[ENTRY:%.*]] ]
; CHECK-NEXT: [[R:%.*]] = sdiv i8 -128, [[Y]]
; CHECK-NEXT: ret i8 [[R]]
;
@@ -1161,21 +1152,8 @@ return:
define i8 @sdiv_overflow_ub_2x(i8 noundef %i) {
; CHECK-LABEL: @sdiv_overflow_ub_2x(
; 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: [[V:%.*]] = call i8 @get.i8()
-; 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 [ [[V]], [[SW_BB1]] ], [ -1, [[SW_BB2]] ], [ 0, [[ENTRY:%.*]] ]
-; CHECK-NEXT: [[R:%.*]] = sdiv i8 -128, [[Y]]
+; CHECK-NEXT: [[R:%.*]] = sdiv i8 -128, [[V]]
; CHECK-NEXT: ret i8 [[R]]
;
entry:
>From 3755484d1e985366335b6ba907f97d6c120e43c9 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Tue, 17 Sep 2024 11:25:15 -0500
Subject: [PATCH 3/4] Okay
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 24 +++++++------------
.../SimplifyCFG/UnreachableEliminate.ll | 17 +++++++++++--
2 files changed, 24 insertions(+), 17 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 99970b2a9ead45..a3fe0e7981c4f3 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7864,7 +7864,7 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
if (I->use_empty())
return false;
- if (C->isNullValue() || isa<UndefValue>(C) || C->isAllOnesValue()) {
+ if (C->isNullValue() || isa<UndefValue>(C)) {
// Only look at the first use we can handle, avoid hurting compile time with
// long uselists
auto FindUse = llvm::find_if(I->users(), [C](auto *U) {
@@ -7881,13 +7881,15 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
case Instruction::Call:
case Instruction::CallBr:
case Instruction::Invoke:
- return C->isNullValue() || isa<UndefValue>(C);
+ return true;
case Instruction::UDiv:
case Instruction::URem:
- return C->isNullValue();
+ // Note: signed div/rem of INT_MIN / -1 is also immediate UB, not
+ // implemented to avoid code complexity as it is unclear how useful such
+ // logic is.
case Instruction::SDiv:
case Instruction::SRem:
- return C->isNullValue() || C->isAllOnesValue();
+ return C->isNullValue();
}
});
if (FindUse == I->user_end())
@@ -7988,17 +7990,9 @@ 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()));
- }
+ // Div/Rem by zero is immediate UB
+ if (match(Use, m_BinOp(m_Value(), m_Specific(I))) && Use->isIntDivRem())
+ return true;
}
return false;
}
diff --git a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
index f3315c31919978..8312057190143d 100644
--- a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
+++ b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll
@@ -1115,14 +1115,17 @@ define i8 @sdiv_overflow_ub(i8 noundef %i) {
; 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: [[V:%.*]] = call i8 @get.i8()
; 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 [ [[V]], [[SW_BB1]] ], [ 4, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[Y:%.*]] = phi i8 [ [[V]], [[SW_BB1]] ], [ -1, [[SW_BB2]] ], [ 4, [[ENTRY:%.*]] ]
; CHECK-NEXT: [[R:%.*]] = sdiv i8 -128, [[Y]]
; CHECK-NEXT: ret i8 [[R]]
;
@@ -1152,8 +1155,18 @@ return:
define i8 @sdiv_overflow_ub_2x(i8 noundef %i) {
; CHECK-LABEL: @sdiv_overflow_ub_2x(
; CHECK-NEXT: entry:
+; CHECK-NEXT: switch i8 [[I:%.*]], label [[SW_DEFAULT:%.*]] [
+; CHECK-NEXT: i8 9, label [[RETURN:%.*]]
+; CHECK-NEXT: i8 2, label [[SW_BB1:%.*]]
+; CHECK-NEXT: ]
+; CHECK: sw.bb1:
; CHECK-NEXT: [[V:%.*]] = call i8 @get.i8()
-; CHECK-NEXT: [[R:%.*]] = sdiv i8 -128, [[V]]
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: sw.default:
+; CHECK-NEXT: unreachable
+; CHECK: return:
+; CHECK-NEXT: [[Y:%.*]] = phi i8 [ [[V]], [[SW_BB1]] ], [ -1, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[R:%.*]] = sdiv i8 -128, [[Y]]
; CHECK-NEXT: ret i8 [[R]]
;
entry:
>From ceeb62762f24a1f5b91fb8c993f68962c8870a4c Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Tue, 17 Sep 2024 14:16:11 -0500
Subject: [PATCH 4/4] Under as well
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index a3fe0e7981c4f3..77b0b2b7e1da7d 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7867,7 +7867,7 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
if (C->isNullValue() || isa<UndefValue>(C)) {
// Only look at the first use we can handle, avoid hurting compile time with
// long uselists
- auto FindUse = llvm::find_if(I->users(), [C](auto *U) {
+ auto FindUse = llvm::find_if(I->users(), [](auto *U) {
auto *Use = cast<Instruction>(U);
// Change this list when we want to add new instructions.
switch (Use->getOpcode()) {
@@ -7881,7 +7881,6 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
case Instruction::Call:
case Instruction::CallBr:
case Instruction::Invoke:
- return true;
case Instruction::UDiv:
case Instruction::URem:
// Note: signed div/rem of INT_MIN / -1 is also immediate UB, not
@@ -7889,7 +7888,7 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
// logic is.
case Instruction::SDiv:
case Instruction::SRem:
- return C->isNullValue();
+ return true;
}
});
if (FindUse == I->user_end())
More information about the llvm-commits
mailing list