@@ -924,18 +924,74 @@ LazyValueInfoImpl::solveBlockValueBinaryOpImpl(
924924 Instruction *I, BasicBlock *BB,
925925 std::function<ConstantRange(const ConstantRange &, const ConstantRange &)>
926926 OpFn) {
927+ Value *LHS = I->getOperand (0 );
928+ Value *RHS = I->getOperand (1 );
929+
930+ auto GetValueFromCondition =
931+ [&](Value *V, Value *Cond,
932+ bool CondIsTrue) -> std::optional<ConstantRange> {
933+ std::optional<ValueLatticeElement> OptVal = getBlockValue (V, BB, I);
934+ if (!OptVal)
935+ return std::nullopt ;
936+ return OptVal->asConstantRange (V->getType ());
937+ };
938+
939+ auto ThreadBinOpOverSelect =
940+ [&](Value *X, const ConstantRange &CRX, SelectInst *Y,
941+ bool XIsLHS) -> std::optional<ValueLatticeElement> {
942+ Value *Cond = Y->getCondition ();
943+ // Only handle selects with constant values.
944+ Constant *TrueC = dyn_cast<Constant>(Y->getTrueValue ());
945+ if (!TrueC)
946+ return std::nullopt ;
947+ Constant *FalseC = dyn_cast<Constant>(Y->getFalseValue ());
948+ if (!FalseC)
949+ return std::nullopt ;
950+ if (!isGuaranteedNotToBeUndef (Cond, AC))
951+ return std::nullopt ;
952+
953+ ConstantRange TrueX =
954+ CRX.intersectWith (getValueFromCondition (X, Cond, /* CondIsTrue=*/ true ,
955+ /* UseBlockValue=*/ false )
956+ ->asConstantRange (X->getType ()));
957+ ConstantRange FalseX =
958+ CRX.intersectWith (getValueFromCondition (X, Cond, /* CondIsTrue=*/ false ,
959+ /* UseBlockValue=*/ false )
960+ ->asConstantRange (X->getType ()));
961+ ConstantRange TrueY = TrueC->toConstantRange ();
962+ ConstantRange FalseY = FalseC->toConstantRange ();
963+
964+ if (XIsLHS)
965+ return ValueLatticeElement::getRange (
966+ OpFn (TrueX, TrueY).unionWith (OpFn (FalseX, FalseY)));
967+ return ValueLatticeElement::getRange (
968+ OpFn (TrueY, TrueX).unionWith (OpFn (FalseY, FalseX)));
969+ };
970+
927971 // Figure out the ranges of the operands. If that fails, use a
928972 // conservative range, but apply the transfer rule anyways. This
929973 // lets us pick up facts from expressions like "and i32 (call i32
930974 // @foo()), 32"
931- std::optional<ConstantRange> LHSRes = getRangeFor (I-> getOperand ( 0 ) , I, BB);
975+ std::optional<ConstantRange> LHSRes = getRangeFor (LHS , I, BB);
932976 if (!LHSRes)
933977 return std::nullopt ;
934978
935- std::optional<ConstantRange> RHSRes = getRangeFor (I->getOperand (1 ), I, BB);
979+ // Try to thread binop over rhs select
980+ if (auto *SI = dyn_cast<SelectInst>(RHS)) {
981+ if (auto Res = ThreadBinOpOverSelect (LHS, *LHSRes, SI, /* XIsLHS=*/ true ))
982+ return *Res;
983+ }
984+
985+ std::optional<ConstantRange> RHSRes = getRangeFor (RHS, I, BB);
936986 if (!RHSRes)
937987 return std::nullopt ;
938988
989+ // Try to thread binop over lhs select
990+ if (auto *SI = dyn_cast<SelectInst>(LHS)) {
991+ if (auto Res = ThreadBinOpOverSelect (RHS, *RHSRes, SI, /* XIsLHS=*/ false ))
992+ return *Res;
993+ }
994+
939995 const ConstantRange &LHSRange = *LHSRes;
940996 const ConstantRange &RHSRange = *RHSRes;
941997 return ValueLatticeElement::getRange (OpFn (LHSRange, RHSRange));
0 commit comments