feat(arch): the SVE predicate family in the extended layer
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
83052ab466
commit
8231302bca
5 files changed
+1451
-42
No files matched your search
+108
-18
@@ -11,7 +11,11 @@
|
||||
//
|
||||
// The spellings are the layer's own Plan 9 forms, the ones its metadata
|
||||
// documents: Zn, Zm, Zd for the unpredicated three-vector class, Zm, Pg/M,
|
||||
// Zdn for the predicated class, imm{, LSL #8}, Zdn for the immediate classes.
|
||||
// Zdn for the predicated class, imm{, LSL #8}, Zdn for the immediate
|
||||
// classes, and for the predicate family Pm.B, Pn.B, Pg/Z (or Pg.Z), Pd.B
|
||||
// for the logical operations, Pn.B, Pg.Z, Pd.B for the breaks, Pm.T, Pn.T,
|
||||
// Pd.T for the permutations, Rm, Rn, Pd.T for the while compares, PN8-PN15
|
||||
// for the counter destinations, and the bare SETFFR.
|
||||
|
||||
package asm
|
||||
|
||||
@@ -27,14 +31,16 @@ import (
|
||||
// arm64ExtStatement converts one instruction's operands into the extended
|
||||
// layer's operand form. pinned reports that the statement belongs to the
|
||||
// layer: the mnemonic is registered in the registry and the operand list
|
||||
// carries at least one scalable vector or predicate register. A pinned
|
||||
// statement can only encode through the layer, so every operand is read
|
||||
// here and its diagnostic replaces whatever the scalar paths would have
|
||||
// said about operands they cannot read; err is non-nil for a pinned
|
||||
// statement whose operands the layer refuses, and extops is complete only
|
||||
// when err is nil. Unpinned means the statement is nobody's: the caller
|
||||
// falls through to the ordinary arm64 encoders, which keep their exact
|
||||
// behaviour for every scalar, NEON and FP operand list.
|
||||
// carries at least one scalable vector, predicate or predicate-as-counter
|
||||
// register, or no operands at all (the zero-operand forms such as SETFFR,
|
||||
// which no scalar path could mean instead). A pinned statement can only
|
||||
// encode through the layer, so every operand is read here and its
|
||||
// diagnostic replaces whatever the scalar paths would have said about
|
||||
// operands they cannot read; err is non-nil for a pinned statement whose
|
||||
// operands the layer refuses, and extops is complete only when err is nil.
|
||||
// Unpinned means the statement is nobody's: the caller falls through to the
|
||||
// ordinary arm64 encoders, which keep their exact behaviour for every
|
||||
// scalar, NEON and FP operand list.
|
||||
func arm64ExtStatement(mnem string, ops []*ast.Operand) (extops []arch.ExtOperand, pinned bool, err error) {
|
||||
if _, ok := LookupExtension(arch.ARM64, mnem); !ok {
|
||||
return nil, false, nil
|
||||
@@ -77,18 +83,31 @@ func arm64ExtStatement(mnem string, ops []*ast.Operand) (extops []arch.ExtOperan
|
||||
out = append(out, ext)
|
||||
continue
|
||||
}
|
||||
return nil, true, fmt.Errorf("%s: operand %d (%s) is not an extended-layer operand: want a scalable vector register, a predicate register or an immediate", mnem, i+1, op.Raw)
|
||||
if ext, ok := arm64ExtCounter(text); ok {
|
||||
out = append(out, ext)
|
||||
continue
|
||||
}
|
||||
if ext, ok := arm64ExtGeneral(text); ok {
|
||||
out = append(out, ext)
|
||||
continue
|
||||
}
|
||||
return nil, true, fmt.Errorf("%s: operand %d (%s) is not an extended-layer operand: want a scalable vector, predicate, general or counter register, or an immediate", mnem, i+1, op.Raw)
|
||||
}
|
||||
return out, true, nil
|
||||
}
|
||||
|
||||
// arm64ExtPinned reports whether any operand is a scalable vector or
|
||||
// predicate register, the shapes only the extension layer reads. The test
|
||||
// arm64ExtPinned reports whether the statement belongs to the layer: any
|
||||
// operand is a scalable vector, predicate or predicate-as-counter register,
|
||||
// the shapes only the extension layer reads, or the statement carries no
|
||||
// operands at all and the mnemonic's zero-operand forms claim it. The test
|
||||
// is deliberately loose about the suffixes: P0/B is not a spelling the
|
||||
// layer takes, but the P of it makes the statement the layer's, and the
|
||||
// conversion then diagnoses the operand precisely instead of leaving it to
|
||||
// a scalar path that would report an unrelated register error.
|
||||
func arm64ExtPinned(ops []*ast.Operand) bool {
|
||||
if len(ops) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, op := range ops {
|
||||
if op.Kind == ast.OpImmediate {
|
||||
continue
|
||||
@@ -104,16 +123,20 @@ func arm64ExtPinned(ops []*ast.Operand) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// arm64ExtPredicateShape reports whether text spells a predicate register at
|
||||
// all: P, digits, an optional arrangement suffix and an optional qualifier
|
||||
// after a slash, whatever the qualifier says. The strict parse in
|
||||
// arm64ExtPredicate judges the suffix; this shape only decides who the
|
||||
// operand belongs to.
|
||||
// arm64ExtPredicateShape reports whether text spells a predicate or
|
||||
// predicate-as-counter register at all: PN or P, digits, an optional
|
||||
// arrangement suffix and an optional qualifier after a slash, whatever the
|
||||
// qualifier says. The strict parses in arm64ExtPredicate and
|
||||
// arm64ExtCounter judge the suffix; this shape only decides who the operand
|
||||
// belongs to.
|
||||
func arm64ExtPredicateShape(text string) bool {
|
||||
if text == "" || text[0] != 'P' {
|
||||
return false
|
||||
}
|
||||
text = text[1:]
|
||||
if rest, found := strings.CutPrefix(text, "N"); found {
|
||||
text = rest
|
||||
}
|
||||
if i := strings.IndexByte(text, '/'); i >= 0 {
|
||||
text = text[:i]
|
||||
}
|
||||
@@ -160,7 +183,8 @@ func arm64ExtVector(text string) (arch.ExtOperand, bool) {
|
||||
}
|
||||
|
||||
// arm64ExtPredicate parses a predicate register operand: P0..P15 with an
|
||||
// optional element-size suffix and an optional qualifier, P0/M, P0.Z, P0.B/M.
|
||||
// optional element-size suffix (P0.B) and an optional qualifier in either
|
||||
// spelling the corpus and the wired forms use, P0/M and P0.Z.
|
||||
func arm64ExtPredicate(text string) (arch.ExtOperand, bool) {
|
||||
qual := arch.ExtQualNone
|
||||
if base, suffix, found := strings.Cut(text, "/"); found {
|
||||
@@ -173,6 +197,15 @@ func arm64ExtPredicate(text string) (arch.ExtOperand, bool) {
|
||||
return arch.ExtOperand{}, false
|
||||
}
|
||||
text = base
|
||||
} else if base, suffix, found := strings.Cut(text, "."); found &&
|
||||
(suffix == "Z" || suffix == "M") {
|
||||
// The dot qualifier stands in place of an arrangement, the spelling
|
||||
// the toolchain's corpus writes (P1.Z, P14.M).
|
||||
qual = arch.ExtQualMerging
|
||||
if suffix == "Z" {
|
||||
qual = arch.ExtQualZeroing
|
||||
}
|
||||
text = base
|
||||
}
|
||||
reg, arr, ok := arm64ExtReg(text, 'P')
|
||||
if !ok {
|
||||
@@ -181,6 +214,63 @@ func arm64ExtPredicate(text string) (arch.ExtOperand, bool) {
|
||||
return arch.ExtOperand{Kind: arch.ExtPReg, Reg: reg, Arr: arr, Qual: qual}, true
|
||||
}
|
||||
|
||||
// arm64ExtCounter parses a predicate-as-counter register operand: PN8..PN15
|
||||
// with an optional element-size suffix, PN14.S. The register range is the
|
||||
// counter range the layer's convention carries; the encoding validates it.
|
||||
func arm64ExtCounter(text string) (arch.ExtOperand, bool) {
|
||||
rest, ok := strings.CutPrefix(text, "PN")
|
||||
if !ok {
|
||||
return arch.ExtOperand{}, false
|
||||
}
|
||||
reg, arr, ok := arm64ExtRegDigits(rest)
|
||||
if !ok {
|
||||
return arch.ExtOperand{}, false
|
||||
}
|
||||
return arch.ExtOperand{Kind: arch.ExtPNReg, Reg: reg, Arr: arr}, true
|
||||
}
|
||||
|
||||
// arm64ExtGeneral parses a general register operand: R0..R30, the plain
|
||||
// spelling the while-compare forms take. The register range is left to the
|
||||
// encoding, whose diagnostics name it.
|
||||
func arm64ExtGeneral(text string) (arch.ExtOperand, bool) {
|
||||
rest, ok := strings.CutPrefix(text, "R")
|
||||
if !ok {
|
||||
return arch.ExtOperand{}, false
|
||||
}
|
||||
reg, arr, ok := arm64ExtRegDigits(rest)
|
||||
if !ok || arr != arch.ExtArrNone {
|
||||
return arch.ExtOperand{}, false
|
||||
}
|
||||
return arch.ExtOperand{Kind: arch.ExtGReg, Reg: reg}, true
|
||||
}
|
||||
|
||||
// arm64ExtRegDigits parses the digits and optional arrangement suffix of a
|
||||
// register spelling once the letter prefix is gone.
|
||||
func arm64ExtRegDigits(text string) (reg int, arr arch.ExtArrangement, ok bool) {
|
||||
if base, suffix, found := strings.Cut(text, "."); found {
|
||||
switch suffix {
|
||||
case "B":
|
||||
arr = arch.ExtArrB
|
||||
case "H":
|
||||
arr = arch.ExtArrH
|
||||
case "S":
|
||||
arr = arch.ExtArrS
|
||||
case "D":
|
||||
arr = arch.ExtArrD
|
||||
case "Q":
|
||||
arr = arch.ExtArrQ
|
||||
default:
|
||||
return 0, 0, false
|
||||
}
|
||||
text = base
|
||||
}
|
||||
n, err := strconv.Atoi(text)
|
||||
if err != nil || n < 0 {
|
||||
return 0, 0, false
|
||||
}
|
||||
return n, arr, true
|
||||
}
|
||||
|
||||
// arm64ExtReg parses Pn or Zn with an optional arrangement suffix off a
|
||||
// normalised operand text. The register range is left to the encoding: the
|
||||
// layer's own diagnostics name the range a form carries.
|
||||
|
||||
Reference in new issue
Block a user