go evex 源码

2022-07-15 浏览 (924)

golang evex 代码

文件路径:/src/cmd/internal/obj/x86/evex.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 (
	"cmd/internal/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]
}

相关信息

go 源码目录

相关文章

go a.out 源码

go aenum 源码

go anames 源码

go asm6 源码

go asm_test 源码

go avx_optabs 源码

go list6 源码

go obj6 源码

go obj6_test 源码

go pcrelative_test 源码

^