diff options
author | Sanjoy Das <sanjoy@playingwithpointers.com> | 2016-04-21 05:09:12 +0000 |
---|---|---|
committer | Sanjoy Das <sanjoy@playingwithpointers.com> | 2016-04-21 05:09:12 +0000 |
commit | 54a3a006cab5a108322ed61ce4fbe2939177274c (patch) | |
tree | c4333b4bb38c01709d4f43209437ffb66df22abd /llvm | |
parent | afd1b06d8eaaaf15b38a5ad79dcab20f437b1bc0 (diff) | |
download | bcm5719-llvm-54a3a006cab5a108322ed61ce4fbe2939177274c.tar.gz bcm5719-llvm-54a3a006cab5a108322ed61ce4fbe2939177274c.zip |
[SimplifyCFG] Fold `llvm.guard(false)` to unreachable
Summary:
`llvm.guard(false)` always bails out of the current compilation unit, so
we can prune any control flow following it.
Reviewers: hfinkel, pcc, reames
Subscribers: majnemer, reames, mcrosier, llvm-commits
Differential Revision: http://reviews.llvm.org/D19245
llvm-svn: 266955
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/lib/Transforms/Utils/Local.cpp | 20 | ||||
-rw-r--r-- | llvm/test/Transforms/SimplifyCFG/guards.ll | 86 |
2 files changed, 105 insertions, 1 deletions
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index bd8b7c45d1a..75eda97d0a3 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -1310,7 +1310,7 @@ static bool markAliveBlocks(Function &F, // Assumptions that are known to be false are equivalent to unreachable. // Also, if the condition is undefined, then we make the choice most // beneficial to the optimizer, and choose that to also be unreachable. - if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(BBI)) + if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(BBI)) { if (II->getIntrinsicID() == Intrinsic::assume) { bool MakeUnreachable = false; if (isa<UndefValue>(II->getArgOperand(0))) @@ -1327,6 +1327,24 @@ static bool markAliveBlocks(Function &F, } } + if (II->getIntrinsicID() == Intrinsic::experimental_guard) { + // A call to the guard intrinsic bails out of the current compilation + // unit if the predicate passed to it is false. If the predicate is a + // constant false, then we know the guard will bail out of the current + // compile unconditionally, so all code following it is dead. + // + // Note: unlike in llvm.assume, it is not "obviously profitable" for + // guards to treat `undef` as `false` since a guard on `undef` can + // still be useful for widening. + if (auto *CI = dyn_cast<ConstantInt>(II->getArgOperand(0))) + if (CI->isZero() && !isa<UnreachableInst>(II->getNextNode())) { + changeToUnreachable(II->getNextNode(), /*UseLLVMTrap=*/ false); + Changed = true; + break; + } + } + } + if (CallInst *CI = dyn_cast<CallInst>(BBI)) { if (CI->doesNotReturn()) { // If we found a call to a no-return function, insert an unreachable diff --git a/llvm/test/Transforms/SimplifyCFG/guards.ll b/llvm/test/Transforms/SimplifyCFG/guards.ll new file mode 100644 index 00000000000..71144d7ac24 --- /dev/null +++ b/llvm/test/Transforms/SimplifyCFG/guards.ll @@ -0,0 +1,86 @@ +; RUN: opt -S -simplifycfg < %s | FileCheck %s + +declare void @llvm.experimental.guard(i1, ...) + +define i32 @f_0(i1 %c) { +; CHECK-LABEL: @f_0( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] +; CHECK-NEXT: unreachable +entry: + call void(i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] + ret i32 10 +} + +define i32 @f_1(i1 %c) { +; Demonstrate that we (intentionally) do not simplify a guard on undef + +; CHECK-LABEL: @f_1( +; CHECK: ret i32 10 +; CHECK: ret i32 20 + +entry: + br i1 %c, label %true, label %false + +true: + call void(i1, ...) @llvm.experimental.guard(i1 undef) [ "deopt"() ] + ret i32 10 + +false: + ret i32 20 +} + +define i32 @f_2(i1 %c, i32* %buf) { +; CHECK-LABEL: @f_2( +entry: + br i1 %c, label %guard_block, label %merge_block + +guard_block: + call void(i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] + %val = load i32, i32* %buf + br label %merge_block + +merge_block: + %to.return = phi i32 [ %val, %guard_block ], [ 50, %entry ] + ret i32 %to.return +; CHECK: guard_block: +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] +; CHECK-NEXT: unreachable + +; CHECK: merge_block: +; CHECK-NEXT: ret i32 50 +} + +define i32 @f_3(i1* %c, i32* %buf) { +; CHECK-LABEL: @f_3( +entry: + %c0 = load volatile i1, i1* %c + br i1 %c0, label %guard_block, label %merge_block + +guard_block: + call void(i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] + %val = load i32, i32* %buf + %c2 = load volatile i1, i1* %c + br i1 %c2, label %left, label %right + +merge_block: + %c1 = load volatile i1, i1* %c + br i1 %c1, label %left, label %right + +left: + %val.left = phi i32 [ %val, %guard_block ], [ 50, %merge_block ] + ret i32 %val.left + +right: + %val.right = phi i32 [ %val, %guard_block ], [ 100, %merge_block ] + ret i32 %val.right + +; CHECK: guard_block: +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] +; CHECK-NEXT: unreachable + +; CHECK: merge_block: +; CHECK-NEXT: %c1 = load volatile i1, i1* %c +; CHECK-NEXT: [[VAL:%[^ ]]] = select i1 %c1, i32 50, i32 100 +; CHECK-NEXT: ret i32 [[VAL]] +} |