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
This commit is contained in:
1 parent
f0b08ccea0
commit
f83bc8ddef
3 files changed
+788
-1
No files matched your search
+48
-1
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (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)
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user