mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 01:02:25 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			382 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			382 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]
 | 
						|
}
 |