Black Lives Matter. Support the Equal Justice Initiative.

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

Documentation: cmd/internal/obj/x86

     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  //	"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  

View as plain text