Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion llvm/lib/Transforms/Scalar/JumpThreading.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2785,11 +2785,19 @@ void JumpThreadingPass::unfoldSelectInstr(BasicBlock *Pred, BasicBlock *BB,
UncondBrInst *PredTerm = cast<UncondBrInst>(Pred->getTerminator());
BasicBlock *NewBB = BasicBlock::Create(BB->getContext(), "select.unfold",
BB->getParent(), BB);
// When converting a select into a branch, make sure we freeze the condition
// when it may be poison or undef.
Value *Cond = SI->getCondition();
if (!isGuaranteedNotToBeUndefOrPoison(Cond)) {
Cond = new FreezeInst(Cond, "cond.fr", SI->getIterator());
cast<Instruction>(Cond)->setDebugLoc(DebugLoc::getTemporary());
}

// Move the unconditional branch to NewBB.
PredTerm->removeFromParent();
PredTerm->insertInto(NewBB, NewBB->end());
// Create a conditional branch and update PHI nodes.
auto *BI = CondBrInst::Create(SI->getCondition(), NewBB, BB, Pred);
auto *BI = CondBrInst::Create(Cond, NewBB, BB, Pred);
BI->applyMergedLocation(PredTerm->getDebugLoc(), SI->getDebugLoc());
BI->copyMetadata(*SI, {LLVMContext::MD_prof});
SIUse->setIncomingValue(Idx, SI->getFalseValue());
Expand Down
51 changes: 46 additions & 5 deletions llvm/test/Transforms/JumpThreading/branch-debug-info2.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -passes=jump-threading -S %s -o - | FileCheck %s
define void @prepare_next_shadow() !dbg !7 {
; CHECK-LABEL: define void @prepare_next_shadow(
; CHECK-SAME: ) !dbg [[DBG7:![0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br label %[[SHADOW_TO_PTR_EXIT:.*]], !dbg [[DBG9:![0-9]+]]
; CHECK: [[SHADOW_TO_PTR_EXIT]]:
; CHECK-NEXT: [[SHR_I:%.*]] = lshr i64 0, 12, !dbg [[DBG10:![0-9]+]]
; CHECK-NEXT: [[CMP_I:%.*]] = icmp ult i64 [[SHR_I]], undef, !dbg [[DBG11:![0-9]+]]
; CHECK-NEXT: br i1 [[CMP_I]], label %[[FOR_INC:.*]], label %[[IF_END_I60:.*]], !dbg [[DBG12:![0-9]+]]
; CHECK: [[IF_END_I60]]:
; CHECK-NEXT: [[SUB_I:%.*]] = sub i64 [[SHR_I]], undef, !dbg [[DBG13:![0-9]+]]
; CHECK-NEXT: [[CMP3_I:%.*]] = icmp ugt i64 [[SUB_I]], 32763, !dbg [[DBG14:![0-9]+]]
; CHECK-NEXT: [[CONV7_I:%.*]] = trunc i64 [[SUB_I]] to i32, !dbg [[DBG15:![0-9]+]]
; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[CMP3_I]]
; Jump threading folds the select below into this branch. Ensure debug info is
; not lost, and is merged from the select and the branch.
; CHECK-NEXT: br i1 [[COND_FR]], label %[[FOR_INC]], label %[[PTR_TO_SHADOW_EXIT:.*]], !dbg [[DBG16:![0-9]+]]
; CHECK: [[PTR_TO_SHADOW_EXIT]]:
; CHECK-NEXT: [[CALL1861:%.*]] = phi i32 [ [[CONV7_I]], %[[IF_END_I60]] ], !dbg [[DBG17:![0-9]+]]
; CHECK-NEXT: [[CMP19:%.*]] = icmp slt i32 [[CALL1861]], 0, !dbg [[DBG18:![0-9]+]]
; CHECK-NEXT: br i1 [[CMP19]], label %[[FOR_INC]], label %[[IF_END22:.*]], !dbg [[DBG19:![0-9]+]]
; CHECK: [[IF_END22]]:
; CHECK-NEXT: unreachable, !dbg [[DBG20:![0-9]+]]
; CHECK: [[FOR_INC]]:
; CHECK-NEXT: br label %[[SHADOW_TO_PTR_EXIT]], !dbg [[DBG21:![0-9]+]]
;
entry:
br label %for.cond, !dbg !9

Expand All @@ -16,11 +42,6 @@ if.end.i60: ; preds = %shadow_to_ptr.exit
%cmp3.i = icmp ugt i64 %sub.i, 32763, !dbg !15
%conv7.i = trunc i64 %sub.i to i32, !dbg !16
%spec.select.i = select i1 %cmp3.i, i32 -1, i32 %conv7.i, !dbg !17
; Jump threading is going to fold the select in to the branch. Ensure debug
; info is not lost, and is merged from the select and the branch.
; CHECK-NOT: br i1 %cmp3.i, label %for.inc, label %if.end22
; CHECK: br i1 %cmp3.i, label %for.inc, label %if.end22, !dbg [[DBG:![0-9]+]]
; CHECK: [[DBG]] = !DILocation(line: 9, column: 1, scope: !{{.*}})

br label %ptr_to_shadow.exit, !dbg !17

Expand Down Expand Up @@ -68,3 +89,23 @@ for.inc: ; preds = %ptr_to_shadow.exit
!22 = !DILocation(line: 14, column: 1, scope: !7)
!23 = !DILocation(line: 15, column: 1, scope: !7)
!24 = !DILocation(line: 16, column: 1, scope: !7)
;.
; CHECK: [[META1:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C, file: [[META2:![0-9]+]], producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: [[META3:![0-9]+]])
; CHECK: [[META2]] = !DIFile(filename: "{{.*}}jump_threading.ll", directory: {{.*}})
; CHECK: [[META3]] = !{}
; CHECK: [[DBG7]] = distinct !DISubprogram(name: "prepare_next_shadow", linkageName: "prepare_next_shadow", scope: null, file: [[META2]], line: 1, type: [[META8:![0-9]+]], scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META1]], retainedNodes: [[META3]])
; CHECK: [[META8]] = !DISubroutineType(types: [[META3]])
; CHECK: [[DBG9]] = !DILocation(line: 1, column: 1, scope: [[DBG7]])
; CHECK: [[DBG10]] = !DILocation(line: 3, column: 1, scope: [[DBG7]])
; CHECK: [[DBG11]] = !DILocation(line: 4, column: 1, scope: [[DBG7]])
; CHECK: [[DBG12]] = !DILocation(line: 5, column: 1, scope: [[DBG7]])
; CHECK: [[DBG13]] = !DILocation(line: 6, column: 1, scope: [[DBG7]])
; CHECK: [[DBG14]] = !DILocation(line: 7, column: 1, scope: [[DBG7]])
; CHECK: [[DBG15]] = !DILocation(line: 8, column: 1, scope: [[DBG7]])
; CHECK: [[DBG16]] = !DILocation(line: 9, column: 1, scope: [[DBG7]])
; CHECK: [[DBG17]] = !DILocation(line: 12, column: 1, scope: [[DBG7]])
; CHECK: [[DBG18]] = !DILocation(line: 13, column: 1, scope: [[DBG7]])
; CHECK: [[DBG19]] = !DILocation(line: 14, column: 1, scope: [[DBG7]])
; CHECK: [[DBG20]] = !DILocation(line: 15, column: 1, scope: [[DBG7]])
; CHECK: [[DBG21]] = !DILocation(line: 16, column: 1, scope: [[DBG7]])
;.
9 changes: 6 additions & 3 deletions llvm/test/Transforms/JumpThreading/select.ll
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ define void @unfold1(double %x, double %y) nounwind !prof !1 {
; CHECK: cond.false:
; CHECK-NEXT: [[ADD:%.*]] = fadd double [[X]], [[Y]]
; CHECK-NEXT: [[CMP1:%.*]] = fcmp ogt double [[ADD]], 1.000000e+01
; CHECK-NEXT: br i1 [[CMP1]], label [[COND_END4]], label [[IF_THEN:%.*]], !prof [[PROF1:![0-9]+]]
; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[CMP1]]
; CHECK-NEXT: br i1 [[COND_FR]], label [[COND_END4]], label [[IF_THEN:%.*]], !prof [[PROF1:![0-9]+]]
; CHECK: cond.end4:
; CHECK-NEXT: [[COND5:%.*]] = phi double [ [[SUB]], [[ENTRY:%.*]] ], [ [[ADD]], [[COND_FALSE]] ]
; CHECK-NEXT: [[CMP6:%.*]] = fcmp oeq double [[COND5]], 0.000000e+00
Expand Down Expand Up @@ -332,7 +333,8 @@ define void @unfold2(i32 %x, i32 %y) nounwind !prof !1 {
; CHECK: cond.false:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[X]], [[Y]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[ADD]], 10
; CHECK-NEXT: br i1 [[CMP1]], label [[IF_THEN:%.*]], label [[COND_END4:%.*]], !prof [[PROF1]]
; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[CMP1]]
; CHECK-NEXT: br i1 [[COND_FR]], label [[IF_THEN:%.*]], label [[COND_END4:%.*]], !prof [[PROF1]]
; CHECK: cond.end4:
; CHECK-NEXT: [[COND5:%.*]] = phi i32 [ [[ADD]], [[COND_FALSE]] ]
; CHECK-NEXT: [[CMP6:%.*]] = icmp eq i32 [[COND5]], 0
Expand Down Expand Up @@ -556,7 +558,8 @@ define void @test_func(ptr nocapture readonly %a, ptr nocapture readonly %b, ptr
; CHECK-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds i32, ptr [[C:%.*]], i64 [[TMP0]]
; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX5]], align 4
; CHECK-NEXT: [[CMP6:%.*]] = icmp eq i32 [[TMP2]], [[TMP3]]
; CHECK-NEXT: br i1 [[CMP6]], label [[SW_BB:%.*]], label [[SW_BB7:%.*]]
; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[CMP6]]
; CHECK-NEXT: br i1 [[COND_FR]], label [[SW_BB:%.*]], label [[SW_BB7:%.*]]
; CHECK: if.end:
; CHECK-NEXT: [[LOCAL_VAR_0:%.*]] = phi i32 [ [[TMP1]], [[FOR_BODY]] ]
; CHECK-NEXT: switch i32 [[LOCAL_VAR_0]], label [[SW_DEFAULT]] [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,40 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -S -passes='require<loops>,jump-threading,verify<loops>' < %s | FileCheck %s

%"type1" = type { i8 }
%"type2" = type opaque

define dso_local ptr @func2(ptr %this, ptr) {
; CHECK-LABEL: @func2(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[WHILE_COND:%.*]]
; CHECK: select.unfold:
; CHECK-NEXT: br label [[WHILE_COND]]
; CHECK: while.cond:
; CHECK-NEXT: [[MONTH_0:%.*]] = phi i32 [ undef, [[ENTRY:%.*]] ], [ [[CALL2:%.*]], [[FUNC1_EXIT:%.*]] ], [ [[ADD:%.*]], [[SELECT_UNFOLD:%.*]] ]
; CHECK-NEXT: switch i32 [[MONTH_0]], label [[IF_END_I:%.*]] [
; CHECK-NEXT: i32 4, label [[FUNC1_EXIT]]
; CHECK-NEXT: i32 1, label [[FUNC1_EXIT]]
; CHECK-LABEL: define dso_local ptr @func2(
; CHECK-SAME: ptr [[THIS:%.*]], ptr [[TMP0:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br label %[[WHILE_COND:.*]]
; CHECK: [[SELECT_UNFOLD:.*]]:
; CHECK-NEXT: br label %[[WHILE_COND]]
; CHECK: [[WHILE_COND]]:
; CHECK-NEXT: [[MONTH_0:%.*]] = phi i32 [ undef, %[[ENTRY]] ], [ [[CALL2:%.*]], %[[FUNC1_EXIT:.*]] ], [ [[ADD:%.*]], %[[SELECT_UNFOLD]] ]
; CHECK-NEXT: switch i32 [[MONTH_0]], label %[[IF_END_I:.*]] [
; CHECK-NEXT: i32 4, label %[[FUNC1_EXIT]]
; CHECK-NEXT: i32 1, label %[[FUNC1_EXIT]]
; CHECK-NEXT: ]
; CHECK: if.end.i:
; CHECK-NEXT: br label [[FUNC1_EXIT]]
; CHECK: func1.exit:
; CHECK-NEXT: [[RETVAL_0_I:%.*]] = phi i32 [ 9, [[IF_END_I]] ], [ 0, [[WHILE_COND]] ], [ 0, [[WHILE_COND]] ]
; CHECK: [[IF_END_I]]:
; CHECK-NEXT: br label %[[FUNC1_EXIT]]
; CHECK: [[FUNC1_EXIT]]:
; CHECK-NEXT: [[RETVAL_0_I:%.*]] = phi i32 [ 9, %[[IF_END_I]] ], [ 0, %[[WHILE_COND]] ], [ 0, %[[WHILE_COND]] ]
; CHECK-NEXT: [[CALL2]] = tail call signext i32 @func3(i32 signext [[RETVAL_0_I]], i32 signext 1, i32 signext 3)
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[CALL2]], 1
; CHECK-NEXT: [[ADD]] = add nsw i32 [[CALL2]], 2
; CHECK-NEXT: br i1 [[CMP]], label [[SELECT_UNFOLD]], label [[WHILE_COND]]
; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[CMP]]
; CHECK-NEXT: br i1 [[COND_FR]], label %[[SELECT_UNFOLD]], label %[[WHILE_COND]]
;
entry:
br label %while.cond

while.cond: ; preds = %func1.exit, %entry
%month.0 = phi i32 [ undef, %entry ], [ %month.0.be, %func1.exit ]
switch i32 %month.0, label %if.end.i [
i32 4, label %func1.exit
i32 1, label %func1.exit
i32 4, label %func1.exit
i32 1, label %func1.exit
]

if.end.i: ; preds = %while.cond
Expand All @@ -49,3 +51,82 @@ func1.exit: ; preds = %if.end.i, %while.cond, %while.cond

declare i32 @func3(i32, i32, i32)

declare void @sink(i32)

; Check that unfoldSelectInstr freezes a possibly-undef-or-poison condition
; before using it as a branch input. A select with an undef or poison condition
; does not immediately cause UB, but branching on undef or poison does.
define void @unfold_select_poison_cond(i1 %c, i1 %maybe_poison) {
; CHECK-LABEL: define void @unfold_select_poison_cond(
; CHECK-SAME: i1 [[C:%.*]], i1 [[MAYBE_POISON:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br i1 [[C]], label %[[PRED1:.*]], label %[[IF_ELSE:.*]]
; CHECK: [[PRED1]]:
; CHECK-NEXT: [[COND_FR:%.*]] = freeze i1 [[MAYBE_POISON]]
; CHECK-NEXT: br i1 [[COND_FR]], label %[[IF_THEN:.*]], label %[[IF_ELSE]]
; CHECK: [[IF_THEN]]:
; CHECK-NEXT: call void @sink(i32 1)
; CHECK-NEXT: ret void
; CHECK: [[IF_ELSE]]:
; CHECK-NEXT: call void @sink(i32 2)
; CHECK-NEXT: ret void
;
entry:
br i1 %c, label %pred1, label %pred2

pred1:
%s = select i1 %maybe_poison, i32 0, i32 10
br label %merge

pred2:
br label %merge

merge:
%p = phi i32 [ %s, %pred1 ], [ 5, %pred2 ]
%cmp = icmp eq i32 %p, 0
%fcmp = freeze i1 %cmp
br i1 %fcmp, label %if_then, label %if_else

if_then:
call void @sink(i32 1)
ret void

if_else:
call void @sink(i32 2)
ret void
}

define void @unfold_select_poison_cond_2(i1 %c) {
; CHECK-LABEL: define void @unfold_select_poison_cond_2(
; CHECK-SAME: i1 [[C:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ 0, %[[LATCH:.*]] ]
; CHECK-NEXT: br i1 [[C]], label %[[LATCH]], label %[[LATCH]]
; CHECK: [[LATCH]]:
; CHECK-NEXT: br label %[[LOOP]]
;
entry:
br label %loop

loop:
%iv = phi i64 [ 0, %entry ], [ %iv.next, %latch ]
br i1 %c, label %merge, label %if.then

if.then:
%cmp = icmp eq i64 %iv, 0
%sel = select i1 %cmp, i32 0, i32 0
br label %merge

merge:
%iv.next = phi i64 [ 0, %if.then ], [ 0, %loop ]
%val = phi i32 [ %sel, %if.then ], [ 0, %loop ]
switch i32 %val, label %latch [
i32 0, label %latch
i32 6, label %latch
]

latch:
br label %loop
}
Loading