383 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			383 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2018 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package x86
 | |
| 
 | |
| import (
 | |
| 	"github.com/twitchyliquid64/golang-asm/obj"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| // evexBits stores EVEX prefix info that is used during instruction encoding.
 | |
| type evexBits struct {
 | |
| 	b1 byte // [W1mmLLpp]
 | |
| 	b2 byte // [NNNbbZRS]
 | |
| 
 | |
| 	// Associated instruction opcode.
 | |
| 	opcode byte
 | |
| }
 | |
| 
 | |
| // newEVEXBits creates evexBits object from enc bytes at z position.
 | |
| func newEVEXBits(z int, enc *opBytes) evexBits {
 | |
| 	return evexBits{
 | |
| 		b1:     enc[z+0],
 | |
| 		b2:     enc[z+1],
 | |
| 		opcode: enc[z+2],
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // P returns EVEX.pp value.
 | |
| func (evex evexBits) P() byte { return (evex.b1 & evexP) >> 0 }
 | |
| 
 | |
| // L returns EVEX.L'L value.
 | |
| func (evex evexBits) L() byte { return (evex.b1 & evexL) >> 2 }
 | |
| 
 | |
| // M returns EVEX.mm value.
 | |
| func (evex evexBits) M() byte { return (evex.b1 & evexM) >> 4 }
 | |
| 
 | |
| // W returns EVEX.W value.
 | |
| func (evex evexBits) W() byte { return (evex.b1 & evexW) >> 7 }
 | |
| 
 | |
| // BroadcastEnabled reports whether BCST suffix is permitted.
 | |
| func (evex evexBits) BroadcastEnabled() bool {
 | |
| 	return evex.b2&evexBcst != 0
 | |
| }
 | |
| 
 | |
| // ZeroingEnabled reports whether Z suffix is permitted.
 | |
| func (evex evexBits) ZeroingEnabled() bool {
 | |
| 	return (evex.b2&evexZeroing)>>2 != 0
 | |
| }
 | |
| 
 | |
| // RoundingEnabled reports whether RN_SAE, RZ_SAE, RD_SAE and RU_SAE suffixes
 | |
| // are permitted.
 | |
| func (evex evexBits) RoundingEnabled() bool {
 | |
| 	return (evex.b2&evexRounding)>>1 != 0
 | |
| }
 | |
| 
 | |
| // SaeEnabled reports whether SAE suffix is permitted.
 | |
| func (evex evexBits) SaeEnabled() bool {
 | |
| 	return (evex.b2&evexSae)>>0 != 0
 | |
| }
 | |
| 
 | |
| // DispMultiplier returns displacement multiplier that is calculated
 | |
| // based on tuple type, EVEX.W and input size.
 | |
| // If embedded broadcast is used, bcst should be true.
 | |
| func (evex evexBits) DispMultiplier(bcst bool) int32 {
 | |
| 	if bcst {
 | |
| 		switch evex.b2 & evexBcst {
 | |
| 		case evexBcstN4:
 | |
| 			return 4
 | |
| 		case evexBcstN8:
 | |
| 			return 8
 | |
| 		}
 | |
| 		return 1
 | |
| 	}
 | |
| 
 | |
| 	switch evex.b2 & evexN {
 | |
| 	case evexN1:
 | |
| 		return 1
 | |
| 	case evexN2:
 | |
| 		return 2
 | |
| 	case evexN4:
 | |
| 		return 4
 | |
| 	case evexN8:
 | |
| 		return 8
 | |
| 	case evexN16:
 | |
| 		return 16
 | |
| 	case evexN32:
 | |
| 		return 32
 | |
| 	case evexN64:
 | |
| 		return 64
 | |
| 	case evexN128:
 | |
| 		return 128
 | |
| 	}
 | |
| 	return 1
 | |
| }
 | |
| 
 | |
| // EVEX is described by using 2-byte sequence.
 | |
| // See evexBits for more details.
 | |
| const (
 | |
| 	evexW   = 0x80 // b1[W... ....]
 | |
| 	evexWIG = 0 << 7
 | |
| 	evexW0  = 0 << 7
 | |
| 	evexW1  = 1 << 7
 | |
| 
 | |
| 	evexM    = 0x30 // b2[..mm ...]
 | |
| 	evex0F   = 1 << 4
 | |
| 	evex0F38 = 2 << 4
 | |
| 	evex0F3A = 3 << 4
 | |
| 
 | |
| 	evexL   = 0x0C // b1[.... LL..]
 | |
| 	evexLIG = 0 << 2
 | |
| 	evex128 = 0 << 2
 | |
| 	evex256 = 1 << 2
 | |
| 	evex512 = 2 << 2
 | |
| 
 | |
| 	evexP  = 0x03 // b1[.... ..pp]
 | |
| 	evex66 = 1 << 0
 | |
| 	evexF3 = 2 << 0
 | |
| 	evexF2 = 3 << 0
 | |
| 
 | |
| 	// Precalculated Disp8 N value.
 | |
| 	// N acts like a multiplier for 8bit displacement.
 | |
| 	// Note that some N are not used, but their bits are reserved.
 | |
| 	evexN    = 0xE0 // b2[NNN. ....]
 | |
| 	evexN1   = 0 << 5
 | |
| 	evexN2   = 1 << 5
 | |
| 	evexN4   = 2 << 5
 | |
| 	evexN8   = 3 << 5
 | |
| 	evexN16  = 4 << 5
 | |
| 	evexN32  = 5 << 5
 | |
| 	evexN64  = 6 << 5
 | |
| 	evexN128 = 7 << 5
 | |
| 
 | |
| 	// Disp8 for broadcasts.
 | |
| 	evexBcst   = 0x18 // b2[...b b...]
 | |
| 	evexBcstN4 = 1 << 3
 | |
| 	evexBcstN8 = 2 << 3
 | |
| 
 | |
| 	// Flags that permit certain AVX512 features.
 | |
| 	// It's semantically illegal to combine evexZeroing and evexSae.
 | |
| 	evexZeroing         = 0x4 // b2[.... .Z..]
 | |
| 	evexZeroingEnabled  = 1 << 2
 | |
| 	evexRounding        = 0x2 // b2[.... ..R.]
 | |
| 	evexRoundingEnabled = 1 << 1
 | |
| 	evexSae             = 0x1 // b2[.... ...S]
 | |
| 	evexSaeEnabled      = 1 << 0
 | |
| )
 | |
| 
 | |
| // compressedDisp8 calculates EVEX compressed displacement, if applicable.
 | |
| func compressedDisp8(disp, elemSize int32) (disp8 byte, ok bool) {
 | |
| 	if disp%elemSize == 0 {
 | |
| 		v := disp / elemSize
 | |
| 		if v >= -128 && v <= 127 {
 | |
| 			return byte(v), true
 | |
| 		}
 | |
| 	}
 | |
| 	return 0, false
 | |
| }
 | |
| 
 | |
| // evexZcase reports whether given Z-case belongs to EVEX group.
 | |
| func evexZcase(zcase uint8) bool {
 | |
| 	return zcase > Zevex_first && zcase < Zevex_last
 | |
| }
 | |
| 
 | |
| // evexSuffixBits carries instruction EVEX suffix set flags.
 | |
| //
 | |
| // Examples:
 | |
| //	"RU_SAE.Z" => {rounding: 3, zeroing: true}
 | |
| //	"Z" => {zeroing: true}
 | |
| //	"BCST" => {broadcast: true}
 | |
| //	"SAE.Z" => {sae: true, zeroing: true}
 | |
| type evexSuffix struct {
 | |
| 	rounding  byte
 | |
| 	sae       bool
 | |
| 	zeroing   bool
 | |
| 	broadcast bool
 | |
| }
 | |
| 
 | |
| // Rounding control values.
 | |
| // Match exact value for EVEX.L'L field (with exception of rcUnset).
 | |
| const (
 | |
| 	rcRNSAE = 0 // Round towards nearest
 | |
| 	rcRDSAE = 1 // Round towards -Inf
 | |
| 	rcRUSAE = 2 // Round towards +Inf
 | |
| 	rcRZSAE = 3 // Round towards zero
 | |
| 	rcUnset = 4
 | |
| )
 | |
| 
 | |
| // newEVEXSuffix returns proper zero value for evexSuffix.
 | |
| func newEVEXSuffix() evexSuffix {
 | |
| 	return evexSuffix{rounding: rcUnset}
 | |
| }
 | |
| 
 | |
| // evexSuffixMap maps obj.X86suffix to its decoded version.
 | |
| // Filled during init().
 | |
| var evexSuffixMap [255]evexSuffix
 | |
| 
 | |
| func init() {
 | |
| 	// Decode all valid suffixes for later use.
 | |
| 	for i := range opSuffixTable {
 | |
| 		suffix := newEVEXSuffix()
 | |
| 		parts := strings.Split(opSuffixTable[i], ".")
 | |
| 		for j := range parts {
 | |
| 			switch parts[j] {
 | |
| 			case "Z":
 | |
| 				suffix.zeroing = true
 | |
| 			case "BCST":
 | |
| 				suffix.broadcast = true
 | |
| 			case "SAE":
 | |
| 				suffix.sae = true
 | |
| 
 | |
| 			case "RN_SAE":
 | |
| 				suffix.rounding = rcRNSAE
 | |
| 			case "RD_SAE":
 | |
| 				suffix.rounding = rcRDSAE
 | |
| 			case "RU_SAE":
 | |
| 				suffix.rounding = rcRUSAE
 | |
| 			case "RZ_SAE":
 | |
| 				suffix.rounding = rcRZSAE
 | |
| 			}
 | |
| 		}
 | |
| 		evexSuffixMap[i] = suffix
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // toDisp8 tries to convert disp to proper 8-bit displacement value.
 | |
| func toDisp8(disp int32, p *obj.Prog, asmbuf *AsmBuf) (disp8 byte, ok bool) {
 | |
| 	if asmbuf.evexflag {
 | |
| 		bcst := evexSuffixMap[p.Scond].broadcast
 | |
| 		elemSize := asmbuf.evex.DispMultiplier(bcst)
 | |
| 		return compressedDisp8(disp, elemSize)
 | |
| 	}
 | |
| 	return byte(disp), disp >= -128 && disp < 128
 | |
| }
 | |
| 
 | |
| // EncodeRegisterRange packs [reg0-reg1] list into 64-bit value that
 | |
| // is intended to be stored inside obj.Addr.Offset with TYPE_REGLIST.
 | |
| func EncodeRegisterRange(reg0, reg1 int16) int64 {
 | |
| 	return (int64(reg0) << 0) |
 | |
| 		(int64(reg1) << 16) |
 | |
| 		obj.RegListX86Lo
 | |
| }
 | |
| 
 | |
| // decodeRegisterRange unpacks [reg0-reg1] list from 64-bit value created by EncodeRegisterRange.
 | |
| func decodeRegisterRange(list int64) (reg0, reg1 int) {
 | |
| 	return int((list >> 0) & 0xFFFF),
 | |
| 		int((list >> 16) & 0xFFFF)
 | |
| }
 | |
| 
 | |
| // ParseSuffix handles the special suffix for the 386/AMD64.
 | |
| // Suffix bits are stored into p.Scond.
 | |
| //
 | |
| // Leading "." in cond is ignored.
 | |
| func ParseSuffix(p *obj.Prog, cond string) error {
 | |
| 	cond = strings.TrimPrefix(cond, ".")
 | |
| 
 | |
| 	suffix := newOpSuffix(cond)
 | |
| 	if !suffix.IsValid() {
 | |
| 		return inferSuffixError(cond)
 | |
| 	}
 | |
| 
 | |
| 	p.Scond = uint8(suffix)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // inferSuffixError returns non-nil error that describes what could be
 | |
| // the cause of suffix parse failure.
 | |
| //
 | |
| // At the point this function is executed there is already assembly error,
 | |
| // so we can burn some clocks to construct good error message.
 | |
| //
 | |
| // Reported issues:
 | |
| //	- duplicated suffixes
 | |
| //	- illegal rounding/SAE+broadcast combinations
 | |
| //	- unknown suffixes
 | |
| //	- misplaced suffix (e.g. wrong Z suffix position)
 | |
| func inferSuffixError(cond string) error {
 | |
| 	suffixSet := make(map[string]bool)  // Set for duplicates detection.
 | |
| 	unknownSet := make(map[string]bool) // Set of unknown suffixes.
 | |
| 	hasBcst := false
 | |
| 	hasRoundSae := false
 | |
| 	var msg []string // Error message parts
 | |
| 
 | |
| 	suffixes := strings.Split(cond, ".")
 | |
| 	for i, suffix := range suffixes {
 | |
| 		switch suffix {
 | |
| 		case "Z":
 | |
| 			if i != len(suffixes)-1 {
 | |
| 				msg = append(msg, "Z suffix should be the last")
 | |
| 			}
 | |
| 		case "BCST":
 | |
| 			hasBcst = true
 | |
| 		case "SAE", "RN_SAE", "RZ_SAE", "RD_SAE", "RU_SAE":
 | |
| 			hasRoundSae = true
 | |
| 		default:
 | |
| 			if !unknownSet[suffix] {
 | |
| 				msg = append(msg, fmt.Sprintf("unknown suffix %q", suffix))
 | |
| 			}
 | |
| 			unknownSet[suffix] = true
 | |
| 		}
 | |
| 
 | |
| 		if suffixSet[suffix] {
 | |
| 			msg = append(msg, fmt.Sprintf("duplicate suffix %q", suffix))
 | |
| 		}
 | |
| 		suffixSet[suffix] = true
 | |
| 	}
 | |
| 
 | |
| 	if hasBcst && hasRoundSae {
 | |
| 		msg = append(msg, "can't combine rounding/SAE and broadcast")
 | |
| 	}
 | |
| 
 | |
| 	if len(msg) == 0 {
 | |
| 		return errors.New("bad suffix combination")
 | |
| 	}
 | |
| 	return errors.New(strings.Join(msg, "; "))
 | |
| }
 | |
| 
 | |
| // opSuffixTable is a complete list of possible opcode suffix combinations.
 | |
| // It "maps" uint8 suffix bits to their string representation.
 | |
| // With the exception of first and last elements, order is not important.
 | |
| var opSuffixTable = [...]string{
 | |
| 	"", // Map empty suffix to empty string.
 | |
| 
 | |
| 	"Z",
 | |
| 
 | |
| 	"SAE",
 | |
| 	"SAE.Z",
 | |
| 
 | |
| 	"RN_SAE",
 | |
| 	"RZ_SAE",
 | |
| 	"RD_SAE",
 | |
| 	"RU_SAE",
 | |
| 	"RN_SAE.Z",
 | |
| 	"RZ_SAE.Z",
 | |
| 	"RD_SAE.Z",
 | |
| 	"RU_SAE.Z",
 | |
| 
 | |
| 	"BCST",
 | |
| 	"BCST.Z",
 | |
| 
 | |
| 	"<bad suffix>",
 | |
| }
 | |
| 
 | |
| // opSuffix represents instruction opcode suffix.
 | |
| // Compound (multi-part) suffixes expressed with single opSuffix value.
 | |
| //
 | |
| // uint8 type is used to fit obj.Prog.Scond.
 | |
| type opSuffix uint8
 | |
| 
 | |
| // badOpSuffix is used to represent all invalid suffix combinations.
 | |
| const badOpSuffix = opSuffix(len(opSuffixTable) - 1)
 | |
| 
 | |
| // newOpSuffix returns opSuffix object that matches suffixes string.
 | |
| //
 | |
| // If no matching suffix is found, special "invalid" suffix is returned.
 | |
| // Use IsValid method to check against this case.
 | |
| func newOpSuffix(suffixes string) opSuffix {
 | |
| 	for i := range opSuffixTable {
 | |
| 		if opSuffixTable[i] == suffixes {
 | |
| 			return opSuffix(i)
 | |
| 		}
 | |
| 	}
 | |
| 	return badOpSuffix
 | |
| }
 | |
| 
 | |
| // IsValid reports whether suffix is valid.
 | |
| // Empty suffixes are valid.
 | |
| func (suffix opSuffix) IsValid() bool {
 | |
| 	return suffix != badOpSuffix
 | |
| }
 | |
| 
 | |
| // String returns suffix printed representation.
 | |
| //
 | |
| // It matches the string that was used to create suffix with NewX86Suffix()
 | |
| // for valid suffixes.
 | |
| // For all invalid suffixes, special marker is returned.
 | |
| func (suffix opSuffix) String() string {
 | |
| 	return opSuffixTable[suffix]
 | |
| }
 |