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