fix(asm): carry the riscv64 scalar pseudos and the swapped branches
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
82e8919208
commit
955bc6643e
2 files changed
+124
-46
No files matched your search
+91
-46
@@ -437,7 +437,7 @@ func riscvExtendedSize(mnem string, ops []*ast.Operand) int {
|
||||
case "NOP":
|
||||
// The toolchain drops a bare NOP entirely.
|
||||
return 0
|
||||
case "ANDN", "ORN", "XNOR":
|
||||
case "ANDN", "ORN", "XNOR", "FNES", "FNED":
|
||||
return 8
|
||||
case "MAX", "MAXU", "MIN", "MINU":
|
||||
if riscvIdenticalMinMax(mnem, ops) {
|
||||
@@ -894,7 +894,7 @@ func riscvPCRelOffset(instr *ast.Instr) (int, bool) {
|
||||
if len(instr.Operands) != 1 && len(instr.Operands) != 2 {
|
||||
return 0, false
|
||||
}
|
||||
case "BEQ", "BNE", "BLT", "BGE", "BLTU", "BGEU",
|
||||
case "BEQ", "BNE", "BLT", "BGE", "BLTU", "BGEU", "BGT", "BLE", "BGTU", "BLEU",
|
||||
"BEQZ", "BNEZ", "BLTZ", "BGEZ", "BLEZ", "BGTZ":
|
||||
if len(instr.Operands) < 2 {
|
||||
return 0, false
|
||||
@@ -1138,16 +1138,22 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
word = riscvJType(0, offset)
|
||||
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
|
||||
}
|
||||
// JMP (X5): an indirect branch, the toolchain's JALR X0, 0(X5).
|
||||
// JMP (X5) and JMP 4(X5): an indirect branch, the toolchain's
|
||||
// JALR X0, imm(X5) with the offset carried in the I-type
|
||||
// immediate (JMP 4(X5) is 0x67804200).
|
||||
if ops[0].Addr.Sym == nil && ops[0].Addr.Base != "" {
|
||||
if ops[0].Addr.Offset != 0 || ops[0].Addr.Index != "" {
|
||||
if ops[0].Addr.Index != "" {
|
||||
return nil, fmt.Errorf("JMP: invalid indirect operand %q", ops[0].Raw)
|
||||
}
|
||||
rs1 := riscvRegNum(ops[0].Addr.Base)
|
||||
if rs1 < 0 {
|
||||
return nil, fmt.Errorf("JMP: unknown branch register %q", ops[0].Addr.Base)
|
||||
}
|
||||
word = riscvIType(riscvEnc{0x67, 0x0, 0x00}, 0, rs1, 0)
|
||||
imm := int32(ops[0].Addr.Offset)
|
||||
if imm < -2048 || imm > 2047 {
|
||||
return nil, fmt.Errorf("JMP: displacement %d does not fit in 12 bits", imm)
|
||||
}
|
||||
word = riscvIType(riscvEnc{0x67, 0x0, 0x00}, 0, rs1, imm)
|
||||
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
|
||||
}
|
||||
}
|
||||
@@ -1301,6 +1307,47 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
}
|
||||
word = riscvRType(riscvEnc{0x73, 0x0, 0x09}, 0, rs1, rs2)
|
||||
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
|
||||
|
||||
case "NEG", "NEGW":
|
||||
// INSTR rs [, rd]: SUB/SUBW with X0 in the rs1 field, the
|
||||
// one-operand form negating in place (ANEG: NEG rs, rd -> SUB rs,
|
||||
// X0, rd). The toolchain pins the bytes: NEG X5 is 0x405002b3.
|
||||
if len(ops) != 1 && len(ops) != 2 {
|
||||
return nil, fmt.Errorf("%s expects 1 or 2 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
rs := regFromOperand(ops[0])
|
||||
rd := rs
|
||||
if len(ops) == 2 {
|
||||
rd = regFromOperand(ops[1])
|
||||
}
|
||||
if rs < 0 || rd < 0 {
|
||||
return nil, fmt.Errorf("%s: invalid register operand", mnem)
|
||||
}
|
||||
enc := riscvEnc{0x33, 0x0, 0x20} // sub
|
||||
if mnem == "NEGW" {
|
||||
enc = riscvEnc{0x3B, 0x0, 0x20} // subw
|
||||
}
|
||||
word = riscvRType(enc, rd, 0, rs)
|
||||
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
|
||||
|
||||
case "SEQZ", "SNEZ":
|
||||
// INSTR rs, rd: the set-equal and set-not-equal pseudos read as
|
||||
// SLTIU $1 and SLTU against X0 (ASEQZ/ASNEZ). The toolchain pins
|
||||
// the bytes: SEQZ X14, X15 is 0x00173793.
|
||||
if len(ops) != 2 {
|
||||
return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
rs := regFromOperand(ops[0])
|
||||
rd := regFromOperand(ops[1])
|
||||
if rs < 0 || rd < 0 {
|
||||
return nil, fmt.Errorf("%s: invalid register operand", mnem)
|
||||
}
|
||||
if mnem == "SEQZ" {
|
||||
word = riscvIType(riscvEnc{0x13, 0x3, 0x00}, rd, rs, 1) // sltiu $1
|
||||
} else {
|
||||
word = riscvRType(riscvEnc{0x33, 0x3, 0x00}, rd, 0, rs) // sltu rd, x0, rs
|
||||
}
|
||||
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
|
||||
}
|
||||
|
||||
// FP conversion / move instructions use a separate table (rs2 encodes
|
||||
@@ -2767,7 +2814,9 @@ func encodeRISCVExtended(mnem string, instr *ast.Instr, pc int, offsets map[stri
|
||||
}
|
||||
return wordLE(riscvIType(riscvEnc{0x73, 0x2, 0x00}, rd, 0, 0xC01)), true, nil
|
||||
|
||||
case "NEG", "NOT", "SEQZ":
|
||||
case "NOT":
|
||||
// NEG and SEQZ have their own cases in encodeRISCVInstr's pseudo
|
||||
// switch; NOT reads as XORI $-1.
|
||||
if len(ops) != 1 && len(ops) != 2 {
|
||||
return nil, true, fmt.Errorf("%s expects 1 or 2 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
@@ -2781,12 +2830,8 @@ func encodeRISCVExtended(mnem string, instr *ast.Instr, pc int, offsets map[stri
|
||||
}
|
||||
var word uint32
|
||||
switch mnem {
|
||||
case "NEG":
|
||||
word = riscvRType(riscvInstrTable["SUB"], rd, 0, rs)
|
||||
case "NOT":
|
||||
word = riscvIType(riscvInstrTable["XORI"], rd, rs, -1)
|
||||
case "SEQZ":
|
||||
word = riscvIType(riscvInstrTable["SLTIU"], rd, rs, 1)
|
||||
}
|
||||
return wordLE(word), true, nil
|
||||
|
||||
@@ -2975,49 +3020,49 @@ func encodeRISCVExtended(mnem string, instr *ast.Instr, pc int, offsets map[stri
|
||||
out = append(out, wordLE(riscvRType(riscvInstrTable["OR"], rd, 31, rd))...)
|
||||
return out, true, nil
|
||||
|
||||
case "BGT", "BGTU", "BLE", "BLEU":
|
||||
// The reversed conditional branches: BGT a, b, label is BLT b, a.
|
||||
if len(ops) != 3 {
|
||||
return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
a := regFromOperand(ops[0])
|
||||
b := regFromOperand(ops[1])
|
||||
if a < 0 || b < 0 {
|
||||
return nil, true, fmt.Errorf("%s: invalid register", mnem)
|
||||
}
|
||||
target := labelFromOperand(ops[2])
|
||||
targetOff, ok := offsets[target]
|
||||
if !ok {
|
||||
return nil, true, fmt.Errorf("undefined label %q%s", target, suggestLabel(target, offsets))
|
||||
}
|
||||
offset := int32(targetOff - pc)
|
||||
if err := riscvCheckBranchOffset(target, offset); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
var enc riscvEnc
|
||||
switch mnem {
|
||||
case "BGT":
|
||||
enc = riscvEnc{0x63, 0x4, 0x00} // blt b, a
|
||||
case "BGTU":
|
||||
enc = riscvEnc{0x63, 0x6, 0x00} // bltu b, a
|
||||
case "BLE":
|
||||
enc = riscvEnc{0x63, 0x5, 0x00} // bge b, a
|
||||
case "BLEU":
|
||||
enc = riscvEnc{0x63, 0x7, 0x00} // bgeu b, a
|
||||
}
|
||||
return wordLE(riscvBType(enc, b, a, offset)), true, nil
|
||||
// BGT/BGTU/BLE/BLEU have no extended handler: the main table's
|
||||
// branch path owns them, including the N(PC) forms.
|
||||
|
||||
case "FABSD":
|
||||
// FABSD rs, rd is FSGNJX.D (sign XOR, funct3 2) with the source in
|
||||
case "FABSS", "FABSD", "FNEGS", "FNEGD":
|
||||
// INSTR fs, fd: the sign-injection pseudos, the source in both the
|
||||
// rs1 and rs2 fields (AFABSS: FSGNJXS rs, rs, rd; AFNEGS: FSGNJNS).
|
||||
// The toolchain pins the bytes: FABSS F0, F1 is 0x200020d3.
|
||||
if len(ops) != 2 {
|
||||
return nil, true, fmt.Errorf("FABSD expects 2 operands, got %d", len(ops))
|
||||
return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
rs := regFromOperand(ops[0])
|
||||
rd := regFromOperand(ops[1])
|
||||
if rs < 0 || rd < 0 {
|
||||
return nil, true, fmt.Errorf("FABSD: invalid register")
|
||||
return nil, true, fmt.Errorf("%s: invalid register", mnem)
|
||||
}
|
||||
return wordLE(riscvRType(riscvEnc{0x53, 0x2, 0x11}, rd, rs, rs)), true, nil
|
||||
enc := map[string]riscvEnc{
|
||||
"FABSS": {0x53, 0x2, 0x10}, // fsgnjx.s
|
||||
"FABSD": {0x53, 0x2, 0x11}, // fsgnjx.d
|
||||
"FNEGS": {0x53, 0x1, 0x10}, // fsgnjn.s
|
||||
"FNEGD": {0x53, 0x1, 0x11}, // fsgnjn.d
|
||||
}[mnem]
|
||||
return wordLE(riscvRType(enc, rd, rs, rs)), true, nil
|
||||
|
||||
case "FNES", "FNED":
|
||||
// INSTR fs1, fs2, xrd: the not-equal pseudos read as FEQ.S/FEQ.D
|
||||
// followed by XORI $1 on the result register (AFNES), two words.
|
||||
if len(ops) != 3 {
|
||||
return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
rs2 := regFromOperand(ops[0])
|
||||
rs1 := regFromOperand(ops[1])
|
||||
rd := regFromOperand(ops[2])
|
||||
if rs1 < 0 || rs2 < 0 || rd < 0 {
|
||||
return nil, true, fmt.Errorf("%s: invalid register", mnem)
|
||||
}
|
||||
feq := riscvEnc{0x53, 0x2, 0x50} // feq.s
|
||||
if mnem == "FNED" {
|
||||
feq = riscvEnc{0x53, 0x2, 0x51} // feq.d
|
||||
}
|
||||
eq := riscvRType(feq, rd, rs1, rs2)
|
||||
not := riscvIType(riscvEnc{0x13, 0x4, 0x00}, rd, rd, 1) // xori $1
|
||||
return []byte{byte(eq), byte(eq >> 8), byte(eq >> 16), byte(eq >> 24),
|
||||
byte(not), byte(not >> 8), byte(not >> 16), byte(not >> 24)}, true, nil
|
||||
|
||||
default:
|
||||
return encodeRISCVVector(mnem, ops)
|
||||
|
||||
@@ -810,3 +810,36 @@ TEXT ·vecarith(SB), NOSPLIT, $0
|
||||
}
|
||||
assertRISCVDifferential(t, path, src, "vecarith")
|
||||
}
|
||||
|
||||
// TestRISCVScalarPseudos_Differential proves the scalar pseudo tail against
|
||||
// the toolchain: the NEG/NEGW and SEQZ/SNEZ one- and two-operand forms, the
|
||||
// sign-injection pseudos and the two-word not-equal expansions, exactly as
|
||||
// the toolchain's own testdata spells them.
|
||||
func TestRISCVScalarPseudos_Differential(t *testing.T) {
|
||||
src := `#include "textflag.h"
|
||||
|
||||
TEXT ·pseudos(SB), NOSPLIT, $0-8
|
||||
NEG X5
|
||||
NEG X5, X6
|
||||
NEGW X5
|
||||
NEGW X5, X6
|
||||
SEQZ X14, X15
|
||||
SNEZ X14, X15
|
||||
FABSS F0, F1
|
||||
FNEGS F0, F1
|
||||
FNES F0, F1, X7
|
||||
FABSD F0, F1
|
||||
FNEGD F0, F1
|
||||
FNED F0, F1, X5
|
||||
FLTD F0, F1, X5
|
||||
FLED F0, F1, X5
|
||||
FEQD F0, F1, X5
|
||||
RET
|
||||
`
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "pseudos_riscv64.s")
|
||||
if err := os.WriteFile(path, []byte(src), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertRISCVDifferential(t, path, src, "pseudos")
|
||||
}
|
||||
Reference in new issue
Block a user