|  | // 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] | 
|  | } |