diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp --- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -605,6 +605,15 @@ VK == RISCVMCExpr::VK_RISCV_None; } + bool isUImm12() const { + if (!isImm()) + return false; + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); + return IsConstantImm && isUInt<12>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; + } + bool isSImm12() const { RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; int64_t Imm; diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h --- a/llvm/lib/Target/RISCV/RISCVISelLowering.h +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h @@ -219,6 +219,22 @@ VSEXT_VL, VZEXT_VL, + // Reads value of CSR. + // The first operand is a chain pointer. The second specifies address of the + // required CSR. Two results are produced, the read value and the new chain + // pointer. + READ_CSR, + // Write value to CSR. + // The first operand is a chain pointer, the second specifies address of the + // required CSR and the third is the value to write. The result is the new + // chain pointer. + WRITE_CSR, + // Read-Modify-Write value of CSR. + // The first operand is a chain pointer, the second specifies address of the + // required CSR and the third is the value to write. Two results are produced, + // the value read before the modification and the new chain pointer. + SWAP_CSR, + // Memory opcodes start here. VLE_VL = ISD::FIRST_TARGET_MEMORY_OPCODE, VSE_VL, diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -6610,6 +6610,9 @@ NODE_NAME_CASE(VZEXT_VL) NODE_NAME_CASE(VLE_VL) NODE_NAME_CASE(VSE_VL) + NODE_NAME_CASE(READ_CSR) + NODE_NAME_CASE(WRITE_CSR) + NODE_NAME_CASE(SWAP_CSR) } // clang-format on return nullptr; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -28,6 +28,9 @@ def SDT_RISCVBrCC : SDTypeProfile<0, 4, [SDTCisSameAs<0, 1>, SDTCisVT<2, OtherVT>, SDTCisVT<3, OtherVT>]>; +def SDT_ReadCSR : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisInt<1>]>; +def SDT_WriteCSR : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisInt<1>]>; +def SDT_SwapCSR : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisInt<1>, SDTCisInt<2>]>; def SDT_RISCVReadCycleWide : SDTypeProfile<2, 0, [SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; def SDT_RISCVIntBinOpW : SDTypeProfile<1, 2, [ @@ -61,6 +64,12 @@ def riscv_sllw : SDNode<"RISCVISD::SLLW", SDT_RISCVIntBinOpW>; def riscv_sraw : SDNode<"RISCVISD::SRAW", SDT_RISCVIntBinOpW>; def riscv_srlw : SDNode<"RISCVISD::SRLW", SDT_RISCVIntBinOpW>; +def riscv_read_csr : SDNode<"RISCVISD::READ_CSR", SDT_ReadCSR, + [SDNPHasChain]>; +def riscv_write_csr : SDNode<"RISCVISD::WRITE_CSR", SDT_WriteCSR, + [SDNPHasChain]>; +def riscv_swap_csr : SDNode<"RISCVISD::SWAP_CSR", SDT_SwapCSR, + [SDNPHasChain]>; def riscv_read_cycle_wide : SDNode<"RISCVISD::READ_CYCLE_WIDE", SDT_RISCVReadCycleWide, @@ -143,6 +152,20 @@ let OperandNamespace = "RISCVOp"; } +def uimm12 : Operand, ImmLeaf(Imm);}]> { + let ParserMatchClass = UImmAsmOperand<12>; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeUImmOperand<12>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return isUInt<12>(Imm); + return MCOp.isBareSymbolRef(); + }]; + let OperandType = "OPERAND_UIMM12"; + let OperandNamespace = "RISCVOp"; +} + def simm12 : Operand, ImmLeaf(Imm);}]> { let ParserMatchClass = SImmAsmOperand<12>; let EncoderMethod = "getImmOpValue"; @@ -1146,6 +1169,56 @@ // present. This is necessary as it isn't valid to mix __atomic_* libcalls // with inline atomic operations for the same object. +/// Access to system registers + +// General operations. A system register is represented in them by its number. +// Dependencies between such operation and other instructions are expressed via +// side effect. + +let hasSideEffects = 0, isCodeGenOnly = 1 in +def Read_CSR : Pseudo<(outs GPR:$rd), (ins csr_sysreg:$reg), + [(set GPR:$rd, (riscv_read_csr uimm12:$reg))]>, + PseudoInstExpansion<(CSRRC GPR:$rd, csr_sysreg:$reg, X0)>; + +let hasSideEffects = 1, isCodeGenOnly = 1 in +def Write_CSR : Pseudo<(outs), (ins csr_sysreg:$reg, GPR:$val), + [(riscv_write_csr uimm12:$reg, GPR:$val)]>, + PseudoInstExpansion<(CSRRW X0, csr_sysreg:$reg, GPR:$val)>; + +let hasSideEffects = 1, isCodeGenOnly = 1 in +def Swap_CSR : Pseudo<(outs GPR:$rd), (ins csr_sysreg:$reg, GPR:$val), + [(set GPR:$rd, (riscv_swap_csr uimm12:$reg, GPR:$val))]>, + PseudoInstExpansion<(CSRRW GPR:$rd, csr_sysreg:$reg, GPR:$val)>; + +// Helpers for defining specific operations. They are defined for each system +// register separately. Side effect is not used because dependencies are +// expressed via use-def properties. + +class ReadSysReg Regs> + : Pseudo<(outs GPR:$rd), (ins), + [(set GPR:$rd, (riscv_read_csr (XLenVT SR.Encoding)))]>, + PseudoInstExpansion<(CSRRC GPR:$rd, SR.Encoding, X0)> { + let hasSideEffects = 0; + let Uses = Regs; +} + +class WriteSysReg Regs> + : Pseudo<(outs), (ins GPR:$val), + [(riscv_write_csr (XLenVT SR.Encoding), GPR:$val)]>, + PseudoInstExpansion<(CSRRW X0, SR.Encoding, GPR:$val)> { + let hasSideEffects = 0; + let Defs = Regs; +} + +class SwapSysReg Regs> + : Pseudo<(outs GPR:$rd), (ins GPR:$val), + [(set GPR:$rd, (riscv_swap_csr (XLenVT SR.Encoding), GPR:$val))]>, + PseudoInstExpansion<(CSRRW GPR:$rd, SR.Encoding, GPR:$val)> { + let hasSideEffects = 0; + let Uses = Regs; + let Defs = Regs; +} + /// Other pseudo-instructions // Pessimistically assume the stack pointer will be clobbered