The packed integer and float binaries, the imm8-controlled SSE4.1 forms, the variable blends with their X0 mask, the high/low half moves, the sign-mask extractions, the non-temporal stores, the MOVQ bank crossings and their odd spellings, the MMX shifts and shuffle and the cache-line mask stores, plus the scalar leaves LEAVE, INVPCID and the RTM controls. Every encoding is pinned byte for byte against go tool asm through every corpus line the toolchain's own amd64enc.s carries for the families (1465 lines). Two corpus-wide gaps fell out of the comparison: the 64-bit MOV immediate uses the zero-extending form across the unsigned 32-bit span, and the MMX-to-GPR MOVQ puts the bank register in reg. Assisted-by: GLM 5.3 Flash
123 lines
2.8 KiB
Go
123 lines
2.8 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package asm
|
|
|
|
import "strings"
|
|
|
|
// encodeAmd64Family routes a mnemonic through the amd64 corpus families the
|
|
// dedicated amd64_*.go files implement. The first family that owns the name
|
|
// decides the outcome: its bytes, or its error. A name no family claims
|
|
// falls through to the scalar dispatch in (*enc).encode untouched.
|
|
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
|
|
}
|
|
if ok, err := e.encodeXsave(upper, ops); ok {
|
|
return true, err
|
|
}
|
|
if ok, err := e.encodeSSEMore(upper, ops); ok {
|
|
return true, err
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
// amd64FamilyEncodable mirrors encodeAmd64Family for the lint-time
|
|
// predicate: true when some family owns the mnemonic, whatever the operand
|
|
// shapes. It must claim exactly the names encodeAmd64Family does.
|
|
func amd64FamilyEncodable(upper string) bool {
|
|
if _, ok := x87NoOperand[upper]; ok {
|
|
return true
|
|
}
|
|
if _, ok := x87Arith[upper]; ok {
|
|
return true
|
|
}
|
|
if _, ok := x87FCmov[upper]; ok {
|
|
return true
|
|
}
|
|
if _, ok := x87Compare[upper]; ok {
|
|
return true
|
|
}
|
|
if _, ok := x87MemUnary[upper]; ok {
|
|
return true
|
|
}
|
|
if _, ok := x87Fxsav[upper]; ok {
|
|
return true
|
|
}
|
|
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
|
|
}
|
|
if upper == "CMPXCHG8B" || upper == "CMPXCHG16B" {
|
|
return true
|
|
}
|
|
if upper == "INVPCID" || upper == "XABORT" {
|
|
return true
|
|
}
|
|
if _, ok := xsaveTable[upper]; ok {
|
|
return true
|
|
}
|
|
if before, ok := strings.CutSuffix(upper, "64"); ok {
|
|
if _, ok := xsaveTable[before]; ok {
|
|
return true
|
|
}
|
|
}
|
|
switch upper {
|
|
case "EMMS", "PSHUFW", "PSLLO", "PSRLO", "PMOVMSKB", "MOVQOZX", "MOVZWW", "MOVSWW":
|
|
return true
|
|
}
|
|
if _, ok := sseMaskmov[upper]; ok {
|
|
return true
|
|
}
|
|
if _, ok := mmxShiftImm[upper]; ok {
|
|
return true
|
|
}
|
|
if _, ok := mmxShiftVar[upper]; ok {
|
|
return true
|
|
}
|
|
return false
|
|
}
|