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