318 lines
12 KiB
Go
318 lines
12 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|||
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
|
||
|
|
package arch
|
||
|
|
|
||
|
|
import "fmt"
|
||
|
|
|
||
|
|
func buildAMD64() *Table {
|
||
|
|
return newTable(AMD64, amd64Registers(), mergedInstrs(amd64Summaries(), commonGeneratedInstrs, amd64GeneratedInstrs, amd64Aliases()))
|
||
|
|
}
|
||
|
|
|
||
|
|
// amd64Aliases are the traditional x86 conditional-jump spellings (plus a few
|
||
|
|
// instruction aliases) that Go's assembler accepts and maps onto its canonical
|
||
|
|
// opcodes. They are user-writable but absent from the generated opcode table,
|
||
|
|
// so they are listed explicitly here.
|
||
|
|
func amd64Aliases() []string {
|
||
|
|
return []string{
|
||
|
|
"JA", "JAE", "JB", "JBE", "JC", "JCC", "JCS", "JE", "JG", "JHI", "JHS",
|
||
|
|
"JL", "JLO", "JLS", "JMI", "JNA", "JNAE", "JNB", "JNBE", "JNC", "JNG",
|
||
|
|
"JNGE", "JNL", "JNLE", "JNO", "JNP", "JNS", "JNZ", "JO", "JOC", "JOS",
|
||
|
|
"JP", "JPC", "JPE", "JPL", "JPO", "JPS", "JS", "JZ",
|
||
|
|
"MASKMOVDQU", "MOVDQ2Q", "MOVNTDQ", "MOVOA", "PSLLDQ", "PSRLDQ",
|
||
|
|
"MOVD", "PADDD", "MOVBELL", "MOVBEQQ", "MOVBEWW",
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// amd64Summaries returns the curated documentation/operand-count table keyed by
|
||
|
|
// upper-case mnemonic. It enriches the complete generated name list; names
|
||
|
|
// without a curated entry are still recognised, just without a summary.
|
||
|
|
func amd64Summaries() map[string]Instr { return toMap(amd64Curated()) }
|
||
|
|
|
||
|
|
// amd64Registers builds the amd64 register file. Numbered registers are
|
||
|
|
// generated; the irregularly named ones are listed explicitly.
|
||
|
|
func amd64Registers() []Register {
|
||
|
|
var regs []Register
|
||
|
|
add := func(name string, class RegClass, desc string) {
|
||
|
|
regs = append(regs, Register{Name: name, Class: class, Desc: desc})
|
||
|
|
}
|
||
|
|
|
||
|
|
// 64-bit general-purpose registers.
|
||
|
|
for _, n := range []string{"AX", "BX", "CX", "DX", "SI", "DI", "BP", "SP"} {
|
||
|
|
add(n, GPR, "64-bit general-purpose register")
|
||
|
|
}
|
||
|
|
for i := 8; i <= 15; i++ {
|
||
|
|
add(fmt.Sprintf("R%d", i), GPR, "64-bit general-purpose register")
|
||
|
|
}
|
||
|
|
// 8-bit low/high sub-registers.
|
||
|
|
for _, n := range []string{"AL", "BL", "CL", "DL", "SIL", "DIL", "BPL", "SPL"} {
|
||
|
|
add(n, GPRSub, "8-bit low sub-register")
|
||
|
|
}
|
||
|
|
for _, n := range []string{"AH", "BH", "CH", "DH"} {
|
||
|
|
add(n, GPRSub, "8-bit high sub-register")
|
||
|
|
}
|
||
|
|
// Sized numbered sub-registers.
|
||
|
|
for i := 8; i <= 15; i++ {
|
||
|
|
add(fmt.Sprintf("R%dB", i), GPRSub, "8-bit sub-register")
|
||
|
|
add(fmt.Sprintf("R%dW", i), GPRSub, "16-bit sub-register")
|
||
|
|
add(fmt.Sprintf("R%dD", i), GPRSub, "32-bit sub-register")
|
||
|
|
}
|
||
|
|
// SIMD vector registers: X (SSE), Y (AVX2), Z (AVX-512).
|
||
|
|
for i := 0; i <= 15; i++ {
|
||
|
|
add(fmt.Sprintf("X%d", i), Vector, "128-bit SSE/AVX vector register")
|
||
|
|
add(fmt.Sprintf("Y%d", i), Vector, "256-bit AVX2 vector register")
|
||
|
|
add(fmt.Sprintf("Z%d", i), Vector, "512-bit AVX-512 vector register")
|
||
|
|
}
|
||
|
|
for i := 16; i <= 31; i++ {
|
||
|
|
add(fmt.Sprintf("Z%d", i), Vector, "512-bit AVX-512 vector register")
|
||
|
|
}
|
||
|
|
// AVX-512 mask registers.
|
||
|
|
for i := 0; i <= 7; i++ {
|
||
|
|
add(fmt.Sprintf("K%d", i), Mask, "AVX-512 mask register")
|
||
|
|
}
|
||
|
|
return regs
|
||
|
|
}
|
||
|
|
|
||
|
|
// i builds an instruction with an unknown/variable operand count.
|
||
|
|
func i(name, summary string) Instr {
|
||
|
|
return Instr{Name: name, Summary: summary, MinOps: -1, MaxOps: -1}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ic builds an instruction with an explicit operand-count range.
|
||
|
|
func ic(name, summary string, min, max int) Instr {
|
||
|
|
return Instr{Name: name, Summary: summary, MinOps: min, MaxOps: max}
|
||
|
|
}
|
||
|
|
|
||
|
|
// amd64Curated returns the hand-written subset of amd64 instructions that carry
|
||
|
|
// a summary and/or an explicit operand-count range. The authoritative,
|
||
|
|
// complete instruction set is amd64GeneratedInstrs (see amd64_gen.go).
|
||
|
|
func amd64Curated() []Instr {
|
||
|
|
var t []Instr
|
||
|
|
|
||
|
|
// Data movement.
|
||
|
|
for _, s := range []string{"B", "W", "L", "Q"} {
|
||
|
|
t = append(t, ic("MOV"+s, "Move "+s+"-width value", 2, 2))
|
||
|
|
}
|
||
|
|
for _, m := range []string{
|
||
|
|
"MOVBLZX", "MOVBQSX", "MOVWLZX", "MOVWQSX", "MOVWLSX", "MOVLQSX", "MOVBLSX", "MOVQL",
|
||
|
|
} {
|
||
|
|
t = append(t, ic(m, "Sign/zero-extending move", 2, 2))
|
||
|
|
}
|
||
|
|
for _, m := range []string{"MOVO", "MOVOU"} {
|
||
|
|
t = append(t, ic(m, "Move 16-byte aligned/unaligned vector", 2, 2))
|
||
|
|
}
|
||
|
|
for _, s := range []string{"B", "W", "L", "Q"} {
|
||
|
|
t = append(t, ic("LEA"+s, "Load effective address", 2, 2))
|
||
|
|
}
|
||
|
|
for _, s := range []string{"B", "W", "L", "Q"} {
|
||
|
|
t = append(t, ic("XCHG"+s, "Exchange operands", 2, 2))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Integer arithmetic and logic.
|
||
|
|
for _, op := range []string{"ADD", "SUB", "AND", "OR", "XOR", "ADC", "SBB"} {
|
||
|
|
for _, s := range []string{"B", "W", "L", "Q"} {
|
||
|
|
t = append(t, ic(op+s, op+" integer", 2, 2))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for _, s := range []string{"B", "W", "L", "Q"} {
|
||
|
|
t = append(t, ic("INC"+s, "Increment", 1, 1))
|
||
|
|
t = append(t, ic("DEC"+s, "Decrement", 1, 1))
|
||
|
|
t = append(t, ic("NEG"+s, "Two's-complement negate", 1, 1))
|
||
|
|
t = append(t, ic("NOT"+s, "Bitwise complement", 1, 1))
|
||
|
|
}
|
||
|
|
for _, s := range []string{"B", "W", "L", "Q"} {
|
||
|
|
t = append(t, i("IMUL"+s, "Signed multiply"))
|
||
|
|
t = append(t, ic("IMUL3"+s, "Signed multiply by immediate", 3, 3))
|
||
|
|
t = append(t, ic("MUL"+s, "Unsigned multiply", 1, 1))
|
||
|
|
t = append(t, ic("DIV"+s, "Unsigned divide", 1, 1))
|
||
|
|
t = append(t, ic("IDIV"+s, "Signed divide", 1, 1))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Shifts and rotates.
|
||
|
|
for _, op := range []string{"SHL", "SHR", "SAR", "SAL", "ROL", "ROR", "RCL", "RCR"} {
|
||
|
|
for _, s := range []string{"B", "W", "L", "Q"} {
|
||
|
|
t = append(t, i(op+s, op+" shift/rotate"))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for _, s := range []string{"W", "L", "Q"} {
|
||
|
|
t = append(t, i("SHLD"+s, "Double-precision left shift"))
|
||
|
|
t = append(t, i("SHRD"+s, "Double-precision right shift"))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Compare and test.
|
||
|
|
for _, s := range []string{"B", "W", "L", "Q"} {
|
||
|
|
t = append(t, ic("CMP"+s, "Compare (subtract, flags only)", 2, 2))
|
||
|
|
t = append(t, ic("TEST"+s, "AND, flags only", 2, 2))
|
||
|
|
}
|
||
|
|
for _, op := range []string{"BT", "BTS", "BTR", "BTC"} {
|
||
|
|
for _, s := range []string{"W", "L", "Q"} {
|
||
|
|
t = append(t, i(op+s, "Bit test"+op[1:]))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Control flow.
|
||
|
|
t = append(t, ic("JMP", "Unconditional jump", 1, 1))
|
||
|
|
for _, cc := range []string{
|
||
|
|
"EQ", "NE", "Z", "NZ", "L", "LE", "G", "GE", "LT", "GT", "MI", "PL",
|
||
|
|
"B", "BE", "A", "AE", "CS", "CC", "HI", "LS", "C", "NC",
|
||
|
|
"S", "NS", "O", "NO", "P", "NP", "PE", "PO", "OS", "OC",
|
||
|
|
"CXZ", "ECXZ", "RCXZ",
|
||
|
|
} {
|
||
|
|
t = append(t, ic("J"+cc, "Conditional jump", 1, 1))
|
||
|
|
}
|
||
|
|
t = append(t, ic("CALL", "Call subroutine", 1, 1))
|
||
|
|
t = append(t, ic("RET", "Return from subroutine", 0, 0))
|
||
|
|
t = append(t, ic("RETF", "Far return", 0, 0))
|
||
|
|
t = append(t, ic("NOP", "No operation", 0, 1))
|
||
|
|
t = append(t, i("INT", "Software interrupt"))
|
||
|
|
t = append(t, ic("SYSCALL", "System call", 0, 0))
|
||
|
|
t = append(t, ic("HLT", "Halt", 0, 0))
|
||
|
|
t = append(t, ic("UD2", "Undefined instruction (trap)", 0, 0))
|
||
|
|
|
||
|
|
// Conditional set and move.
|
||
|
|
for _, cc := range []string{
|
||
|
|
"EQ", "NE", "L", "LE", "G", "GE", "LT", "GT", "B", "BE", "A", "AE",
|
||
|
|
"CS", "CC", "HI", "LS", "S", "NS", "O", "NO", "P", "NP", "MI", "PL",
|
||
|
|
} {
|
||
|
|
t = append(t, ic("SET"+cc, "Set byte on condition", 1, 1))
|
||
|
|
}
|
||
|
|
for _, s := range []string{"L", "Q", "W"} {
|
||
|
|
for _, cc := range []string{"EQ", "NE", "LT", "LE", "GT", "GE"} {
|
||
|
|
t = append(t, ic("CMOV"+s+cc, "Conditional move", 2, 2))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Bit scanning and counting.
|
||
|
|
for _, op := range []string{"LZCNT", "TZCNT", "POPCNT", "BSF", "BSR"} {
|
||
|
|
for _, s := range []string{"W", "L", "Q"} {
|
||
|
|
t = append(t, ic(op+s, op+" bit operation", 2, 2))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for _, s := range []string{"L", "Q"} {
|
||
|
|
t = append(t, ic("BSWAP"+s, "Byte-swap", 1, 1))
|
||
|
|
}
|
||
|
|
for _, m := range []string{"CDQ", "CQO", "CBW", "CWDE", "CDQE"} {
|
||
|
|
t = append(t, ic(m, "Sign-extend accumulator", 0, 0))
|
||
|
|
}
|
||
|
|
for _, m := range []string{"CPUID", "RDTSC", "LFENCE", "SFENCE", "MFENCE", "PAUSE"} {
|
||
|
|
t = append(t, ic(m, "Serialising/system instruction", 0, 0))
|
||
|
|
}
|
||
|
|
|
||
|
|
// SIMD data movement.
|
||
|
|
for _, m := range []string{
|
||
|
|
"VMOVDQU", "VMOVDQA", "VMOVUPS", "VMOVUPD", "VMOVAPS", "VMOVAPD",
|
||
|
|
"VMOVSD", "VMOVSS", "VMOVQ", "VMOVD",
|
||
|
|
"VMOVDQU32", "VMOVDQU64", "VMOVDQA32", "VMOVDQA64",
|
||
|
|
"MOVDQU", "MOVDQA", "MOVUPS", "MOVUPD", "MOVAPS", "MOVAPD", "MOVSD", "MOVSS", "MOVD",
|
||
|
|
} {
|
||
|
|
t = append(t, i(m, "SIMD move"))
|
||
|
|
}
|
||
|
|
|
||
|
|
// SIMD integer logic and arithmetic.
|
||
|
|
for _, m := range []string{
|
||
|
|
"VPXOR", "VPXORD", "VPXORQ", "VPAND", "VPANDN", "VPANDD", "VPANDND", "VPOR", "VPORD", "VPORQ",
|
||
|
|
"VPADDB", "VPADDW", "VPADDD", "VPADDQ",
|
||
|
|
"VPSUBB", "VPSUBW", "VPSUBD", "VPSUBQ",
|
||
|
|
"VPMULLW", "VPMULLD", "VPMULLQ", "VPMULDQ", "VPMULUDQ", "VPMULHUW", "VPMULHW",
|
||
|
|
"VPADUSB", "VPADUSW", "VPSUBUSB", "VPSUBUSW",
|
||
|
|
"VPMINSB", "VPMINSW", "VPMINSD", "VPMAXSB", "VPMAXSW", "VPMAXSD",
|
||
|
|
"VPABSB", "VPABSW", "VPABSD", "VPABSQ",
|
||
|
|
"VPSLLW", "VPSLLD", "VPSLLQ", "VPSRLW", "VPSRLD", "VPSRLQ",
|
||
|
|
"VPSRAW", "VPSRAD", "VPSRAQ", "VPSRAVD", "VPSRAVQ", "VPSLLVD", "VPSLLVQ", "VPSRLVD", "VPSRLVQ",
|
||
|
|
"VPAVGB", "VPAVGW",
|
||
|
|
"VPACKSSDW", "VPACKSSWB", "VPACKUSDW", "VPACKUSWB",
|
||
|
|
} {
|
||
|
|
t = append(t, i(m, "Packed integer SIMD"))
|
||
|
|
}
|
||
|
|
|
||
|
|
// SIMD comparison.
|
||
|
|
for _, m := range []string{
|
||
|
|
"VPCMPEQB", "VPCMPEQW", "VPCMPEQD", "VPCMPEQQ",
|
||
|
|
"VPCMPGTB", "VPCMPGTW", "VPCMPGTD", "VPCMPGTQ",
|
||
|
|
"VPCMPB", "VPCMPW", "VPCMPD", "VPCMPQ",
|
||
|
|
} {
|
||
|
|
t = append(t, i(m, "Packed compare"))
|
||
|
|
}
|
||
|
|
|
||
|
|
// SIMD unpack, shuffle, permute, broadcast, extract, insert.
|
||
|
|
for _, m := range []string{
|
||
|
|
"VPUNPCKLBW", "VPUNPCKLWD", "VPUNPCKLDQ", "VPUNPCKLQDQ",
|
||
|
|
"VPUNPCKHBW", "VPUNPCKHWD", "VPUNPCKHDQ", "VPUNPCKHQDQ",
|
||
|
|
"VPSHUFD", "VPSHUFHW", "VPSHUFLW", "VPSHUFB",
|
||
|
|
"VEXTRACTI128", "VEXTRACTF128", "VEXTRACTI32X4", "VEXTRACTI64X4",
|
||
|
|
"VEXTRACTF32X4", "VEXTRACTF64X4", "VEXTRACTI32X8", "VEXTRACTI64X2",
|
||
|
|
"VINSERTI128", "VINSERTF128", "VINSERTI32X4", "VINSERTI64X4", "VINSERTF32X4", "VINSERTF64X4",
|
||
|
|
"VPERMQ", "VPERMD", "VPERMPS", "VPERM2I128", "VPERM2F128",
|
||
|
|
"VPBROADCASTD", "VPBROADCASTQ", "VPBROADCASTB", "VPBROADCASTW",
|
||
|
|
"VBROADCASTSD", "VBROADCASTSS", "VBROADCASTI128", "VBROADCASTI32X4",
|
||
|
|
"VALIGND", "VALIGNQ", "VPBLENDD", "VPBLENDW", "VBLENDVPD", "VBLENDVPS",
|
||
|
|
"VSHUFPD", "VSHUFPS",
|
||
|
|
} {
|
||
|
|
t = append(t, i(m, "Shuffle / permute / broadcast"))
|
||
|
|
}
|
||
|
|
|
||
|
|
// SIMD sign/zero extension and truncation.
|
||
|
|
for _, m := range []string{
|
||
|
|
"VPMOVSXBW", "VPMOVSXBD", "VPMOVSXBQ", "VPMOVSXWD", "VPMOVSXWQ", "VPMOVSXDQ",
|
||
|
|
"VPMOVZXBW", "VPMOVZXBD", "VPMOVZXBQ", "VPMOVZXWD", "VPMOVZXWQ", "VPMOVZXDQ",
|
||
|
|
"VPMOVDW", "VPMOVQW", "VPMOVQD", "VPMOVDB", "VPMOVWB", "VPMOVQB",
|
||
|
|
"VPMOVMSKB", "VMOVMSKPS", "VMOVMSKPD", "VMOVQ2DQ", "VMOVDQ2Q",
|
||
|
|
} {
|
||
|
|
t = append(t, i(m, "Packed extend / truncate / mask"))
|
||
|
|
}
|
||
|
|
|
||
|
|
// SIMD floating point.
|
||
|
|
for _, m := range []string{
|
||
|
|
"VADDPD", "VADDPS", "VADDSD", "VADDSS",
|
||
|
|
"VSUBPD", "VSUBPS", "VSUBSD", "VSUBSS",
|
||
|
|
"VMULPD", "VMULPS", "VMULSD", "VMULSS",
|
||
|
|
"VDIVPD", "VDIVPS", "VDIVSD", "VDIVSS",
|
||
|
|
"VMINPD", "VMINPS", "VMINSD", "VMINSS", "VMAXPD", "VMAXPS", "VMAXSD", "VMAXSS",
|
||
|
|
"VXORPD", "VXORPS", "VANDPD", "VANDPS", "VANDNPD", "VANDNPS", "VORPD", "VORPS",
|
||
|
|
"VUNPCKHPD", "VUNPCKLPD", "VUNPCKHPS", "VUNPCKLPS",
|
||
|
|
"VSQRTPD", "VSQRTPS", "VSQRTSD", "VSQRTSS", "VRSQRTPS", "VRCPPS",
|
||
|
|
"VCMPPD", "VCMPPS", "VCMPSD", "VCMPSS",
|
||
|
|
} {
|
||
|
|
t = append(t, i(m, "Packed/scalar floating point"))
|
||
|
|
}
|
||
|
|
|
||
|
|
// FMA.
|
||
|
|
for _, ord := range []string{"132", "213", "231"} {
|
||
|
|
for _, sfx := range []string{"PD", "PS", "SD", "SS"} {
|
||
|
|
t = append(t, i("VFMADD"+ord+sfx, "Fused multiply-add"))
|
||
|
|
t = append(t, i("VFMSUB"+ord+sfx, "Fused multiply-subtract"))
|
||
|
|
t = append(t, i("VFNMADD"+ord+sfx, "Fused negated multiply-add"))
|
||
|
|
t = append(t, i("VFNMSUB"+ord+sfx, "Fused negated multiply-subtract"))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// SIMD conversion.
|
||
|
|
for _, m := range []string{
|
||
|
|
"VCVTDQ2PD", "VCVTDQ2PS", "VCVTPD2DQ", "VCVTPS2DQ", "VCVTPD2PS", "VCVTPS2PD",
|
||
|
|
"VCVTQQ2PD", "VCVTQQ2PS", "VCVTUQQ2PD", "VCVTUQQ2PS",
|
||
|
|
"VCVTTPD2DQ", "VCVTTPS2DQ", "VCVTSI2SD", "VCVTSI2SS", "VCVTSD2SI", "VCVTSS2SI",
|
||
|
|
"VCVTSD2SS", "VCVTSS2SD",
|
||
|
|
"CVTSL2SD", "CVTSL2SS", "CVTSQ2SD", "CVTSQ2SS", "CVTTSD2SL", "CVTTSD2SQ", "CVTTSS2SL",
|
||
|
|
} {
|
||
|
|
t = append(t, i(m, "Numeric conversion"))
|
||
|
|
}
|
||
|
|
|
||
|
|
// SIMD zeroing.
|
||
|
|
t = append(t, ic("VZEROUPPER", "Zero upper halves of YMM/ZMM", 0, 0))
|
||
|
|
t = append(t, ic("VZEROALL", "Zero all YMM/ZMM state", 0, 0))
|
||
|
|
|
||
|
|
// AVX-512 mask register operations.
|
||
|
|
for _, s := range []string{"B", "W", "D", "Q"} {
|
||
|
|
t = append(t, ic("KMOV"+s, "Move mask register", 2, 2))
|
||
|
|
t = append(t, ic("KTEST"+s, "Test mask registers", 2, 2))
|
||
|
|
t = append(t, i("KAND"+s, "AND masks"))
|
||
|
|
t = append(t, i("KOR"+s, "OR masks"))
|
||
|
|
t = append(t, i("KXOR"+s, "XOR masks"))
|
||
|
|
t = append(t, i("KNOT"+s, "NOT mask"))
|
||
|
|
t = append(t, i("KANDN"+s, "AND-NOT masks"))
|
||
|
|
t = append(t, i("KUNPCK"+s, "Unpack masks"))
|
||
|
|
}
|
||
|
|
|
||
|
|
return t
|
||
|
|
}
|