feat(amd64): encode the GOROOT instruction families
Assisted-by: GLM 5.3 Flash
This commit is contained in:
+81
-6
@@ -58,6 +58,41 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
||||
if cc, ok := condCode(upper); ok {
|
||||
return e.encodeJcc(cc, ops)
|
||||
}
|
||||
// No-operand system and string-control instructions (CPUID, RDTSC,
|
||||
// SYSCALL, the fences, UNDEF, …).
|
||||
if op, ok := noOperandTable[upper]; ok {
|
||||
if len(ops) != 0 {
|
||||
return fmt.Errorf("%s takes no operands, got %d", upper, len(ops))
|
||||
}
|
||||
return e.emit(&instr{opcode: op, modrm: -1, sib: -1})
|
||||
}
|
||||
// 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 {
|
||||
case "POPFQ":
|
||||
if len(ops) != 0 {
|
||||
return fmt.Errorf("POPFQ takes no operands, got %d", len(ops))
|
||||
}
|
||||
return e.emit(&instr{opcode: []byte{0x9D}, modrm: -1, sib: -1})
|
||||
case "PUSHFQ":
|
||||
if len(ops) != 0 {
|
||||
return fmt.Errorf("PUSHFQ takes no operands, got %d", len(ops))
|
||||
}
|
||||
return e.emit(&instr{opcode: []byte{0x9C}, modrm: -1, sib: -1})
|
||||
case "INT":
|
||||
return e.encodeInt(ops)
|
||||
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).
|
||||
case "CMPSD":
|
||||
return e.encodeCmpsd(ops)
|
||||
// SHA256RNDS2 carries the round constant in a literal X0 first operand.
|
||||
case "SHA256RNDS2":
|
||||
return e.encodeSha256rnds2(ops)
|
||||
}
|
||||
|
||||
// VEX (AVX/AVX2) and EVEX (AVX-512) instructions: the trailing
|
||||
// B/W/L/Q/D is part of the mnemonic, not a size suffix, so dispatch
|
||||
@@ -67,7 +102,8 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isVex(base) || isEvex(base) || isKOp(base) || isGather(base) || isScatter(base) || base == "KMOVW" || base == "KMOVQ" {
|
||||
if isVex(base) || isEvex(base) || isKOp(base) || isGather(base) || isScatter(base) ||
|
||||
base == "KMOVW" || base == "KMOVQ" || base == "KMOVB" || base == "KMOVD" {
|
||||
return e.encodeVec(base, ops, sfx)
|
||||
}
|
||||
if sfx.any() {
|
||||
@@ -101,6 +137,21 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
||||
if m, ok := sseBinTable[base]; ok {
|
||||
return e.encodeSSEBin(m, ops)
|
||||
}
|
||||
// The imm8-controlled legacy instructions, the lane extracts and inserts
|
||||
// and the packed integer shifts all dispatch on the full name: a trailing
|
||||
// width letter here belongs to the mnemonic, not to the size split.
|
||||
if m, ok := sseImm3Table[upper]; ok {
|
||||
return e.encodeSSEImm3(m, ops)
|
||||
}
|
||||
if m, ok := sseExtractTable[upper]; ok {
|
||||
return e.encodeSSEExtract(m, ops)
|
||||
}
|
||||
if m, ok := sseInsertTable[upper]; ok {
|
||||
return e.encodeSSEInsert(m, ops)
|
||||
}
|
||||
if _, ok := sseShiftImm[upper]; ok {
|
||||
return e.encodeSSEShift(upper, ops)
|
||||
}
|
||||
// PMOVMSKB ends in a width letter the size split would eat, so it
|
||||
// dispatches on the full name like the packed binaries above.
|
||||
if upper == "PMOVMSKB" {
|
||||
@@ -109,16 +160,36 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
||||
switch base {
|
||||
case "MOV":
|
||||
return e.encodeMov(ops, size)
|
||||
case "ADD", "SUB", "AND", "OR", "XOR", "CMP":
|
||||
// MOVD is the Go assembler's alias of MOVQ: the same byte forms, 64-bit
|
||||
// REX.W and all.
|
||||
case "MOVD":
|
||||
return e.encodeMov(ops, 8)
|
||||
case "ADD", "SUB", "AND", "OR", "XOR", "CMP", "ADC", "SBB":
|
||||
return e.encodeALU(aluOp[base], ops, size)
|
||||
case "TEST":
|
||||
return e.encodeTest(ops, size)
|
||||
case "LEA":
|
||||
return e.encodeLea(ops, size)
|
||||
case "INC", "DEC", "NEG", "NOT":
|
||||
case "INC", "DEC", "NEG", "NOT", "MUL", "DIV", "IDIV":
|
||||
return e.encodeUnary(unaryOp[base], ops, size)
|
||||
case "SHL", "SHR", "SAR":
|
||||
case "SHL", "SHR", "SAR", "SAL", "ROL", "ROR", "RCL", "RCR":
|
||||
return e.encodeShift(shiftOp[base], ops, size)
|
||||
case "BT", "BTS", "BTR", "BTC":
|
||||
return e.encodeBitTest(base, ops, size)
|
||||
case "XCHG":
|
||||
return e.encodeExchange(ops, size)
|
||||
case "CMPXCHG":
|
||||
return e.encodeRegRegOp(0xB0, 0xB1, base, ops, size)
|
||||
case "XADD":
|
||||
return e.encodeRegRegOp(0xC0, 0xC1, base, ops, size)
|
||||
case "CRC32":
|
||||
return e.encodeCrc32(ops, size)
|
||||
case "ADCX":
|
||||
return e.encodeCarryExt(0x66, ops, size)
|
||||
case "ADOX":
|
||||
return e.encodeCarryExt(0xF3, ops, size)
|
||||
case "MOVS", "STOS":
|
||||
return e.encodeStringOp(base, ops, size)
|
||||
case "IMUL", "IMUL3":
|
||||
return e.encodeImul(ops, size)
|
||||
case "PUSH":
|
||||
@@ -136,7 +207,11 @@ func (e *enc) encode(mnem string, ops []Operand) error {
|
||||
return e.encodeMovExtend(base, ops)
|
||||
case "CVTSL2SD", "CVTSQ2SD":
|
||||
return e.encodeCvtsi2sd(base == "CVTSQ2SD", ops)
|
||||
case "MOVOU", "MOVO", "MOVUPS", "MOVAPS", "MOVUPD", "MOVAPD", "MOVSD", "MOVSS":
|
||||
case "CVTSD2S", "CVTTSD2S", "CVTSS2S", "CVTTSS2S":
|
||||
return e.encodeCvtInt(base, ops, size)
|
||||
case "FMOVD":
|
||||
return e.encodeFmov(ops)
|
||||
case "MOVOU", "MOVO", "MOVOA", "MOVUPS", "MOVAPS", "MOVUPD", "MOVAPD", "MOVSD", "MOVSS":
|
||||
return e.encodeSSEMove(sseMoveTable[base], ops)
|
||||
}
|
||||
return fmt.Errorf("unsupported instruction %q", mnem)
|
||||
@@ -195,7 +270,7 @@ func (e *enc) encodeVec(upper string, ops []Operand, sfx evexSuffix) error {
|
||||
if ss, ok := scatterTable[upper]; ok {
|
||||
return e.encodeScatter(upper, ss, ops, sfx)
|
||||
}
|
||||
if upper == "KMOVW" || upper == "KMOVQ" {
|
||||
if upper == "KMOVW" || upper == "KMOVQ" || upper == "KMOVB" || upper == "KMOVD" {
|
||||
if sfx.any() {
|
||||
return fmt.Errorf("%s takes no EVEX suffixes", upper)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user