From dd8276657f2244193d399f01941ee1d76a79529d Mon Sep 17 00:00:00 2001 From: Mark Ryan Date: Wed, 18 Sep 2024 10:14:04 +0200 Subject: [PATCH] cmd/asm, cmd/internal/obj: add riscv64 generic CSR ops Support is added for the generic RISC-V CSR operations; CSRRC, CSRRCI, CSRRS, CSRRSI, CSRRW, CSRRWI. These instructions require special handling as their second operand is a symbolic CSR register name and not an immediate value or a register. CSR names are implemented as special operands. RISC-V CSRs are not currently saved and restored when a go routine is asynchronously pre-empted so it is only safe to use these instructions in hand written assembler. Note that CSRRS was already partially supported by the assembler so this restriction predates this commit. We mention it here as this commit makes CSRRS much easier to use. Change-Id: I9ff8d804328b418a879d463e7d9cc31f489c7a00 Reviewed-on: https://go-review.googlesource.com/c/go/+/630519 Reviewed-by: Junyang Shao Reviewed-by: Joel Sing LUCI-TryBot-Result: Go LUCI Reviewed-by: Meng Zhuo Reviewed-by: Michael Pratt --- src/cmd/asm/internal/arch/riscv64.go | 29 +++++++++++- src/cmd/asm/internal/asm/asm.go | 15 +++++++ src/cmd/asm/internal/asm/testdata/riscv64.s | 18 ++++++++ .../asm/internal/asm/testdata/riscv64error.s | 21 +++++++++ src/cmd/internal/obj/link.go | 2 +- src/cmd/internal/obj/riscv/cpu.go | 40 +++++++++++++---- src/cmd/internal/obj/riscv/list.go | 9 +++- src/cmd/internal/obj/riscv/obj.go | 44 ++++++++++++++++++- 8 files changed, 165 insertions(+), 13 deletions(-) diff --git a/src/cmd/asm/internal/arch/riscv64.go b/src/cmd/asm/internal/arch/riscv64.go index 69e060a865..891d2be0e5 100644 --- a/src/cmd/asm/internal/arch/riscv64.go +++ b/src/cmd/asm/internal/arch/riscv64.go @@ -11,6 +11,7 @@ package arch import ( "cmd/internal/obj" "cmd/internal/obj/riscv" + "fmt" ) // IsRISCV64AMO reports whether op is an AMO instruction that requires @@ -32,6 +33,20 @@ func IsRISCV64VTypeI(op obj.As) bool { return op == riscv.AVSETVLI || op == riscv.AVSETIVLI } +// IsRISCV64CSRO reports whether the op is an instruction that uses +// CSR symbolic names and whether that instruction expects a register +// or an immediate source operand. +func IsRISCV64CSRO(op obj.As) (imm bool, ok bool) { + switch op { + case riscv.ACSRRCI, riscv.ACSRRSI, riscv.ACSRRWI: + imm = true + fallthrough + case riscv.ACSRRC, riscv.ACSRRS, riscv.ACSRRW: + ok = true + } + return +} + var riscv64SpecialOperand map[string]riscv.SpecialOperand // RISCV64SpecialOperand returns the internal representation of a special operand. @@ -39,9 +54,21 @@ func RISCV64SpecialOperand(name string) riscv.SpecialOperand { if riscv64SpecialOperand == nil { // Generate mapping when function is first called. riscv64SpecialOperand = map[string]riscv.SpecialOperand{} - for opd := riscv.SPOP_BEGIN; opd < riscv.SPOP_END; opd++ { + for opd := riscv.SPOP_RVV_BEGIN; opd < riscv.SPOP_RVV_END; opd++ { riscv64SpecialOperand[opd.String()] = opd } + // Add the CSRs + for csrCode, csrName := range riscv.CSRs { + // The set of RVV special operand names and the set of CSR special operands + // names are disjoint and so can safely share a single namespace. However, + // it's possible that a future update to the CSRs in inst.go could introduce + // a conflict. This check ensures that such a conflict does not go + // unnoticed. + if _, ok := riscv64SpecialOperand[csrName]; ok { + panic(fmt.Sprintf("riscv64 special operand %q redefined", csrName)) + } + riscv64SpecialOperand[csrName] = riscv.SpecialOperand(int(csrCode) + int(riscv.SPOP_CSR_BEGIN)) + } } if opd, ok := riscv64SpecialOperand[name]; ok { return opd diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go index 6bdbcb9c1b..389307af29 100644 --- a/src/cmd/asm/internal/asm/asm.go +++ b/src/cmd/asm/internal/asm/asm.go @@ -782,6 +782,21 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.RegTo2 = a[2].Reg break } + // RISCV64 instructions that reference CSRs with symbolic names. + if isImm, ok := arch.IsRISCV64CSRO(op); ok { + if a[0].Type != obj.TYPE_CONST && isImm { + p.errorf("invalid value for first operand to %s instruction, must be a 5 bit unsigned immediate", op) + return + } + if a[1].Type != obj.TYPE_SPECIAL { + p.errorf("invalid value for second operand to %s instruction, must be a CSR name", op) + return + } + prog.AddRestSourceArgs([]obj.Addr{a[1]}) + prog.From = a[0] + prog.To = a[2] + break + } prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s index 75abcefa10..30f9989982 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv64.s +++ b/src/cmd/asm/internal/asm/testdata/riscv64.s @@ -172,6 +172,24 @@ start: SD X5, (X6) // 23305300 SD X5, 4(X6) // 23325300 + // 7.1: CSR Instructions + CSRRC X0, CYCLE, X5 // f33200c0 + CSRRC X0, CYCLE, X0 // 733000c0 + CSRRC X10, CYCLE, X5 // f33205c0 + CSRRC $2, TIME, X5 // f37211c0 + CSRRCI $2, TIME, X5 // f37211c0 + CSRRS X0, CYCLE, X5 // f32200c0 + CSRRS X0, CYCLE, X0 // 732000c0 + CSRRS X10, CYCLE, X5 // f32205c0 + CSRRS $2, TIME, X5 // f36211c0 + CSRRS X0, VLENB, X5 // f32220c2 + CSRRSI $2, TIME, X5 // f36211c0 + CSRRW X0, CYCLE, X5 // f31200c0 + CSRRW X0, CYCLE, X0 // 731000c0 + CSRRW X10, CYCLE, X5 // f31205c0 + CSRRW $2, TIME, X5 // f35211c0 + CSRRWI $2, TIME, X5 // f35211c0 + // 8.1: Base Counters and Timers (Zicntr) RDCYCLE X5 // f32200c0 RDTIME X5 // f32210c0 diff --git a/src/cmd/asm/internal/asm/testdata/riscv64error.s b/src/cmd/asm/internal/asm/testdata/riscv64error.s index 4e6afa0ac2..434f221fd9 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv64error.s +++ b/src/cmd/asm/internal/asm/testdata/riscv64error.s @@ -3,6 +3,27 @@ // license that can be found in the LICENSE file. TEXT errors(SB),$0 + CSRRC (X10), CYCLE, X5 // ERROR "integer register or immediate expected for 1st operand" + CSRRC X0, TU, X5 // ERROR "unknown CSR" + CSRRC X0, CYCLE // ERROR "missing CSR name" + CSRRC X0, CYCLE, (X10) // ERROR "needs an integer register output" + CSRRC $-1, TIME, X15 // ERROR "immediate out of range 0 to 31" + CSRRCI $32, TIME, X15 // ERROR "immediate out of range 0 to 31" + CSRRCI $1, TIME, (X15) // ERROR "needs an integer register output" + CSRRS (X10), CYCLE, X5 // ERROR "integer register or immediate expected for 1st operand" + CSRRS X0, CYCLE, (X10) // ERROR "needs an integer register output" + CSRRS X0, TU, X5 // ERROR "unknown CSR" + CSRRS X0, CYCLE // ERROR "missing CSR name" + CSRRS $-1, TIME, X15 // ERROR "immediate out of range 0 to 31" + CSRRSI $32, TIME, X15 // ERROR "immediate out of range 0 to 31" + CSRRSI $1, TIME, (X15) // ERROR "needs an integer register output" + CSRRW (X10), CYCLE, X5 // ERROR "integer register or immediate expected for 1st operand" + CSRRW X0, TU, X5 // ERROR "unknown CSR" + CSRRW X0, CYCLE // ERROR "missing CSR name" + CSRRW X0, CYCLE, (X5) // ERROR "needs an integer register output" + CSRRW $-1, TIME, X15 // ERROR "immediate out of range 0 to 31" + CSRRWI $32, TIME, X15 // ERROR "immediate out of range 0 to 31" + CSRRWI $1, TIME, (X15) // ERROR "needs an integer register output" MOV $errors(SB), (X5) // ERROR "address load must target register" MOV $8(SP), (X5) // ERROR "address load must target register" MOVB $8(SP), X5 // ERROR "unsupported address load" diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index ea7f518f42..6513e11687 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -99,7 +99,7 @@ import ( // // // Special symbolic constants for ARM64 (such as conditional flags, tlbi_op and so on) -// and RISCV64 (such as names for vector configuration instruction arguments). +// and RISCV64 (such as names for vector configuration instruction arguments and CSRs). // Encoding: // type = TYPE_SPECIAL // offset = The constant value corresponding to this symbol diff --git a/src/cmd/internal/obj/riscv/cpu.go b/src/cmd/internal/obj/riscv/cpu.go index 116ccb4ea4..b0fcda218c 100644 --- a/src/cmd/internal/obj/riscv/cpu.go +++ b/src/cmd/internal/obj/riscv/cpu.go @@ -35,6 +35,8 @@ import ( "cmd/internal/obj" ) +var CSRs map[uint16]string = csrs + //go:generate go run ../stringer.go -i $GOFILE -o anames.go -p riscv const ( @@ -1315,9 +1317,10 @@ type SpecialOperand int const ( SPOP_BEGIN SpecialOperand = obj.SpecialOperandRISCVBase + SPOP_RVV_BEGIN // Vector mask policy. - SPOP_MA SpecialOperand = obj.SpecialOperandRISCVBase + iota - 1 + SPOP_MA SpecialOperand = obj.SpecialOperandRISCVBase + iota - 2 SPOP_MU // Vector tail policy. @@ -1338,8 +1341,13 @@ const ( SPOP_E16 SPOP_E32 SPOP_E64 + SPOP_RVV_END + + // CSR names. 4096 special operands are reserved for RISC-V CSR names. + SPOP_CSR_BEGIN = SPOP_RVV_END + SPOP_CSR_END = SPOP_CSR_BEGIN + 4096 - SPOP_END + SPOP_END = SPOP_CSR_END + 1 ) var specialOperands = map[SpecialOperand]struct { @@ -1367,17 +1375,33 @@ var specialOperands = map[SpecialOperand]struct { } func (so SpecialOperand) encode() uint32 { - op, ok := specialOperands[so] - if ok { - return op.encoding + switch { + case so >= SPOP_RVV_BEGIN && so < SPOP_RVV_END: + op, ok := specialOperands[so] + if ok { + return op.encoding + } + case so >= SPOP_CSR_BEGIN && so < SPOP_CSR_END: + csrNum := uint16(so - SPOP_CSR_BEGIN) + if _, ok := csrs[csrNum]; ok { + return uint32(csrNum) + } } return 0 } +// String returns the textual representation of a SpecialOperand. func (so SpecialOperand) String() string { - op, ok := specialOperands[so] - if ok { - return op.name + switch { + case so >= SPOP_RVV_BEGIN && so < SPOP_RVV_END: + op, ok := specialOperands[so] + if ok { + return op.name + } + case so >= SPOP_CSR_BEGIN && so < SPOP_CSR_END: + if csrName, ok := csrs[uint16(so-SPOP_CSR_BEGIN)]; ok { + return csrName + } } return "" } diff --git a/src/cmd/internal/obj/riscv/list.go b/src/cmd/internal/obj/riscv/list.go index 8eb97a476d..da703c02ad 100644 --- a/src/cmd/internal/obj/riscv/list.go +++ b/src/cmd/internal/obj/riscv/list.go @@ -52,9 +52,14 @@ func opSuffixString(s uint8) string { } func specialOperandConv(a int64) string { + var s string + spc := SpecialOperand(a) if spc >= SPOP_BEGIN && spc < SPOP_END { - return spc.String() + s = spc.String() + } + if s == "" { + return "SPC_??" } - return "SPC_??" + return s } diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index 40f143be5c..db8d663c5a 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -1920,7 +1920,12 @@ var instructions = [ALAST & obj.AMask]instructionData{ ASD & obj.AMask: {enc: sIEncoding}, // 7.1: CSR Instructions - ACSRRS & obj.AMask: {enc: iIIEncoding}, + ACSRRC & obj.AMask: {enc: iIIEncoding, immForm: ACSRRCI}, + ACSRRCI & obj.AMask: {enc: iIIEncoding}, + ACSRRS & obj.AMask: {enc: iIIEncoding, immForm: ACSRRSI}, + ACSRRSI & obj.AMask: {enc: iIIEncoding}, + ACSRRW & obj.AMask: {enc: iIIEncoding, immForm: ACSRRWI}, + ACSRRWI & obj.AMask: {enc: iIIEncoding}, // 13.1: Multiplication Operations AMUL & obj.AMask: {enc: rIIIEncoding, ternary: true}, @@ -3327,6 +3332,43 @@ func instructionsForProg(p *obj.Prog) []*instruction { ins.imm = -1022 } + case ACSRRC, ACSRRCI, ACSRRS, ACSRRSI, ACSRRW, ACSRRWI: + if len(p.RestArgs) == 0 || p.RestArgs[0].Type != obj.TYPE_SPECIAL { + p.Ctxt.Diag("%v: missing CSR name", p) + return nil + } + if p.From.Type == obj.TYPE_CONST { + imm := p.From.Offset + if imm < 0 || imm >= 32 { + p.Ctxt.Diag("%v: immediate out of range 0 to 31", p) + return nil + } + ins.rs1 = uint32(imm) + REG_ZERO + } else if p.From.Type == obj.TYPE_REG { + ins.rs1 = uint32(p.From.Reg) + } else { + p.Ctxt.Diag("%v: integer register or immediate expected for 1st operand", p) + return nil + } + if p.To.Type != obj.TYPE_REG { + p.Ctxt.Diag("%v: needs an integer register output", p) + return nil + } + csrNum := SpecialOperand(p.RestArgs[0].Offset).encode() + if csrNum >= 1<<12 { + p.Ctxt.Diag("%v: unknown CSR", p) + return nil + } + if _, ok := CSRs[uint16(csrNum)]; !ok { + p.Ctxt.Diag("%v: unknown CSR", p) + return nil + } + ins.imm = int64(csrNum) + if ins.imm > 2047 { + ins.imm -= 4096 + } + ins.rs2 = obj.REG_NONE + case AFENCE: ins.rd, ins.rs1, ins.rs2 = REG_ZERO, REG_ZERO, obj.REG_NONE ins.imm = 0x0ff -- 2.52.0