test(asm): sweep every registered amd64 extension mnemonic from .s text
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
fa50521619
commit
1120a52a54
1 file changed
+225
@@ -6,6 +6,7 @@ package asm
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -282,3 +283,227 @@ func TestAmd64AssembleExtensionLeavesTheMainEncoderAlone(t *testing.T) {
|
||||
t.Errorf("VPOPCNTD Z1, Z2: got %x, want the main encoder's %x", body, e.out)
|
||||
}
|
||||
}
|
||||
|
||||
// amd64SweepClass recomputes the vector class an entry encodes, the read of
|
||||
// the template's length field the arch package keeps private: EVEX.L'L in
|
||||
// byte three, VEX.L in byte two.
|
||||
func amd64SweepClass(in arch.ExtInstr) arch.ExtOperandKind {
|
||||
if in.Vex {
|
||||
if in.Bytes[2]&0x04 != 0 {
|
||||
return arch.ExtYMM
|
||||
}
|
||||
return arch.ExtXMM
|
||||
}
|
||||
switch (in.Bytes[3] >> 5) & 3 {
|
||||
case 0:
|
||||
return arch.ExtXMM
|
||||
case 1:
|
||||
return arch.ExtYMM
|
||||
default:
|
||||
return arch.ExtZMM
|
||||
}
|
||||
}
|
||||
|
||||
// amd64SweepVec spells and models one vector register of the class, the
|
||||
// house names X, Y and Z the layer's text forms carry.
|
||||
func amd64SweepVec(kind arch.ExtOperandKind, n int) (string, arch.ExtOperand) {
|
||||
switch kind {
|
||||
case arch.ExtXMM:
|
||||
return fmt.Sprintf("X%d", n), arch.ExtXmm(n)
|
||||
case arch.ExtYMM:
|
||||
return fmt.Sprintf("Y%d", n), arch.ExtYmm(n)
|
||||
default:
|
||||
return fmt.Sprintf("Z%d", n), arch.ExtZmm(n)
|
||||
}
|
||||
}
|
||||
|
||||
// amd64SweepGpr spells and models the general register of an entry: the W bit
|
||||
// picks the width, and an entry that ignores W takes the 64-bit spelling.
|
||||
func amd64SweepGpr(in arch.ExtInstr) (string, arch.ExtOperand) {
|
||||
if in.Wig || in.Bytes[2]&0x80 != 0 {
|
||||
return "R12", arch.ExtGpr64(12)
|
||||
}
|
||||
return "R12D", arch.ExtGpr32(12)
|
||||
}
|
||||
|
||||
// amd64SweepImm spells and models the control immediate of an entry: the
|
||||
// mantissa control keeps its reserved upper nibble at zero, every other
|
||||
// layout takes a whole byte.
|
||||
func amd64SweepImm(in arch.ExtInstr) (string, arch.ExtOperand) {
|
||||
if in.Imm8 == arch.ExtImm8GetMant {
|
||||
return "$0x0b", arch.ExtImmediate(0x0b)
|
||||
}
|
||||
return "$0x7b", arch.ExtImmediate(0x7b)
|
||||
}
|
||||
|
||||
// amd64SweepDest spells and models the register destination with the
|
||||
// decorations the entry carries on its register form: the write mask beside
|
||||
// every masked entry, the rounding or the exception suppression beside every
|
||||
// entry that takes one.
|
||||
func amd64SweepDest(in arch.ExtInstr, kind arch.ExtOperandKind, n int) (string, arch.ExtOperand) {
|
||||
text, model := amd64SweepVec(kind, n)
|
||||
dec := ""
|
||||
if in.Mask {
|
||||
dec += "{K5}"
|
||||
}
|
||||
switch {
|
||||
case in.Er:
|
||||
dec += "{RZ-SAE}"
|
||||
case in.Sae:
|
||||
dec += "{SAE}"
|
||||
}
|
||||
if dec == "" {
|
||||
return text, model
|
||||
}
|
||||
if in.Mask {
|
||||
model = arch.ExtWriteMasked(model, 5, false)
|
||||
}
|
||||
switch {
|
||||
case in.Er:
|
||||
model = arch.ExtRounded(model, arch.ExtRoundTruncate)
|
||||
case in.Sae:
|
||||
model = arch.ExtRounded(model, arch.ExtRoundSAE)
|
||||
}
|
||||
return text + dec, model
|
||||
}
|
||||
|
||||
// amd64SweepMaskedDest spells and models the destination with the write mask
|
||||
// alone, the one decoration the memory shape keeps.
|
||||
func amd64SweepMaskedDest(in arch.ExtInstr, kind arch.ExtOperandKind, n int) (string, arch.ExtOperand) {
|
||||
text, model := amd64SweepVec(kind, n)
|
||||
if in.Mask {
|
||||
return text + "{K5}", arch.ExtWriteMasked(model, 5, false)
|
||||
}
|
||||
return text, model
|
||||
}
|
||||
|
||||
// amd64ExtSweepStatements builds, for the first registered entry of every
|
||||
// distinct amd64 mnemonic, the register-form statement the sweep drives and,
|
||||
// where the entry carries a memory position, the memory-form statement beside
|
||||
// it. Each statement comes back with its mnemonic and the model operands the
|
||||
// text spells, so the sweep can pin the assembled bytes against
|
||||
// EncodeExtension. The statements follow the registry: a mnemonic registered
|
||||
// on a form this builder knows lands in the sweep in the same change.
|
||||
func amd64ExtSweepStatements() (stmts, mnems []string, models [][]arch.ExtOperand, distinct int) {
|
||||
memText, memModel := "(R9)", arch.ExtMemory(9, 0)
|
||||
seen := make(map[string]bool)
|
||||
for _, in := range arch.Extensions(arch.AMD64) {
|
||||
if seen[in.Name] {
|
||||
continue
|
||||
}
|
||||
seen[in.Name] = true
|
||||
distinct++
|
||||
class := amd64SweepClass(in)
|
||||
// The two-vector forms narrow one side: the half form the
|
||||
// destination, the wide form the source, and the quarter forms pin
|
||||
// one side to the XMM class.
|
||||
srcClass, destClass := class, class
|
||||
switch in.Form {
|
||||
case arch.ExtFormAmdVec2Half:
|
||||
destClass = arch.ExtXMM
|
||||
if class == arch.ExtZMM {
|
||||
destClass = arch.ExtYMM
|
||||
}
|
||||
case arch.ExtFormAmdVec2Wide:
|
||||
srcClass = arch.ExtXMM
|
||||
if class == arch.ExtZMM {
|
||||
srcClass = arch.ExtYMM
|
||||
}
|
||||
case arch.ExtFormAmdVec2Quarter:
|
||||
srcClass = arch.ExtXMM
|
||||
case arch.ExtFormAmdVec2ToQuarter:
|
||||
destClass = arch.ExtXMM
|
||||
}
|
||||
srcText, srcModel := amd64SweepVec(srcClass, 1)
|
||||
src2Text, src2Model := amd64SweepVec(class, 2)
|
||||
gprText, gprModel := amd64SweepGpr(in)
|
||||
immText, immModel := amd64SweepImm(in)
|
||||
add := func(stmt, mnem string, ops ...arch.ExtOperand) {
|
||||
stmts = append(stmts, stmt)
|
||||
mnems = append(mnems, mnem)
|
||||
models = append(models, ops)
|
||||
}
|
||||
switch in.Form {
|
||||
case arch.ExtFormAmdVec3:
|
||||
dText, dModel := amd64SweepDest(in, class, 3)
|
||||
add(fmt.Sprintf("%s %s, %s, %s", in.Name, srcText, src2Text, dText), in.Name, srcModel, src2Model, dModel)
|
||||
if in.Mem == 2 {
|
||||
dText, dModel = amd64SweepMaskedDest(in, class, 3)
|
||||
add(fmt.Sprintf("%s %s, %s, %s", in.Name, srcText, memText, dText), in.Name, srcModel, memModel, dModel)
|
||||
}
|
||||
case arch.ExtFormAmdVec2, arch.ExtFormAmdVec2Half, arch.ExtFormAmdVec2Wide,
|
||||
arch.ExtFormAmdVec2Quarter, arch.ExtFormAmdVec2ToQuarter:
|
||||
dText, dModel := amd64SweepDest(in, destClass, 2)
|
||||
add(fmt.Sprintf("%s %s, %s", in.Name, srcText, dText), in.Name, srcModel, dModel)
|
||||
if in.Mem == 1 {
|
||||
dText, dModel = amd64SweepMaskedDest(in, destClass, 2)
|
||||
add(fmt.Sprintf("%s %s, %s", in.Name, memText, dText), in.Name, memModel, dModel)
|
||||
}
|
||||
case arch.ExtFormAmdMask2:
|
||||
add(fmt.Sprintf("%s %s, %s, K3", in.Name, srcText, src2Text), in.Name, srcModel, src2Model, arch.ExtMask(3))
|
||||
case arch.ExtFormAmdVecGprVec:
|
||||
dText, dModel := amd64SweepVec(class, 2)
|
||||
add(fmt.Sprintf("%s %s, %s, %s", in.Name, srcText, gprText, dText), in.Name, srcModel, gprModel, dModel)
|
||||
case arch.ExtFormAmdGprVec:
|
||||
dText, dModel := amd64SweepVec(class, 1)
|
||||
add(fmt.Sprintf("%s %s, %s", in.Name, gprText, dText), in.Name, gprModel, dModel)
|
||||
case arch.ExtFormAmdVecGpr:
|
||||
add(fmt.Sprintf("%s %s, %s", in.Name, srcText, gprText), in.Name, srcModel, gprModel)
|
||||
case arch.ExtFormAmdVec3Imm:
|
||||
dText, dModel := amd64SweepMaskedDest(in, class, 4)
|
||||
add(fmt.Sprintf("%s %s, %s, %s, %s", in.Name, immText, srcText, src2Text, dText), in.Name, immModel, srcModel, src2Model, dModel)
|
||||
if in.Mem == 3 {
|
||||
add(fmt.Sprintf("%s %s, %s, %s, %s", in.Name, immText, srcText, memText, dText), in.Name, immModel, srcModel, memModel, dModel)
|
||||
}
|
||||
case arch.ExtFormAmdMask2Imm:
|
||||
add(fmt.Sprintf("%s %s, %s, %s, K3", in.Name, immText, srcText, src2Text), in.Name, immModel, srcModel, src2Model, arch.ExtMask(3))
|
||||
if in.Mem == 3 {
|
||||
add(fmt.Sprintf("%s %s, %s, %s, K3", in.Name, immText, srcText, memText), in.Name, immModel, srcModel, memModel, arch.ExtMask(3))
|
||||
}
|
||||
case arch.ExtFormAmdVec2Imm:
|
||||
dText, dModel := amd64SweepMaskedDest(in, class, 3)
|
||||
add(fmt.Sprintf("%s %s, %s, %s", in.Name, immText, srcText, dText), in.Name, immModel, srcModel, dModel)
|
||||
if in.Mem == 2 {
|
||||
add(fmt.Sprintf("%s %s, %s, %s", in.Name, immText, memText, dText), in.Name, immModel, memModel, dModel)
|
||||
}
|
||||
case arch.ExtFormAmdMemVec:
|
||||
dText, dModel := amd64SweepVec(class, 1)
|
||||
add(fmt.Sprintf("%s %s, %s", in.Name, memText, dText), in.Name, memModel, dModel)
|
||||
case arch.ExtFormAmdVecMem:
|
||||
add(fmt.Sprintf("%s %s, %s", in.Name, srcText, memText), in.Name, srcModel, memModel)
|
||||
default:
|
||||
stmts = append(stmts, "")
|
||||
mnems = append(mnems, in.Name)
|
||||
models = append(models, nil)
|
||||
}
|
||||
}
|
||||
return stmts, mnems, models, distinct
|
||||
}
|
||||
|
||||
// TestAmd64AssembleExtensionSweep drives every distinct registered mnemonic
|
||||
// through the full assembler from .s text: the register-form statement of the
|
||||
// mnemonic's first entry, and the memory-form statement beside it where the
|
||||
// entry carries a memory position. Every statement must assemble, and every
|
||||
// body must equal EncodeExtension's encoding of the model operands the text
|
||||
// spells, so the front end and the registry cannot drift apart on any family.
|
||||
func TestAmd64AssembleExtensionSweep(t *testing.T) {
|
||||
stmts, mnems, models, distinct := amd64ExtSweepStatements()
|
||||
if distinct != 86 {
|
||||
t.Errorf("the amd64 layer registers %d distinct mnemonics, want 86", distinct)
|
||||
}
|
||||
for i, stmt := range stmts {
|
||||
if stmt == "" {
|
||||
t.Errorf("%s registers a form the sweep builder does not spell", mnems[i])
|
||||
continue
|
||||
}
|
||||
body := amd64ExtProbe(t, stmt)
|
||||
want, err := EncodeExtension(arch.AMD64, mnems[i], models[i]...)
|
||||
if err != nil {
|
||||
t.Errorf("%s: registry encode: %v", stmt, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(body, want) {
|
||||
t.Errorf("%s:\n got %x\n want %x (the registry encoding)", stmt, body, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user