XSAVE, XSAVEOPT, XSAVEC and XSAVES with their restore twins, plain and 64, each pinned byte for byte against go tool asm through every corpus line the toolchain's own amd64enc.s carries for the family (24 lines). The toolchain emits XSAVEOPT without the manual's 0x66 prefix; the bytes are the oracle, so the family carries none. Assisted-by: GLM 5.3 Flash
59 lines
1.9 KiB
Go
59 lines
1.9 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package asm
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// This file implements the XSAVE family: the extended-state save and restore
|
|
// pair in its four generations (XSAVE/XRSTOR, XSAVEOPT, XSAVEC, XSAVES) and
|
|
// their 64 spellings. Each takes one memory operand alone; the 64 spellings
|
|
// carry REX.W. Every encoding here is pinned byte for byte against
|
|
// go tool asm through the corpus lines in amd64_xsave_test.go.
|
|
|
|
// xsaveSpec is one XSAVE family member: the opcode group and the /digit.
|
|
// XSAVEOPT carries no mandatory prefix in the toolchain's encoding despite
|
|
// the manual's 0x66, so the family has none anywhere.
|
|
type xsaveSpec struct {
|
|
op []byte
|
|
digit int
|
|
}
|
|
|
|
// xsaveTable maps the save/restore mnemonics to their encodings. The 64
|
|
// spellings share the base's digit with REX.W.
|
|
var xsaveTable = map[string]xsaveSpec{
|
|
"XSAVE": {[]byte{0x0F, 0xAE}, 4},
|
|
"XRSTOR": {[]byte{0x0F, 0xAE}, 5},
|
|
"XSAVEOPT": {[]byte{0x0F, 0xAE}, 6},
|
|
"XSAVEC": {[]byte{0x0F, 0xC7}, 4},
|
|
"XSAVES": {[]byte{0x0F, 0xC7}, 5},
|
|
"XRSTORS": {[]byte{0x0F, 0xC7}, 3},
|
|
}
|
|
|
|
// encodeXsave encodes the XSAVE family. It reports whether the mnemonic
|
|
// belongs to the family.
|
|
func (e *enc) encodeXsave(upper string, ops []Operand) (bool, error) {
|
|
name, rexW := upper, false
|
|
if base, ok := strings.CutSuffix(upper, "64"); ok {
|
|
name, rexW = base, true
|
|
}
|
|
spec, ok := xsaveTable[name]
|
|
if !ok {
|
|
return false, nil
|
|
}
|
|
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: append([]byte(nil), spec.op...), modrm: -1, sib: -1, rexW: rexW}
|
|
if err := setRMDigit(i, spec.digit, ops[0], 8); err != nil {
|
|
return true, err
|
|
}
|
|
return true, e.emit(i)
|
|
}
|