golang.org/x/arch@v0.17.0/x86/x86avxgen/decode.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 main 6 7 import ( 8 "fmt" 9 "log" 10 "regexp" 11 "strings" 12 13 "golang.org/x/arch/x86/xeddata" 14 ) 15 16 // encoding is decoded XED instruction pattern. 17 type encoding struct { 18 // opbyte is opcode byte (one that follows [E]VEX prefix). 19 // It's called "opcode" in Intel manual, but we use that for 20 // instruction name (iclass in XED terms). 21 opbyte string 22 23 // opdigit is ModRM.Reg field used to encode opcode extension. 24 // In Intel manual, "/digit" notation is used. 25 opdigit string 26 27 // vex represents [E]VEX fields that are used in a first [E]VEX 28 // opBytes element (see prefixExpr function). 29 vex struct { 30 P string // 66/F2/F3 31 L string // 128/256/512 32 M string // 0F/0F38/0F3A 33 W string // W0/W1 34 } 35 36 // evexScale is a scaling factor used to calculate compact disp-8. 37 evexScale string 38 39 // evexBcstScale is like evexScale, but used during broadcasting. 40 // Empty for optab entries that do not have broadcasting support. 41 evexBcstScale string 42 43 // evex describes which features of EVEX can be used by optab entry. 44 // All flags are "false" for VEX-encoded insts. 45 evex struct { 46 // There is no "broadcast" flag because it's inferred 47 // from non-empty evexBcstScale. 48 49 SAE bool // EVEX.b controls SAE for reg-reg insts 50 Rounding bool // EVEX.b + EVEX.RC (VL) control rounding for FP insts 51 Zeroing bool // Instruction can use zeroing. 52 } 53 } 54 55 type decoder struct { 56 ctx *context 57 insts []*instruction 58 } 59 60 // decodeGroups fills ctx.groups with decoded instruction groups. 61 // 62 // Reads XED objects from ctx.xedPath. 63 func decodeGroups(ctx *context) { 64 d := decoder{ctx: ctx} 65 groups := make(map[string][]*instruction) 66 for _, inst := range d.DecodeAll() { 67 groups[inst.opcode] = append(groups[inst.opcode], inst) 68 } 69 for op, insts := range groups { 70 ctx.groups = append(ctx.groups, &instGroup{ 71 opcode: op, 72 list: insts, 73 }) 74 } 75 } 76 77 // DecodeAll decodes every XED instruction. 78 func (d *decoder) DecodeAll() []*instruction { 79 err := xeddata.WalkInsts(d.ctx.xedPath, func(inst *xeddata.Inst) { 80 inst.Pattern = xeddata.ExpandStates(d.ctx.db, inst.Pattern) 81 pset := xeddata.NewPatternSet(inst.Pattern) 82 83 opcode := inst.Iclass 84 85 switch { 86 case inst.HasAttribute("AMDONLY") || inst.Extension == "XOP": 87 return // Only VEX and EVEX are supported 88 case !pset.Is("VEX") && !pset.Is("EVEX"): 89 return // Skip non-AVX instructions 90 case inst.RealOpcode == "N": 91 return // Skip unstable instructions 92 } 93 94 // Expand some patterns to simplify decodePattern. 95 pset.Replace("FIX_ROUND_LEN128()", "VL=0") 96 pset.Replace("FIX_ROUND_LEN512()", "VL=2") 97 98 mask, args := d.decodeArgs(pset, inst) 99 d.insts = append(d.insts, &instruction{ 100 pset: pset, 101 opcode: opcode, 102 mask: mask, 103 args: args, 104 enc: d.decodePattern(pset, inst), 105 }) 106 }) 107 if err != nil { 108 log.Fatalf("walk insts: %v", err) 109 } 110 return d.insts 111 } 112 113 // registerArgs maps XED argument name RHS to its decoded version. 114 var registerArgs = map[string]argument{ 115 "GPR32_R()": {"Yrl", "reg"}, 116 "GPR64_R()": {"Yrl", "reg"}, 117 "VGPR32_R()": {"Yrl", "reg"}, 118 "VGPR64_R()": {"Yrl", "reg"}, 119 "VGPR32_N()": {"Yrl", "regV"}, 120 "VGPR64_N()": {"Yrl", "regV"}, 121 "GPR32_B()": {"Yrl", "reg/mem"}, 122 "GPR64_B()": {"Yrl", "reg/mem"}, 123 "VGPR32_B()": {"Yrl", "reg/mem"}, 124 "VGPR64_B()": {"Yrl", "reg/mem"}, 125 126 "XMM_R()": {"Yxr", "reg"}, 127 "XMM_R3()": {"YxrEvex", "reg"}, 128 "XMM_N()": {"Yxr", "regV"}, 129 "XMM_N3()": {"YxrEvex", "regV"}, 130 "XMM_B()": {"Yxr", "reg/mem"}, 131 "XMM_B3()": {"YxrEvex", "reg/mem"}, 132 "XMM_SE()": {"Yxr", "regIH"}, 133 134 "YMM_R()": {"Yyr", "reg"}, 135 "YMM_R3()": {"YyrEvex", "reg"}, 136 "YMM_N()": {"Yyr", "regV"}, 137 "YMM_N3()": {"YyrEvex", "regV"}, 138 "YMM_B()": {"Yyr", "reg/mem"}, 139 "YMM_B3()": {"YyrEvex", "reg/mem"}, 140 "YMM_SE()": {"Yyr", "regIH"}, 141 142 "ZMM_R3()": {"Yzr", "reg"}, 143 "ZMM_N3()": {"Yzr", "regV"}, 144 "ZMM_B3()": {"Yzr", "reg/mem"}, 145 146 "MASK_R()": {"Yk", "reg"}, 147 "MASK_N()": {"Yk", "regV"}, 148 "MASK_B()": {"Yk", "reg/mem"}, 149 150 "MASKNOT0()": {"Yknot0", "kmask"}, 151 152 // Handled specifically in "generate". 153 "MASK1()": {"MASK1()", "MASK1()"}, 154 } 155 156 func (d *decoder) decodeArgs(pset xeddata.PatternSet, inst *xeddata.Inst) (mask *argument, args []*argument) { 157 for i, f := range strings.Fields(inst.Operands) { 158 xarg, err := xeddata.NewOperand(d.ctx.db, f) 159 if err != nil { 160 log.Fatalf("%s: args[%d]: %v", inst, i, err) 161 } 162 163 switch { 164 case xarg.Action == "": 165 continue // Skip meta args like EMX_BROADCAST_1TO32_8 166 case !xarg.IsVisible(): 167 continue 168 } 169 170 arg := &argument{} 171 args = append(args, arg) 172 173 switch xarg.NameLHS() { 174 case "IMM0": 175 if xarg.Width != "b" { 176 log.Fatalf("%s: args[%d]: expected width=b, found %s", inst, i, xarg.Width) 177 } 178 if pset["IMM0SIGNED=1"] { 179 arg.ytype = "Yi8" 180 } else { 181 arg.ytype = "Yu8" 182 } 183 arg.zkind = "imm8" 184 185 case "REG0", "REG1", "REG2", "REG3": 186 rhs := xarg.NameRHS() 187 if rhs == "MASK1()" { 188 mask = arg 189 } 190 *arg = registerArgs[rhs] 191 if arg.ytype == "" { 192 log.Fatalf("%s: args[%d]: unexpected %s reg", inst, i, rhs) 193 } 194 if xarg.Attributes["MULTISOURCE4"] { 195 arg.ytype += "Multi4" 196 } 197 198 case "MEM0": 199 arg.ytype = pset.MatchOrDefault("Ym", 200 "VMODRM_XMM()", "Yxvm", 201 "VMODRM_YMM()", "Yyvm", 202 "UISA_VMODRM_XMM()", "YxvmEvex", 203 "UISA_VMODRM_YMM()", "YyvmEvex", 204 "UISA_VMODRM_ZMM()", "Yzvm", 205 ) 206 arg.zkind = "reg/mem" 207 208 default: 209 log.Fatalf("%s: args[%d]: unexpected %s", inst, i, xarg.NameRHS()) 210 } 211 } 212 213 // Reverse args. 214 for i := len(args)/2 - 1; i >= 0; i-- { 215 j := len(args) - 1 - i 216 args[i], args[j] = args[j], args[i] 217 } 218 219 return mask, args 220 } 221 222 func (d *decoder) decodePattern(pset xeddata.PatternSet, inst *xeddata.Inst) *encoding { 223 var enc encoding 224 225 enc.opdigit = d.findOpdigit(pset) 226 enc.opbyte = d.findOpbyte(pset, inst) 227 228 if strings.Contains(inst.Attributes, "DISP8_") { 229 enc.evexScale = d.findEVEXScale(pset) 230 enc.evexBcstScale = d.findEVEXBcstScale(pset, inst) 231 } 232 233 enc.vex.P = pset.Match( 234 "VEX_PREFIX=1", "66", 235 "VEX_PREFIX=2", "F2", 236 "VEX_PREFIX=3", "F3") 237 enc.vex.M = pset.Match( 238 "MAP=1", "0F", 239 "MAP=2", "0F38", 240 "MAP=3", "0F3A") 241 enc.vex.L = pset.MatchOrDefault("128", 242 "VL=0", "128", 243 "VL=1", "256", 244 "VL=2", "512") 245 enc.vex.W = pset.MatchOrDefault("W0", 246 "REXW=0", "W0", 247 "REXW=1", "W1") 248 249 if pset.Is("EVEX") { 250 enc.evex.SAE = strings.Contains(inst.Operands, "TXT=SAESTR") 251 enc.evex.Rounding = strings.Contains(inst.Operands, "TXT=ROUNDC") 252 enc.evex.Zeroing = strings.Contains(inst.Operands, "TXT=ZEROSTR") 253 } 254 255 // Prefix each non-empty part with vex or evex. 256 parts := [...]*string{ 257 &enc.evexScale, &enc.evexBcstScale, 258 &enc.vex.P, &enc.vex.M, &enc.vex.L, &enc.vex.W, 259 } 260 for _, p := range parts { 261 if *p == "" { 262 continue 263 } 264 if pset.Is("EVEX") { 265 *p = "evex" + *p 266 } else { 267 *p = "vex" + *p 268 } 269 } 270 271 return &enc 272 } 273 274 func (d *decoder) findOpdigit(pset xeddata.PatternSet) string { 275 reg := pset.Index( 276 "REG[0b000]", 277 "REG[0b001]", 278 "REG[0b010]", 279 "REG[0b011]", 280 "REG[0b100]", 281 "REG[0b101]", 282 "REG[0b110]", 283 "REG[0b111]", 284 ) 285 // Fixed ModRM.Reg field means that it is used for opcode extension. 286 if reg != -1 { 287 return fmt.Sprintf("0%d", reg) 288 } 289 return "" 290 } 291 292 // opbyteRE matches uint8 hex literal. 293 var opbyteRE = regexp.MustCompile(`0x[0-9A-F]{2}`) 294 295 func (d *decoder) findOpbyte(pset xeddata.PatternSet, inst *xeddata.Inst) string { 296 opbyte := "" 297 for k := range pset { 298 if opbyteRE.MatchString(k) { 299 if opbyte == "" { 300 opbyte = k 301 } else { 302 log.Fatalf("%s: multiple opbytes", inst) 303 } 304 } 305 } 306 return opbyte 307 } 308 309 func (d *decoder) findEVEXScale(pset xeddata.PatternSet) string { 310 switch { 311 case pset["NELEM_FULL()"], pset["NELEM_FULLMEM()"]: 312 return pset.Match( 313 "VL=0", "N16", 314 "VL=1", "N32", 315 "VL=2", "N64") 316 case pset["NELEM_MOVDDUP()"]: 317 return pset.Match( 318 "VL=0", "N8", 319 "VL=1", "N32", 320 "VL=2", "N64") 321 case pset["NELEM_HALF()"], pset["NELEM_HALFMEM()"]: 322 return pset.Match( 323 "VL=0", "N8", 324 "VL=1", "N16", 325 "VL=2", "N32") 326 case pset["NELEM_QUARTERMEM()"]: 327 return pset.Match( 328 "VL=0", "N4", 329 "VL=1", "N8", 330 "VL=2", "N16") 331 case pset["NELEM_EIGHTHMEM()"]: 332 return pset.Match( 333 "VL=0", "N2", 334 "VL=1", "N4", 335 "VL=2", "N8") 336 case pset["NELEM_TUPLE2()"]: 337 return pset.Match( 338 "ESIZE_32_BITS()", "N8", 339 "ESIZE_64_BITS()", "N16") 340 case pset["NELEM_TUPLE4()"]: 341 return pset.Match( 342 "ESIZE_32_BITS()", "N16", 343 "ESIZE_64_BITS()", "N32") 344 case pset["NELEM_TUPLE8()"]: 345 return "N32" 346 case pset["NELEM_MEM128()"], pset["NELEM_TUPLE1_4X()"]: 347 return "N16" 348 } 349 350 // Explicit list is required to make it possible to 351 // detect unhandled nonterminals for the caller. 352 scalars := [...]string{ 353 "NELEM_SCALAR()", 354 "NELEM_GSCAT()", 355 "NELEM_GPR_READER()", 356 "NELEM_GPR_READER_BYTE()", 357 "NELEM_GPR_READER_WORD()", 358 "NELEM_GPR_WRITER_STORE()", 359 "NELEM_GPR_WRITER_STORE_BYTE()", 360 "NELEM_GPR_WRITER_STORE_WORD()", 361 "NELEM_GPR_WRITER_LDOP_D()", 362 "NELEM_GPR_WRITER_LDOP_Q()", 363 "NELEM_TUPLE1()", 364 "NELEM_TUPLE1_BYTE()", 365 "NELEM_TUPLE1_WORD()", 366 } 367 for _, scalar := range scalars { 368 if pset[scalar] { 369 return pset.Match( 370 "ESIZE_8_BITS()", "N1", 371 "ESIZE_16_BITS()", "N2", 372 "ESIZE_32_BITS()", "N4", 373 "ESIZE_64_BITS()", "N8") 374 } 375 } 376 377 return "" 378 } 379 380 func (d *decoder) findEVEXBcstScale(pset xeddata.PatternSet, inst *xeddata.Inst) string { 381 // Only FULL and HALF tuples are affected by the broadcasting. 382 switch { 383 case pset["NELEM_FULL()"]: 384 return pset.Match( 385 "ESIZE_32_BITS()", "BcstN4", 386 "ESIZE_64_BITS()", "BcstN8") 387 case pset["NELEM_HALF()"]: 388 return "BcstN4" 389 default: 390 if inst.HasAttribute("BROADCAST_ENABLED") { 391 log.Fatalf("%s: unexpected tuple for bcst", inst) 392 } 393 return "" 394 } 395 }