Index: llvm/lib/Target/RISCV/RISCVISelLowering.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -5477,6 +5477,65 @@ return SDValue(); } +// Transform `binOp (select cond, x, c0), c1` where `c0` and `c1` are constants +// into `select cond, binOp(x, c1), binOp(c0, c1)` if profitable. +// For now we only consider transformation profitable if `binOp(c0, c1)` ends up +// being `0` or `-1`. In such cases we can replace `select` with `and`. +// TODO: Should we also do this if `binOp(c0, c1)` is cheaper to materialize +// then `c0`? +static SDValue +foldBinOpIntoSelectIfProfitable(SDNode *BO, SelectionDAG &DAG, + const RISCVSubtarget &Subtarget) { + if (Subtarget.hasShortForwardBranchOpt()) + return SDValue(); + + unsigned SelOpNo = 0; + SDValue Sel = BO->getOperand(0); + if (Sel.getOpcode() != ISD::SELECT || !Sel.hasOneUse()) { + SelOpNo = 1; + Sel = BO->getOperand(1); + } + + if (Sel.getOpcode() != ISD::SELECT || !Sel.hasOneUse()) + return SDValue(); + + unsigned ConstSelOpNo = 1; + unsigned OtherSelOpNo = 2; + SDValue ConstSelOp = Sel->getOperand(1); + ConstantSDNode *ConstSelOpNode = dyn_cast(ConstSelOp); + if (!ConstSelOpNode) { + ConstSelOpNo = 2; + OtherSelOpNo = 1; + ConstSelOp = Sel->getOperand(2); + ConstSelOpNode = dyn_cast(ConstSelOp); + } + if (!ConstSelOpNode) + return SDValue(); + if (!ConstSelOpNode || ConstSelOpNode->isOpaque()) + return SDValue(); + + SDValue ConstBinOp = BO->getOperand(SelOpNo ^ 1); + ConstantSDNode *ConstBinOpNode = dyn_cast(ConstBinOp); + if (!ConstBinOpNode || ConstBinOpNode->isOpaque()) + return SDValue(); + + SDLoc DL(Sel); + EVT VT = BO->getValueType(0); + + SDValue NewConstOp = DAG.FoldConstantArithmetic(BO->getOpcode(), DL, VT, + {ConstBinOp, ConstSelOp}); + APInt NewConstAPInt = dyn_cast(NewConstOp)->getAPIntValue(); + if (!NewConstAPInt.isZero() && !NewConstAPInt.isAllOnes()) + return SDValue(); + + SDValue NewNonConstOp = DAG.getNode( + BO->getOpcode(), DL, VT, Sel->getOperand(OtherSelOpNo), ConstBinOp); + NewNonConstOp.dump(); + SDValue NewT = (ConstSelOpNo == 1) ? NewConstOp : NewNonConstOp; + SDValue NewF = (ConstSelOpNo == 1) ? NewNonConstOp : NewConstOp; + return DAG.getSelect(DL, VT, Sel.getOperand(0), NewT, NewF); +} + SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { SDValue CondV = Op.getOperand(0); SDValue TrueV = Op.getOperand(1); @@ -5492,6 +5551,15 @@ return DAG.getNode(ISD::VSELECT, DL, VT, CondSplat, TrueV, FalseV); } + if (Op.hasOneUse() && isBinOp(Op->use_begin()->getOpcode())) { + SDNode *BinOp = *Op->use_begin(); + if (SDValue NewSel = + foldBinOpIntoSelectIfProfitable(*Op->use_begin(), DAG, Subtarget)) { + DAG.ReplaceAllUsesWith(BinOp, &NewSel); + return lowerSELECT(NewSel, DAG); + } + } + if (SDValue V = combineSelectToBinOp(Op.getNode(), DAG, Subtarget)) return V; Index: llvm/test/CodeGen/RISCV/fold-binop-into-select.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/RISCV/fold-binop-into-select.ll @@ -0,0 +1,31 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -mtriple=riscv64 < %s | FileCheck %s + +define i64 @select_add(i1 %c, i64 %x) { +; CHECK-LABEL: select_add: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: andi a0, a0, 1 +; CHECK-NEXT: addi a1, a1, 2 +; CHECK-NEXT: addi a0, a0, -1 +; CHECK-NEXT: and a0, a0, a1 +; CHECK-NEXT: ret +entry: + %select_ = select i1 %c, i64 -2, i64 %x + %res = add i64 %select_, 2 + ret i64 %res +} + +define i64 @select_and(i1 %c, i64 %x) { +; CHECK-LABEL: select_and: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: andi a0, a0, 1 +; CHECK-NEXT: addiw a0, a0, -1 +; CHECK-NEXT: and a0, a1, a0 +; CHECK-NEXT: andi a0, a0, 64 +; CHECK-NEXT: ret +entry: + %select_ = select i1 %c, i64 63, i64 %x + %res = and i64 %select_, 64 + ret i64 %res +} +