Source file src/cmd/internal/obj/x86/evex.go

     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  	"cmd/internal/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  //
   172  //	"RU_SAE.Z" => {rounding: 3, zeroing: true}
   173  //	"Z" => {zeroing: true}
   174  //	"BCST" => {broadcast: true}
   175  //	"SAE.Z" => {sae: true, zeroing: true}
   176  type evexSuffix struct {
   177  	rounding  byte
   178  	sae       bool
   179  	zeroing   bool
   180  	broadcast bool
   181  }
   182  
   183  // Rounding control values.
   184  // Match exact value for EVEX.L'L field (with exception of rcUnset).
   185  const (
   186  	rcRNSAE = 0 // Round towards nearest
   187  	rcRDSAE = 1 // Round towards -Inf
   188  	rcRUSAE = 2 // Round towards +Inf
   189  	rcRZSAE = 3 // Round towards zero
   190  	rcUnset = 4
   191  )
   192  
   193  // newEVEXSuffix returns proper zero value for evexSuffix.
   194  func newEVEXSuffix() evexSuffix {
   195  	return evexSuffix{rounding: rcUnset}
   196  }
   197  
   198  // evexSuffixMap maps obj.X86suffix to its decoded version.
   199  // Filled during init().
   200  var evexSuffixMap [255]evexSuffix
   201  
   202  func init() {
   203  	// Decode all valid suffixes for later use.
   204  	for i := range opSuffixTable {
   205  		suffix := newEVEXSuffix()
   206  		parts := strings.Split(opSuffixTable[i], ".")
   207  		for j := range parts {
   208  			switch parts[j] {
   209  			case "Z":
   210  				suffix.zeroing = true
   211  			case "BCST":
   212  				suffix.broadcast = true
   213  			case "SAE":
   214  				suffix.sae = true
   215  
   216  			case "RN_SAE":
   217  				suffix.rounding = rcRNSAE
   218  			case "RD_SAE":
   219  				suffix.rounding = rcRDSAE
   220  			case "RU_SAE":
   221  				suffix.rounding = rcRUSAE
   222  			case "RZ_SAE":
   223  				suffix.rounding = rcRZSAE
   224  			}
   225  		}
   226  		evexSuffixMap[i] = suffix
   227  	}
   228  }
   229  
   230  // toDisp8 tries to convert disp to proper 8-bit displacement value.
   231  func toDisp8(disp int32, p *obj.Prog, asmbuf *AsmBuf) (disp8 byte, ok bool) {
   232  	if asmbuf.evexflag {
   233  		bcst := evexSuffixMap[p.Scond].broadcast
   234  		elemSize := asmbuf.evex.DispMultiplier(bcst)
   235  		return compressedDisp8(disp, elemSize)
   236  	}
   237  	return byte(disp), disp >= -128 && disp < 128
   238  }
   239  
   240  // EncodeRegisterRange packs [reg0-reg1] list into 64-bit value that
   241  // is intended to be stored inside obj.Addr.Offset with TYPE_REGLIST.
   242  func EncodeRegisterRange(reg0, reg1 int16) int64 {
   243  	return (int64(reg0) << 0) |
   244  		(int64(reg1) << 16) |
   245  		obj.RegListX86Lo
   246  }
   247  
   248  // decodeRegisterRange unpacks [reg0-reg1] list from 64-bit value created by EncodeRegisterRange.
   249  func decodeRegisterRange(list int64) (reg0, reg1 int) {
   250  	return int((list >> 0) & 0xFFFF),
   251  		int((list >> 16) & 0xFFFF)
   252  }
   253  
   254  // ParseSuffix handles the special suffix for the 386/AMD64.
   255  // Suffix bits are stored into p.Scond.
   256  //
   257  // Leading "." in cond is ignored.
   258  func ParseSuffix(p *obj.Prog, cond string) error {
   259  	cond = strings.TrimPrefix(cond, ".")
   260  
   261  	suffix := newOpSuffix(cond)
   262  	if !suffix.IsValid() {
   263  		return inferSuffixError(cond)
   264  	}
   265  
   266  	p.Scond = uint8(suffix)
   267  	return nil
   268  }
   269  
   270  // inferSuffixError returns non-nil error that describes what could be
   271  // the cause of suffix parse failure.
   272  //
   273  // At the point this function is executed there is already assembly error,
   274  // so we can burn some clocks to construct good error message.
   275  //
   276  // Reported issues:
   277  //   - duplicated suffixes
   278  //   - illegal rounding/SAE+broadcast combinations
   279  //   - unknown suffixes
   280  //   - misplaced suffix (e.g. wrong Z suffix position)
   281  func inferSuffixError(cond string) error {
   282  	suffixSet := make(map[string]bool)  // Set for duplicates detection.
   283  	unknownSet := make(map[string]bool) // Set of unknown suffixes.
   284  	hasBcst := false
   285  	hasRoundSae := false
   286  	var msg []string // Error message parts
   287  
   288  	suffixes := strings.Split(cond, ".")
   289  	for i, suffix := range suffixes {
   290  		switch suffix {
   291  		case "Z":
   292  			if i != len(suffixes)-1 {
   293  				msg = append(msg, "Z suffix should be the last")
   294  			}
   295  		case "BCST":
   296  			hasBcst = true
   297  		case "SAE", "RN_SAE", "RZ_SAE", "RD_SAE", "RU_SAE":
   298  			hasRoundSae = true
   299  		default:
   300  			if !unknownSet[suffix] {
   301  				msg = append(msg, fmt.Sprintf("unknown suffix %q", suffix))
   302  			}
   303  			unknownSet[suffix] = true
   304  		}
   305  
   306  		if suffixSet[suffix] {
   307  			msg = append(msg, fmt.Sprintf("duplicate suffix %q", suffix))
   308  		}
   309  		suffixSet[suffix] = true
   310  	}
   311  
   312  	if hasBcst && hasRoundSae {
   313  		msg = append(msg, "can't combine rounding/SAE and broadcast")
   314  	}
   315  
   316  	if len(msg) == 0 {
   317  		return errors.New("bad suffix combination")
   318  	}
   319  	return errors.New(strings.Join(msg, "; "))
   320  }
   321  
   322  // opSuffixTable is a complete list of possible opcode suffix combinations.
   323  // It "maps" uint8 suffix bits to their string representation.
   324  // With the exception of first and last elements, order is not important.
   325  var opSuffixTable = [...]string{
   326  	"", // Map empty suffix to empty string.
   327  
   328  	"Z",
   329  
   330  	"SAE",
   331  	"SAE.Z",
   332  
   333  	"RN_SAE",
   334  	"RZ_SAE",
   335  	"RD_SAE",
   336  	"RU_SAE",
   337  	"RN_SAE.Z",
   338  	"RZ_SAE.Z",
   339  	"RD_SAE.Z",
   340  	"RU_SAE.Z",
   341  
   342  	"BCST",
   343  	"BCST.Z",
   344  
   345  	"<bad suffix>",
   346  }
   347  
   348  // opSuffix represents instruction opcode suffix.
   349  // Compound (multi-part) suffixes expressed with single opSuffix value.
   350  //
   351  // uint8 type is used to fit obj.Prog.Scond.
   352  type opSuffix uint8
   353  
   354  // badOpSuffix is used to represent all invalid suffix combinations.
   355  const badOpSuffix = opSuffix(len(opSuffixTable) - 1)
   356  
   357  // newOpSuffix returns opSuffix object that matches suffixes string.
   358  //
   359  // If no matching suffix is found, special "invalid" suffix is returned.
   360  // Use IsValid method to check against this case.
   361  func newOpSuffix(suffixes string) opSuffix {
   362  	for i := range opSuffixTable {
   363  		if opSuffixTable[i] == suffixes {
   364  			return opSuffix(i)
   365  		}
   366  	}
   367  	return badOpSuffix
   368  }
   369  
   370  // IsValid reports whether suffix is valid.
   371  // Empty suffixes are valid.
   372  func (suffix opSuffix) IsValid() bool {
   373  	return suffix != badOpSuffix
   374  }
   375  
   376  // String returns suffix printed representation.
   377  //
   378  // It matches the string that was used to create suffix with NewX86Suffix()
   379  // for valid suffixes.
   380  // For all invalid suffixes, special marker is returned.
   381  func (suffix opSuffix) String() string {
   382  	return opSuffixTable[suffix]
   383  }
   384  

View as plain text