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 <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>
This commit is contained in:
Mark Ryan 2024-09-18 10:14:04 +02:00
parent f37d75472d
commit dd8276657f
8 changed files with 165 additions and 13 deletions

View 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

View 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]

View 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

View 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"

View 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

View 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
SPOP_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_CSR_END + 1
)
var specialOperands = map[SpecialOperand]struct {
@ -1367,18 +1375,34 @@ var specialOperands = map[SpecialOperand]struct {
}
func (so SpecialOperand) encode() uint32 {
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 {
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 ""
}

View 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 s
}

View 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