fix(asm): reject the operand shapes the toolchain rejects on riscv64
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
6c66f5bbd9
commit
f0942ff7f3
3 files changed
+1014
-456
No files matched your search
+817
-348
File diff suppressed because it is too large.
Load diff
+195
-106
@@ -3,153 +3,192 @@
|
|||||||
|
|
||||||
package asm
|
package asm
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
// RISC-V register encoding: maps register names to their 5-bit numbers.
|
// RISC-V register encoding: maps register names to their 5-bit numbers.
|
||||||
// The Go assembler uses the standard RISC-V ABI naming.
|
// The Go assembler uses the standard RISC-V ABI naming.
|
||||||
|
|
||||||
// riscvRegNum returns the 5-bit register number for a RISC-V register name.
|
// riscvRegNum returns the 5-bit register number for a RISC-V register name.
|
||||||
// Returns -1 if the register is not recognized.
|
// Returns -1 if the register is not recognized.
|
||||||
func riscvRegNum(name string) int {
|
func riscvRegNum(name string) int {
|
||||||
|
n, _ := riscvBankedRegNum(name)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// riscvRegBank classifies a RISC-V register name by its bank: the integer
|
||||||
|
// file (X0-X31 with the ABI aliases), the floating-point file (F0-F31 with
|
||||||
|
// the FP aliases) and the vector file (V0-V31). The toolchain's validate
|
||||||
|
// stage checks every operand against its instruction's bank and rejects the
|
||||||
|
// wrong one; the bank is what makes "F1" and "X1" different operands even
|
||||||
|
// though both encode as the number 1.
|
||||||
|
type riscvRegBank uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
riscvBankNone riscvRegBank = iota // not a register name
|
||||||
|
riscvBankInt // X0-X31, ZERO, RA, SP, ...
|
||||||
|
riscvBankFloat // F0-F31, FT0-FT11, FS0-FS11, FA0-FA7
|
||||||
|
riscvBankVec // V0-V31
|
||||||
|
)
|
||||||
|
|
||||||
|
// String names the bank the way the bank-mismatch diagnostics spell it.
|
||||||
|
func (b riscvRegBank) String() string {
|
||||||
|
switch b {
|
||||||
|
case riscvBankInt:
|
||||||
|
return "integer"
|
||||||
|
case riscvBankFloat:
|
||||||
|
return "float"
|
||||||
|
case riscvBankVec:
|
||||||
|
return "vector"
|
||||||
|
}
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
// riscvBankedRegNum returns the 5-bit register number for a name together
|
||||||
|
// with the bank the name belongs to. riscvBankNone with a negative number
|
||||||
|
// names no register at all.
|
||||||
|
func riscvBankedRegNum(name string) (int, riscvRegBank) {
|
||||||
|
switch name {
|
||||||
|
// Floating-point registers (F0-F31).
|
||||||
|
case "F0", "FT0":
|
||||||
|
return 0, riscvBankFloat
|
||||||
|
case "F1", "FT1":
|
||||||
|
return 1, riscvBankFloat
|
||||||
|
case "F2", "FT2":
|
||||||
|
return 2, riscvBankFloat
|
||||||
|
case "F3", "FT3":
|
||||||
|
return 3, riscvBankFloat
|
||||||
|
case "F4", "FT4":
|
||||||
|
return 4, riscvBankFloat
|
||||||
|
case "F5", "FT5":
|
||||||
|
return 5, riscvBankFloat
|
||||||
|
case "F6", "FT6":
|
||||||
|
return 6, riscvBankFloat
|
||||||
|
case "F7", "FT7":
|
||||||
|
return 7, riscvBankFloat
|
||||||
|
case "F8", "FS0":
|
||||||
|
return 8, riscvBankFloat
|
||||||
|
case "F9", "FS1":
|
||||||
|
return 9, riscvBankFloat
|
||||||
|
case "F10", "FA0":
|
||||||
|
return 10, riscvBankFloat
|
||||||
|
case "F11", "FA1":
|
||||||
|
return 11, riscvBankFloat
|
||||||
|
case "F12", "FA2":
|
||||||
|
return 12, riscvBankFloat
|
||||||
|
case "F13", "FA3":
|
||||||
|
return 13, riscvBankFloat
|
||||||
|
case "F14", "FA4":
|
||||||
|
return 14, riscvBankFloat
|
||||||
|
case "F15", "FA5":
|
||||||
|
return 15, riscvBankFloat
|
||||||
|
case "F16", "FA6":
|
||||||
|
return 16, riscvBankFloat
|
||||||
|
case "F17", "FA7":
|
||||||
|
return 17, riscvBankFloat
|
||||||
|
case "F18", "FS2":
|
||||||
|
return 18, riscvBankFloat
|
||||||
|
case "F19", "FS3":
|
||||||
|
return 19, riscvBankFloat
|
||||||
|
case "F20", "FS4":
|
||||||
|
return 20, riscvBankFloat
|
||||||
|
case "F21", "FS5":
|
||||||
|
return 21, riscvBankFloat
|
||||||
|
case "F22", "FS6":
|
||||||
|
return 22, riscvBankFloat
|
||||||
|
case "F23", "FS7":
|
||||||
|
return 23, riscvBankFloat
|
||||||
|
case "F24", "FS8":
|
||||||
|
return 24, riscvBankFloat
|
||||||
|
case "F25", "FS9":
|
||||||
|
return 25, riscvBankFloat
|
||||||
|
case "F26", "FS10":
|
||||||
|
return 26, riscvBankFloat
|
||||||
|
case "F27", "FS11":
|
||||||
|
return 27, riscvBankFloat
|
||||||
|
case "F28", "FT8":
|
||||||
|
return 28, riscvBankFloat
|
||||||
|
case "F29", "FT9":
|
||||||
|
return 29, riscvBankFloat
|
||||||
|
case "F30", "FT10":
|
||||||
|
return 30, riscvBankFloat
|
||||||
|
case "F31", "FT11":
|
||||||
|
return 31, riscvBankFloat
|
||||||
|
}
|
||||||
|
// Vector registers V0-V31 (the "V" extension). They share the
|
||||||
|
// register numbering with the integer file: a bare number 0-31.
|
||||||
|
if len(name) >= 2 && name[0] == 'V' {
|
||||||
|
if n, ok := parseRegDigits(name[1:], 31); ok {
|
||||||
|
return n, riscvBankVec
|
||||||
|
}
|
||||||
|
}
|
||||||
switch name {
|
switch name {
|
||||||
// Numbered integer registers.
|
// Numbered integer registers.
|
||||||
case "X0", "ZERO":
|
case "X0", "ZERO":
|
||||||
return 0
|
return 0, riscvBankInt
|
||||||
case "X1", "RA", "LR":
|
case "X1", "RA", "LR":
|
||||||
return 1
|
return 1, riscvBankInt
|
||||||
case "X2", "SP":
|
case "X2", "SP":
|
||||||
return 2
|
return 2, riscvBankInt
|
||||||
case "X3", "GP":
|
case "X3", "GP":
|
||||||
return 3
|
return 3, riscvBankInt
|
||||||
case "X4", "TP":
|
case "X4", "TP":
|
||||||
return 4
|
return 4, riscvBankInt
|
||||||
case "X5", "T0":
|
case "X5", "T0":
|
||||||
return 5
|
return 5, riscvBankInt
|
||||||
case "X6", "T1":
|
case "X6", "T1":
|
||||||
return 6
|
return 6, riscvBankInt
|
||||||
case "X7", "T2":
|
case "X7", "T2":
|
||||||
return 7
|
return 7, riscvBankInt
|
||||||
case "X8", "S0", "FP":
|
case "X8", "S0", "FP":
|
||||||
return 8
|
return 8, riscvBankInt
|
||||||
case "X9", "S1":
|
case "X9", "S1":
|
||||||
return 9
|
return 9, riscvBankInt
|
||||||
case "X10", "A0":
|
case "X10", "A0":
|
||||||
return 10
|
return 10, riscvBankInt
|
||||||
case "X11", "A1":
|
case "X11", "A1":
|
||||||
return 11
|
return 11, riscvBankInt
|
||||||
case "X12", "A2":
|
case "X12", "A2":
|
||||||
return 12
|
return 12, riscvBankInt
|
||||||
case "X13", "A3":
|
case "X13", "A3":
|
||||||
return 13
|
return 13, riscvBankInt
|
||||||
case "X14", "A4":
|
case "X14", "A4":
|
||||||
return 14
|
return 14, riscvBankInt
|
||||||
case "X15", "A5":
|
case "X15", "A5":
|
||||||
return 15
|
return 15, riscvBankInt
|
||||||
case "X16", "A6":
|
case "X16", "A6":
|
||||||
return 16
|
return 16, riscvBankInt
|
||||||
case "X17", "A7":
|
case "X17", "A7":
|
||||||
return 17
|
return 17, riscvBankInt
|
||||||
case "X18", "S2":
|
case "X18", "S2":
|
||||||
return 18
|
return 18, riscvBankInt
|
||||||
case "X19", "S3":
|
case "X19", "S3":
|
||||||
return 19
|
return 19, riscvBankInt
|
||||||
case "X20", "S4":
|
case "X20", "S4":
|
||||||
return 20
|
return 20, riscvBankInt
|
||||||
case "X21", "S5":
|
case "X21", "S5":
|
||||||
return 21
|
return 21, riscvBankInt
|
||||||
case "X22", "S6":
|
case "X22", "S6":
|
||||||
return 22
|
return 22, riscvBankInt
|
||||||
case "X23", "S7":
|
case "X23", "S7":
|
||||||
return 23
|
return 23, riscvBankInt
|
||||||
case "X24", "S8":
|
case "X24", "S8":
|
||||||
return 24
|
return 24, riscvBankInt
|
||||||
case "X25", "S9":
|
case "X25", "S9":
|
||||||
return 25
|
return 25, riscvBankInt
|
||||||
case "X26", "S10", "CTXT":
|
case "X26", "S10", "CTXT":
|
||||||
return 26
|
return 26, riscvBankInt
|
||||||
case "X27", "S11", "g":
|
case "X27", "S11", "g":
|
||||||
return 27
|
return 27, riscvBankInt
|
||||||
case "X28", "T3":
|
case "X28", "T3":
|
||||||
return 28
|
return 28, riscvBankInt
|
||||||
case "X29", "T4":
|
case "X29", "T4":
|
||||||
return 29
|
return 29, riscvBankInt
|
||||||
case "X30", "T5":
|
case "X30", "T5":
|
||||||
return 30
|
return 30, riscvBankInt
|
||||||
case "X31", "T6", "TMP":
|
case "X31", "T6", "TMP":
|
||||||
return 31
|
return 31, riscvBankInt
|
||||||
// Floating-point registers (F0-F31).
|
|
||||||
case "F0", "FT0":
|
|
||||||
return 0
|
|
||||||
case "F1", "FT1":
|
|
||||||
return 1
|
|
||||||
case "F2", "FT2":
|
|
||||||
return 2
|
|
||||||
case "F3", "FT3":
|
|
||||||
return 3
|
|
||||||
case "F4", "FT4":
|
|
||||||
return 4
|
|
||||||
case "F5", "FT5":
|
|
||||||
return 5
|
|
||||||
case "F6", "FT6":
|
|
||||||
return 6
|
|
||||||
case "F7", "FT7":
|
|
||||||
return 7
|
|
||||||
case "F8", "FS0":
|
|
||||||
return 8
|
|
||||||
case "F9", "FS1":
|
|
||||||
return 9
|
|
||||||
case "F10", "FA0":
|
|
||||||
return 10
|
|
||||||
case "F11", "FA1":
|
|
||||||
return 11
|
|
||||||
case "F12", "FA2":
|
|
||||||
return 12
|
|
||||||
case "F13", "FA3":
|
|
||||||
return 13
|
|
||||||
case "F14", "FA4":
|
|
||||||
return 14
|
|
||||||
case "F15", "FA5":
|
|
||||||
return 15
|
|
||||||
case "F16", "FA6":
|
|
||||||
return 16
|
|
||||||
case "F17", "FA7":
|
|
||||||
return 17
|
|
||||||
case "F18", "FS2":
|
|
||||||
return 18
|
|
||||||
case "F19", "FS3":
|
|
||||||
return 19
|
|
||||||
case "F20", "FS4":
|
|
||||||
return 20
|
|
||||||
case "F21", "FS5":
|
|
||||||
return 21
|
|
||||||
case "F22", "FS6":
|
|
||||||
return 22
|
|
||||||
case "F23", "FS7":
|
|
||||||
return 23
|
|
||||||
case "F24", "FS8":
|
|
||||||
return 24
|
|
||||||
case "F25", "FS9":
|
|
||||||
return 25
|
|
||||||
case "F26", "FS10":
|
|
||||||
return 26
|
|
||||||
case "F27", "FS11":
|
|
||||||
return 27
|
|
||||||
case "F28", "FT8":
|
|
||||||
return 28
|
|
||||||
case "F29", "FT9":
|
|
||||||
return 29
|
|
||||||
case "F30", "FT10":
|
|
||||||
return 30
|
|
||||||
case "F31", "FT11":
|
|
||||||
return 31
|
|
||||||
default:
|
|
||||||
// Vector registers V0-V31 (the "V" extension). They share the
|
|
||||||
// register numbering with the integer file: a bare number 0-31.
|
|
||||||
if len(name) >= 2 && name[0] == 'V' {
|
|
||||||
if n, ok := parseRegDigits(name[1:], 31); ok {
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
}
|
||||||
|
return -1, riscvBankNone
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseRegDigits parses a decimal register suffix and reports whether it is
|
// parseRegDigits parses a decimal register suffix and reports whether it is
|
||||||
@@ -472,6 +511,56 @@ var riscvCvtTable = map[string]riscvCvtEnc{
|
|||||||
"FMVSX": {0x78, 0x0, 0x0, 0x53},
|
"FMVSX": {0x78, 0x0, 0x0, 0x53},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// riscvCvtBanks answers which register bank each operand of an FP
|
||||||
|
// conversion or move carries, read from the mnemonic's direction: FCVTWS
|
||||||
|
// moves float32 into an integer register (rd integer, rs1 float), FCVTSW
|
||||||
|
// the reverse, FCLASS reads a float into an integer mask and the FMV
|
||||||
|
// spellings follow their X position. The rd bank comes first.
|
||||||
|
func riscvCvtBanks(mnem string) (riscvRegBank, riscvRegBank) {
|
||||||
|
// The classify and bit-move forms.
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(mnem, "FCLASS"):
|
||||||
|
return riscvBankInt, riscvBankFloat
|
||||||
|
case strings.HasPrefix(mnem, "FMV"):
|
||||||
|
if len(mnem) > 3 && mnem[3] == 'X' {
|
||||||
|
return riscvBankInt, riscvBankFloat // FMVXD, FMVXW, FMVXS
|
||||||
|
}
|
||||||
|
return riscvBankFloat, riscvBankInt // FMVDX, FMVWX, FMVFS, FMVSX
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(mnem, "FCVT") || len(mnem) < 6 {
|
||||||
|
return riscvBankNone, riscvBankNone
|
||||||
|
}
|
||||||
|
rest := mnem[4:]
|
||||||
|
// intWU/LU + floatSDQ: float → integer.
|
||||||
|
switch rest[:2] {
|
||||||
|
case "WU", "LU":
|
||||||
|
if isRiscvWidthLetter(rest[2]) {
|
||||||
|
return riscvBankInt, riscvBankFloat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch rest[0] {
|
||||||
|
case 'W', 'L':
|
||||||
|
if isRiscvWidthLetter(rest[1]) {
|
||||||
|
return riscvBankInt, riscvBankFloat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// floatSDQ + intWU/LU: integer → float.
|
||||||
|
if isRiscvWidthLetter(rest[0]) {
|
||||||
|
switch rest[1:] {
|
||||||
|
case "W", "WU", "L", "LU":
|
||||||
|
return riscvBankFloat, riscvBankInt
|
||||||
|
}
|
||||||
|
// float → float width conversion.
|
||||||
|
return riscvBankFloat, riscvBankFloat
|
||||||
|
}
|
||||||
|
return riscvBankNone, riscvBankNone
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRiscvWidthLetter reports whether c names a float width (S, D or Q).
|
||||||
|
func isRiscvWidthLetter(c byte) bool {
|
||||||
|
return c == 'S' || c == 'D' || c == 'Q'
|
||||||
|
}
|
||||||
|
|
||||||
// riscvCvtType encodes an FP conversion instruction.
|
// riscvCvtType encodes an FP conversion instruction.
|
||||||
// Layout: funct7 | rs2(convtype) | rs1 | funct3(rm) | rd | opcode.
|
// Layout: funct7 | rs2(convtype) | rs1 | funct3(rm) | rd | opcode.
|
||||||
func riscvCvtType(enc riscvCvtEnc, rd, rs1 int) uint32 {
|
func riscvCvtType(enc riscvCvtEnc, rd, rs1 int) uint32 {
|
||||||
|
|||||||
@@ -209,12 +209,12 @@ func TestRISCVToolchainRejections(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "fence predecessor",
|
name: "fence predecessor",
|
||||||
src: "\tFENCE X, W\n",
|
src: "\tFENCE X, W\n",
|
||||||
want: "FENCE: invalid predecessor operand",
|
want: "invalid FENCE predecessor operand",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fence successor",
|
name: "fence successor",
|
||||||
src: "\tFENCE W, Q\n",
|
src: "\tFENCE W, Q\n",
|
||||||
want: "FENCE: invalid successor operand",
|
want: "invalid FENCE successor operand",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "immediate CSR pseudo with register",
|
name: "immediate CSR pseudo with register",
|
||||||
|
|||||||
Reference in new issue
Block a user