diff --git a/llvm/lib/Target/Mips/MicroMipsInstrFPU.td b/llvm/lib/Target/Mips/MicroMipsInstrFPU.td index d5fc30cef695c..67d4c82a32ef4 100644 --- a/llvm/lib/Target/Mips/MicroMipsInstrFPU.td +++ b/llvm/lib/Target/Mips/MicroMipsInstrFPU.td @@ -445,6 +445,8 @@ def : MipsPat<(MipsTruncIntFP FGR64Opnd:$src), FGR_64; def : MipsPat<(MipsTruncIntFP FGR32Opnd:$src), (TRUNC_W_S_MM FGR32Opnd:$src)>, ISA_MICROMIPS32_NOT_MIPS32R6; +def PseudoReadFCSR_MM : ReadFCSRImpl<[FCR31]>; +def PseudoWriteFCSR_MM : WriteFCSRImpl<[FCR31]>; // Selects defm : MovzPats0, diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp index eec908e453724..81f7f46ed0347 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp @@ -289,6 +289,8 @@ const char *MipsTargetLowering::getTargetNodeName(unsigned Opcode) const { case MipsISD::PCKEV: return "MipsISD::PCKEV"; case MipsISD::PCKOD: return "MipsISD::PCKOD"; case MipsISD::INSVE: return "MipsISD::INSVE"; + case MipsISD::ReadFCSR: return "MipsISD::ReadFCSR"; + case MipsISD::WriteFCSR: return "MipsISD::WriteFCSR"; } return nullptr; } @@ -358,6 +360,8 @@ MipsTargetLowering::MipsTargetLowering(const MipsTargetMachine &TM, setOperationAction(ISD::FP_TO_SINT, MVT::i32, Custom); setOperationAction(ISD::STRICT_FP_TO_SINT, MVT::i32, Custom); setOperationAction(ISD::STRICT_FP_TO_UINT, MVT::i32, Custom); + setOperationAction(ISD::GET_ROUNDING, MVT::i32, Custom); + setOperationAction(ISD::SET_ROUNDING, MVT::Other, Custom); setOperationAction(ISD::STRICT_FSETCC, MVT::f32, Custom); setOperationAction(ISD::STRICT_FSETCCS, MVT::f32, Custom); @@ -1372,6 +1376,8 @@ LowerOperation(SDValue Op, SelectionDAG &DAG) const case ISD::STRICT_FP_TO_UINT: return lowerSTRICT_FP_TO_INT(Op, DAG); case ISD::FP_TO_SINT: return lowerFP_TO_SINT(Op, DAG); + case ISD::GET_ROUNDING: return lowerGET_ROUNDING(Op, DAG); + case ISD::SET_ROUNDING: return lowerSET_ROUNDING(Op, DAG); case ISD::READCYCLECOUNTER: return lowerREADCYCLECOUNTER(Op, DAG); } @@ -3011,6 +3017,68 @@ static SDValue lowerFP_TO_SINT_STORE(StoreSDNode *SD, SelectionDAG &DAG, SD->getMemOperand()->getFlags()); } +SDValue MipsTargetLowering::lowerGET_ROUNDING(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + SDValue Chain = Op.getOperand(0); + + // Use formula: + // ((FCSR & 0x3) ^ ((~FCSR & 0x3) >> 1)) + // to transform Mips rounding mode value stored in bits 1:0 of FCSR + // (FCR31) to LLVM rounding mode format: 1->0, 0->1, 2->2, 3->3. + SDValue FCSR = DAG.getNode(MipsISD::ReadFCSR, DL, {MVT::i32, MVT::Other}, Chain); + Chain = FCSR.getValue(1); + FCSR = FCSR.getValue(0); + + SDValue Expr1 = DAG.getNode(ISD::AND, DL, MVT::i32, + FCSR, DAG.getConstant(3, DL, MVT::i32)); + SDValue Expr2 = + DAG.getNode(ISD::SRL, DL, MVT::i32, + DAG.getNode(ISD::AND, DL, MVT::i32, + DAG.getNode(ISD::XOR, DL, MVT::i32, + FCSR, DAG.getConstant(3, DL, MVT::i32)), + DAG.getConstant(3, DL, MVT::i32)), + DAG.getConstant(1, DL, MVT::i32)); + + SDValue RM = DAG.getNode(ISD::XOR, DL, MVT::i32, Expr1, Expr2); + + return DAG.getMergeValues({RM, Chain}, DL); +} + +SDValue MipsTargetLowering::lowerSET_ROUNDING(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + SDValue Chain = Op->getOperand(0); + SDValue RMValue = Op->getOperand(1); + + // Use formula x ^ (~(x >> 1) & 1) to transform LLVM rounding mode to Mips + // FCSR (FCR31) format: 0->1, 1->0, 2->2, 3->3. + // + // It is expected that the argument of llvm.set.rounding is within the + // segment [0, 3], so NearestTiesToAway (4) is not handled here. It is + // responsibility of the code generated llvm.set.rounding to ensure this + // condition. + SDValue One = DAG.getConstant(1, DL, MVT::i32); + SDValue NewRM = DAG.getNode( + ISD::XOR, DL, MVT::i32, RMValue, + DAG.getNode(ISD::AND, DL, MVT::i32, + DAG.getNOT(DL, + DAG.getNode(ISD::SRL, DL, MVT::i32, RMValue, One), + MVT::i32), + One)); + + // Put calculated rounding mode into FCSR[1:0]: + // FCSR = (FCSR & FFFFFFFC) | NewRM + SDValue FCSR = DAG.getNode(MipsISD::ReadFCSR, DL, {MVT::i32, MVT::Other}, Chain); + Chain = FCSR.getValue(1); + FCSR = FCSR.getValue(0); + + FCSR = DAG.getNode(ISD::AND, DL, MVT::i32, FCSR, DAG.getConstant(0xFFFFFFFC, DL, MVT::i32)); + FCSR = DAG.getNode(ISD::OR, DL, MVT::i32, FCSR, NewRM); + + return DAG.getNode(MipsISD::WriteFCSR, DL, MVT::Other, Chain, FCSR); +} + SDValue MipsTargetLowering::lowerSTORE(SDValue Op, SelectionDAG &DAG) const { StoreSDNode *SD = cast(Op); EVT MemVT = SD->getMemoryVT(); diff --git a/llvm/lib/Target/Mips/MipsISelLowering.h b/llvm/lib/Target/Mips/MipsISelLowering.h index 7c489b3b268a4..4c396e971368a 100644 --- a/llvm/lib/Target/Mips/MipsISelLowering.h +++ b/llvm/lib/Target/Mips/MipsISelLowering.h @@ -106,6 +106,10 @@ class TargetRegisterClass; // Node used to generate an MTC1 i32 to f64 instruction MTC1_D64, + // Nodes used to access FCR31 (fcsr) + ReadFCSR, + WriteFCSR, + // Floating Point Conditional Moves CMovFP_T, CMovFP_F, @@ -573,6 +577,8 @@ class TargetRegisterClass; SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const; SDValue lowerSETCC(SDValue Op, SelectionDAG &DAG) const; SDValue lowerFSETCC(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerGET_ROUNDING(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerSET_ROUNDING(SDValue Op, SelectionDAG &DAG) const; SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const; SDValue lowerVAARG(SDValue Op, SelectionDAG &DAG) const; SDValue lowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/Mips/MipsInstrFPU.td b/llvm/lib/Target/Mips/MipsInstrFPU.td index c065ba6c9632e..f1f6359abc761 100644 --- a/llvm/lib/Target/Mips/MipsInstrFPU.td +++ b/llvm/lib/Target/Mips/MipsInstrFPU.td @@ -41,6 +41,9 @@ def SDT_MipsExtractElementF64 : SDTypeProfile<1, 2, [SDTCisVT<0, i32>, def SDT_MipsMTC1_D64 : SDTypeProfile<1, 1, [SDTCisVT<0, f64>, SDTCisVT<1, i32>]>; +def SDT_MipsReadFCSR : SDTypeProfile<1, 0, [SDTCisInt<0>]>; +def SDT_MipsWriteFCSR : SDTypeProfile<0, 1, [SDTCisInt<0>]>; + def MipsFPCmp : SDNode<"MipsISD::FPCmp", SDT_MipsFPCmp, [SDNPOutGlue]>; def MipsCMovFP_T : SDNode<"MipsISD::CMovFP_T", SDT_MipsCMovFP, [SDNPInGlue]>; def MipsCMovFP_F : SDNode<"MipsISD::CMovFP_F", SDT_MipsCMovFP, [SDNPInGlue]>; @@ -53,6 +56,10 @@ def MipsExtractElementF64 : SDNode<"MipsISD::ExtractElementF64", SDT_MipsExtractElementF64>; def MipsMTC1_D64 : SDNode<"MipsISD::MTC1_D64", SDT_MipsMTC1_D64>; +def MipsReadFCSR : SDNode<"MipsISD::ReadFCSR", SDT_MipsReadFCSR, + [SDNPHasChain]>; +def MipsWriteFCSR : SDNode<"MipsISD::WriteFCSR", SDT_MipsWriteFCSR, + [SDNPHasChain, SDNPSideEffect]>; // Operand for printing out a condition code. let PrintMethod = "printFCCOperand", DecoderMethod = "DecodeCondCode" in @@ -787,6 +794,27 @@ class ExtractElementF64Base : def ExtractElementF64 : ExtractElementF64Base, FGR_32, HARDFLOAT; def ExtractElementF64_64 : ExtractElementF64Base, FGR_64, HARDFLOAT; +class ReadFCSRImpl Regs> : + PseudoSE<(outs GPR32Opnd:$rd), (ins), + [(set GPR32Opnd:$rd, (MipsReadFCSR))]> { + let Uses = Regs; + let hasSideEffects = 0; + let hasNoSchedulingInfo = 1; +} + +class WriteFCSRImpl Regs> : + PseudoSE<(outs), (ins GPR32Opnd:$val), + [(MipsWriteFCSR GPR32Opnd:$val)]> { + let Defs = Regs; + let hasSideEffects = 1; + let hasNoSchedulingInfo = 1; +} + +let AdditionalPredicates = [NotInMicroMips] in { + def PseudoReadFCSR : ReadFCSRImpl<[FCR31]>; + def PseudoWriteFCSR : WriteFCSRImpl<[FCR31]>; +} + def PseudoTRUNC_W_S : MipsAsmPseudoInst<(outs FGR32Opnd:$fd), (ins FGR32Opnd:$fs, GPR32Opnd:$rs), "trunc.w.s\t$fd, $fs, $rs">; diff --git a/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp b/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp index a1d0aa089c089..a5df5201c2efd 100644 --- a/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp +++ b/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp @@ -444,6 +444,18 @@ bool MipsSEInstrInfo::expandPostRAPseudo(MachineInstr &MI) const { case Mips::MIPSeh_return64: expandEhReturn(MBB, MI); break; + case Mips::PseudoReadFCSR: + expandReadFCSR(MBB, MI, Mips::CFC1); + break; + case Mips::PseudoWriteFCSR: + expandWriteFCSR(MBB, MI, Mips::CTC1); + break; + case Mips::PseudoReadFCSR_MM: + expandReadFCSR(MBB, MI, Mips::CFC1_MM); + break; + case Mips::PseudoWriteFCSR_MM: + expandWriteFCSR(MBB, MI, Mips::CTC1_MM); + break; } MBB.erase(MI); @@ -877,6 +889,23 @@ void MipsSEInstrInfo::expandEhReturn(MachineBasicBlock &MBB, expandRetRA(MBB, I); } +void MipsSEInstrInfo::expandReadFCSR(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned Opc) const { + Register Dst = I->getOperand(0).getReg(); + BuildMI(MBB, I, I->getDebugLoc(), get(Opc), Dst) + .addImm(31); +} + +void MipsSEInstrInfo::expandWriteFCSR(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned Opc) const { + Register Src = I->getOperand(0).getReg(); + BuildMI(MBB, I, I->getDebugLoc(), get(Opc)) + .addImm(31) + .addReg(Src); +} + const MipsInstrInfo *llvm::createMipsSEInstrInfo(const MipsSubtarget &STI) { return new MipsSEInstrInfo(STI); } diff --git a/llvm/lib/Target/Mips/MipsSEInstrInfo.h b/llvm/lib/Target/Mips/MipsSEInstrInfo.h index 5c48ccdc27f02..a9fe9bf36ac1a 100644 --- a/llvm/lib/Target/Mips/MipsSEInstrInfo.h +++ b/llvm/lib/Target/Mips/MipsSEInstrInfo.h @@ -120,6 +120,10 @@ class MipsSEInstrInfo : public MipsInstrInfo { bool FP64) const; void expandEhReturn(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const; + void expandWriteFCSR(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, unsigned Opc) const; + void expandReadFCSR(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, unsigned Opc) const; }; } diff --git a/llvm/test/CodeGen/Mips/frounds.ll b/llvm/test/CodeGen/Mips/frounds.ll new file mode 100644 index 0000000000000..06e60fb3dce20 --- /dev/null +++ b/llvm/test/CodeGen/Mips/frounds.ll @@ -0,0 +1,58 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 +; RUN: llc -mtriple=mips -mcpu=mips32r2 < %s | FileCheck %s +; RUN: llc -mtriple=mips -mattr=+micromips < %s | FileCheck %s --check-prefix=CHECK-MM + +define i32 @get_rounding() { +; CHECK-LABEL: get_rounding: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: cfc1 $1, 31 +; CHECK-NEXT: andi $2, $1, 3 +; CHECK-NEXT: not $1, $1 +; CHECK-NEXT: andi $1, $1, 2 +; CHECK-NEXT: srl $1, $1, 1 +; CHECK-NEXT: jr $ra +; CHECK-NEXT: xor $2, $2, $1 +; +; CHECK-MM-LABEL: get_rounding: +; CHECK-MM: # %bb.0: # %entry +; CHECK-MM-NEXT: cfc1 $2, 31 +; CHECK-MM-NEXT: andi16 $3, $2, 3 +; CHECK-MM-NEXT: not16 $2, $2 +; CHECK-MM-NEXT: andi16 $2, $2, 2 +; CHECK-MM-NEXT: srl16 $2, $2, 1 +; CHECK-MM-NEXT: xor16 $2, $3 +; CHECK-MM-NEXT: jrc $ra +entry: + %0 = call i32 @llvm.get.rounding() + ret i32 %0 +} + +define void @set_rounding(i32 %a) { +; CHECK-LABEL: set_rounding: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addiu $1, $zero, -4 +; CHECK-NEXT: cfc1 $2, 31 +; CHECK-NEXT: and $1, $2, $1 +; CHECK-NEXT: andi $2, $4, 2 +; CHECK-NEXT: sltiu $2, $2, 1 +; CHECK-NEXT: xor $2, $4, $2 +; CHECK-NEXT: or $1, $1, $2 +; CHECK-NEXT: ctc1 $1, 31 +; CHECK-NEXT: jr $ra +; CHECK-NEXT: nop +; +; CHECK-MM-LABEL: set_rounding: +; CHECK-MM: # %bb.0: # %entry +; CHECK-MM-NEXT: addiu $2, $zero, -4 +; CHECK-MM-NEXT: cfc1 $3, 31 +; CHECK-MM-NEXT: and16 $3, $2 +; CHECK-MM-NEXT: andi16 $2, $4, 2 +; CHECK-MM-NEXT: sltiu $2, $2, 1 +; CHECK-MM-NEXT: xor16 $2, $4 +; CHECK-MM-NEXT: or16 $2, $3 +; CHECK-MM-NEXT: ctc1 $2, 31 +; CHECK-MM-NEXT: jrc $ra +entry: + call void @llvm.set.rounding(i32 %a) + ret void +}