]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/asm, cmd/internal/obj: add riscv64 generic CSR ops
authorMark Ryan <markdryan@rivosinc.com>
Wed, 18 Sep 2024 08:14:04 +0000 (10:14 +0200)
committerMark Ryan <markdryan@rivosinc.com>
Fri, 12 Sep 2025 08:12:45 +0000 (01:12 -0700)
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 <shaojunyang@google.com>
Reviewed-by: Joel Sing <joel@sing.id.au>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Meng Zhuo <mengzhuo1203@gmail.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
src/cmd/asm/internal/arch/riscv64.go
src/cmd/asm/internal/asm/asm.go
src/cmd/asm/internal/asm/testdata/riscv64.s
src/cmd/asm/internal/asm/testdata/riscv64error.s
src/cmd/internal/obj/link.go
src/cmd/internal/obj/riscv/cpu.go
src/cmd/internal/obj/riscv/list.go
src/cmd/internal/obj/riscv/obj.go

index 69e060a865ed7b9c708da543007e87ac9fdd7461..891d2be0e5cf20054199e0e93fb0a7229d8c91bf 100644 (file)
@@ -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
index 6bdbcb9c1b7f0e56c11ad221fb039720d1d897b6..389307af29ea533c5ed7ba18072693226c1cafe6 100644 (file)
@@ -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]
index 75abcefa10227ba2da615feaea3fb473bc112f25..30f9989982f643a6ffd81cd45ddae62bf8930d6a 100644 (file)
@@ -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
index 4e6afa0ac2a619047be312efffcb23b405401d96..434f221fd9871fd7f5ffda96668b4faad633d72a 100644 (file)
@@ -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"
index ea7f518f424a4c278d7ab13f4fcaf501cbc32192..6513e116872a0a86a2261daf0bb038c0e2b776c5 100644 (file)
@@ -99,7 +99,7 @@ import (
 //
 //     <symbolic constant name>
 //             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
index 116ccb4ea4d7f0e143f7c44e5316b4425a219011..b0fcda218c715c20edf69f8b9bf7feeddb13e64e 100644 (file)
@@ -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 ""
 }
index 8eb97a476dc7747f6cc83828e91d4475f4026430..da703c02ad228f2a30a0c793f8c9b24c1d989b0e 100644 (file)
@@ -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
 }
index 40f143be5cccb572dd9fe0f782a6bab21e40b3a4..db8d663c5a311518dc943222afb48aebec4fb59f 100644 (file)
@@ -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