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  }