From f83bc8ddefc99dc218ffaaa3a21c4ff65fdf5c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Tue, 6 Oct 2026 20:09:40 +0200 Subject: [PATCH] feat(asm): encode the amd64 system, string and segment families The no-operand flag and system controls, the sign-extension pair, the string primitives, the multi-byte no-ops, the cache controls, MOVBE, the compare-exchange doubles, the random source and FS/GS base pairs, the descriptor-table accesses, the 0F 00/01 register controls and the LAR/LSL selector reads and far-segment loads, each pinned byte for byte against go tool asm through every corpus line the toolchain's own amd64enc.s carries for the families (279 lines). Assisted-by: GLM 5.3 Flash --- asm/amd64_families.go | 49 ++++- asm/amd64_system.go | 429 +++++++++++++++++++++++++++++++++++++++ asm/amd64_system_test.go | 311 ++++++++++++++++++++++++++++ 3 files changed, 788 insertions(+), 1 deletion(-) create mode 100644 asm/amd64_system.go create mode 100644 asm/amd64_system_test.go diff --git a/asm/amd64_families.go b/asm/amd64_families.go index 9812518..a78c8c2 100644 --- a/asm/amd64_families.go +++ b/asm/amd64_families.go @@ -11,6 +11,9 @@ func (e *enc) encodeAmd64Family(upper string, ops []Operand) (bool, error) { if ok, err := e.encodeX87(upper, ops); ok { return true, err } + if ok, err := e.encodeSystem(upper, ops); ok { + return true, err + } return false, nil } @@ -36,5 +39,49 @@ func amd64FamilyEncodable(upper string) bool { if _, ok := x87Fxsav[upper]; ok { return true } - return upper == "FADDDP" + if upper == "FADDDP" { + return true + } + if _, ok := systemNoOperand[upper]; ok { + return true + } + if b, size := splitSize(upper); size != 0 { + switch upper[len(upper)-1] { + case 'B', 'W', 'L', 'Q': + if _, ok := stringOp[b]; ok { + return true + } + } + if m, ok := sysRm[b]; ok && m.sized { + return true + } + } + if _, ok := nopWidth[upper]; ok { + return true + } + if _, ok := cacheControl[upper]; ok { + return true + } + if _, ok := movbeSize[upper]; ok { + return true + } + if _, ok := randSource[base(upper, 6)]; ok { + return true + } + if _, ok := fsGsBase[base(upper, 8)]; ok { + return true + } + if _, ok := descTable[upper]; ok { + return true + } + if _, ok := sysRm[upper]; ok { + return true + } + if _, ok := selectorRead[base(upper, 3)]; ok { + return true + } + if _, ok := farSegLoad[base(upper, 3)]; ok { + return true + } + return upper == "CMPXCHG8B" || upper == "CMPXCHG16B" } diff --git a/asm/amd64_system.go b/asm/amd64_system.go new file mode 100644 index 0000000..e8d30e9 --- /dev/null +++ b/asm/amd64_system.go @@ -0,0 +1,429 @@ +// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) +// SPDX-License-Identifier: BSD-3-Clause + +package asm + +import "fmt" + +// This file implements the system, flag, string and segment families the Go +// assembler carries: the no-operand controls, the string primitives, the +// sign-extension pair, the multi-byte no-ops, the cache controls, MOVBE, the +// compare-exchange doubles, the random source pair, the FS/GS base pair, the +// descriptor-table controls and the LAR/LSL selector reads. Every encoding +// here is pinned byte for byte against go tool asm through the corpus lines +// in amd64_system_test.go. + +// systemNoOperand maps a fixed no-operand mnemonic to its opcode bytes, the +// prefixes spelled out in full. +var systemNoOperand = map[string][]byte{ + "CLC": {0xF8}, + "STC": {0xF9}, + "CMC": {0xF5}, + "CLI": {0xFA}, + "STI": {0xFB}, + "HLT": {0xF4}, + "ICEBP": {0xF1}, + "XLAT": {0xD7}, + "LAHF": {0x9F}, + "SAHF": {0x9E}, + "PUSHFW": {0x66, 0x9C}, + "POPFW": {0x66, 0x9D}, + "IRETW": {0x66, 0xCF}, + "IRETL": {0xCF}, + "IRETQ": {0x48, 0xCF}, + "UD1": {0x0F, 0xB9}, + "UD2": {0x0F, 0x0B}, + "CLAC": {0x0F, 0x01, 0xCA}, + "STAC": {0x0F, 0x01, 0xCB}, + "CLTS": {0x0F, 0x06}, + "INVD": {0x0F, 0x08}, + "WBINVD": {0x0F, 0x09}, + "SWAPGS": {0x0F, 0x01, 0xF8}, + "RSM": {0x0F, 0xAA}, + "MONITOR": {0x0F, 0x01, 0xC8}, + "MWAIT": {0x0F, 0x01, 0xC9}, + "RDMSR": {0x0F, 0x32}, + "WRMSR": {0x0F, 0x30}, + "RDPMC": {0x0F, 0x33}, + "RDPKRU": {0x0F, 0x01, 0xEE}, + "WRPKRU": {0x0F, 0x01, 0xEF}, + "XSETBV": {0x0F, 0x01, 0xD1}, + "SYSENTER": {0x0F, 0x34}, + "SYSENTER64": {0x48, 0x0F, 0x34}, + "SYSEXIT": {0x0F, 0x35}, + "SYSEXIT64": {0x48, 0x0F, 0x35}, + "SYSRET": {0x0F, 0x07}, + "CBW": {0x66, 0x98}, + "CWDE": {0x98}, + "CDQE": {0x48, 0x98}, + "CWD": {0x66, 0x99}, + "CDQ": {0x99}, + "CQO": {0x48, 0x99}, +} + +// stringOp maps the string-primitive bases to their 32-bit opcode; the byte +// form is one lower, the word spelling carries 0x66 and the quad spelling +// REX.W, exactly the prefix ladder newInstr applies. +var stringOp = map[string]byte{ + "CMPS": 0xA7, + "INS": 0x6D, + "LODS": 0xAD, + "OUTS": 0x6F, + "SCAS": 0xAF, +} + +// nopWidth maps the multi-byte no-op spellings to their operand size. +var nopWidth = map[string]int{ + "NOPW": 2, + "NOPL": 4, + "NOPQ": 8, +} + +// cacheControl maps the one-memory-operand cache controls to their mandatory +// prefix, opcode group and /digit. +var cacheControl = map[string]struct { + prefix byte + op []byte + digit int +}{ + "CLFLUSH": {0, []byte{0x0F, 0xAE}, 7}, + "CLFLUSHOPT": {0x66, []byte{0x0F, 0xAE}, 7}, + "INVLPG": {0, []byte{0x0F, 0x01}, 7}, +} + +// movbeSize maps the MOVBE spellings to their operand size. +var movbeSize = map[string]int{ + "MOVBEW": 2, + "MOVBEL": 4, + "MOVBEQ": 8, +} + +// randSource maps the random-source bases to their /digit (RDRAND /6, +// RDSEED /7); the destination register rides r/m, mod 11. +var randSource = map[string]int{ + "RDRAND": 6, + "RDSEED": 7, +} + +// fsGsBase maps the FS/GS base accessors to their /digit in the F3-prefixed +// 0F AE group; the L and Q spellings exist. +var fsGsBase = map[string]int{ + "RDFSBASE": 0, + "RDGSBASE": 1, + "WRFSBASE": 2, + "WRGSBASE": 3, +} + +// descTable maps the descriptor-table accesses to their /digit in 0F 01; +// each takes one memory operand alone. +var descTable = map[string]int{ + "LGDT": 2, + "LIDT": 3, + "SGDT": 0, + "SIDT": 1, +} + +// sysRmEntry is one 0F 00/01 register-or-memory access. sized marks the +// members whose trailing width letter (SLDTW, STRQ, SMSWL) carries the width +// prefix ladder; the rest are fixed-width single names. +type sysRmEntry struct { + group byte + digit int + sized bool +} + +// sysRm maps the system register accesses LLDT/LTR/VERR/VERW/SLDT/STR (group +// 0F 00), LMSW/SMSW (0F 01). +var sysRm = map[string]sysRmEntry{ + "LLDT": {0x00, 2, false}, + "LTR": {0x00, 3, false}, + "VERR": {0x00, 4, false}, + "VERW": {0x00, 5, false}, + "SLDT": {0x00, 0, true}, + "STR": {0x00, 1, true}, + "LMSW": {0x01, 6, false}, + "SMSW": {0x01, 4, true}, +} + +// selectorRead maps the selector reads LAR and LSL to their opcodes; both +// load the destination register from an r/m selector, width prefixes per the +// suffix. +var selectorRead = map[string]byte{ + "LAR": 0x02, + "LSL": 0x03, +} + +// farSegLoad maps the far-segment loads to their opcodes; memory source +// alone, destination register, width prefixes per the suffix. +var farSegLoad = map[string]byte{ + "LFS": 0xB4, + "LGS": 0xB5, + "LSS": 0xB2, +} + +// encodeSystem encodes the system, flag, string and segment families. It +// reports whether the mnemonic belongs to the family. +func (e *enc) encodeSystem(upper string, ops []Operand) (bool, error) { + if op, ok := systemNoOperand[upper]; ok { + if len(ops) != 0 { + return true, fmt.Errorf("%s takes no operands, got %d", upper, len(ops)) + } + return true, e.emit(&instr{opcode: append([]byte(nil), op...), modrm: -1, sib: -1}) + } + // The string primitives carry a B/W/L/Q suffix only; CMPSD and friends + // are the SSE compare family's names and must reach their own dispatch. + if b, size := splitSize(upper); size != 0 { + switch upper[len(upper)-1] { + case 'B', 'W', 'L', 'Q': + if op32, ok := stringOp[b]; ok { + return true, e.encodeSystemString(upper, op32, ops) + } + } + } + if size, ok := nopWidth[upper]; ok { + if len(ops) != 1 { + return true, fmt.Errorf("%s expects 1 operand, got %d", upper, len(ops)) + } + i := newInstr(size, []byte{0x0F, 0x1F}) + if err := setRMDigit(i, 0, ops[0], size); err != nil { + return true, err + } + return true, e.emit(i) + } + if m, ok := cacheControl[upper]; ok { + if len(ops) != 1 { + return true, fmt.Errorf("%s expects 1 memory operand, got %d", upper, len(ops)) + } + if !isX86Mem(ops[0]) { + return true, fmt.Errorf("%s requires a memory operand", upper) + } + i := &instr{prefix: m.prefix, opcode: m.op, modrm: -1, sib: -1} + if err := setRMDigit(i, m.digit, ops[0], 8); err != nil { + return true, err + } + return true, e.emit(i) + } + if _, ok := movbeSize[upper]; ok { + return true, e.encodeSystemMovbe(upper, ops) + } + if digit, ok := randSource[base(upper, 6)]; ok { + return true, e.encodeSystemRand(upper, digit, ops) + } + if digit, ok := fsGsBase[base(upper, 8)]; ok { + return true, e.encodeSystemFsGsBase(upper, digit, ops) + } + if digit, ok := descTable[upper]; ok { + if len(ops) != 1 { + return true, fmt.Errorf("%s expects 1 memory operand, got %d", upper, len(ops)) + } + if !isX86Mem(ops[0]) { + return true, fmt.Errorf("%s requires a memory operand", upper) + } + i := &instr{opcode: []byte{0x0F, 0x01}, modrm: -1, sib: -1} + if err := setRMDigit(i, digit, ops[0], 8); err != nil { + return true, err + } + return true, e.emit(i) + } + if m, ok := sysRm[upper]; ok { + return true, e.encodeSystemRm(upper, m, ops) + } + if b, size := splitSize(upper); size != 0 { + if m, ok := sysRm[b]; ok && m.sized { + return true, e.encodeSystemRm(upper, m, ops) + } + } + if op, ok := selectorRead[base(upper, 3)]; ok { + return true, e.encodeSystemSelectorRead(upper, op, ops) + } + if op, ok := farSegLoad[base(upper, 3)]; ok { + return true, e.encodeSystemFarLoad(upper, op, ops) + } + if upper == "CMPXCHG8B" || upper == "CMPXCHG16B" { + if len(ops) != 1 { + return true, fmt.Errorf("%s expects 1 memory operand, got %d", upper, len(ops)) + } + if !isX86Mem(ops[0]) { + return true, fmt.Errorf("%s requires a memory operand", upper) + } + i := newInstr(0, []byte{0x0F, 0xC7}) + i.rexW = upper == "CMPXCHG16B" + if err := setRMDigit(i, 1, ops[0], 8); err != nil { + return true, err + } + return true, e.emit(i) + } + return false, nil +} + +// base returns the first n characters of an upper-case mnemonic, or the empty +// string when the mnemonic is shorter: the safe head lookup for the families +// whose width suffix rides the tail (RDRANDW, RDFSBASEQ, LARW). +func base(upper string, n int) string { + if len(upper) <= n { + return "" + } + return upper[:n] +} + +// encodeSystemString encodes a string primitive: no operands, the width +// suffix picks the byte form, the 0x66 prefix or REX.W. Only the B/W/L/Q +// suffixes belong to the family: CMPSD and friends are the SSE compare +// family's names and must reach their own dispatch. +func (e *enc) encodeSystemString(upper string, op32 byte, ops []Operand) error { + switch upper[len(upper)-1] { + case 'B', 'W', 'L', 'Q': + default: + return fmt.Errorf("unsupported instruction %q", upper) + } + b, size := splitSize(upper) + if _, ok := stringOp[b]; !ok || size == 0 { + return fmt.Errorf("unsupported instruction %q", upper) + } + if len(ops) != 0 { + return fmt.Errorf("%s takes no operands, got %d", upper, len(ops)) + } + // The byte spelling is the 32-bit opcode minus one; the W and Q forms + // ride newInstr's prefix ladder, the L form the bare opcode. + op := op32 + if size == 1 { + op-- + } + return e.emit(newInstr(size, []byte{op})) +} + +// encodeSystemMovbe encodes MOVBE: a register source stores (F1, reg = the +// register, r/m = memory), a register destination loads (F0, same fields). +func (e *enc) encodeSystemMovbe(mnem string, ops []Operand) error { + size := movbeSize[mnem] + if len(ops) != 2 { + return fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) + } + srcReg, srcIsReg := ops[0].(Reg) + dstReg, dstIsReg := ops[1].(Reg) + var op byte + var reg Reg + var rm Operand + switch { + case srcIsReg && isX86Mem(ops[1]): + op, reg, rm = 0xF1, srcReg, ops[1] // store + case dstIsReg && isX86Mem(ops[0]): + op, reg, rm = 0xF0, dstReg, ops[0] // load + default: + return fmt.Errorf("%s takes one register and one memory operand", mnem) + } + i := newInstr(size, []byte{0x0F, 0x38, op}) + if err := setRM(i, reg, rm, size); err != nil { + return err + } + return e.emit(i) +} + +// encodeSystemRand encodes RDRAND/RDSEED: the single register operand rides +// r/m under the /digit, mod 11, with the width prefix the suffix picks. +func (e *enc) encodeSystemRand(mnem string, digit int, ops []Operand) error { + b, size := splitSize(mnem) + if _, ok := randSource[b]; !ok || size == 0 { + return fmt.Errorf("unsupported instruction %q", mnem) + } + if len(ops) != 1 { + return fmt.Errorf("%s expects 1 register operand, got %d", mnem, len(ops)) + } + dstReg, ok := ops[0].(Reg) + if !ok || dstReg.isVec() { + return fmt.Errorf("%s destination must be a general register", mnem) + } + i := newInstr(size, []byte{0x0F, 0xC7}) + if err := setRMDigit(i, digit, dstReg, size); err != nil { + return err + } + return e.emit(i) +} + +// encodeSystemFsGsBase encodes the FS/GS base accessors: F3-prefixed 0F AE +// under the /digit, the register in r/m; the Q spellings add REX.W. +func (e *enc) encodeSystemFsGsBase(mnem string, digit int, ops []Operand) error { + b, size := splitSize(mnem) + if _, ok := fsGsBase[b]; !ok || (size != 4 && size != 8) { + return fmt.Errorf("unsupported instruction %q", mnem) + } + if len(ops) != 1 { + return fmt.Errorf("%s expects 1 register operand, got %d", mnem, len(ops)) + } + dstReg, ok := ops[0].(Reg) + if !ok || dstReg.isVec() { + return fmt.Errorf("%s destination must be a general register", mnem) + } + i := newInstr(0, []byte{0x0F, 0xAE}) + i.prefix = 0xF3 + i.rexW = size == 8 + if err := setRMDigit(i, digit, dstReg, 8); err != nil { + return err + } + return e.emit(i) +} + +// encodeSystemRm encodes a 0F 00/01 r/m access: the operand is a register or +// memory; the sized members carry the width prefix ladder the suffix fixes. +func (e *enc) encodeSystemRm(mnem string, m sysRmEntry, ops []Operand) error { + size := 0 + if m.sized { + _, size = splitSize(mnem) + if size == 0 { + return fmt.Errorf("unsupported instruction %q", mnem) + } + } + if len(ops) != 1 { + return fmt.Errorf("%s expects 1 operand, got %d", mnem, len(ops)) + } + i := newInstr(size, []byte{0x0F, m.group}) + if err := setRMDigit(i, m.digit, ops[0], size); err != nil { + return err + } + return e.emit(i) +} + +// encodeSystemSelectorRead encodes LAR/LSL: the destination register loads +// from an r/m selector, width prefixes per the suffix. +func (e *enc) encodeSystemSelectorRead(mnem string, op byte, ops []Operand) error { + b, size := splitSize(mnem) + if _, ok := selectorRead[b]; !ok || size == 0 { + return fmt.Errorf("unsupported instruction %q", mnem) + } + if len(ops) != 2 { + return fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) + } + dstReg, ok := ops[1].(Reg) + if !ok || dstReg.isVec() { + return fmt.Errorf("%s destination must be a general register", mnem) + } + i := newInstr(size, []byte{0x0F, op}) + if err := setRM(i, dstReg, ops[0], size); err != nil { + return err + } + return e.emit(i) +} + +// encodeSystemFarLoad encodes LFS/LGS/LSS: the destination register loads a +// far pointer from memory, width prefixes per the suffix. +func (e *enc) encodeSystemFarLoad(mnem string, op byte, ops []Operand) error { + b, size := splitSize(mnem) + if _, ok := farSegLoad[b]; !ok || size == 0 { + return fmt.Errorf("unsupported instruction %q", mnem) + } + if len(ops) != 2 { + return fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) + } + if !isX86Mem(ops[0]) { + return fmt.Errorf("%s requires a memory source", mnem) + } + dstReg, ok := ops[1].(Reg) + if !ok || dstReg.isVec() { + return fmt.Errorf("%s destination must be a general register", mnem) + } + i := newInstr(size, []byte{0x0F, op}) + if err := setRM(i, dstReg, ops[0], size); err != nil { + return err + } + return e.emit(i) +} diff --git a/asm/amd64_system_test.go b/asm/amd64_system_test.go new file mode 100644 index 0000000..2d5eec6 --- /dev/null +++ b/asm/amd64_system_test.go @@ -0,0 +1,311 @@ +// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) +// SPDX-License-Identifier: BSD-3-Clause + +package asm + +import "testing" + +// amd64SystemCorpus holds every line the Go toolchain's own +// amd64enc.s carries for the system, flag, string and segment families, with the bytes go tool asm +// emits for each: the differential ground truth the family is proven +// against, line for line. +var amd64SystemCorpus = []struct { + line string + want string +}{ + {"CBW", "66 98"}, + {"CDQ", "99"}, + {"CDQE", "48 98"}, + {"CLAC", "0f 01 ca"}, + {"CLC", "f8"}, + {"CLFLUSH (BX)", "0f ae 3b"}, + {"CLFLUSH (R11)", "41 0f ae 3b"}, + {"CLFLUSHOPT (BX)", "66 0f ae 3b"}, + {"CLFLUSHOPT (R11)", "66 41 0f ae 3b"}, + {"CLI", "fa"}, + {"CLTS", "0f 06"}, + {"CMC", "f5"}, + {"CMPSB", "a6"}, + {"CMPSL", "a7"}, + {"CMPSQ", "48 a7"}, + {"CMPSW", "66 a7"}, + {"CMPXCHG16B (BX)", "48 0f c7 0b"}, + {"CMPXCHG16B (R11)", "49 0f c7 0b"}, + {"CMPXCHG8B (BX)", "0f c7 0b"}, + {"CMPXCHG8B (R11)", "41 0f c7 0b"}, + {"CQO", "48 99"}, + {"CWD", "66 99"}, + {"CWDE", "98"}, + {"HLT", "f4"}, + {"ICEBP", "f1"}, + {"INSB", "6c"}, + {"INSL", "6d"}, + {"INSW", "66 6d"}, + {"INVD", "0f 08"}, + {"INVLPG (BX)", "0f 01 3b"}, + {"INVLPG (R11)", "41 0f 01 3b"}, + {"IRETW", "66 cf"}, + {"IRETL", "cf"}, + {"IRETQ", "48 cf"}, + {"LAHF", "9f"}, + {"LARW (BX), DX", "66 0f 02 13"}, + {"LARW (R11), DX", "66 41 0f 02 13"}, + {"LARW DX, DX", "66 0f 02 d2"}, + {"LARW R11, DX", "66 41 0f 02 d3"}, + {"LARW (BX), R11", "66 44 0f 02 1b"}, + {"LARW (R11), R11", "66 45 0f 02 1b"}, + {"LARW DX, R11", "66 44 0f 02 da"}, + {"LARW R11, R11", "66 45 0f 02 db"}, + {"LARL (BX), DX", "0f 02 13"}, + {"LARL (R11), DX", "41 0f 02 13"}, + {"LARL DX, DX", "0f 02 d2"}, + {"LARL R11, DX", "41 0f 02 d3"}, + {"LARL (BX), R11", "44 0f 02 1b"}, + {"LARL (R11), R11", "45 0f 02 1b"}, + {"LARL DX, R11", "44 0f 02 da"}, + {"LARL R11, R11", "45 0f 02 db"}, + {"LARQ (BX), DX", "48 0f 02 13"}, + {"LARQ (R11), DX", "49 0f 02 13"}, + {"LARQ DX, DX", "48 0f 02 d2"}, + {"LARQ R11, DX", "49 0f 02 d3"}, + {"LARQ (BX), R11", "4c 0f 02 1b"}, + {"LARQ (R11), R11", "4d 0f 02 1b"}, + {"LARQ DX, R11", "4c 0f 02 da"}, + {"LARQ R11, R11", "4d 0f 02 db"}, + {"LFSW (BX), DX", "66 0f b4 13"}, + {"LFSW (R11), DX", "66 41 0f b4 13"}, + {"LFSW (BX), R11", "66 44 0f b4 1b"}, + {"LFSW (R11), R11", "66 45 0f b4 1b"}, + {"LFSL (BX), DX", "0f b4 13"}, + {"LFSL (R11), DX", "41 0f b4 13"}, + {"LFSL (BX), R11", "44 0f b4 1b"}, + {"LFSL (R11), R11", "45 0f b4 1b"}, + {"LFSQ (BX), DX", "48 0f b4 13"}, + {"LFSQ (R11), DX", "49 0f b4 13"}, + {"LFSQ (BX), R11", "4c 0f b4 1b"}, + {"LFSQ (R11), R11", "4d 0f b4 1b"}, + {"LGDT (BX)", "0f 01 13"}, + {"LGDT (R11)", "41 0f 01 13"}, + {"LGSW (BX), DX", "66 0f b5 13"}, + {"LGSW (R11), DX", "66 41 0f b5 13"}, + {"LGSW (BX), R11", "66 44 0f b5 1b"}, + {"LGSW (R11), R11", "66 45 0f b5 1b"}, + {"LGSL (BX), DX", "0f b5 13"}, + {"LGSL (R11), DX", "41 0f b5 13"}, + {"LGSL (BX), R11", "44 0f b5 1b"}, + {"LGSL (R11), R11", "45 0f b5 1b"}, + {"LGSQ (BX), DX", "48 0f b5 13"}, + {"LGSQ (R11), DX", "49 0f b5 13"}, + {"LGSQ (BX), R11", "4c 0f b5 1b"}, + {"LGSQ (R11), R11", "4d 0f b5 1b"}, + {"LIDT (BX)", "0f 01 1b"}, + {"LIDT (R11)", "41 0f 01 1b"}, + {"LLDT (BX)", "0f 00 13"}, + {"LLDT (R11)", "41 0f 00 13"}, + {"LLDT DX", "0f 00 d2"}, + {"LLDT R11", "41 0f 00 d3"}, + {"LMSW (BX)", "0f 01 33"}, + {"LMSW (R11)", "41 0f 01 33"}, + {"LMSW DX", "0f 01 f2"}, + {"LMSW R11", "41 0f 01 f3"}, + {"LODSB", "ac"}, + {"LODSL", "ad"}, + {"LODSQ", "48 ad"}, + {"LODSW", "66 ad"}, + {"LSLW (BX), DX", "66 0f 03 13"}, + {"LSLW (R11), DX", "66 41 0f 03 13"}, + {"LSLW DX, DX", "66 0f 03 d2"}, + {"LSLW R11, DX", "66 41 0f 03 d3"}, + {"LSLW (BX), R11", "66 44 0f 03 1b"}, + {"LSLW (R11), R11", "66 45 0f 03 1b"}, + {"LSLW DX, R11", "66 44 0f 03 da"}, + {"LSLW R11, R11", "66 45 0f 03 db"}, + {"LSLL (BX), DX", "0f 03 13"}, + {"LSLL (R11), DX", "41 0f 03 13"}, + {"LSLL DX, DX", "0f 03 d2"}, + {"LSLL R11, DX", "41 0f 03 d3"}, + {"LSLL (BX), R11", "44 0f 03 1b"}, + {"LSLL (R11), R11", "45 0f 03 1b"}, + {"LSLL DX, R11", "44 0f 03 da"}, + {"LSLL R11, R11", "45 0f 03 db"}, + {"LSLQ (BX), DX", "48 0f 03 13"}, + {"LSLQ (R11), DX", "49 0f 03 13"}, + {"LSLQ DX, DX", "48 0f 03 d2"}, + {"LSLQ R11, DX", "49 0f 03 d3"}, + {"LSLQ (BX), R11", "4c 0f 03 1b"}, + {"LSLQ (R11), R11", "4d 0f 03 1b"}, + {"LSLQ DX, R11", "4c 0f 03 da"}, + {"LSLQ R11, R11", "4d 0f 03 db"}, + {"LSSW (BX), DX", "66 0f b2 13"}, + {"LSSW (R11), DX", "66 41 0f b2 13"}, + {"LSSW (BX), R11", "66 44 0f b2 1b"}, + {"LSSW (R11), R11", "66 45 0f b2 1b"}, + {"LSSL (BX), DX", "0f b2 13"}, + {"LSSL (R11), DX", "41 0f b2 13"}, + {"LSSL (BX), R11", "44 0f b2 1b"}, + {"LSSL (R11), R11", "45 0f b2 1b"}, + {"LSSQ (BX), DX", "48 0f b2 13"}, + {"LSSQ (R11), DX", "49 0f b2 13"}, + {"LSSQ (BX), R11", "4c 0f b2 1b"}, + {"LSSQ (R11), R11", "4d 0f b2 1b"}, + {"LTR (BX)", "0f 00 1b"}, + {"LTR (R11)", "41 0f 00 1b"}, + {"LTR DX", "0f 00 da"}, + {"LTR R11", "41 0f 00 db"}, + {"MONITOR", "0f 01 c8"}, + {"MOVBEW DX, (BX)", "66 0f 38 f1 13"}, + {"MOVBEW R11, (BX)", "66 44 0f 38 f1 1b"}, + {"MOVBEW DX, (R11)", "66 41 0f 38 f1 13"}, + {"MOVBEW R11, (R11)", "66 45 0f 38 f1 1b"}, + {"MOVBEW (BX), DX", "66 0f 38 f0 13"}, + {"MOVBEW (R11), DX", "66 41 0f 38 f0 13"}, + {"MOVBEW (BX), R11", "66 44 0f 38 f0 1b"}, + {"MOVBEW (R11), R11", "66 45 0f 38 f0 1b"}, + {"MOVBEL DX, (BX)", "0f 38 f1 13"}, + {"MOVBEL R11, (BX)", "44 0f 38 f1 1b"}, + {"MOVBEL DX, (R11)", "41 0f 38 f1 13"}, + {"MOVBEL R11, (R11)", "45 0f 38 f1 1b"}, + {"MOVBEL (BX), DX", "0f 38 f0 13"}, + {"MOVBEL (R11), DX", "41 0f 38 f0 13"}, + {"MOVBEL (BX), R11", "44 0f 38 f0 1b"}, + {"MOVBEL (R11), R11", "45 0f 38 f0 1b"}, + {"MOVBEQ DX, (BX)", "48 0f 38 f1 13"}, + {"MOVBEQ R11, (BX)", "4c 0f 38 f1 1b"}, + {"MOVBEQ DX, (R11)", "49 0f 38 f1 13"}, + {"MOVBEQ R11, (R11)", "4d 0f 38 f1 1b"}, + {"MOVBEQ (BX), DX", "48 0f 38 f0 13"}, + {"MOVBEQ (R11), DX", "49 0f 38 f0 13"}, + {"MOVBEQ (BX), R11", "4c 0f 38 f0 1b"}, + {"MOVBEQ (R11), R11", "4d 0f 38 f0 1b"}, + {"MWAIT", "0f 01 c9"}, + {"NOPW (BX)", "66 0f 1f 03"}, + {"NOPW (R11)", "66 41 0f 1f 03"}, + {"NOPW DX", "66 0f 1f c2"}, + {"NOPW R11", "66 41 0f 1f c3"}, + {"NOPL (BX)", "0f 1f 03"}, + {"NOPL (R11)", "41 0f 1f 03"}, + {"NOPL DX", "0f 1f c2"}, + {"NOPL R11", "41 0f 1f c3"}, + {"OUTSB", "6e"}, + {"OUTSL", "6f"}, + {"OUTSW", "66 6f"}, + {"POPFW", "66 9d"}, + {"PUSHFW", "66 9c"}, + {"RDFSBASEL DX", "f3 0f ae c2"}, + {"RDFSBASEL R11", "f3 41 0f ae c3"}, + {"RDGSBASEL DX", "f3 0f ae ca"}, + {"RDGSBASEL R11", "f3 41 0f ae cb"}, + {"RDFSBASEQ DX", "f3 48 0f ae c2"}, + {"RDFSBASEQ R11", "f3 49 0f ae c3"}, + {"RDGSBASEQ DX", "f3 48 0f ae ca"}, + {"RDGSBASEQ R11", "f3 49 0f ae cb"}, + {"RDMSR", "0f 32"}, + {"RDPKRU", "0f 01 ee"}, + {"RDPMC", "0f 33"}, + {"RDRANDW DX", "66 0f c7 f2"}, + {"RDRANDW R11", "66 41 0f c7 f3"}, + {"RDRANDL DX", "0f c7 f2"}, + {"RDRANDL R11", "41 0f c7 f3"}, + {"RDRANDQ DX", "48 0f c7 f2"}, + {"RDRANDQ R11", "49 0f c7 f3"}, + {"RDSEEDW DX", "66 0f c7 fa"}, + {"RDSEEDW R11", "66 41 0f c7 fb"}, + {"RDSEEDL DX", "0f c7 fa"}, + {"RDSEEDL R11", "41 0f c7 fb"}, + {"RDSEEDQ DX", "48 0f c7 fa"}, + {"RDSEEDQ R11", "49 0f c7 fb"}, + {"RSM", "0f aa"}, + {"SAHF", "9e"}, + {"SCASB", "ae"}, + {"SCASL", "af"}, + {"SCASQ", "48 af"}, + {"SCASW", "66 af"}, + {"SGDT (BX)", "0f 01 03"}, + {"SGDT (R11)", "41 0f 01 03"}, + {"SIDT (BX)", "0f 01 0b"}, + {"SIDT (R11)", "41 0f 01 0b"}, + {"SLDTW (BX)", "66 0f 00 03"}, + {"SLDTW (R11)", "66 41 0f 00 03"}, + {"SLDTW DX", "66 0f 00 c2"}, + {"SLDTW R11", "66 41 0f 00 c3"}, + {"SLDTL (BX)", "0f 00 03"}, + {"SLDTL (R11)", "41 0f 00 03"}, + {"SLDTL DX", "0f 00 c2"}, + {"SLDTL R11", "41 0f 00 c3"}, + {"SLDTQ (BX)", "48 0f 00 03"}, + {"SLDTQ (R11)", "49 0f 00 03"}, + {"SLDTQ DX", "48 0f 00 c2"}, + {"SLDTQ R11", "49 0f 00 c3"}, + {"SMSWW (BX)", "66 0f 01 23"}, + {"SMSWW (R11)", "66 41 0f 01 23"}, + {"SMSWW DX", "66 0f 01 e2"}, + {"SMSWW R11", "66 41 0f 01 e3"}, + {"SMSWL (BX)", "0f 01 23"}, + {"SMSWL (R11)", "41 0f 01 23"}, + {"SMSWL DX", "0f 01 e2"}, + {"SMSWL R11", "41 0f 01 e3"}, + {"SMSWQ (BX)", "48 0f 01 23"}, + {"SMSWQ (R11)", "49 0f 01 23"}, + {"SMSWQ DX", "48 0f 01 e2"}, + {"SMSWQ R11", "49 0f 01 e3"}, + {"STAC", "0f 01 cb"}, + {"STC", "f9"}, + {"STI", "fb"}, + {"STRW (BX)", "66 0f 00 0b"}, + {"STRW (R11)", "66 41 0f 00 0b"}, + {"STRW DX", "66 0f 00 ca"}, + {"STRW R11", "66 41 0f 00 cb"}, + {"STRL (BX)", "0f 00 0b"}, + {"STRL (R11)", "41 0f 00 0b"}, + {"STRL DX", "0f 00 ca"}, + {"STRL R11", "41 0f 00 cb"}, + {"STRQ (BX)", "48 0f 00 0b"}, + {"STRQ (R11)", "49 0f 00 0b"}, + {"STRQ DX", "48 0f 00 ca"}, + {"STRQ R11", "49 0f 00 cb"}, + {"SWAPGS", "0f 01 f8"}, + {"SYSENTER", "0f 34"}, + {"SYSENTER64", "48 0f 34"}, + {"SYSEXIT", "0f 35"}, + {"SYSEXIT64", "48 0f 35"}, + {"SYSRET", "0f 07"}, + {"UD1", "0f b9"}, + {"UD2", "0f 0b"}, + {"VERR (BX)", "0f 00 23"}, + {"VERR (R11)", "41 0f 00 23"}, + {"VERR DX", "0f 00 e2"}, + {"VERR R11", "41 0f 00 e3"}, + {"VERW (BX)", "0f 00 2b"}, + {"VERW (R11)", "41 0f 00 2b"}, + {"VERW DX", "0f 00 ea"}, + {"VERW R11", "41 0f 00 eb"}, + {"WBINVD", "0f 09"}, + {"WRFSBASEL DX", "f3 0f ae d2"}, + {"WRFSBASEL R11", "f3 41 0f ae d3"}, + {"WRGSBASEL DX", "f3 0f ae da"}, + {"WRGSBASEL R11", "f3 41 0f ae db"}, + {"WRFSBASEQ DX", "f3 48 0f ae d2"}, + {"WRFSBASEQ R11", "f3 49 0f ae d3"}, + {"WRGSBASEQ DX", "f3 48 0f ae da"}, + {"WRGSBASEQ R11", "f3 49 0f ae db"}, + {"WRMSR", "0f 30"}, + {"WRPKRU", "0f 01 ef"}, + {"XLAT", "d7"}, + {"XSETBV", "0f 01 d1"}, +} + +// TestAmd64SystemCorpus assembles every corpus line and requires the same bytes +// go tool asm emits for it. +func TestAmd64SystemCorpus(t *testing.T) { + for _, tc := range amd64SystemCorpus { + fn := firstText(t, "TEXT ·p(SB), 4, $0\n\t"+tc.line+"\n") + code, _, err := Assemble(fn) + if err != nil { + t.Errorf("%s: %v", tc.line, err) + continue + } + if got := hexBytes(code); got != tc.want { + t.Errorf("%s: got %s, want %s", tc.line, got, tc.want) + } + } +}