diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp @@ -1135,21 +1135,39 @@ // Shift Lo up to occupy the upper bits of the promoted type. SDLoc DL(N); EVT VT = Lo.getValueType(); - Lo = DAG.getNode(ISD::SHL, DL, VT, Lo, - DAG.getConstant(NewBits - OldBits, DL, VT)); + unsigned Opcode = N->getOpcode(); + bool IsFSHR = Opcode == ISD::FSHR; // Amount has to be interpreted modulo the old bit width. Amount = DAG.getNode(ISD::UREM, DL, VT, Amount, DAG.getConstant(OldBits, DL, VT)); - unsigned Opcode = N->getOpcode(); - if (Opcode == ISD::FSHR) { - // Increase Amount to shift the result into the lower bits of the promoted - // type. - Amount = DAG.getNode(ISD::ADD, DL, VT, Amount, - DAG.getConstant(NewBits - OldBits, DL, VT)); + // If the promoted type is twice the size (or more), then we use the + // traditional funnel 'double' shift codegen. This isn't necessary if the + // shift amount is constant. + // fshl(x,y,z) -> (((aext(x) << bw) | zext(y)) << (z % bw)) >> bw. + // fshr(x,y,z) -> (((aext(x) << bw) | zext(y)) >> (z % bw)). + if (NewBits >= (2 * OldBits) && !isa(Amount) && + !TLI.isOperationLegalOrCustom(Opcode, VT)) { + APInt Mask = APInt::getLowBitsSet(NewBits, OldBits); + SDValue HiShift = DAG.getConstant(OldBits, DL, VT); + Hi = DAG.getNode(ISD::SHL, DL, VT, Hi, HiShift); + Lo = DAG.getNode(ISD::AND, DL, VT, Lo, DAG.getConstant(Mask, DL, VT)); + SDValue Res = DAG.getNode(ISD::OR, DL, VT, Hi, Lo); + Res = DAG.getNode(IsFSHR ? ISD::SRL : ISD::SHL, DL, VT, Res, Amount); + if (!IsFSHR) + Res = DAG.getNode(ISD::SRL, DL, VT, Res, HiShift); + return Res; } + SDValue ShiftOffset = DAG.getConstant(NewBits - OldBits, DL, VT); + Lo = DAG.getNode(ISD::SHL, DL, VT, Lo, ShiftOffset); + + // Increase Amount to shift the result into the lower bits of the promoted + // type. + if (IsFSHR) + Amount = DAG.getNode(ISD::ADD, DL, VT, Amount, ShiftOffset); + return DAG.getNode(Opcode, DL, VT, Hi, Lo, Amount); } diff --git a/llvm/test/CodeGen/RISCV/rv64Zbt.ll b/llvm/test/CodeGen/RISCV/rv64Zbt.ll --- a/llvm/test/CodeGen/RISCV/rv64Zbt.ll +++ b/llvm/test/CodeGen/RISCV/rv64Zbt.ll @@ -109,14 +109,13 @@ define signext i32 @fshl_i32(i32 signext %a, i32 signext %b, i32 signext %c) nounwind { ; RV64I-LABEL: fshl_i32: ; RV64I: # %bb.0: -; RV64I-NEXT: andi a2, a2, 31 -; RV64I-NEXT: sll a0, a0, a2 -; RV64I-NEXT: not a2, a2 +; RV64I-NEXT: slli a0, a0, 32 ; RV64I-NEXT: slli a1, a1, 32 -; RV64I-NEXT: srli a1, a1, 1 -; RV64I-NEXT: srl a1, a1, a2 +; RV64I-NEXT: srli a1, a1, 32 ; RV64I-NEXT: or a0, a0, a1 -; RV64I-NEXT: sext.w a0, a0 +; RV64I-NEXT: andi a1, a2, 31 +; RV64I-NEXT: sll a0, a0, a1 +; RV64I-NEXT: srai a0, a0, 32 ; RV64I-NEXT: ret ; ; RV64IB-LABEL: fshl_i32: @@ -162,14 +161,12 @@ define signext i32 @fshr_i32(i32 signext %a, i32 signext %b, i32 signext %c) nounwind { ; RV64I-LABEL: fshr_i32: ; RV64I: # %bb.0: +; RV64I-NEXT: slli a0, a0, 32 ; RV64I-NEXT: slli a1, a1, 32 -; RV64I-NEXT: andi a2, a2, 31 -; RV64I-NEXT: ori a3, a2, 32 -; RV64I-NEXT: srl a1, a1, a3 -; RV64I-NEXT: slli a0, a0, 1 -; RV64I-NEXT: xori a2, a2, 31 -; RV64I-NEXT: sll a0, a0, a2 +; RV64I-NEXT: srli a1, a1, 32 ; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: andi a1, a2, 31 +; RV64I-NEXT: srl a0, a0, a1 ; RV64I-NEXT: sext.w a0, a0 ; RV64I-NEXT: ret ;