58 lines
1.9 KiB
Go
58 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)
|
||
|
|
}
|