diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h index 2df3c9049c7d6..001f61cecc3d2 100644 --- a/llvm/include/llvm/Transforms/Utils/Local.h +++ b/llvm/include/llvm/Transforms/Utils/Local.h @@ -357,6 +357,13 @@ Value *salvageDebugInfoImpl(Instruction &I, uint64_t CurrentLocOps, bool replaceAllDbgUsesWith(Instruction &From, Value &To, Instruction &DomPoint, DominatorTree &DT); +/// If a terminator in an unreachable basic block has an operand of type +/// Instruction, transform it into poison. Return true if any operands +/// are changed to poison. Original Values prior to being changed to poison +/// are returned in \p PoisonedValues. +bool handleUnreachableTerminator(Instruction *I, + SmallVectorImpl &PoisonedValues); + /// Remove all instructions from a basic block other than its terminator /// and any present EH pad instructions. Returns a pair where the first element /// is the number of instructions (excluding debug info intrinsics) that have diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index b1e2262fac479..78b69da63690d 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -3231,9 +3231,12 @@ void InstCombinerImpl::handleUnreachableFrom( MadeIRChange = true; } - // RemoveDIs: to match behaviour in dbg.value mode, drop debug-info on - // terminator too. - BB->getTerminator()->dropDbgValues(); + SmallVector Changed; + if (handleUnreachableTerminator(BB->getTerminator(), Changed)) { + MadeIRChange = true; + for (Value *V : Changed) + addToWorklist(cast(V)); + } // Handle potentially dead successors. for (BasicBlock *Succ : successors(BB)) diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index e4aa25f7ac6ad..2fd9f44e53f0b 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -2762,6 +2762,23 @@ bool llvm::replaceAllDbgUsesWith(Instruction &From, Value &To, return false; } +bool llvm::handleUnreachableTerminator( + Instruction *I, SmallVectorImpl &PoisonedValues) { + bool Changed = false; + // RemoveDIs: erase debug-info on this instruction manually. + I->dropDbgValues(); + for (Use &U : I->operands()) { + Value *Op = U.get(); + if (isa(Op) && !Op->getType()->isTokenTy()) { + U.set(PoisonValue::get(Op->getType())); + PoisonedValues.push_back(Op); + Changed = true; + } + } + + return Changed; +} + std::pair llvm::removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB) { unsigned NumDeadInst = 0; @@ -2769,8 +2786,9 @@ llvm::removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB) { // Delete the instructions backwards, as it has a reduced likelihood of // having to update as many def-use and use-def chains. Instruction *EndInst = BB->getTerminator(); // Last not to be deleted. - // RemoveDIs: erasing debug-info must be done manually. - EndInst->dropDbgValues(); + SmallVector Uses; + handleUnreachableTerminator(EndInst, Uses); + while (EndInst != &BB->front()) { // Delete the next to last instruction. Instruction *Inst = &*--EndInst->getIterator(); diff --git a/llvm/test/Transforms/InstCombine/phi-select-constant.ll b/llvm/test/Transforms/InstCombine/phi-select-constant.ll index 1260ef47f65ef..601a3d208a084 100644 --- a/llvm/test/Transforms/InstCombine/phi-select-constant.ll +++ b/llvm/test/Transforms/InstCombine/phi-select-constant.ll @@ -140,12 +140,11 @@ end: define i16 @sink_to_unreachable_crash(i1 %a) { ; CHECK-LABEL: @sink_to_unreachable_crash( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[S:%.*]] = select i1 [[A:%.*]], i16 0, i16 5 ; CHECK-NEXT: br label [[INF_LOOP:%.*]] ; CHECK: inf_loop: ; CHECK-NEXT: br label [[INF_LOOP]] ; CHECK: unreachable: -; CHECK-NEXT: ret i16 [[S]] +; CHECK-NEXT: ret i16 poison ; entry: %s = select i1 %a, i16 0, i16 5 diff --git a/llvm/test/Transforms/InstCombine/pr63791.ll b/llvm/test/Transforms/InstCombine/pr63791.ll index a489b2e3e6221..78cc1130fb33f 100644 --- a/llvm/test/Transforms/InstCombine/pr63791.ll +++ b/llvm/test/Transforms/InstCombine/pr63791.ll @@ -17,7 +17,7 @@ define void @y() { ; CHECK: for.cond5.preheader.i: ; CHECK-NEXT: br i1 false, label [[FOR_INC19_I:%.*]], label [[FOR_COND1_LOOPEXIT_I:%.*]] ; CHECK: for.inc19.i: -; CHECK-NEXT: br i1 false, label [[FOR_INC19_I]], label [[FOR_COND1_LOOPEXIT_I]] +; CHECK-NEXT: br i1 poison, label [[FOR_INC19_I]], label [[FOR_COND1_LOOPEXIT_I]] ; entry: br label %for.cond.i diff --git a/llvm/test/Transforms/InstCombine/sink_to_unreachable.ll b/llvm/test/Transforms/InstCombine/sink_to_unreachable.ll index e788b634da886..02ed22217854e 100644 --- a/llvm/test/Transforms/InstCombine/sink_to_unreachable.ll +++ b/llvm/test/Transforms/InstCombine/sink_to_unreachable.ll @@ -157,3 +157,157 @@ bb3: %p = phi i32 [0, %bb1], [%a, %bb2] ret i32 %p } + +define i1 @sink_to_unreachable_ret(i16 %X) { +; CHECK-LABEL: @sink_to_unreachable_ret( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]] +; CHECK: unreach: +; CHECK-NEXT: ret i1 poison +; +entry: + br label %loop + +loop: + %p = icmp sgt i16 %X, 16 + br i1 true, label %loop, label %unreach + +unreach: + ret i1 %p +} + +define void @sink_to_unreachable_condbr(i16 %X) { +; CHECK-LABEL: @sink_to_unreachable_condbr( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]] +; CHECK: unreach: +; CHECK-NEXT: br i1 poison, label [[DUMMY:%.*]], label [[LOOP]] +; CHECK: dummy: +; CHECK-NEXT: unreachable +; +entry: + br label %loop + +loop: + %p = icmp sgt i16 %X, 16 + br i1 true, label %loop, label %unreach + +unreach: + br i1 %p, label %dummy, label %loop + +dummy: + unreachable +} + +define void @sink_to_unreachable_switch(i16 %X) { +; CHECK-LABEL: @sink_to_unreachable_switch( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]] +; CHECK: unreach: +; CHECK-NEXT: switch i16 poison, label [[UNREACH_RET:%.*]] [ +; CHECK-NEXT: ] +; CHECK: unreach.ret: +; CHECK-NEXT: unreachable +; +entry: + br label %loop + +loop: + %quantum = srem i16 %X, 32 + br i1 true, label %loop, label %unreach + +unreach: + switch i16 %quantum, label %unreach.ret [] + +unreach.ret: + unreachable +} + +define void @sink_to_unreachable_indirectbr(ptr %Ptr) { +; CHECK-LABEL: @sink_to_unreachable_indirectbr( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]] +; CHECK: unreach: +; CHECK-NEXT: indirectbr ptr poison, [label %loop] +; +entry: + br label %loop + +loop: + %gep = getelementptr inbounds ptr, ptr %Ptr, i16 1 + br i1 true, label %loop, label %unreach + +unreach: + indirectbr ptr %gep, [label %loop] +} + +define void @sink_to_unreachable_invoke(ptr %Ptr) personality ptr @__CxxFrameHandler3 { +; CHECK-LABEL: @sink_to_unreachable_invoke( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]] +; CHECK: unreach: +; CHECK-NEXT: invoke void poison(i1 false) +; CHECK-NEXT: to label [[DUMMY:%.*]] unwind label [[ICATCH_DISPATCH:%.*]] +; CHECK: unreach2: +; CHECK-NEXT: invoke void @__CxxFrameHandler3(ptr poison) +; CHECK-NEXT: to label [[DUMMY]] unwind label [[ICATCH_DISPATCH]] +; CHECK: unreach3: +; CHECK-NEXT: [[CLEAN:%.*]] = cleanuppad within none [] +; CHECK-NEXT: invoke void @__CxxFrameHandler3(ptr poison) [ "funclet"(token [[CLEAN]]) ] +; CHECK-NEXT: to label [[DUMMY]] unwind label [[ICATCH_DISPATCH]] +; CHECK: icatch.dispatch: +; CHECK-NEXT: [[TMP1:%.*]] = catchswitch within none [label %icatch] unwind to caller +; CHECK: icatch: +; CHECK-NEXT: [[TMP2:%.*]] = catchpad within [[TMP1]] [ptr null, i32 64, ptr null] +; CHECK-NEXT: catchret from [[TMP2]] to label [[DUMMY2:%.*]] +; CHECK: dummy: +; CHECK-NEXT: ret void +; CHECK: dummy2: +; CHECK-NEXT: ret void +; +entry: + br label %loop + +loop: + %gep = getelementptr inbounds ptr, ptr %Ptr, i16 1 + br i1 true, label %loop, label %unreach + +unreach: + invoke void %gep(i1 false) + to label %dummy unwind label %icatch.dispatch + +unreach2: + invoke void @__CxxFrameHandler3(ptr %gep) + to label %dummy unwind label %icatch.dispatch + +unreach3: + %clean = cleanuppad within none [] + invoke void @__CxxFrameHandler3(ptr %gep) [ "funclet"(token %clean) ] + to label %dummy unwind label %icatch.dispatch + +icatch.dispatch: + %tmp1 = catchswitch within none [label %icatch] unwind to caller + +icatch: + %tmp2 = catchpad within %tmp1 [ptr null, i32 64, ptr null] + catchret from %tmp2 to label %dummy2 + +dummy: + ret void + +dummy2: + ret void +} + +declare void @may_throw() +declare i32 @__CxxFrameHandler3(...) diff --git a/llvm/test/Transforms/InstCombine/unreachable-code.ll b/llvm/test/Transforms/InstCombine/unreachable-code.ll index 72ef1c79f3a8e..bb4be9fa71f83 100644 --- a/llvm/test/Transforms/InstCombine/unreachable-code.ll +++ b/llvm/test/Transforms/InstCombine/unreachable-code.ll @@ -540,6 +540,63 @@ bb2: br label %bb } +declare void @invoke(ptr) +declare i32 @__gxx_personality_v0(...) +define void @test(i1 %x) personality ptr @__gxx_personality_v0 { +; CHECK-LABEL: define void @test +; CHECK-SAME: (i1 [[X:%.*]]) personality ptr @__gxx_personality_v0 { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[X]], label [[IF_ELSE:%.*]], label [[CLEAN1:%.*]] +; CHECK: if.else: +; CHECK-NEXT: store i32 1, ptr undef, align 4 +; CHECK-NEXT: invoke void @invoke(ptr poison) +; CHECK-NEXT: to label [[CONT:%.*]] unwind label [[LPAD5:%.*]] +; CHECK: cont: +; CHECK-NEXT: invoke void @invoke(ptr poison) +; CHECK-NEXT: to label [[CLEAN1]] unwind label [[LPAD6:%.*]] +; CHECK: lpad5: +; CHECK-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: br label [[CLEAN1]] +; CHECK: lpad6: +; CHECK-NEXT: [[TMP1:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: br label [[CLEAN2:%.*]] +; CHECK: clean1: +; CHECK-NEXT: ret void +; CHECK: clean2: +; CHECK-NEXT: ret void +; +entry: + %ref = alloca ptr + br i1 %x, label %if.else, label %clean1 + +if.else: + store i32 1, ptr undef + invoke void @invoke(ptr %ref) + to label %cont unwind label %lpad5 + +cont: + invoke void @invoke(ptr %ref) + to label %clean1 unwind label %lpad6 + +lpad5: + %13 = landingpad { ptr, i32 } + cleanup + br label %clean1 + +lpad6: + %14 = landingpad { ptr, i32 } + cleanup + br label %clean2 + +clean1: + ret void + +clean2: + ret void +} + ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: ; DEFAULT_ITER: {{.*}} ; MAX1: {{.*}}