diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/internal/obj/x86/evex.go | |
parent | Initial commit. (diff) | |
download | golang-1.20-upstream.tar.xz golang-1.20-upstream.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/internal/obj/x86/evex.go')
-rw-r--r-- | src/cmd/internal/obj/x86/evex.go | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/src/cmd/internal/obj/x86/evex.go b/src/cmd/internal/obj/x86/evex.go new file mode 100644 index 0000000..aa93cd8 --- /dev/null +++ b/src/cmd/internal/obj/x86/evex.go @@ -0,0 +1,383 @@ +// 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] +} |