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":
|
case "NOP":
|
||||||
// The toolchain drops a bare NOP entirely.
|
// The toolchain drops a bare NOP entirely.
|
||||||
return 0
|
return 0
|
||||||
case "ANDN", "ORN", "XNOR":
|
case "ANDN", "ORN", "XNOR", "FNES", "FNED":
|
||||||
return 8
|
return 8
|
||||||
case "MAX", "MAXU", "MIN", "MINU":
|
case "MAX", "MAXU", "MIN", "MINU":
|
||||||
if riscvIdenticalMinMax(mnem, ops) {
|
if riscvIdenticalMinMax(mnem, ops) {
|
||||||
@@ -894,7 +894,7 @@ func riscvPCRelOffset(instr *ast.Instr) (int, bool) {
|
|||||||
if len(instr.Operands) != 1 && len(instr.Operands) != 2 {
|
if len(instr.Operands) != 1 && len(instr.Operands) != 2 {
|
||||||
return 0, false
|
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":
|
"BEQZ", "BNEZ", "BLTZ", "BGEZ", "BLEZ", "BGTZ":
|
||||||
if len(instr.Operands) < 2 {
|
if len(instr.Operands) < 2 {
|
||||||
return 0, false
|
return 0, false
|
||||||
@@ -1138,16 +1138,22 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
|||||||
word = riscvJType(0, offset)
|
word = riscvJType(0, offset)
|
||||||
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
|
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.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)
|
return nil, fmt.Errorf("JMP: invalid indirect operand %q", ops[0].Raw)
|
||||||
}
|
}
|
||||||
rs1 := riscvRegNum(ops[0].Addr.Base)
|
rs1 := riscvRegNum(ops[0].Addr.Base)
|
||||||
if rs1 < 0 {
|
if rs1 < 0 {
|
||||||
return nil, fmt.Errorf("JMP: unknown branch register %q", ops[0].Addr.Base)
|
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
|
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)
|
word = riscvRType(riscvEnc{0x73, 0x0, 0x09}, 0, rs1, rs2)
|
||||||
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
|
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
|
// 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
|
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 {
|
if len(ops) != 1 && len(ops) != 2 {
|
||||||
return nil, true, fmt.Errorf("%s expects 1 or 2 operands, got %d", mnem, len(ops))
|
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
|
var word uint32
|
||||||
switch mnem {
|
switch mnem {
|
||||||
case "NEG":
|
|
||||||
word = riscvRType(riscvInstrTable["SUB"], rd, 0, rs)
|
|
||||||
case "NOT":
|
case "NOT":
|
||||||
word = riscvIType(riscvInstrTable["XORI"], rd, rs, -1)
|
word = riscvIType(riscvInstrTable["XORI"], rd, rs, -1)
|
||||||
case "SEQZ":
|
|
||||||
word = riscvIType(riscvInstrTable["SLTIU"], rd, rs, 1)
|
|
||||||
}
|
}
|
||||||
return wordLE(word), true, nil
|
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))...)
|
out = append(out, wordLE(riscvRType(riscvInstrTable["OR"], rd, 31, rd))...)
|
||||||
return out, true, nil
|
return out, true, nil
|
||||||
|
|
||||||
case "BGT", "BGTU", "BLE", "BLEU":
|
// BGT/BGTU/BLE/BLEU have no extended handler: the main table's
|
||||||
// The reversed conditional branches: BGT a, b, label is BLT b, a.
|
// branch path owns them, including the N(PC) forms.
|
||||||
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
|
|
||||||
|
|
||||||
case "FABSD":
|
case "FABSS", "FABSD", "FNEGS", "FNEGD":
|
||||||
// FABSD rs, rd is FSGNJX.D (sign XOR, funct3 2) with the source in
|
// 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 {
|
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])
|
rs := regFromOperand(ops[0])
|
||||||
rd := regFromOperand(ops[1])
|
rd := regFromOperand(ops[1])
|
||||||
if rs < 0 || rd < 0 {
|
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:
|
default:
|
||||||
return encodeRISCVVector(mnem, ops)
|
return encodeRISCVVector(mnem, ops)
|
||||||
|
|||||||
@@ -810,3 +810,36 @@ TEXT ·vecarith(SB), NOSPLIT, $0
|
|||||||
}
|
}
|
||||||
assertRISCVDifferential(t, path, src, "vecarith")
|
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