summaryrefslogtreecommitdiffstats
path: root/src/cmd/internal/obj/x86/evex.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
commit43a123c1ae6613b3efeed291fa552ecd909d3acf (patch)
treefd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/internal/obj/x86/evex.go
parentInitial commit. (diff)
downloadgolang-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.go383
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]
+}