return nullptr;
}
+static Instruction *foldSelectToPhi(SelectInst &Sel, const DominatorTree &DT,
+ InstCombiner::BuilderTy &Builder) {
+ // Find the block's immediate dominator that ends with a conditional branch
+ // that matches select's condition (maybe inverted).
+ BasicBlock *BB = Sel.getParent();
+ auto *IDomNode = DT[BB]->getIDom();
+ if (!IDomNode)
+ return nullptr;
+ BasicBlock *IDom = IDomNode->getBlock();
+
+ Value *Cond = Sel.getCondition();
+ Value *IfTrue, *IfFalse;
+ BasicBlock *TrueSucc, *FalseSucc;
+ if (match(IDom->getTerminator(),
+ m_Br(m_Specific(Cond), m_BasicBlock(TrueSucc),
+ m_BasicBlock(FalseSucc)))) {
+ IfTrue = Sel.getTrueValue();
+ IfFalse = Sel.getFalseValue();
+ } else if (match(IDom->getTerminator(),
+ m_Br(m_Not(m_Specific(Cond)), m_BasicBlock(TrueSucc),
+ m_BasicBlock(FalseSucc)))) {
+ IfTrue = Sel.getFalseValue();
+ IfFalse = Sel.getTrueValue();
+ } else
+ return nullptr;
+
+ BasicBlockEdge TrueEdge(IDom, TrueSucc);
+ BasicBlockEdge FalseEdge(IDom, FalseSucc);
+ DenseMap<BasicBlock *, Value *> Inputs;
+ for (auto *Pred : predecessors(BB)) {
+ // Check implication.
+ BasicBlockEdge Incoming(Pred, BB);
+ if (DT.dominates(TrueEdge, Incoming))
+ Inputs[Pred] = IfTrue;
+ else if (DT.dominates(FalseEdge, Incoming))
+ Inputs[Pred] = IfFalse;
+ else
+ return nullptr;
+ // Check availability.
+ if (auto *Insn = dyn_cast<Instruction>(Inputs[Pred]))
+ if (!DT.dominates(Insn, Pred->getTerminator()))
+ return nullptr;
+ }
+
+ Builder.SetInsertPoint(&*BB->begin());
+ auto *PN = Builder.CreatePHI(Sel.getType(), Inputs.size());
+ for (auto *Pred : predecessors(BB))
+ PN->addIncoming(Inputs[Pred], Pred);
+ PN->takeName(&Sel);
+ return PN;
+}
+
Instruction *InstCombiner::visitSelectInst(SelectInst &SI) {
Value *CondVal = SI.getCondition();
Value *TrueVal = SI.getTrueValue();
if (Instruction *Copysign = foldSelectToCopysign(SI, Builder))
return Copysign;
+ if (Instruction *PN = foldSelectToPhi(SI, DT, Builder))
+ return replaceInstUsesWith(SI, PN);
+
return nullptr;
}
; CHECK: if.false:
; CHECK-NEXT: br label [[MERGE]]
; CHECK: merge:
-; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 [[X:%.*]], i32 [[Y:%.*]]
+; CHECK-NEXT: [[S:%.*]] = phi i32 [ [[Y:%.*]], [[IF_FALSE]] ], [ [[X:%.*]], [[IF_TRUE]] ]
; CHECK-NEXT: ret i32 [[S]]
;
entry:
ret i32 %s
}
+define i32 @select_dominating_inverted(i1 %cond, i32 %x, i32 %y) {
+; CHECK-LABEL: @select_dominating_inverted(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_FALSE:%.*]], label [[IF_TRUE:%.*]]
+; CHECK: if.true:
+; CHECK-NEXT: br label [[MERGE:%.*]]
+; CHECK: if.false:
+; CHECK-NEXT: br label [[MERGE]]
+; CHECK: merge:
+; CHECK-NEXT: [[S:%.*]] = phi i32 [ [[X:%.*]], [[IF_FALSE]] ], [ [[Y:%.*]], [[IF_TRUE]] ]
+; CHECK-NEXT: ret i32 [[S]]
+;
+entry:
+ %inverted = xor i1 %cond, 1
+ br i1 %inverted, label %if.true, label %if.false
+
+if.true:
+ br label %merge
+
+if.false:
+ br label %merge
+
+merge:
+ %s = select i1 %cond, i32 %x, i32 %y
+ ret i32 %s
+}
+
; More complex CFG: the block with select has multiple predecessors.
define i32 @select_dominating_cond_multiple_preds(i1 %cond, i1 %cond2, i1 %cond3, i32 %x, i32 %y) {
; CHECK-LABEL: @select_dominating_cond_multiple_preds(
; CHECK: if.false.1:
; CHECK-NEXT: br label [[MERGE]]
; CHECK: merge:
-; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 [[X:%.*]], i32 [[Y:%.*]]
+; CHECK-NEXT: [[S:%.*]] = phi i32 [ [[Y:%.*]], [[IF_FALSE_1]] ], [ [[X:%.*]], [[IF_TRUE_2]] ], [ [[X]], [[IF_TRUE_1]] ]
; CHECK-NEXT: ret i32 [[S]]
; CHECK: exit:
; CHECK-NEXT: ret i32 0
; CHECK: if.false.1:
; CHECK-NEXT: br label [[MERGE]]
; CHECK: merge:
-; CHECK-NEXT: [[S:%.*]] = select i1 [[COND]], i32 [[X:%.*]], i32 [[Y:%.*]]
+; CHECK-NEXT: [[S:%.*]] = phi i32 [ [[X:%.*]], [[IF_FALSE_1]] ], [ [[Y:%.*]], [[IF_TRUE_2]] ], [ [[Y]], [[IF_TRUE_1]] ]
; CHECK-NEXT: ret i32 [[S]]
; CHECK: exit:
; CHECK-NEXT: ret i32 0
ret i32 0
}
+; More complex CFG for inverted case: the block with select has multiple predecessors that can duplicate.
+define i32 @select_dominating_cond_inverted_multiple_duplicating_preds(i1 %cond, i32 %cond2, i1 %cond3, i32 %x, i32 %y) {
+; CHECK-LABEL: @select_dominating_cond_inverted_multiple_duplicating_preds(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_FALSE:%.*]], label [[IF_TRUE:%.*]]
+; CHECK: if.true:
+; CHECK-NEXT: switch i32 [[COND2:%.*]], label [[SWITCH_CASE_1:%.*]] [
+; CHECK-NEXT: i32 1, label [[MERGE:%.*]]
+; CHECK-NEXT: i32 2, label [[MERGE]]
+; CHECK-NEXT: i32 3, label [[MERGE]]
+; CHECK-NEXT: ]
+; CHECK: switch.case.1:
+; CHECK-NEXT: br label [[MERGE]]
+; CHECK: if.false:
+; CHECK-NEXT: br i1 [[COND3:%.*]], label [[IF_FALSE_1:%.*]], label [[EXIT:%.*]]
+; CHECK: if.false.1:
+; CHECK-NEXT: br label [[MERGE]]
+; CHECK: merge:
+; CHECK-NEXT: [[S:%.*]] = phi i32 [ [[X:%.*]], [[IF_FALSE_1]] ], [ [[Y:%.*]], [[SWITCH_CASE_1]] ], [ [[Y]], [[IF_TRUE]] ], [ [[Y]], [[IF_TRUE]] ], [ [[Y]], [[IF_TRUE]] ]
+; CHECK-NEXT: ret i32 [[S]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %inverted = xor i1 %cond, 1
+ br i1 %inverted, label %if.true, label %if.false
+
+if.true:
+ switch i32 %cond2, label %switch.case.1 [
+ i32 1, label %merge
+ i32 2, label %merge
+ i32 3, label %merge
+ ]
+
+switch.case.1:
+ br label %merge
+
+if.false:
+ br i1 %cond3, label %if.false.1, label %exit
+
+if.false.1:
+ br label %merge
+
+merge:
+ %s = select i1 %cond, i32 %x, i32 %y
+ ret i32 %s
+
+exit:
+ ret i32 0
+}
+
; Negative test: currently we take condition from IDom, but might be willing to expand it in the future.
define i32 @select_not_imm_dominating_cond_neg(i1 %cond, i32 %x, i32 %y) {
; CHECK-LABEL: @select_not_imm_dominating_cond_neg(
; CHECK: if.false.3:
; CHECK-NEXT: br label [[MERGE_3]]
; CHECK: merge.3:
-; CHECK-NEXT: [[S_2:%.*]] = select i1 [[COND]], i32 [[X:%.*]], i32 [[Y:%.*]]
-; CHECK-NEXT: [[S_1:%.*]] = select i1 [[COND]], i32 [[X]], i32 [[Y]]
-; CHECK-NEXT: [[S_3:%.*]] = select i1 [[COND]], i32 [[X]], i32 [[Y]]
+; CHECK-NEXT: [[S_3:%.*]] = phi i32 [ [[Y:%.*]], [[IF_FALSE_3]] ], [ [[X:%.*]], [[IF_TRUE_3]] ]
+; CHECK-NEXT: [[S_2:%.*]] = phi i32 [ [[Y]], [[IF_FALSE_3]] ], [ [[X]], [[IF_TRUE_3]] ]
+; CHECK-NEXT: [[S_1:%.*]] = phi i32 [ [[Y]], [[IF_FALSE_3]] ], [ [[X]], [[IF_TRUE_3]] ]
; CHECK-NEXT: [[SUM_1:%.*]] = add i32 [[S_1]], [[S_2]]
; CHECK-NEXT: [[SUM_2:%.*]] = add i32 [[SUM_1]], [[S_3]]
; CHECK-NEXT: ret i32 [[SUM_2]]
%s = select i1 %cond, i32 %a, i32 %phi
ret i32 %s
}
+
+declare i32 @__gxx_personality_v0(...)
+declare i1 @foo()
+
+define i32 @test_invoke_neg(i32 %x, i32 %y) nounwind uwtable ssp personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
+; CHECK-LABEL: @test_invoke_neg(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[COND:%.*]] = invoke i1 @foo()
+; CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]]
+; CHECK: invoke.cont:
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 [[X:%.*]], i32 [[Y:%.*]]
+; CHECK-NEXT: ret i32 [[SEL]]
+; CHECK: lpad:
+; CHECK-NEXT: [[LP:%.*]] = landingpad { i1, i32 }
+; CHECK-NEXT: filter [0 x i1] zeroinitializer
+; CHECK-NEXT: unreachable
+;
+entry:
+ %cond = invoke i1 @foo()
+ to label %invoke.cont unwind label %lpad
+
+invoke.cont:
+ %sel = select i1 %cond, i32 %x, i32 %y
+ ret i32 %sel
+
+lpad:
+ %lp = landingpad { i1, i32 }
+ filter [0 x i1] zeroinitializer
+ unreachable
+}
+
+declare i32 @bar()
+
+define i32 @test_invoke_2_neg(i1 %cond, i32 %x, i32 %y) nounwind uwtable ssp personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
+; CHECK-LABEL: @test_invoke_2_neg(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
+; CHECK: if.true:
+; CHECK-NEXT: br label [[MERGE:%.*]]
+; CHECK: if.false:
+; CHECK-NEXT: [[RESULT:%.*]] = invoke i32 @bar()
+; CHECK-NEXT: to label [[MERGE]] unwind label [[LPAD:%.*]]
+; CHECK: merge:
+; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 0, [[IF_TRUE]] ], [ [[RESULT]], [[IF_FALSE]] ]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], i32 1, i32 [[PHI]]
+; CHECK-NEXT: ret i32 [[SEL]]
+; CHECK: lpad:
+; CHECK-NEXT: [[LP:%.*]] = landingpad { i1, i32 }
+; CHECK-NEXT: filter [0 x i1] zeroinitializer
+; CHECK-NEXT: unreachable
+;
+entry:
+ br i1 %cond, label %if.true, label %if.false
+
+if.true:
+ br label %merge
+
+if.false:
+ %result = invoke i32 @bar()
+ to label %merge unwind label %lpad
+
+merge:
+ %phi = phi i32 [ 0, %if.true ], [ %result, %if.false ]
+ %sel = select i1 %cond, i32 1, i32 %phi
+ ret i32 %sel
+
+lpad:
+ %lp = landingpad { i1, i32 }
+ filter [0 x i1] zeroinitializer
+ unreachable
+}