2026-08-29 13:42:47 +02:00
|
|
|
// 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 {
|
2026-09-20 06:44:51 +02:00
|
|
|
case "RET", "NOP", "CALL", "JMP",
|
|
|
|
|
"POPFQ", "PUSHFQ", "INT", "LDMXCSR", "STMXCSR", "CMPSD", "SHA256RNDS2":
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if _, ok := noOperandTable[upper]; ok {
|
2026-08-29 13:42:47 +02:00
|
|
|
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) ||
|
2026-09-20 06:44:51 +02:00
|
|
|
base == "KMOVW" || base == "KMOVQ" || base == "KMOVB" || base == "KMOVD" {
|
2026-08-29 13:42:47 +02:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CMOV carries size then condition (CMOVLGT); SET carries the condition
|
2026-09-19 23:49:07 +02:00
|
|
|
// 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.
|
2026-08-29 13:42:47 +02:00
|
|
|
if rest, ok := strings.CutPrefix(upper, "CMOV"); ok && len(rest) >= 2 {
|
2026-09-19 23:49:07 +02:00
|
|
|
switch rest[0] {
|
|
|
|
|
case 'W', 'L', 'Q':
|
|
|
|
|
if _, ok := jccMap[rest[1:]]; ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
2026-08-29 13:42:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if rest, ok := strings.CutPrefix(upper, "SET"); ok {
|
|
|
|
|
if _, ok := jccMap[rest]; ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-20 06:44:51 +02:00
|
|
|
// Legacy SSE shuffles and packed binaries dispatch on the full name; so
|
|
|
|
|
// do the imm8-controlled instructions, the lane extracts and inserts and
|
|
|
|
|
// the packed integer shifts (their trailing width letters belong to the
|
|
|
|
|
// mnemonic).
|
2026-08-29 13:42:47 +02:00
|
|
|
if _, ok := sseShufTable[upper]; ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if _, ok := sseBinTable[upper]; ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
2026-09-20 06:44:51 +02:00
|
|
|
if _, ok := sseImm3Table[upper]; ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if _, ok := sseExtractTable[upper]; ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if _, ok := sseInsertTable[upper]; ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if _, ok := sseShiftImm[upper]; ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
2026-08-29 13:42:47 +02:00
|
|
|
|
|
|
|
|
// 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 {
|
2026-09-20 06:44:51 +02:00
|
|
|
case "MOV", "MOVD",
|
|
|
|
|
"ADD", "SUB", "AND", "OR", "XOR", "CMP", "ADC", "SBB",
|
2026-08-29 13:42:47 +02:00
|
|
|
"TEST",
|
|
|
|
|
"LEA",
|
2026-09-20 06:44:51 +02:00
|
|
|
"INC", "DEC", "NEG", "NOT", "MUL", "DIV", "IDIV",
|
|
|
|
|
"SHL", "SHR", "SAR", "SAL", "ROL", "ROR", "RCL", "RCR",
|
|
|
|
|
"BT", "BTS", "BTR", "BTC",
|
|
|
|
|
"XCHG", "CMPXCHG", "XADD", "CRC32", "ADCX", "ADOX",
|
|
|
|
|
"MOVS", "STOS",
|
2026-08-29 13:42:47 +02:00
|
|
|
"IMUL", "IMUL3",
|
|
|
|
|
"PUSH", "POP",
|
|
|
|
|
"BSF", "BSR", "LZCNT", "TZCNT", "POPCNT",
|
|
|
|
|
"BSWAP",
|
|
|
|
|
"PREFETCHNTA", "PREFETCHT0", "PREFETCHT1", "PREFETCHT2",
|
|
|
|
|
"MOVBLZX", "MOVBQZX", "MOVWLZX", "MOVWQZX", "MOVWLSX", "MOVLQSX",
|
2026-09-20 00:38:24 +02:00
|
|
|
"MOVBWZX", "MOVBWSX", "MOVBLSX", "MOVBQSX", "MOVWQSX", "MOVLQZX",
|
2026-08-29 13:42:47 +02:00
|
|
|
"CVTSL2SD", "CVTSQ2SD",
|
2026-09-20 06:44:51 +02:00
|
|
|
"CVTSD2S", "CVTTSD2S", "CVTSS2S", "CVTTSS2S",
|
|
|
|
|
"FMOVD",
|
|
|
|
|
"MOVOU", "MOVO", "MOVOA", "MOVUPS", "MOVAPS", "MOVUPD", "MOVAPD", "MOVSD", "MOVSS":
|
2026-08-29 13:42:47 +02:00
|
|
|
return true
|
|
|
|
|
}
|
2026-09-20 00:38:24 +02:00
|
|
|
// Full-name dispatches the size split would eat (a trailing width
|
|
|
|
|
// letter that is part of the mnemonic).
|
|
|
|
|
switch upper {
|
|
|
|
|
case "PMOVMSKB":
|
|
|
|
|
return true
|
|
|
|
|
}
|
2026-08-29 13:42:47 +02:00
|
|
|
return false
|
|
|
|
|
}
|