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
|
||||
|
||||
import "strings"
|
||||
|
||||
// RISC-V register encoding: maps register names to their 5-bit numbers.
|
||||
// The Go assembler uses the standard RISC-V ABI naming.
|
||||
|
||||
// riscvRegNum returns the 5-bit register number for a RISC-V register name.
|
||||
// Returns -1 if the register is not recognized.
|
||||
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 {
|
||||
// Numbered integer registers.
|
||||
case "X0", "ZERO":
|
||||
return 0
|
||||
return 0, riscvBankInt
|
||||
case "X1", "RA", "LR":
|
||||
return 1
|
||||
return 1, riscvBankInt
|
||||
case "X2", "SP":
|
||||
return 2
|
||||
return 2, riscvBankInt
|
||||
case "X3", "GP":
|
||||
return 3
|
||||
return 3, riscvBankInt
|
||||
case "X4", "TP":
|
||||
return 4
|
||||
return 4, riscvBankInt
|
||||
case "X5", "T0":
|
||||
return 5
|
||||
return 5, riscvBankInt
|
||||
case "X6", "T1":
|
||||
return 6
|
||||
return 6, riscvBankInt
|
||||
case "X7", "T2":
|
||||
return 7
|
||||
return 7, riscvBankInt
|
||||
case "X8", "S0", "FP":
|
||||
return 8
|
||||
return 8, riscvBankInt
|
||||
case "X9", "S1":
|
||||
return 9
|
||||
return 9, riscvBankInt
|
||||
case "X10", "A0":
|
||||
return 10
|
||||
return 10, riscvBankInt
|
||||
case "X11", "A1":
|
||||
return 11
|
||||
return 11, riscvBankInt
|
||||
case "X12", "A2":
|
||||
return 12
|
||||
return 12, riscvBankInt
|
||||
case "X13", "A3":
|
||||
return 13
|
||||
return 13, riscvBankInt
|
||||
case "X14", "A4":
|
||||
return 14
|
||||
return 14, riscvBankInt
|
||||
case "X15", "A5":
|
||||
return 15
|
||||
return 15, riscvBankInt
|
||||
case "X16", "A6":
|
||||
return 16
|
||||
return 16, riscvBankInt
|
||||
case "X17", "A7":
|
||||
return 17
|
||||
return 17, riscvBankInt
|
||||
case "X18", "S2":
|
||||
return 18
|
||||
return 18, riscvBankInt
|
||||
case "X19", "S3":
|
||||
return 19
|
||||
return 19, riscvBankInt
|
||||
case "X20", "S4":
|
||||
return 20
|
||||
return 20, riscvBankInt
|
||||
case "X21", "S5":
|
||||
return 21
|
||||
return 21, riscvBankInt
|
||||
case "X22", "S6":
|
||||
return 22
|
||||
return 22, riscvBankInt
|
||||
case "X23", "S7":
|
||||
return 23
|
||||
return 23, riscvBankInt
|
||||
case "X24", "S8":
|
||||
return 24
|
||||
return 24, riscvBankInt
|
||||
case "X25", "S9":
|
||||
return 25
|
||||
return 25, riscvBankInt
|
||||
case "X26", "S10", "CTXT":
|
||||
return 26
|
||||
return 26, riscvBankInt
|
||||
case "X27", "S11", "g":
|
||||
return 27
|
||||
return 27, riscvBankInt
|
||||
case "X28", "T3":
|
||||
return 28
|
||||
return 28, riscvBankInt
|
||||
case "X29", "T4":
|
||||
return 29
|
||||
return 29, riscvBankInt
|
||||
case "X30", "T5":
|
||||
return 30
|
||||
return 30, riscvBankInt
|
||||
case "X31", "T6", "TMP":
|
||||
return 31
|
||||
// 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 31, riscvBankInt
|
||||
}
|
||||
return -1, riscvBankNone
|
||||
}
|
||||
|
||||
// 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},
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Layout: funct7 | rs2(convtype) | rs1 | funct3(rm) | rd | opcode.
|
||||
func riscvCvtType(enc riscvCvtEnc, rd, rs1 int) uint32 {
|
||||
|
||||
@@ -209,12 +209,12 @@ func TestRISCVToolchainRejections(t *testing.T) {
|
||||
{
|
||||
name: "fence predecessor",
|
||||
src: "\tFENCE X, W\n",
|
||||
want: "FENCE: invalid predecessor operand",
|
||||
want: "invalid FENCE predecessor operand",
|
||||
},
|
||||
{
|
||||
name: "fence successor",
|
||||
src: "\tFENCE W, Q\n",
|
||||
want: "FENCE: invalid successor operand",
|
||||
want: "invalid FENCE successor operand",
|
||||
},
|
||||
{
|
||||
name: "immediate CSR pseudo with register",
|
||||
|
||||
Reference in new issue
Block a user