feat(asm): encode the amd64 and loong64 tails of the corpus testdata
Assisted-by: GLM 5.3
This commit is contained in:
+75
-3
@@ -73,9 +73,23 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
||||
// Fixed-name instructions (no size suffix).
|
||||
switch {
|
||||
case upper == "RET":
|
||||
// RET sym(SB), the absolute return: the toolchain encodes it as a
|
||||
// tail jump, E9 rel32 with a call relocation against the symbol.
|
||||
if len(ops) == 1 {
|
||||
if m, ok := ops[0].(sbMem); ok {
|
||||
return e.emit(&instr{opcode: []byte{0xE9}, modrm: -1, sib: -1, disp: le32(0), sb: &sbRef{name: m.name, addend: m.addend}})
|
||||
}
|
||||
return fmt.Errorf("RET: unsupported operand")
|
||||
}
|
||||
if len(ops) != 0 {
|
||||
return fmt.Errorf("RET expects no operands, got %d", len(ops))
|
||||
}
|
||||
return e.encodeRet()
|
||||
case upper == "NOP":
|
||||
return e.emit(&instr{opcode: []byte{0x90}, modrm: -1, sib: -1})
|
||||
// The toolchain consumes every NOP statement as a pseudo and emits
|
||||
// nothing for it, operands included (a bare NOP, NOP AX and
|
||||
// NOP sym(SB) all vanish from the object).
|
||||
return nil
|
||||
case upper == "CALL" || upper == "JMP":
|
||||
// Through a register or memory: FF /2 (CALL) or FF /4 (JMP).
|
||||
// Anything else is a rel32 against a label resolved by the assembler.
|
||||
@@ -102,6 +116,15 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
||||
}
|
||||
return e.emit(&instr{opcode: op, modrm: -1, sib: -1})
|
||||
}
|
||||
// One-operand system instructions whose reg field is a fixed digit:
|
||||
// the cache and wait controls under 0F AE/0F 1C and the RDPID read.
|
||||
if m, ok := sysUnaryTable[upper]; ok {
|
||||
return e.encodeSysUnary(upper, m, ops)
|
||||
}
|
||||
// The store-only SSE moves (the non-temporal store).
|
||||
if m, ok := sseStoreOnly[upper]; ok {
|
||||
return e.encodeSSEStoreOnly(upper, m, ops)
|
||||
}
|
||||
// POPFQ/PUSHFQ are exact names: the bare POPF/PUSHF and the L spellings
|
||||
// are rejected by go tool asm in 64-bit mode, so they stay unsupported.
|
||||
switch upper {
|
||||
@@ -117,14 +140,63 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
||||
return e.emit(&instr{opcode: []byte{0x9C}, modrm: -1, sib: -1})
|
||||
case "INT":
|
||||
return e.encodeInt(ops)
|
||||
// The LOOP family outside the assembler's label settlement: the operand
|
||||
// is the already-computed rel8 (E0-E2).
|
||||
case "LOOP", "LOOPE", "LOOPNE":
|
||||
if len(ops) != 1 {
|
||||
return fmt.Errorf("%s expects 1 operand, got %d", upper, len(ops))
|
||||
}
|
||||
imm, ok := ops[0].(Imm)
|
||||
if !ok || !fits8(int64(imm)) {
|
||||
return fmt.Errorf("%s: relative offset must be a signed byte", upper)
|
||||
}
|
||||
return e.emit(&instr{opcode: []byte{loopOpcode(upper)}, modrm: -1, sib: -1, imm: []byte{byte(int8(imm))}})
|
||||
case "LDMXCSR":
|
||||
return e.encodeMxcsr(2, ops)
|
||||
case "STMXCSR":
|
||||
return e.encodeMxcsr(3, ops)
|
||||
// CMPSD is the scalar double compare, whose predicate immediate comes
|
||||
// LAST in Plan 9 order (src, dst, $imm).
|
||||
// LAST in Plan 9 order (src, dst, $imm); the family shares the shape.
|
||||
case "CMPSD":
|
||||
return e.encodeCmpsd(ops)
|
||||
return e.encodeSSECmp("CMPSD", 0xF2, ops)
|
||||
case "CMPSS":
|
||||
return e.encodeSSECmp("CMPSS", 0xF3, ops)
|
||||
case "CMPPS":
|
||||
return e.encodeSSECmp("CMPPS", 0x00, ops)
|
||||
case "CMPPD":
|
||||
return e.encodeSSECmp("CMPPD", 0x66, ops)
|
||||
// RETFL pops the immediate's worth of bytes after the far return
|
||||
// (LRET iw: CA imm16), the toolchain's RETF spelling with a stack
|
||||
// adjustment.
|
||||
case "RETFL":
|
||||
if len(ops) != 1 {
|
||||
return fmt.Errorf("RETFL expects 1 operand, got %d", len(ops))
|
||||
}
|
||||
imm, ok := ops[0].(Imm)
|
||||
if !ok {
|
||||
return fmt.Errorf("RETFL expects an immediate")
|
||||
}
|
||||
return e.emit(&instr{opcode: []byte{0xCA}, modrm: -1, sib: -1, imm: le16(int64(imm))})
|
||||
// MOVDQ2Q/MOVQ2DQ cross the MMX and XMM banks (F2 0F D6), the register
|
||||
// in the reg field, the other bank's in r/m.
|
||||
case "MOVDQ2Q", "MOVQ2DQ":
|
||||
if len(ops) != 2 {
|
||||
return fmt.Errorf("%s expects 2 operands, got %d", upper, len(ops))
|
||||
}
|
||||
srcReg, ok1 := ops[0].(Reg)
|
||||
dstReg, ok2 := ops[1].(Reg)
|
||||
if !ok1 || !ok2 {
|
||||
return fmt.Errorf("%s takes register operands alone", upper)
|
||||
}
|
||||
if upper == "MOVDQ2Q" && (!srcReg.isVec() || !dstReg.mmx) ||
|
||||
upper == "MOVQ2DQ" && (!srcReg.mmx || !dstReg.isVec()) {
|
||||
return fmt.Errorf("%s crosses the XMM and MMX banks in that order", upper)
|
||||
}
|
||||
i := &instr{prefix: 0xF2, opcode: []byte{0x0F, 0xD6}, modrm: -1, sib: -1}
|
||||
if err := setRM(i, dstReg, srcReg, 8); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.emit(i)
|
||||
// SHA256RNDS2 carries the round constant in a literal X0 first operand.
|
||||
case "SHA256RNDS2":
|
||||
return e.encodeSha256rnds2(ops)
|
||||
|
||||
Reference in New Issue
Block a user