golang.org/x/arch@v0.17.0/x86/xeddata/operand.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package xeddata 6 7 import ( 8 "errors" 9 "strings" 10 ) 11 12 // OperandVisibility describes operand visibility in XED terms. 13 type OperandVisibility int 14 15 const ( 16 // VisExplicit is a default operand visibility. 17 // Explicit operand is "real" kind of operands that 18 // is shown in syntax and can be specified by the programmer. 19 VisExplicit OperandVisibility = iota 20 21 // VisImplicit is for fixed arg (like EAX); usually shown in syntax. 22 VisImplicit 23 24 // VisSuppressed is like VisImplicit, but not shown in syntax. 25 // In some very rare exceptions, they are also shown in syntax string. 26 VisSuppressed 27 28 // VisEcond is encoder-only conditions. Can be ignored. 29 VisEcond 30 ) 31 32 // Operand holds data that is encoded inside 33 // instruction's "OPERANDS" field. 34 // 35 // Use NewOperand function to decode operand fields into Operand object. 36 type Operand struct { 37 // Name is an ID with optional nonterminal name part. 38 // 39 // Possible values: "REG0=GPRv_B", "REG1", "MEM0", ... 40 // 41 // If nonterminal part is present, name 42 // can be split into LHS and RHS with NonTerminalName method. 43 Name string 44 45 // Action describes argument types. 46 // 47 // Possible values: "r", "w", "rw", "cr", "cw", "crw". 48 // Optional "c" prefix represents conditional access. 49 Action string 50 51 // Width descriptor. It can express simple width like "w" (word, 16bit) 52 // or meta-width like "v", which corresponds to {16, 32, 64} bits. 53 // 54 // The first column in all-widths.txt lists all possible widths. 55 // 56 // To deterine the size given a width string and a mode, use 57 // [Database.WidthSize]. 58 // 59 // Possible values: "", "q", "ds", "dq", ... 60 // Optional. 61 Width string 62 63 // Xtype holds XED-specific type information. 64 // 65 // Possible values: "", "f64", "i32", ... 66 // Optional. 67 Xtype string 68 69 // Attributes serves as container for all other properties. 70 // 71 // Possible values: 72 // EVEX.b context { 73 // TXT=ZEROSTR - zeroing 74 // TXT=SAESTR - suppress all exceptions 75 // TXT=ROUNDC - rounding 76 // TXT=BCASTSTR - broadcasting 77 // } 78 // MULTISOURCE4 - 4FMA multi-register operand. 79 // 80 // Optional. For most operands, it's nil. 81 Attributes map[string]bool 82 83 // Visibility tells if operand is explicit, implicit or suspended. 84 Visibility OperandVisibility 85 } 86 87 var xedVisibilities = map[string]OperandVisibility{ 88 "EXPL": VisExplicit, 89 "IMPL": VisImplicit, 90 "SUPP": VisSuppressed, 91 "ECOND": VisEcond, 92 } 93 94 // NewOperand decodes operand string. 95 // 96 // See "$XED/pysrc/opnds.py" to learn about fields format 97 // and valid combinations. 98 // 99 // Requires database with xtypes, widths, and extraWidths info. 100 func NewOperand(db *Database, s string) (*Operand, error) { 101 if db.widths == nil { 102 return nil, errors.New("Database.widths is nil") 103 } 104 if db.extraWidths == nil { 105 return nil, errors.New("Database.extraWidths is nil") 106 } 107 108 fields := strings.Split(s, ":") 109 switch len(fields) { 110 case 0: 111 return nil, errors.New("empty operand fields string") 112 case 1: 113 return &Operand{Name: fields[0]}, nil 114 } 115 var op Operand 116 117 // First two fields are fixed. 118 op.Name = fields[0] 119 op.Action = fields[1] 120 121 // Optional fields. 122 var w string 123 for _, f := range fields[2:] { 124 if db.widths[f] != nil && w == "" { 125 w = f 126 } else if vis, ok := xedVisibilities[f]; ok { 127 op.Visibility = vis 128 } else if xtype := db.xtypes[f]; xtype != nil { 129 op.Xtype = f 130 } else { 131 if op.Attributes == nil { 132 op.Attributes = make(map[string]bool) 133 } 134 op.Attributes[f] = true 135 } 136 } 137 138 // Get default width from operand type. 139 if w == "" { 140 if op.NonterminalName() { 141 if strings.HasPrefix(op.NameLHS(), "REG") { 142 rhs := op.NameRHS() 143 if strings.HasPrefix(rhs, "XED_REG_") { 144 // Register 145 w = db.extraWidths[rhs] 146 } else if strings.HasSuffix(rhs, "()") { 147 // Non-terminal 148 w = db.extraWidths[rhs] 149 } 150 } 151 } else { 152 // Try as an immediate. 153 w = db.extraWidths[op.Name] 154 } 155 } 156 157 if w != "" { 158 op.Width = w 159 // If operand did not specify an xtype, get the default from the width 160 if op.Xtype == "" && db.widths[w] != nil { 161 op.Xtype = db.widths[w].xtype 162 } 163 } 164 165 return &op, nil 166 } 167 168 // NonterminalName returns true if op.Name consist 169 // of LHS and RHS parts. 170 // 171 // RHS is non-terminal name lookup function expression. 172 // Example: "REG0=GPRv()" has "GPRv()" name lookup function. 173 func (op *Operand) NonterminalName() bool { 174 return strings.Contains(op.Name, "=") 175 } 176 177 // NameLHS returns left hand side part of the non-terminal name. 178 // Example: NameLHS("REG0=GPRv()") => "REG0". 179 func (op *Operand) NameLHS() string { 180 lhs, _, _ := strings.Cut(op.Name, "=") 181 return lhs 182 } 183 184 // NameRHS returns right hand side part of the non-terminal name. 185 // Example: NameLHS("REG0=GPRv()") => "GPRv()". 186 func (op *Operand) NameRHS() string { 187 _, rhs, _ := strings.Cut(op.Name, "=") 188 return rhs 189 } 190 191 // IsVisible returns true for operands that are usually 192 // shown in syntax strings. 193 func (op *Operand) IsVisible() bool { 194 return op.Visibility == VisExplicit || 195 op.Visibility == VisImplicit 196 }