Files
gasm-sdk/asm/encodable.go
T
2026-09-20 00:38:24 +02:00

102 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"
// Encodable reports whether the amd64 encoder knows how to encode the
// mnemonic. It mirrors the dispatch in (*enc).encode: the fixed-name
// instructions, conditional jumps, the CMOV/SET condition families, the
// VEX/EVEX/opmask/gather/scatter vector paths, the legacy SSE tables and the
// explicit scalar cases. A mnemonic that parses (is in the architecture
// table) but is not encodable would otherwise surface only at assembly time,
// deep inside a build; the linter uses this predicate to flag it at edit
// time.
func Encodable(mnemonic string) bool {
upper := strings.ToUpper(mnemonic)
// Fixed-name instructions (no size suffix).
switch upper {
case "RET", "NOP", "CALL", "JMP":
return true
}
if _, ok := condCode(upper); ok {
return true
}
// VEX/EVEX and friends: the trailing B/W/L/Q/D is part of the mnemonic.
base, _, err := parseEvexSuffix(upper)
if err != nil {
return false
}
if isVex(base) || isEvex(base) || isKOp(base) || isGather(base) || isScatter(base) ||
base == "KMOVW" || base == "KMOVQ" {
return true
}
// CMOV carries size then condition (CMOVLGT); SET carries the condition
// alone (SETNE). The size letter is checked exactly as encodeCmov does,
// so a spelling like CMOVBGT is not reported encodable when Encode
// would reject it.
if rest, ok := strings.CutPrefix(upper, "CMOV"); ok && len(rest) >= 2 {
switch rest[0] {
case 'W', 'L', 'Q':
if _, ok := jccMap[rest[1:]]; ok {
return true
}
}
}
if rest, ok := strings.CutPrefix(upper, "SET"); ok {
if _, ok := jccMap[rest]; ok {
return true
}
}
// Legacy SSE shuffles and packed binaries dispatch on the full name.
if _, ok := sseShufTable[upper]; ok {
return true
}
if _, ok := sseBinTable[upper]; ok {
return true
}
// The size-suffix split: retry the tables and the scalar switch on the
// base.
base2, size := splitSize(upper)
if size == 0 {
size = 8
}
_ = size
if base2 != upper {
if _, ok := sseBinTable[base2]; ok {
return true
}
}
switch base2 {
case "MOV",
"ADD", "SUB", "AND", "OR", "XOR", "CMP",
"TEST",
"LEA",
"INC", "DEC", "NEG", "NOT",
"SHL", "SHR", "SAR",
"IMUL", "IMUL3",
"PUSH", "POP",
"BSF", "BSR", "LZCNT", "TZCNT", "POPCNT",
"BSWAP",
"PREFETCHNTA", "PREFETCHT0", "PREFETCHT1", "PREFETCHT2",
"MOVBLZX", "MOVBQZX", "MOVWLZX", "MOVWQZX", "MOVWLSX", "MOVLQSX",
"MOVBWZX", "MOVBWSX", "MOVBLSX", "MOVBQSX", "MOVWQSX", "MOVLQZX",
"CVTSL2SD", "CVTSQ2SD",
"MOVOU", "MOVO", "MOVUPS", "MOVAPS", "MOVUPD", "MOVAPD", "MOVSD", "MOVSS":
return true
}
// Full-name dispatches the size split would eat (a trailing width
// letter that is part of the mnemonic).
switch upper {
case "PMOVMSKB":
return true
}
return false
}