2018-11-20 11:44:44 -06:00
|
|
|
package inst
|
|
|
|
|
|
2018-12-02 12:28:33 -08:00
|
|
|
import (
|
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
2018-11-25 18:25:51 -08:00
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Instruction represents an x86 instruction.
|
2018-11-20 11:44:44 -06:00
|
|
|
type Instruction struct {
|
2019-01-05 11:43:39 -08:00
|
|
|
Opcode string // Golang assembly mnemonic
|
|
|
|
|
AliasOf string // Opcode of instruction that this is an alias for
|
|
|
|
|
Summary string // Description of the instruction
|
2021-11-12 18:35:36 -08:00
|
|
|
Forms // Accepted operand forms
|
2018-11-20 11:44:44 -06:00
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// IsTerminal reports whether the instruction exits a function.
|
2018-12-02 12:28:33 -08:00
|
|
|
func (i Instruction) IsTerminal() bool {
|
|
|
|
|
// TODO(mbm): how about the RETF* instructions
|
|
|
|
|
return i.Opcode == "RET"
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// IsBranch reports whether the instruction is a branch; that is, if it can
|
|
|
|
|
// cause control flow to jump to another location.
|
2018-12-02 12:28:33 -08:00
|
|
|
func (i Instruction) IsBranch() bool {
|
|
|
|
|
if i.Opcode == "CALL" {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for _, f := range i.Forms {
|
|
|
|
|
for _, op := range f.Operands {
|
|
|
|
|
if strings.HasPrefix(op.Type, "rel") {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// IsConditionalBranch reports whether the instruction branches dependent on some condition.
|
2018-12-02 12:28:33 -08:00
|
|
|
func (i Instruction) IsConditionalBranch() bool {
|
|
|
|
|
return i.IsBranch() && i.Opcode != "JMP"
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-27 18:53:46 -08:00
|
|
|
// Clone the instruction.
|
|
|
|
|
func (i Instruction) Clone() Instruction {
|
|
|
|
|
c := i
|
|
|
|
|
c.Forms = i.Forms.Clone()
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-12 18:35:36 -08:00
|
|
|
// Forms is a collection of instruction forms.
|
|
|
|
|
type Forms []Form
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Arities returns the unique arities among the instruction forms.
|
2021-11-12 18:35:36 -08:00
|
|
|
func (fs Forms) Arities() []int {
|
2018-11-25 18:25:51 -08:00
|
|
|
s := map[int]bool{}
|
2021-11-12 18:35:36 -08:00
|
|
|
for _, f := range fs {
|
2018-12-08 20:14:51 -08:00
|
|
|
s[f.Arity()] = true
|
2018-11-25 18:25:51 -08:00
|
|
|
}
|
|
|
|
|
a := make([]int, 0, len(s))
|
|
|
|
|
for n := range s {
|
|
|
|
|
a = append(a, n)
|
|
|
|
|
}
|
|
|
|
|
sort.Ints(a)
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Arity is a convenience for returning the unique instruction arity when you
|
|
|
|
|
// know it is not variadic. Panics for a variadic instruction.
|
2021-11-12 18:35:36 -08:00
|
|
|
func (fs Forms) Arity() int {
|
|
|
|
|
if fs.IsVariadic() {
|
2018-11-26 10:13:04 -08:00
|
|
|
panic("variadic")
|
|
|
|
|
}
|
2021-11-12 18:35:36 -08:00
|
|
|
a := fs.Arities()
|
2018-11-26 10:13:04 -08:00
|
|
|
return a[0]
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// IsVariadic reports whether the instruction has more than one arity.
|
2021-11-12 18:35:36 -08:00
|
|
|
func (fs Forms) IsVariadic() bool {
|
|
|
|
|
return len(fs.Arities()) > 1
|
2018-11-26 10:13:04 -08:00
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// IsNiladic reports whether the instruction takes no operands.
|
2021-11-12 18:35:36 -08:00
|
|
|
func (fs Forms) IsNiladic() bool {
|
|
|
|
|
a := fs.Arities()
|
2018-11-26 10:13:04 -08:00
|
|
|
return len(a) == 1 && a[0] == 0
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-27 18:53:46 -08:00
|
|
|
// Clone the instruction forms.
|
|
|
|
|
func (fs Forms) Clone() Forms {
|
|
|
|
|
cs := make(Forms, 0, len(fs))
|
|
|
|
|
for _, f := range fs {
|
|
|
|
|
cs = append(cs, f.Clone())
|
|
|
|
|
}
|
|
|
|
|
return cs
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Form specifies one accepted set of operands for an instruction.
|
2018-11-20 11:44:44 -06:00
|
|
|
type Form struct {
|
2019-01-05 11:43:39 -08:00
|
|
|
// Instruction sets this instruction form requires.
|
|
|
|
|
ISA []string
|
2019-07-28 17:58:49 -07:00
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Operands required for this form.
|
|
|
|
|
Operands []Operand
|
2019-07-28 17:58:49 -07:00
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Registers read or written but not explicitly passed to the instruction.
|
2018-11-23 23:48:47 -08:00
|
|
|
ImplicitOperands []ImplicitOperand
|
2019-07-28 17:58:49 -07:00
|
|
|
|
2021-11-12 18:35:36 -08:00
|
|
|
// Encoding type required for this instruction form.
|
|
|
|
|
EncodingType EncodingType
|
|
|
|
|
|
2019-07-28 17:58:49 -07:00
|
|
|
// CancellingInputs indicates this instruction form has no dependency on the
|
|
|
|
|
// input operands when they refer to the same register. The classic example of
|
|
|
|
|
// this is "XORQ RAX, RAX", in which case the output has no dependence on the
|
|
|
|
|
// value of RAX. Instruction forms with cancelling inputs have only two input
|
|
|
|
|
// operands, which have the same register type.
|
|
|
|
|
CancellingInputs bool
|
2021-11-12 18:35:36 -08:00
|
|
|
|
|
|
|
|
// Zeroing indicates whether the instruction form uses AVX-512 zeroing. This
|
|
|
|
|
// is the .Z suffix in Go, usually indicated with {z} operand suffix in
|
|
|
|
|
// Intel manuals.
|
|
|
|
|
Zeroing bool
|
|
|
|
|
|
|
|
|
|
// EmbeddedRounding indicates whether the instruction form uses AVX-512
|
|
|
|
|
// embedded rounding. This is the RN_SAE, RZ_SAE, RD_SAE and RU_SAE suffixes
|
|
|
|
|
// in Go, usually indicated with {er} in Intel manuals.
|
|
|
|
|
EmbeddedRounding bool
|
|
|
|
|
|
|
|
|
|
// SuppressAllExceptions indicates whether the instruction form uses AVX-512
|
|
|
|
|
// "suppress all exceptions". This is the SAE suffix in Go, usually
|
|
|
|
|
// indicated with {sae} in Intel manuals.
|
|
|
|
|
SuppressAllExceptions bool
|
|
|
|
|
|
|
|
|
|
// Broadcast indicates whether the instruction form uses AVX-512
|
|
|
|
|
// broadcast. This is the BCST suffix in Go, usually indicated by operand
|
|
|
|
|
// types like "m64bcst" in Intel manuals.
|
|
|
|
|
Broadcast bool
|
2018-11-20 11:44:44 -06:00
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Arity returns the number of operands this form expects.
|
2018-12-08 20:14:51 -08:00
|
|
|
func (f Form) Arity() int {
|
|
|
|
|
return len(f.Operands)
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Signature returns the list of operand types.
|
2018-11-25 21:50:46 -08:00
|
|
|
func (f Form) Signature() []string {
|
2018-12-08 20:14:51 -08:00
|
|
|
s := make([]string, f.Arity())
|
2018-11-25 21:50:46 -08:00
|
|
|
for i, op := range f.Operands {
|
|
|
|
|
s[i] = op.Type
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-12 18:35:36 -08:00
|
|
|
// Clone the instruction form.
|
|
|
|
|
func (f Form) Clone() Form {
|
|
|
|
|
c := f
|
|
|
|
|
c.ISA = append([]string(nil), f.ISA...)
|
|
|
|
|
c.Operands = append([]Operand(nil), f.Operands...)
|
|
|
|
|
c.ImplicitOperands = append([]ImplicitOperand(nil), f.ImplicitOperands...)
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AcceptsSuffixes reports whether this form takes any opcode suffixes.
|
|
|
|
|
func (f Form) AcceptsSuffixes() bool {
|
|
|
|
|
return f.Broadcast || f.EmbeddedRounding || f.SuppressAllExceptions || f.Zeroing
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SuffixesClass returns a key representing the class of instruction suffixes it
|
|
|
|
|
// accepts. All instructions sharing a suffix class accept the same suffixes.
|
|
|
|
|
func (f Form) SuffixesClass() string {
|
|
|
|
|
if !f.AcceptsSuffixes() {
|
|
|
|
|
return "nil"
|
|
|
|
|
}
|
|
|
|
|
var parts []string
|
|
|
|
|
for _, flag := range []struct {
|
|
|
|
|
Name string
|
|
|
|
|
Enabled bool
|
|
|
|
|
}{
|
|
|
|
|
{"er", f.EmbeddedRounding},
|
|
|
|
|
{"sae", f.SuppressAllExceptions},
|
|
|
|
|
{"bcst", f.Broadcast},
|
|
|
|
|
{"z", f.Zeroing},
|
|
|
|
|
} {
|
|
|
|
|
if flag.Enabled {
|
|
|
|
|
parts = append(parts, flag.Name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return strings.Join(parts, "_")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SupportedSuffixes returns the list of all possible suffix combinations
|
|
|
|
|
// supported by this instruction form.
|
|
|
|
|
func (f Form) SupportedSuffixes() []Suffixes {
|
|
|
|
|
suffixes := []Suffixes{
|
|
|
|
|
{},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
add := func(ss ...Suffix) {
|
|
|
|
|
var exts []Suffixes
|
|
|
|
|
for _, s := range ss {
|
|
|
|
|
for _, suffix := range suffixes {
|
|
|
|
|
ext := append(Suffixes(nil), suffix...)
|
|
|
|
|
ext = append(ext, s)
|
|
|
|
|
exts = append(exts, ext)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
suffixes = exts
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f.Broadcast {
|
|
|
|
|
add(BCST)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f.EmbeddedRounding {
|
|
|
|
|
add(RN_SAE, RZ_SAE, RD_SAE, RU_SAE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f.SuppressAllExceptions {
|
|
|
|
|
add(SAE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f.Zeroing {
|
|
|
|
|
add(Z)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return suffixes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Suffix is an opcode suffix.
|
|
|
|
|
type Suffix string
|
|
|
|
|
|
|
|
|
|
// Supported opcode suffixes in x86 assembly.
|
|
|
|
|
const (
|
|
|
|
|
BCST Suffix = "BCST"
|
|
|
|
|
RN_SAE Suffix = "RN_SAE"
|
|
|
|
|
RZ_SAE Suffix = "RZ_SAE"
|
|
|
|
|
RD_SAE Suffix = "RD_SAE"
|
|
|
|
|
RU_SAE Suffix = "RU_SAE"
|
|
|
|
|
SAE Suffix = "SAE"
|
|
|
|
|
Z Suffix = "Z"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func (s Suffix) String() string {
|
|
|
|
|
return string(s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Summary of the opcode suffix, for documentation purposes.
|
|
|
|
|
func (s Suffix) Summary() string {
|
|
|
|
|
return suffixsummary[s]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var suffixsummary = map[Suffix]string{
|
|
|
|
|
BCST: "Broadcast",
|
|
|
|
|
RN_SAE: "Round Towards Nearest",
|
|
|
|
|
RZ_SAE: "Round Towards Zero",
|
|
|
|
|
RD_SAE: "Round Towards Negative Infinity",
|
|
|
|
|
RU_SAE: "Round Towards Positive Infinity",
|
|
|
|
|
SAE: "Suppress All Exceptions",
|
|
|
|
|
Z: "Zeroing Masking",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Suffixes is a list of opcode suffixes.
|
|
|
|
|
type Suffixes []Suffix
|
|
|
|
|
|
|
|
|
|
// String returns the dot-separated suffixes.
|
|
|
|
|
func (s Suffixes) String() string { return s.Join(".") }
|
|
|
|
|
|
|
|
|
|
// Join suffixes with the given separator.
|
|
|
|
|
func (s Suffixes) Join(sep string) string {
|
|
|
|
|
return strings.Join(s.Strings(), sep)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Strings returns the suffixes as strings.
|
|
|
|
|
func (s Suffixes) Strings() []string {
|
|
|
|
|
var ss []string
|
|
|
|
|
for _, suffix := range s {
|
|
|
|
|
ss = append(ss, suffix.String())
|
|
|
|
|
}
|
|
|
|
|
return ss
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Summaries returns all the suffix summaries.
|
|
|
|
|
func (s Suffixes) Summaries() []string {
|
|
|
|
|
var summaries []string
|
|
|
|
|
for _, suffix := range s {
|
|
|
|
|
summaries = append(summaries, suffix.Summary())
|
|
|
|
|
}
|
|
|
|
|
return summaries
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Operand is an operand to an instruction, describing the expected type and read/write action.
|
2018-11-20 11:44:44 -06:00
|
|
|
type Operand struct {
|
|
|
|
|
Type string
|
|
|
|
|
Action Action
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// ImplicitOperand describes a register that is implicitly read/written by an instruction.
|
2018-11-23 17:14:18 -06:00
|
|
|
type ImplicitOperand struct {
|
|
|
|
|
Register string
|
|
|
|
|
Action Action
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Action specifies the read/write operation of an instruction on an operand.
|
2018-11-20 11:44:44 -06:00
|
|
|
type Action uint8
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Possible Action types.
|
2018-11-20 11:44:44 -06:00
|
|
|
const (
|
2021-11-12 18:35:36 -08:00
|
|
|
R Action = 1 << iota // Read
|
|
|
|
|
W // Write
|
|
|
|
|
|
|
|
|
|
RW Action = R | W // Read-Write
|
2018-11-20 11:44:44 -06:00
|
|
|
)
|
2018-11-21 13:02:18 -06:00
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// ActionFromReadWrite builds an Action from boolean flags.
|
2018-11-21 13:02:18 -06:00
|
|
|
func ActionFromReadWrite(r, w bool) Action {
|
|
|
|
|
var a Action
|
|
|
|
|
if r {
|
|
|
|
|
a |= R
|
|
|
|
|
}
|
|
|
|
|
if w {
|
|
|
|
|
a |= W
|
|
|
|
|
}
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-12 18:35:36 -08:00
|
|
|
// ContainsAll reports whether a supports all actions in s.
|
|
|
|
|
func (a Action) ContainsAll(s Action) bool {
|
2018-12-02 17:57:12 -08:00
|
|
|
return (a & s) == s
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-12 18:35:36 -08:00
|
|
|
// ContainsAny reports whether a supports any actions in s.
|
|
|
|
|
func (a Action) ContainsAny(s Action) bool {
|
|
|
|
|
return (a & s) != 0
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Read reports whether a supports read.
|
2018-11-21 13:02:18 -06:00
|
|
|
func (a Action) Read() bool {
|
2021-11-12 18:35:36 -08:00
|
|
|
return a.ContainsAll(R)
|
2018-11-21 13:02:18 -06:00
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// Write reports whether a supports write.
|
2018-11-21 13:02:18 -06:00
|
|
|
func (a Action) Write() bool {
|
2021-11-12 18:35:36 -08:00
|
|
|
return a.ContainsAll(W)
|
2018-11-21 13:02:18 -06:00
|
|
|
}
|
|
|
|
|
|
2019-01-05 11:43:39 -08:00
|
|
|
// String represents a as a human-readable string.
|
2018-11-21 13:02:18 -06:00
|
|
|
func (a Action) String() string {
|
|
|
|
|
s := ""
|
|
|
|
|
if a.Read() {
|
|
|
|
|
s += "r"
|
|
|
|
|
}
|
|
|
|
|
if a.Write() {
|
|
|
|
|
s += "w"
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
2021-11-12 18:35:36 -08:00
|
|
|
|
|
|
|
|
// EncodingType specifies a category of encoding types.
|
|
|
|
|
type EncodingType uint8
|
|
|
|
|
|
|
|
|
|
// Supported encoding types.
|
|
|
|
|
const (
|
|
|
|
|
EncodingTypeLegacy EncodingType = 1 + iota
|
|
|
|
|
EncodingTypeREX
|
|
|
|
|
EncodingTypeVEX
|
|
|
|
|
EncodingTypeEVEX
|
|
|
|
|
)
|