gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

evex.go (9505B)


      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 }