diff options
| author | Joel Sing <joel@sing.id.au> | 2024-11-24 12:39:20 +1100 |
|---|---|---|
| committer | Joel Sing <joel@sing.id.au> | 2025-02-14 07:13:11 -0800 |
| commit | c8545439b596ffc88d09b9f6970fefdf69fcfc5d (patch) | |
| tree | a3ec5f07be452bfc110f9e577d908d47efd740b1 /src/cmd/internal/obj/riscv/obj.go | |
| parent | b16c04f43993436f24b1e4155a4652193eb1b90c (diff) | |
| download | go-c8545439b596ffc88d09b9f6970fefdf69fcfc5d.tar.xz | |
cmd/asm,cmd/internal/obj/riscv: implement vector configuration setting instructions
Implement vector configuration setting instructions (VSETVLI,
VSETIVLI, VSETL). These allow the vector length (vl) and vector
type (vtype) CSRs to be configured via a single instruction.
Unfortunately each instruction has its own dedicated encoding.
In the case of VSETVLI/VSETIVLI, the vector type is specified via
a series of special operands, which specify the selected element
width (E8, E16, E32, E64), the vector register group multiplier
(M1, M2, M4, M8, MF2, MF4, MF8), the vector tail policy (TU, TA)
and vector mask policy (MU, MA). Note that the order of these
special operands matches non-Go assemblers.
Partially based on work by Pengcheng Wang <wangpengcheng.pp@bytedance.com>.
Cq-Include-Trybots: luci.golang.try:gotip-linux-riscv64
Change-Id: I431f59c1e048a3e84754f0643a963da473a741fe
Reviewed-on: https://go-review.googlesource.com/c/go/+/631936
Reviewed-by: Mark Ryan <markdryan@rivosinc.com>
Reviewed-by: Meng Zhuo <mengzhuo1203@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Diffstat (limited to 'src/cmd/internal/obj/riscv/obj.go')
| -rw-r--r-- | src/cmd/internal/obj/riscv/obj.go | 149 |
1 files changed, 139 insertions, 10 deletions
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index 54c34af2f4..c6f66d0195 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -1042,27 +1042,35 @@ func immEven(x int64) error { return nil } -// immIFits checks whether the immediate value x fits in nbits bits -// as a signed integer. If it does not, an error is returned. -func immIFits(x int64, nbits uint) error { - nbits-- - min := int64(-1) << nbits - max := int64(1)<<nbits - 1 +func immFits(x int64, nbits uint, signed bool) error { + label := "unsigned" + min, max := int64(0), int64(1)<<nbits-1 + if signed { + label = "signed" + sbits := nbits - 1 + min, max = int64(-1)<<sbits, int64(1)<<sbits-1 + } if x < min || x > max { if nbits <= 16 { - return fmt.Errorf("signed immediate %d must be in range [%d, %d] (%d bits)", x, min, max, nbits) + return fmt.Errorf("%s immediate %d must be in range [%d, %d] (%d bits)", label, x, min, max, nbits) } - return fmt.Errorf("signed immediate %#x must be in range [%#x, %#x] (%d bits)", x, min, max, nbits) + return fmt.Errorf("%s immediate %#x must be in range [%#x, %#x] (%d bits)", label, x, min, max, nbits) } return nil } +// immIFits checks whether the immediate value x fits in nbits bits +// as a signed integer. If it does not, an error is returned. +func immIFits(x int64, nbits uint) error { + return immFits(x, nbits, true) +} + // immI extracts the signed integer of the specified size from an immediate. func immI(as obj.As, imm int64, nbits uint) uint32 { if err := immIFits(imm, nbits); err != nil { panic(fmt.Sprintf("%v: %v", as, err)) } - return uint32(imm) + return uint32(imm) & ((1 << nbits) - 1) } func wantImmI(ctxt *obj.Link, ins *instruction, imm int64, nbits uint) { @@ -1071,6 +1079,26 @@ func wantImmI(ctxt *obj.Link, ins *instruction, imm int64, nbits uint) { } } +// immUFits checks whether the immediate value x fits in nbits bits +// as an unsigned integer. If it does not, an error is returned. +func immUFits(x int64, nbits uint) error { + return immFits(x, nbits, false) +} + +// immU extracts the unsigned integer of the specified size from an immediate. +func immU(as obj.As, imm int64, nbits uint) uint32 { + if err := immUFits(imm, nbits); err != nil { + panic(fmt.Sprintf("%v: %v", as, err)) + } + return uint32(imm) & ((1 << nbits) - 1) +} + +func wantImmU(ctxt *obj.Link, ins *instruction, imm int64, nbits uint) { + if err := immUFits(imm, nbits); err != nil { + ctxt.Diag("%v: %v", ins, err) + } +} + func wantReg(ctxt *obj.Link, ins *instruction, pos string, descr string, r, min, max uint32) { if r < min || r > max { var suffix string @@ -1227,6 +1255,29 @@ func validateJ(ctxt *obj.Link, ins *instruction) { wantNoneReg(ctxt, ins, "rs3", ins.rs3) } +func validateVsetvli(ctxt *obj.Link, ins *instruction) { + wantImmU(ctxt, ins, ins.imm, 11) + wantIntReg(ctxt, ins, "rd", ins.rd) + wantIntReg(ctxt, ins, "rs1", ins.rs1) + wantNoneReg(ctxt, ins, "rs2", ins.rs2) + wantNoneReg(ctxt, ins, "rs3", ins.rs3) +} + +func validateVsetivli(ctxt *obj.Link, ins *instruction) { + wantImmU(ctxt, ins, ins.imm, 10) + wantIntReg(ctxt, ins, "rd", ins.rd) + wantImmU(ctxt, ins, int64(ins.rs1), 5) + wantNoneReg(ctxt, ins, "rs2", ins.rs2) + wantNoneReg(ctxt, ins, "rs3", ins.rs3) +} + +func validateVsetvl(ctxt *obj.Link, ins *instruction) { + wantIntReg(ctxt, ins, "rd", ins.rd) + wantIntReg(ctxt, ins, "rs1", ins.rs1) + wantIntReg(ctxt, ins, "rs2", ins.rs2) + wantNoneReg(ctxt, ins, "rs3", ins.rs3) +} + func validateRaw(ctxt *obj.Link, ins *instruction) { // Treat the raw value specially as a 32-bit unsigned integer. // Nobody wants to enter negative machine code. @@ -1415,6 +1466,29 @@ func encodeCJImmediate(imm uint32) uint32 { return bits << 2 } +func encodeVset(as obj.As, rs1, rs2, rd uint32) uint32 { + enc := encode(as) + if enc == nil { + panic("encodeVset: could not encode instruction") + } + return enc.funct7<<25 | rs2<<20 | rs1<<15 | enc.funct3<<12 | rd<<7 | enc.opcode +} + +func encodeVsetvli(ins *instruction) uint32 { + vtype := immU(ins.as, ins.imm, 11) + return encodeVset(ins.as, regI(ins.rs1), vtype, regI(ins.rd)) +} + +func encodeVsetivli(ins *instruction) uint32 { + vtype := immU(ins.as, ins.imm, 10) + avl := immU(ins.as, int64(ins.rs1), 5) + return encodeVset(ins.as, avl, vtype, regI(ins.rd)) +} + +func encodeVsetvl(ins *instruction) uint32 { + return encodeVset(ins.as, regI(ins.rs1), regI(ins.rs2), regI(ins.rd)) +} + func encodeRawIns(ins *instruction) uint32 { // Treat the raw value specially as a 32-bit unsigned integer. // Nobody wants to enter negative machine code. @@ -1485,6 +1559,27 @@ func EncodeUImmediate(imm int64) (int64, error) { return imm << 12, nil } +func EncodeVectorType(vsew, vlmul, vtail, vmask int64) (int64, error) { + vsewSO := SpecialOperand(vsew) + if vsewSO < SPOP_E8 || vsewSO > SPOP_E64 { + return -1, fmt.Errorf("invalid vector selected element width %q", vsewSO) + } + vlmulSO := SpecialOperand(vlmul) + if vlmulSO < SPOP_M1 || vlmulSO > SPOP_MF8 { + return -1, fmt.Errorf("invalid vector register group multiplier %q", vlmulSO) + } + vtailSO := SpecialOperand(vtail) + if vtailSO != SPOP_TA && vtailSO != SPOP_TU { + return -1, fmt.Errorf("invalid vector tail policy %q", vtailSO) + } + vmaskSO := SpecialOperand(vmask) + if vmaskSO != SPOP_MA && vmaskSO != SPOP_MU { + return -1, fmt.Errorf("invalid vector mask policy %q", vmaskSO) + } + vtype := vmaskSO.encode()<<7 | vtailSO.encode()<<6 | vsewSO.encode()<<3 | vlmulSO.encode() + return int64(vtype), nil +} + type encoding struct { encode func(*instruction) uint32 // encode returns the machine code for an instruction validate func(*obj.Link, *instruction) // validate validates an instruction @@ -1522,6 +1617,11 @@ var ( uEncoding = encoding{encode: encodeU, validate: validateU, length: 4} jEncoding = encoding{encode: encodeJ, validate: validateJ, length: 4} + // Encodings for vector configuration setting instruction. + vsetvliEncoding = encoding{encode: encodeVsetvli, validate: validateVsetvli, length: 4} + vsetivliEncoding = encoding{encode: encodeVsetivli, validate: validateVsetivli, length: 4} + vsetvlEncoding = encoding{encode: encodeVsetvl, validate: validateVsetvl, length: 4} + // rawEncoding encodes a raw instruction byte sequence. rawEncoding = encoding{encode: encodeRawIns, validate: validateRaw, length: 4} @@ -1789,6 +1889,15 @@ var instructions = [ALAST & obj.AMask]instructionData{ ABSETI & obj.AMask: {enc: iIIEncoding, ternary: true}, // + // "V" Standard Extension for Vector Operations, Version 1.0 + // + + // 31.6. Vector Configuration-Setting Instructions + AVSETVLI & obj.AMask: {enc: vsetvliEncoding, immForm: AVSETIVLI}, + AVSETIVLI & obj.AMask: {enc: vsetivliEncoding}, + AVSETVL & obj.AMask: {enc: vsetvlEncoding}, + + // // Privileged ISA // @@ -2345,7 +2454,12 @@ func instructionsForProg(p *obj.Prog) []*instruction { ins := instructionForProg(p) inss := []*instruction{ins} - if len(p.RestArgs) > 1 { + if ins.as == AVSETVLI || ins.as == AVSETIVLI { + if len(p.RestArgs) != 4 { + p.Ctxt.Diag("incorrect number of arguments for instruction") + return nil + } + } else if len(p.RestArgs) > 1 { p.Ctxt.Diag("too many source registers") return nil } @@ -2583,6 +2697,21 @@ func instructionsForProg(p *obj.Prog) []*instruction { // XNOR -> (NOT (XOR x y)) ins.as = AXOR inss = append(inss, &instruction{as: AXORI, rs1: ins.rd, rs2: obj.REG_NONE, rd: ins.rd, imm: -1}) + + case AVSETVLI, AVSETIVLI: + ins.rs1, ins.rs2 = ins.rs2, obj.REG_NONE + vtype, err := EncodeVectorType(p.RestArgs[0].Offset, p.RestArgs[1].Offset, p.RestArgs[2].Offset, p.RestArgs[3].Offset) + if err != nil { + p.Ctxt.Diag("%v: %v", p, err) + } + ins.imm = int64(vtype) + if ins.as == AVSETIVLI { + if p.From.Type != obj.TYPE_CONST { + p.Ctxt.Diag("%v: expected immediate value", p) + } + ins.rs1 = uint32(p.From.Offset) + } + } for _, ins := range inss { |
