[llvm] [InstCombine] If inst in unreachable refers to an inst change it to poison (#65107) (PR #78444)

via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 19 02:31:32 PST 2024


https://github.com/ParkHanbum updated https://github.com/llvm/llvm-project/pull/78444

>From c7f9d45704fb1e70371baa60f025aa438b64bcd5 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Sat, 20 Jan 2024 04:22:57 +0900
Subject: [PATCH] [InstCombine] If Inst in unreachable refers to Inst change it
 to poison (#65107)

Instructions in unreachable basic blocks are removed, but terminators
are not. In this case, even instructions that are only referenced by
a terminator, such as a return instruction, cannot be processed
properly.

This patch changes the operand of terminator instruction in an
unreachable basic block to poison if it refers to the instruction,
allowing the instruction to be properly processed.
---
 llvm/include/llvm/Transforms/Utils/Local.h    |   4 +
 .../InstCombine/InstructionCombining.cpp      |   5 +-
 llvm/lib/Transforms/Utils/Local.cpp           |  19 ++-
 .../InstCombine/phi-select-constant.ll        |   3 +-
 .../InstCombine/sink_to_unreachable.ll        | 154 ++++++++++++++++++
 .../InstCombine/unreachable-code.ll           |  58 +++++++
 6 files changed, 236 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h
index 2df3c9049c7d62..34c646e9aa1933 100644
--- a/llvm/include/llvm/Transforms/Utils/Local.h
+++ b/llvm/include/llvm/Transforms/Utils/Local.h
@@ -357,6 +357,10 @@ Value *salvageDebugInfoImpl(Instruction &I, uint64_t CurrentLocOps,
 bool replaceAllDbgUsesWith(Instruction &From, Value &To, Instruction &DomPoint,
                            DominatorTree &DT);
 
+/// If the terminator in an unreachable basic block refers to an instruction
+/// transform it to poison.
+bool handleUnreachableTerminator(Instruction *I);
+
 /// 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 b1e2262fac4794..c71ed4c68d2e9a 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -3231,9 +3231,8 @@ void InstCombinerImpl::handleUnreachableFrom(
     MadeIRChange = true;
   }
 
-  // RemoveDIs: to match behaviour in dbg.value mode, drop debug-info on
-  // terminator too.
-  BB->getTerminator()->dropDbgValues();
+  if (!DT.isReachableFromEntry(BB))
+    MadeIRChange |= handleUnreachableTerminator(BB->getTerminator());
 
   // 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 e4aa25f7ac6ad3..1ca1591437e6fc 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -2762,6 +2762,21 @@ bool llvm::replaceAllDbgUsesWith(Instruction &From, Value &To,
   return false;
 }
 
+bool llvm::handleUnreachableTerminator(Instruction *I) {
+  bool Changed = false;
+  // RemoveDIs: erase debug-info on this instruction manually.
+  I->dropDbgValues();
+  for (Use &U : I->operands()) {
+    Value *Op = U.get();
+    if (isa<Instruction>(Op) && !Op->getType()->isTokenTy()) {
+      U.set(PoisonValue::get(Op->getType()));
+      Changed = true;
+    }
+  }
+
+  return Changed;
+}
+
 std::pair<unsigned, unsigned>
 llvm::removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB) {
   unsigned NumDeadInst = 0;
@@ -2769,8 +2784,8 @@ 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();
+  handleUnreachableTerminator(EndInst);
+
   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 1260ef47f65ef4..601a3d208a0841 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/sink_to_unreachable.ll b/llvm/test/Transforms/InstCombine/sink_to_unreachable.ll
index e788b634da8868..02ed22217854ea 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 72ef1c79f3a8e5..20a29f9932ae74 100644
--- a/llvm/test/Transforms/InstCombine/unreachable-code.ll
+++ b/llvm/test/Transforms/InstCombine/unreachable-code.ll
@@ -540,6 +540,64 @@ bb2:
   br label %bb
 }
 
+declare void @invoke(ptr)
+declare i32 @__gxx_personality_v0(...)
+define void @no_change_invoke_ref(i1 %x) personality ptr @__gxx_personality_v0  {
+; CHECK-LABEL: define void @no_change_invoke_ref
+; CHECK-SAME: (i1 [[X:%.*]]) personality ptr @__gxx_personality_v0 {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[REF:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    br i1 [[X]], label [[CONT:%.*]], label [[IF_ELSE:%.*]]
+; CHECK:       if.else:
+; CHECK-NEXT:    store i32 1, ptr undef, align 4
+; CHECK-NEXT:    invoke void @invoke(ptr nonnull [[REF]])
+; CHECK-NEXT:            to label [[CONT]] unwind label [[LPAD5:%.*]]
+; CHECK:       cont:
+; CHECK-NEXT:    invoke void @invoke(ptr nonnull [[REF]])
+; 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 %cont, label %if.else
+
+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: {{.*}}



More information about the llvm-commits mailing list