diff options
author | Yingwei Zheng <dtcxzyw2333@gmail.com> | 2024-04-25 20:47:56 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-25 20:47:56 +0800 |
commit | 697fcd009855a579f756dfe34498a815ed9dc3fd (patch) | |
tree | d138cf9674730712cf67ade502169f37b4be7847 | |
parent | ee7365198c5575337cfcbf3cb0a0c6689dd703f5 (diff) | |
download | llvm-697fcd009855a579f756dfe34498a815ed9dc3fd.zip llvm-697fcd009855a579f756dfe34498a815ed9dc3fd.tar.gz llvm-697fcd009855a579f756dfe34498a815ed9dc3fd.tar.bz2 |
[SimplifyCFG] Handle `llvm.assume` in `passingValueIsAlwaysUndefined` (#89929)
See the following example:
```
define i32 @test(i32 %cond) {
entry:
switch i32 %cond, label %default [
i32 0, label %case0
i32 1, label %case1
i32 2, label %case2
]
case0:
br label %exit
case1:
br label %exit
case2:
br label %exit
default:
br label %exit
exit:
%bool = phi i1 [ false, %default ], [ true, %case0 ], [ true, %case1 ], [ true, %case2 ]
%res = phi i32 [ 0, %default ], [ 1, %case0 ], [ 2, %case1 ], [ 3, %case2 ]
call void @llvm.assume(i1 %bool)
ret i32 %res
}
```
The edge `%default -> %bool` is dead since it will trigger an immediate
UB.
Alive2: https://alive2.llvm.org/ce/z/gywJiE
My benchmark shows many rust applications and some c/c++ applications
(e.g., arrow/php/qemu) will benefit from this patch :)
-rw-r--r-- | llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 7 | ||||
-rw-r--r-- | llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll | 226 |
2 files changed, 233 insertions, 0 deletions
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 3eda669..4db7246 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7524,6 +7524,13 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu SI->getPointerAddressSpace())) && SI->getPointerOperand() == I; + // llvm.assume(false/undef) always triggers immediate UB. + if (auto *Assume = dyn_cast<AssumeInst>(Use)) { + // Ignore assume operand bundles. + if (I == Assume->getArgOperand(0)) + return true; + } + if (auto *CB = dyn_cast<CallBase>(Use)) { if (C->isNullValue() && NullPointerIsDefined(CB->getFunction())) return false; diff --git a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll index 7573405..ef2d321 100644 --- a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll +++ b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll @@ -627,7 +627,233 @@ else: ret void } +define i32 @test_assume_false(i32 %cond) { +; CHECK-LABEL: @test_assume_false( +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i32 [[COND:%.*]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[EXIT:%.*]] +; CHECK-NEXT: i32 1, label [[CASE1:%.*]] +; CHECK-NEXT: i32 2, label [[CASE2:%.*]] +; CHECK-NEXT: ] +; CHECK: case1: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: case2: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: default: +; CHECK-NEXT: unreachable +; CHECK: exit: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 2, [[CASE1]] ], [ 3, [[CASE2]] ], [ 1, [[ENTRY:%.*]] ] +; CHECK-NEXT: call void @llvm.assume(i1 true) +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + switch i32 %cond, label %default [ + i32 0, label %case0 + i32 1, label %case1 + i32 2, label %case2 + ] + +case0: + br label %exit + +case1: + br label %exit + +case2: + br label %exit + +default: + br label %exit + +exit: + %bool = phi i1 [ false, %default ], [ true, %case0 ], [ true, %case1 ], [ true, %case2 ] + %res = phi i32 [ 0, %default ], [ 1, %case0 ], [ 2, %case1 ], [ 3, %case2 ] + call void @llvm.assume(i1 %bool) + ret i32 %res +} + +define i32 @test_assume_undef(i32 %cond) { +; CHECK-LABEL: @test_assume_undef( +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i32 [[COND:%.*]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[EXIT:%.*]] +; CHECK-NEXT: i32 1, label [[CASE1:%.*]] +; CHECK-NEXT: i32 2, label [[CASE2:%.*]] +; CHECK-NEXT: ] +; CHECK: case1: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: case2: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: default: +; CHECK-NEXT: unreachable +; CHECK: exit: +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 2, [[CASE1]] ], [ 3, [[CASE2]] ], [ 1, [[ENTRY:%.*]] ] +; CHECK-NEXT: call void @llvm.assume(i1 true) +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + switch i32 %cond, label %default [ + i32 0, label %case0 + i32 1, label %case1 + i32 2, label %case2 + ] + +case0: + br label %exit + +case1: + br label %exit + +case2: + br label %exit + +default: + br label %exit + +exit: + %bool = phi i1 [ undef, %default ], [ true, %case0 ], [ true, %case1 ], [ true, %case2 ] + %res = phi i32 [ 0, %default ], [ 1, %case0 ], [ 2, %case1 ], [ 3, %case2 ] + call void @llvm.assume(i1 %bool) + ret i32 %res +} + +define i32 @test_assume_var(i32 %cond, i1 %var) { +; CHECK-LABEL: @test_assume_var( +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i32 [[COND:%.*]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[EXIT:%.*]] +; CHECK-NEXT: i32 1, label [[CASE1:%.*]] +; CHECK-NEXT: i32 2, label [[CASE2:%.*]] +; CHECK-NEXT: ] +; CHECK: case1: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: case2: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: default: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[BOOL:%.*]] = phi i1 [ [[VAR:%.*]], [[DEFAULT]] ], [ true, [[CASE1]] ], [ true, [[CASE2]] ], [ true, [[ENTRY:%.*]] ] +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, [[DEFAULT]] ], [ 2, [[CASE1]] ], [ 3, [[CASE2]] ], [ 1, [[ENTRY]] ] +; CHECK-NEXT: call void @llvm.assume(i1 [[BOOL]]) +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + switch i32 %cond, label %default [ + i32 0, label %case0 + i32 1, label %case1 + i32 2, label %case2 + ] + +case0: + br label %exit +case1: + br label %exit + +case2: + br label %exit + +default: + br label %exit + +exit: + %bool = phi i1 [ %var, %default ], [ true, %case0 ], [ true, %case1 ], [ true, %case2 ] + %res = phi i32 [ 0, %default ], [ 1, %case0 ], [ 2, %case1 ], [ 3, %case2 ] + call void @llvm.assume(i1 %bool) + ret i32 %res +} + +define i32 @test_assume_bundle_nonnull(i32 %cond, ptr nonnull %p) { +; CHECK-LABEL: @test_assume_bundle_nonnull( +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i32 [[COND:%.*]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[EXIT:%.*]] +; CHECK-NEXT: i32 1, label [[CASE1:%.*]] +; CHECK-NEXT: i32 2, label [[CASE2:%.*]] +; CHECK-NEXT: ] +; CHECK: case1: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: case2: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: default: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PTR:%.*]] = phi ptr [ null, [[DEFAULT]] ], [ [[P:%.*]], [[CASE1]] ], [ [[P]], [[CASE2]] ], [ [[P]], [[ENTRY:%.*]] ] +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, [[DEFAULT]] ], [ 2, [[CASE1]] ], [ 3, [[CASE2]] ], [ 1, [[ENTRY]] ] +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[PTR]]) ] +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + switch i32 %cond, label %default [ + i32 0, label %case0 + i32 1, label %case1 + i32 2, label %case2 + ] + +case0: + br label %exit + +case1: + br label %exit + +case2: + br label %exit + +default: + br label %exit + +exit: + %ptr = phi ptr [ null, %default ], [ %p, %case0 ], [ %p, %case1 ], [ %p, %case2 ] + %res = phi i32 [ 0, %default ], [ 1, %case0 ], [ 2, %case1 ], [ 3, %case2 ] + call void @llvm.assume(i1 true) [ "nonnull"(ptr %ptr) ] + ret i32 %res +} + +define i32 @test_assume_bundle_align(i32 %cond, ptr nonnull %p) { +; CHECK-LABEL: @test_assume_bundle_align( +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i32 [[COND:%.*]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i32 0, label [[EXIT:%.*]] +; CHECK-NEXT: i32 1, label [[CASE1:%.*]] +; CHECK-NEXT: i32 2, label [[CASE2:%.*]] +; CHECK-NEXT: ] +; CHECK: case1: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: case2: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: default: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[PTR:%.*]] = phi ptr [ null, [[DEFAULT]] ], [ [[P:%.*]], [[CASE1]] ], [ [[P]], [[CASE2]] ], [ [[P]], [[ENTRY:%.*]] ] +; CHECK-NEXT: [[RES:%.*]] = phi i32 [ 0, [[DEFAULT]] ], [ 2, [[CASE1]] ], [ 3, [[CASE2]] ], [ 1, [[ENTRY]] ] +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i32 8) ] +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + switch i32 %cond, label %default [ + i32 0, label %case0 + i32 1, label %case1 + i32 2, label %case2 + ] + +case0: + br label %exit + +case1: + br label %exit + +case2: + br label %exit + +default: + br label %exit + +exit: + %ptr = phi ptr [ null, %default ], [ %p, %case0 ], [ %p, %case1 ], [ %p, %case2 ] + %res = phi i32 [ 0, %default ], [ 1, %case0 ], [ 2, %case1 ], [ 3, %case2 ] + call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i32 8) ] + ret i32 %res +} attributes #0 = { null_pointer_is_valid } ;. |