Skip to content

Commit

Permalink
[InstCombine] If Inst in unreachable refers to Inst change it to pois…
Browse files Browse the repository at this point in the history
…on (llvm#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.
  • Loading branch information
ParkHanbum committed Feb 19, 2024
1 parent 38c706e commit 98e272f
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 8 deletions.
7 changes: 7 additions & 0 deletions llvm/include/llvm/Transforms/Utils/Local.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// be returned to PoisonedValues.
bool handleUnreachableTerminator(Instruction *I,
SmallVectorImpl<Value *> &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
Expand Down
9 changes: 6 additions & 3 deletions llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value *, 4> Changed;
if (handleUnreachableTerminator(BB->getTerminator(), Changed)) {
MadeIRChange = true;
for (Value *V : Changed)
addToWorklist(cast<Instruction>(V));
}

// Handle potentially dead successors.
for (BasicBlock *Succ : successors(BB))
Expand Down
22 changes: 20 additions & 2 deletions llvm/lib/Transforms/Utils/Local.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2762,15 +2762,33 @@ bool llvm::replaceAllDbgUsesWith(Instruction &From, Value &To,
return false;
}

bool llvm::handleUnreachableTerminator(
Instruction *I, SmallVectorImpl<Value *> &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<Instruction>(Op) && !Op->getType()->isTokenTy()) {
U.set(PoisonValue::get(Op->getType()));
PoisonedValues.push_back(Op);
Changed = true;
}
}

return Changed;
}

std::pair<unsigned, unsigned>
llvm::removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB) {
unsigned NumDeadInst = 0;
unsigned NumDeadDbgInst = 0;
// 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<Value *, 4> Uses;
handleUnreachableTerminator(EndInst, Uses);

while (EndInst != &BB->front()) {
// Delete the next to last instruction.
Instruction *Inst = &*--EndInst->getIterator();
Expand Down
3 changes: 1 addition & 2 deletions llvm/test/Transforms/InstCombine/phi-select-constant.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/InstCombine/pr63791.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
154 changes: 154 additions & 0 deletions llvm/test/Transforms/InstCombine/sink_to_unreachable.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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(...)
57 changes: 57 additions & 0 deletions llvm/test/Transforms/InstCombine/unreachable-code.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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: {{.*}}

0 comments on commit 98e272f

Please sign in to comment.