Skip to content

Commit c7f9d45

Browse files
committed
[InstCombine] If Inst in unreachable refers to Inst change it to poison (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.
1 parent 38c706e commit c7f9d45

File tree

6 files changed

+236
-7
lines changed

6 files changed

+236
-7
lines changed

llvm/include/llvm/Transforms/Utils/Local.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,10 @@ Value *salvageDebugInfoImpl(Instruction &I, uint64_t CurrentLocOps,
357357
bool replaceAllDbgUsesWith(Instruction &From, Value &To, Instruction &DomPoint,
358358
DominatorTree &DT);
359359

360+
/// If the terminator in an unreachable basic block refers to an instruction
361+
/// transform it to poison.
362+
bool handleUnreachableTerminator(Instruction *I);
363+
360364
/// Remove all instructions from a basic block other than its terminator
361365
/// and any present EH pad instructions. Returns a pair where the first element
362366
/// is the number of instructions (excluding debug info intrinsics) that have

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3231,9 +3231,8 @@ void InstCombinerImpl::handleUnreachableFrom(
32313231
MadeIRChange = true;
32323232
}
32333233

3234-
// RemoveDIs: to match behaviour in dbg.value mode, drop debug-info on
3235-
// terminator too.
3236-
BB->getTerminator()->dropDbgValues();
3234+
if (!DT.isReachableFromEntry(BB))
3235+
MadeIRChange |= handleUnreachableTerminator(BB->getTerminator());
32373236

32383237
// Handle potentially dead successors.
32393238
for (BasicBlock *Succ : successors(BB))

llvm/lib/Transforms/Utils/Local.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,15 +2762,30 @@ bool llvm::replaceAllDbgUsesWith(Instruction &From, Value &To,
27622762
return false;
27632763
}
27642764

2765+
bool llvm::handleUnreachableTerminator(Instruction *I) {
2766+
bool Changed = false;
2767+
// RemoveDIs: erase debug-info on this instruction manually.
2768+
I->dropDbgValues();
2769+
for (Use &U : I->operands()) {
2770+
Value *Op = U.get();
2771+
if (isa<Instruction>(Op) && !Op->getType()->isTokenTy()) {
2772+
U.set(PoisonValue::get(Op->getType()));
2773+
Changed = true;
2774+
}
2775+
}
2776+
2777+
return Changed;
2778+
}
2779+
27652780
std::pair<unsigned, unsigned>
27662781
llvm::removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB) {
27672782
unsigned NumDeadInst = 0;
27682783
unsigned NumDeadDbgInst = 0;
27692784
// Delete the instructions backwards, as it has a reduced likelihood of
27702785
// having to update as many def-use and use-def chains.
27712786
Instruction *EndInst = BB->getTerminator(); // Last not to be deleted.
2772-
// RemoveDIs: erasing debug-info must be done manually.
2773-
EndInst->dropDbgValues();
2787+
handleUnreachableTerminator(EndInst);
2788+
27742789
while (EndInst != &BB->front()) {
27752790
// Delete the next to last instruction.
27762791
Instruction *Inst = &*--EndInst->getIterator();

llvm/test/Transforms/InstCombine/phi-select-constant.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,11 @@ end:
140140
define i16 @sink_to_unreachable_crash(i1 %a) {
141141
; CHECK-LABEL: @sink_to_unreachable_crash(
142142
; CHECK-NEXT: entry:
143-
; CHECK-NEXT: [[S:%.*]] = select i1 [[A:%.*]], i16 0, i16 5
144143
; CHECK-NEXT: br label [[INF_LOOP:%.*]]
145144
; CHECK: inf_loop:
146145
; CHECK-NEXT: br label [[INF_LOOP]]
147146
; CHECK: unreachable:
148-
; CHECK-NEXT: ret i16 [[S]]
147+
; CHECK-NEXT: ret i16 poison
149148
;
150149
entry:
151150
%s = select i1 %a, i16 0, i16 5

llvm/test/Transforms/InstCombine/sink_to_unreachable.ll

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,157 @@ bb3:
157157
%p = phi i32 [0, %bb1], [%a, %bb2]
158158
ret i32 %p
159159
}
160+
161+
define i1 @sink_to_unreachable_ret(i16 %X) {
162+
; CHECK-LABEL: @sink_to_unreachable_ret(
163+
; CHECK-NEXT: entry:
164+
; CHECK-NEXT: br label [[LOOP:%.*]]
165+
; CHECK: loop:
166+
; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]]
167+
; CHECK: unreach:
168+
; CHECK-NEXT: ret i1 poison
169+
;
170+
entry:
171+
br label %loop
172+
173+
loop:
174+
%p = icmp sgt i16 %X, 16
175+
br i1 true, label %loop, label %unreach
176+
177+
unreach:
178+
ret i1 %p
179+
}
180+
181+
define void @sink_to_unreachable_condbr(i16 %X) {
182+
; CHECK-LABEL: @sink_to_unreachable_condbr(
183+
; CHECK-NEXT: entry:
184+
; CHECK-NEXT: br label [[LOOP:%.*]]
185+
; CHECK: loop:
186+
; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]]
187+
; CHECK: unreach:
188+
; CHECK-NEXT: br i1 poison, label [[DUMMY:%.*]], label [[LOOP]]
189+
; CHECK: dummy:
190+
; CHECK-NEXT: unreachable
191+
;
192+
entry:
193+
br label %loop
194+
195+
loop:
196+
%p = icmp sgt i16 %X, 16
197+
br i1 true, label %loop, label %unreach
198+
199+
unreach:
200+
br i1 %p, label %dummy, label %loop
201+
202+
dummy:
203+
unreachable
204+
}
205+
206+
define void @sink_to_unreachable_switch(i16 %X) {
207+
; CHECK-LABEL: @sink_to_unreachable_switch(
208+
; CHECK-NEXT: entry:
209+
; CHECK-NEXT: br label [[LOOP:%.*]]
210+
; CHECK: loop:
211+
; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]]
212+
; CHECK: unreach:
213+
; CHECK-NEXT: switch i16 poison, label [[UNREACH_RET:%.*]] [
214+
; CHECK-NEXT: ]
215+
; CHECK: unreach.ret:
216+
; CHECK-NEXT: unreachable
217+
;
218+
entry:
219+
br label %loop
220+
221+
loop:
222+
%quantum = srem i16 %X, 32
223+
br i1 true, label %loop, label %unreach
224+
225+
unreach:
226+
switch i16 %quantum, label %unreach.ret []
227+
228+
unreach.ret:
229+
unreachable
230+
}
231+
232+
define void @sink_to_unreachable_indirectbr(ptr %Ptr) {
233+
; CHECK-LABEL: @sink_to_unreachable_indirectbr(
234+
; CHECK-NEXT: entry:
235+
; CHECK-NEXT: br label [[LOOP:%.*]]
236+
; CHECK: loop:
237+
; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]]
238+
; CHECK: unreach:
239+
; CHECK-NEXT: indirectbr ptr poison, [label %loop]
240+
;
241+
entry:
242+
br label %loop
243+
244+
loop:
245+
%gep = getelementptr inbounds ptr, ptr %Ptr, i16 1
246+
br i1 true, label %loop, label %unreach
247+
248+
unreach:
249+
indirectbr ptr %gep, [label %loop]
250+
}
251+
252+
define void @sink_to_unreachable_invoke(ptr %Ptr) personality ptr @__CxxFrameHandler3 {
253+
; CHECK-LABEL: @sink_to_unreachable_invoke(
254+
; CHECK-NEXT: entry:
255+
; CHECK-NEXT: br label [[LOOP:%.*]]
256+
; CHECK: loop:
257+
; CHECK-NEXT: br i1 true, label [[LOOP]], label [[UNREACH:%.*]]
258+
; CHECK: unreach:
259+
; CHECK-NEXT: invoke void poison(i1 false)
260+
; CHECK-NEXT: to label [[DUMMY:%.*]] unwind label [[ICATCH_DISPATCH:%.*]]
261+
; CHECK: unreach2:
262+
; CHECK-NEXT: invoke void @__CxxFrameHandler3(ptr poison)
263+
; CHECK-NEXT: to label [[DUMMY]] unwind label [[ICATCH_DISPATCH]]
264+
; CHECK: unreach3:
265+
; CHECK-NEXT: [[CLEAN:%.*]] = cleanuppad within none []
266+
; CHECK-NEXT: invoke void @__CxxFrameHandler3(ptr poison) [ "funclet"(token [[CLEAN]]) ]
267+
; CHECK-NEXT: to label [[DUMMY]] unwind label [[ICATCH_DISPATCH]]
268+
; CHECK: icatch.dispatch:
269+
; CHECK-NEXT: [[TMP1:%.*]] = catchswitch within none [label %icatch] unwind to caller
270+
; CHECK: icatch:
271+
; CHECK-NEXT: [[TMP2:%.*]] = catchpad within [[TMP1]] [ptr null, i32 64, ptr null]
272+
; CHECK-NEXT: catchret from [[TMP2]] to label [[DUMMY2:%.*]]
273+
; CHECK: dummy:
274+
; CHECK-NEXT: ret void
275+
; CHECK: dummy2:
276+
; CHECK-NEXT: ret void
277+
;
278+
entry:
279+
br label %loop
280+
281+
loop:
282+
%gep = getelementptr inbounds ptr, ptr %Ptr, i16 1
283+
br i1 true, label %loop, label %unreach
284+
285+
unreach:
286+
invoke void %gep(i1 false)
287+
to label %dummy unwind label %icatch.dispatch
288+
289+
unreach2:
290+
invoke void @__CxxFrameHandler3(ptr %gep)
291+
to label %dummy unwind label %icatch.dispatch
292+
293+
unreach3:
294+
%clean = cleanuppad within none []
295+
invoke void @__CxxFrameHandler3(ptr %gep) [ "funclet"(token %clean) ]
296+
to label %dummy unwind label %icatch.dispatch
297+
298+
icatch.dispatch:
299+
%tmp1 = catchswitch within none [label %icatch] unwind to caller
300+
301+
icatch:
302+
%tmp2 = catchpad within %tmp1 [ptr null, i32 64, ptr null]
303+
catchret from %tmp2 to label %dummy2
304+
305+
dummy:
306+
ret void
307+
308+
dummy2:
309+
ret void
310+
}
311+
312+
declare void @may_throw()
313+
declare i32 @__CxxFrameHandler3(...)

llvm/test/Transforms/InstCombine/unreachable-code.ll

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,64 @@ bb2:
540540
br label %bb
541541
}
542542

543+
declare void @invoke(ptr)
544+
declare i32 @__gxx_personality_v0(...)
545+
define void @no_change_invoke_ref(i1 %x) personality ptr @__gxx_personality_v0 {
546+
; CHECK-LABEL: define void @no_change_invoke_ref
547+
; CHECK-SAME: (i1 [[X:%.*]]) personality ptr @__gxx_personality_v0 {
548+
; CHECK-NEXT: entry:
549+
; CHECK-NEXT: [[REF:%.*]] = alloca ptr, align 8
550+
; CHECK-NEXT: br i1 [[X]], label [[CONT:%.*]], label [[IF_ELSE:%.*]]
551+
; CHECK: if.else:
552+
; CHECK-NEXT: store i32 1, ptr undef, align 4
553+
; CHECK-NEXT: invoke void @invoke(ptr nonnull [[REF]])
554+
; CHECK-NEXT: to label [[CONT]] unwind label [[LPAD5:%.*]]
555+
; CHECK: cont:
556+
; CHECK-NEXT: invoke void @invoke(ptr nonnull [[REF]])
557+
; CHECK-NEXT: to label [[CLEAN1:%.*]] unwind label [[LPAD6:%.*]]
558+
; CHECK: lpad5:
559+
; CHECK-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 }
560+
; CHECK-NEXT: cleanup
561+
; CHECK-NEXT: br label [[CLEAN1]]
562+
; CHECK: lpad6:
563+
; CHECK-NEXT: [[TMP1:%.*]] = landingpad { ptr, i32 }
564+
; CHECK-NEXT: cleanup
565+
; CHECK-NEXT: br label [[CLEAN2:%.*]]
566+
; CHECK: clean1:
567+
; CHECK-NEXT: ret void
568+
; CHECK: clean2:
569+
; CHECK-NEXT: ret void
570+
;
571+
entry:
572+
%ref = alloca ptr
573+
br i1 %x, label %cont, label %if.else
574+
575+
if.else:
576+
store i32 1, ptr undef
577+
invoke void @invoke(ptr %ref)
578+
to label %cont unwind label %lpad5
579+
580+
cont:
581+
invoke void @invoke(ptr %ref)
582+
to label %clean1 unwind label %lpad6
583+
584+
lpad5:
585+
%13 = landingpad { ptr, i32 }
586+
cleanup
587+
br label %clean1
588+
589+
lpad6:
590+
%14 = landingpad { ptr, i32 }
591+
cleanup
592+
br label %clean2
593+
594+
clean1:
595+
ret void
596+
597+
clean2:
598+
ret void
599+
}
600+
543601
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
544602
; DEFAULT_ITER: {{.*}}
545603
; MAX1: {{.*}}

0 commit comments

Comments
 (0)