evex.go raw

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