github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/obj/x86/evex.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 x86 6 7 import ( 8 "errors" 9 "fmt" 10 "strings" 11 12 "github.com/go-asm/go/cmd/obj" 13 ) 14 15 // evexBits stores EVEX prefix info that is used during instruction encoding. 16 type evexBits struct { 17 b1 byte // [W1mmLLpp] 18 b2 byte // [NNNbbZRS] 19 20 // Associated instruction opcode. 21 opcode byte 22 } 23 24 // newEVEXBits creates evexBits object from enc bytes at z position. 25 func newEVEXBits(z int, enc *opBytes) evexBits { 26 return evexBits{ 27 b1: enc[z+0], 28 b2: enc[z+1], 29 opcode: enc[z+2], 30 } 31 } 32 33 // P returns EVEX.pp value. 34 func (evex evexBits) P() byte { return (evex.b1 & evexP) >> 0 } 35 36 // L returns EVEX.L'L value. 37 func (evex evexBits) L() byte { return (evex.b1 & evexL) >> 2 } 38 39 // M returns EVEX.mm value. 40 func (evex evexBits) M() byte { return (evex.b1 & evexM) >> 4 } 41 42 // W returns EVEX.W value. 43 func (evex evexBits) W() byte { return (evex.b1 & evexW) >> 7 } 44 45 // BroadcastEnabled reports whether BCST suffix is permitted. 46 func (evex evexBits) BroadcastEnabled() bool { 47 return evex.b2&evexBcst != 0 48 } 49 50 // ZeroingEnabled reports whether Z suffix is permitted. 51 func (evex evexBits) ZeroingEnabled() bool { 52 return (evex.b2&evexZeroing)>>2 != 0 53 } 54 55 // RoundingEnabled reports whether RN_SAE, RZ_SAE, RD_SAE and RU_SAE suffixes 56 // are permitted. 57 func (evex evexBits) RoundingEnabled() bool { 58 return (evex.b2&evexRounding)>>1 != 0 59 } 60 61 // SaeEnabled reports whether SAE suffix is permitted. 62 func (evex evexBits) SaeEnabled() bool { 63 return (evex.b2&evexSae)>>0 != 0 64 } 65 66 // DispMultiplier returns displacement multiplier that is calculated 67 // based on tuple type, EVEX.W and input size. 68 // If embedded broadcast is used, bcst should be true. 69 func (evex evexBits) DispMultiplier(bcst bool) int32 { 70 if bcst { 71 switch evex.b2 & evexBcst { 72 case evexBcstN4: 73 return 4 74 case evexBcstN8: 75 return 8 76 } 77 return 1 78 } 79 80 switch evex.b2 & evexN { 81 case evexN1: 82 return 1 83 case evexN2: 84 return 2 85 case evexN4: 86 return 4 87 case evexN8: 88 return 8 89 case evexN16: 90 return 16 91 case evexN32: 92 return 32 93 case evexN64: 94 return 64 95 case evexN128: 96 return 128 97 } 98 return 1 99 } 100 101 // EVEX is described by using 2-byte sequence. 102 // See evexBits for more details. 103 const ( 104 evexW = 0x80 // b1[W... ....] 105 evexWIG = 0 << 7 106 evexW0 = 0 << 7 107 evexW1 = 1 << 7 108 109 evexM = 0x30 // b2[..mm ...] 110 evex0F = 1 << 4 111 evex0F38 = 2 << 4 112 evex0F3A = 3 << 4 113 114 evexL = 0x0C // b1[.... LL..] 115 evexLIG = 0 << 2 116 evex128 = 0 << 2 117 evex256 = 1 << 2 118 evex512 = 2 << 2 119 120 evexP = 0x03 // b1[.... ..pp] 121 evex66 = 1 << 0 122 evexF3 = 2 << 0 123 evexF2 = 3 << 0 124 125 // Precalculated Disp8 N value. 126 // N acts like a multiplier for 8bit displacement. 127 // Note that some N are not used, but their bits are reserved. 128 evexN = 0xE0 // b2[NNN. ....] 129 evexN1 = 0 << 5 130 evexN2 = 1 << 5 131 evexN4 = 2 << 5 132 evexN8 = 3 << 5 133 evexN16 = 4 << 5 134 evexN32 = 5 << 5 135 evexN64 = 6 << 5 136 evexN128 = 7 << 5 137 138 // Disp8 for broadcasts. 139 evexBcst = 0x18 // b2[...b b...] 140 evexBcstN4 = 1 << 3 141 evexBcstN8 = 2 << 3 142 143 // Flags that permit certain AVX512 features. 144 // It's semantically illegal to combine evexZeroing and evexSae. 145 evexZeroing = 0x4 // b2[.... .Z..] 146 evexZeroingEnabled = 1 << 2 147 evexRounding = 0x2 // b2[.... ..R.] 148 evexRoundingEnabled = 1 << 1 149 evexSae = 0x1 // b2[.... ...S] 150 evexSaeEnabled = 1 << 0 151 ) 152 153 // compressedDisp8 calculates EVEX compressed displacement, if applicable. 154 func compressedDisp8(disp, elemSize int32) (disp8 byte, ok bool) { 155 if disp%elemSize == 0 { 156 v := disp / elemSize 157 if v >= -128 && v <= 127 { 158 return byte(v), true 159 } 160 } 161 return 0, false 162 } 163 164 // evexZcase reports whether given Z-case belongs to EVEX group. 165 func evexZcase(zcase uint8) bool { 166 return zcase > Zevex_first && zcase < Zevex_last 167 } 168 169 // evexSuffixBits carries instruction EVEX suffix set flags. 170 // 171 // Examples: 172 // 173 // "RU_SAE.Z" => {rounding: 3, zeroing: true} 174 // "Z" => {zeroing: true} 175 // "BCST" => {broadcast: true} 176 // "SAE.Z" => {sae: true, zeroing: true} 177 type evexSuffix struct { 178 rounding byte 179 sae bool 180 zeroing bool 181 broadcast bool 182 } 183 184 // Rounding control values. 185 // Match exact value for EVEX.L'L field (with exception of rcUnset). 186 const ( 187 rcRNSAE = 0 // Round towards nearest 188 rcRDSAE = 1 // Round towards -Inf 189 rcRUSAE = 2 // Round towards +Inf 190 rcRZSAE = 3 // Round towards zero 191 rcUnset = 4 192 ) 193 194 // newEVEXSuffix returns proper zero value for evexSuffix. 195 func newEVEXSuffix() evexSuffix { 196 return evexSuffix{rounding: rcUnset} 197 } 198 199 // evexSuffixMap maps obj.X86suffix to its decoded version. 200 // Filled during init(). 201 var evexSuffixMap [255]evexSuffix 202 203 func init() { 204 // Decode all valid suffixes for later use. 205 for i := range opSuffixTable { 206 suffix := newEVEXSuffix() 207 parts := strings.Split(opSuffixTable[i], ".") 208 for j := range parts { 209 switch parts[j] { 210 case "Z": 211 suffix.zeroing = true 212 case "BCST": 213 suffix.broadcast = true 214 case "SAE": 215 suffix.sae = true 216 217 case "RN_SAE": 218 suffix.rounding = rcRNSAE 219 case "RD_SAE": 220 suffix.rounding = rcRDSAE 221 case "RU_SAE": 222 suffix.rounding = rcRUSAE 223 case "RZ_SAE": 224 suffix.rounding = rcRZSAE 225 } 226 } 227 evexSuffixMap[i] = suffix 228 } 229 } 230 231 // toDisp8 tries to convert disp to proper 8-bit displacement value. 232 func toDisp8(disp int32, p *obj.Prog, asmbuf *AsmBuf) (disp8 byte, ok bool) { 233 if asmbuf.evexflag { 234 bcst := evexSuffixMap[p.Scond].broadcast 235 elemSize := asmbuf.evex.DispMultiplier(bcst) 236 return compressedDisp8(disp, elemSize) 237 } 238 return byte(disp), disp >= -128 && disp < 128 239 } 240 241 // EncodeRegisterRange packs [reg0-reg1] list into 64-bit value that 242 // is intended to be stored inside obj.Addr.Offset with TYPE_REGLIST. 243 func EncodeRegisterRange(reg0, reg1 int16) int64 { 244 return (int64(reg0) << 0) | 245 (int64(reg1) << 16) | 246 obj.RegListX86Lo 247 } 248 249 // decodeRegisterRange unpacks [reg0-reg1] list from 64-bit value created by EncodeRegisterRange. 250 func decodeRegisterRange(list int64) (reg0, reg1 int) { 251 return int((list >> 0) & 0xFFFF), 252 int((list >> 16) & 0xFFFF) 253 } 254 255 // ParseSuffix handles the special suffix for the 386/AMD64. 256 // Suffix bits are stored into p.Scond. 257 // 258 // Leading "." in cond is ignored. 259 func ParseSuffix(p *obj.Prog, cond string) error { 260 cond = strings.TrimPrefix(cond, ".") 261 262 suffix := newOpSuffix(cond) 263 if !suffix.IsValid() { 264 return inferSuffixError(cond) 265 } 266 267 p.Scond = uint8(suffix) 268 return nil 269 } 270 271 // inferSuffixError returns non-nil error that describes what could be 272 // the cause of suffix parse failure. 273 // 274 // At the point this function is executed there is already assembly error, 275 // so we can burn some clocks to construct good error message. 276 // 277 // Reported issues: 278 // - duplicated suffixes 279 // - illegal rounding/SAE+broadcast combinations 280 // - unknown suffixes 281 // - misplaced suffix (e.g. wrong Z suffix position) 282 func inferSuffixError(cond string) error { 283 suffixSet := make(map[string]bool) // Set for duplicates detection. 284 unknownSet := make(map[string]bool) // Set of unknown suffixes. 285 hasBcst := false 286 hasRoundSae := false 287 var msg []string // Error message parts 288 289 suffixes := strings.Split(cond, ".") 290 for i, suffix := range suffixes { 291 switch suffix { 292 case "Z": 293 if i != len(suffixes)-1 { 294 msg = append(msg, "Z suffix should be the last") 295 } 296 case "BCST": 297 hasBcst = true 298 case "SAE", "RN_SAE", "RZ_SAE", "RD_SAE", "RU_SAE": 299 hasRoundSae = true 300 default: 301 if !unknownSet[suffix] { 302 msg = append(msg, fmt.Sprintf("unknown suffix %q", suffix)) 303 } 304 unknownSet[suffix] = true 305 } 306 307 if suffixSet[suffix] { 308 msg = append(msg, fmt.Sprintf("duplicate suffix %q", suffix)) 309 } 310 suffixSet[suffix] = true 311 } 312 313 if hasBcst && hasRoundSae { 314 msg = append(msg, "can't combine rounding/SAE and broadcast") 315 } 316 317 if len(msg) == 0 { 318 return errors.New("bad suffix combination") 319 } 320 return errors.New(strings.Join(msg, "; ")) 321 } 322 323 // opSuffixTable is a complete list of possible opcode suffix combinations. 324 // It "maps" uint8 suffix bits to their string representation. 325 // With the exception of first and last elements, order is not important. 326 var opSuffixTable = [...]string{ 327 "", // Map empty suffix to empty string. 328 329 "Z", 330 331 "SAE", 332 "SAE.Z", 333 334 "RN_SAE", 335 "RZ_SAE", 336 "RD_SAE", 337 "RU_SAE", 338 "RN_SAE.Z", 339 "RZ_SAE.Z", 340 "RD_SAE.Z", 341 "RU_SAE.Z", 342 343 "BCST", 344 "BCST.Z", 345 346 "<bad suffix>", 347 } 348 349 // opSuffix represents instruction opcode suffix. 350 // Compound (multi-part) suffixes expressed with single opSuffix value. 351 // 352 // uint8 type is used to fit obj.Prog.Scond. 353 type opSuffix uint8 354 355 // badOpSuffix is used to represent all invalid suffix combinations. 356 const badOpSuffix = opSuffix(len(opSuffixTable) - 1) 357 358 // newOpSuffix returns opSuffix object that matches suffixes string. 359 // 360 // If no matching suffix is found, special "invalid" suffix is returned. 361 // Use IsValid method to check against this case. 362 func newOpSuffix(suffixes string) opSuffix { 363 for i := range opSuffixTable { 364 if opSuffixTable[i] == suffixes { 365 return opSuffix(i) 366 } 367 } 368 return badOpSuffix 369 } 370 371 // IsValid reports whether suffix is valid. 372 // Empty suffixes are valid. 373 func (suffix opSuffix) IsValid() bool { 374 return suffix != badOpSuffix 375 } 376 377 // String returns suffix printed representation. 378 // 379 // It matches the string that was used to create suffix with NewX86Suffix() 380 // for valid suffixes. 381 // For all invalid suffixes, special marker is returned. 382 func (suffix opSuffix) String() string { 383 return opSuffixTable[suffix] 384 }