Index: include/llvm/Transforms/Utils/Local.h =================================================================== --- include/llvm/Transforms/Utils/Local.h +++ include/llvm/Transforms/Utils/Local.h @@ -289,6 +289,11 @@ /// instruction, making it and the rest of the code in the block dead. unsigned changeToUnreachable(Instruction *I, bool UseLLVMTrap); +/// Append an `unreachable` instruction after \p I, making everything following +/// it in the CFG dead. Does not delete \p I. Returns the number of +/// instructions deleted. +unsigned appendUnreachable(Instruction *I); + /// Replace 'BB's terminator with one that does not have an unwind successor /// block. Rewrites `invoke` to `call`, etc. Updates any PHIs in unwind /// successor. Index: lib/Transforms/Utils/Local.cpp =================================================================== --- lib/Transforms/Utils/Local.cpp +++ lib/Transforms/Utils/Local.cpp @@ -1242,7 +1242,8 @@ return NumDeadInst; } -unsigned llvm::changeToUnreachable(Instruction *I, bool UseLLVMTrap) { +static unsigned makeUnreachableImpl(Instruction *I, bool UseLLVMTrap, + bool KeepOriginalInst) { BasicBlock *BB = I->getParent(); // Loop over all of the successors, removing BB's entry from any PHI // nodes. @@ -1252,25 +1253,41 @@ // Insert a call to llvm.trap right before this. This turns the undefined // behavior into a hard fail instead of falling through into random code. if (UseLLVMTrap) { - Function *TrapFn = - Intrinsic::getDeclaration(BB->getParent()->getParent(), Intrinsic::trap); + Function *TrapFn = Intrinsic::getDeclaration(BB->getParent()->getParent(), + Intrinsic::trap); CallInst *CallTrap = CallInst::Create(TrapFn, "", I); CallTrap->setDebugLoc(I->getDebugLoc()); } - new UnreachableInst(I->getContext(), I); + + assert((!KeepOriginalInst || !isa(I)) && + "Can't have two terminators!"); // All instructions after this are dead. unsigned NumInstrsRemoved = 0; BasicBlock::iterator BBI = I->getIterator(), BBE = BB->end(); + if (KeepOriginalInst) + BBI = std::next(BBI); + while (BBI != BBE) { if (!BBI->use_empty()) BBI->replaceAllUsesWith(UndefValue::get(BBI->getType())); BB->getInstList().erase(BBI++); ++NumInstrsRemoved; } + + new UnreachableInst(I->getContext(), BB); return NumInstrsRemoved; } +unsigned llvm::changeToUnreachable(Instruction *I, bool UseLLVMTrap) { + return makeUnreachableImpl(I, UseLLVMTrap, /* KeepOriginalInst = */ false); +} + +unsigned llvm::appendUnreachable(Instruction *I) { + return makeUnreachableImpl(I, /* UseLLVMTrap = */ false, + /* KeepOriginalInst = */ true); +} + /// changeToCall - Convert the specified invoke into a normal call. static void changeToCall(InvokeInst *II) { SmallVector Args(II->arg_begin(), II->arg_end()); @@ -1310,7 +1327,7 @@ // 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(BBI)) + if (IntrinsicInst *II = dyn_cast(BBI)) { if (II->getIntrinsicID() == Intrinsic::assume) { bool MakeUnreachable = false; if (isa(II->getArgOperand(0))) @@ -1327,6 +1344,18 @@ } } + if (II->getIntrinsicID() == Intrinsic::experimental_guard) + // 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(II->getArgOperand(0))) + if (CI->isZero() && !isa(II->getNextNode())) { + appendUnreachable(II); + Changed = true; + break; + } + } + if (CallInst *CI = dyn_cast(BBI)) { if (CI->doesNotReturn()) { // If we found a call to a no-return function, insert an unreachable Index: test/Transforms/SimplifyCFG/guards.ll =================================================================== --- /dev/null +++ test/Transforms/SimplifyCFG/guards.ll @@ -0,0 +1,93 @@ +; 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: true: +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] +; CHECK-NEXT: unreachable +entry: + br i1 %c, label %true, label %false + +true: + call void(i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] + ret i32 10 + +false: + ret i32 20 +} + +define i32 @f_1(i1 %c) { +; Demonstrate that we do not yet 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 %true, label %false + +true: + call void(i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] + %val = load i32, i32* %buf + br label %false + +false: + %to.return = phi i32 [ %val, %true ], [ 50, %entry ] + ret i32 %to.return +; CHECK: true: +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] +; CHECK-NEXT: unreachable + +; CHECK: false: +; 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 %true, label %false + +true: + 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 + +false: + %c1 = load volatile i1, i1* %c + br i1 %c1, label %left, label %right + +left: + %val.left = phi i32 [ %val, %true ], [ 50, %false ] + ret i32 %val.left + +right: + %val.right = phi i32 [ %val, %true ], [ 100, %false ] + ret i32 %val.right + +; CHECK: true: +; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] +; CHECK-NEXT: unreachable + +; CHECK: false: +; CHECK-NEXT: %c1 = load volatile i1, i1* %c +; CHECK-NEXT: [[VAL:%[^ ]]] = select i1 %c1, i32 50, i32 100 +; CHECK-NEXT: ret i32 [[VAL]] +}