// Copyright (c) 2026 Petr BalvĂ­n (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 }