internal/{unify,simdgen}: replace NewDef with DefBuilder
NewDef requires two separate slices of field names and field values,
which is really awkward to use, both because you have to pair up the
i'th positions in two slices when reading code, and because it makes
it really awkward to conditionally add fields.
Fix this by replacing NewDef with a DefBuilder type that lets you add
field/value pairs one by one to build a Def.
No effect on generated godefs.
Change-Id: I75dfb6ac798585e717965ab9d5d0f1bc6a157aef
Reviewed-on: https://go-review.googlesource.com/c/arch/+/691337
Auto-Submit: Austin Clements <austin@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
diff --git a/internal/simdgen/xed.go b/internal/simdgen/xed.go
index 26d0adb..dddf539 100644
--- a/internal/simdgen/xed.go
+++ b/internal/simdgen/xed.go
@@ -119,7 +119,8 @@
operandCommon
vecShape
// Bits in the mask is w/bits.
- allMasks bool
+
+ allMasks bool // If set, size cannot be inferred because all operands are masks.
}
type operandImm struct {
@@ -129,7 +130,7 @@
type operand interface {
common() operandCommon
- toValue() (fields []string, vals []*unify.Value)
+ addToDef(b *unify.DefBuilder)
}
func strVal(s any) *unify.Value {
@@ -140,53 +141,52 @@
return o
}
-func (o operandMem) toValue() (fields []string, vals []*unify.Value) {
+func (o operandMem) addToDef(b *unify.DefBuilder) {
// TODO: w, base
- return []string{"class"}, []*unify.Value{strVal("memory")}
+ b.Add("class", strVal("memory"))
}
-func (o operandVReg) toValue() (fields []string, vals []*unify.Value) {
+func (o operandVReg) addToDef(b *unify.DefBuilder) {
baseDomain, err := unify.NewStringRegex(o.elemBaseType.regex())
if err != nil {
panic("parsing baseRe: " + err.Error())
}
- fields, vals = []string{"class", "bits", "base"}, []*unify.Value{
- strVal("vreg"),
- strVal(o.bits),
- unify.NewValue(baseDomain)}
+ b.Add("class", strVal("vreg"))
+ b.Add("bits", strVal(o.bits))
+ b.Add("base", unify.NewValue(baseDomain))
+ // If elemBits == bits, then the vector can be ANY shape. This happens with,
+ // for example, logical ops.
if o.elemBits != o.bits {
- fields, vals = append(fields, "elemBits"), append(vals, strVal(o.elemBits))
+ b.Add("elemBits", strVal(o.elemBits))
}
- // otherwise it means the vector could be any shape.
- return
}
-func (o operandGReg) toValue() (fields []string, vals []*unify.Value) {
+func (o operandGReg) addToDef(b *unify.DefBuilder) {
baseDomain, err := unify.NewStringRegex(o.elemBaseType.regex())
if err != nil {
panic("parsing baseRe: " + err.Error())
}
- fields, vals = []string{"class", "bits", "base"}, []*unify.Value{
- strVal("greg"),
- strVal(o.bits),
- unify.NewValue(baseDomain)}
+ b.Add("class", strVal("greg"))
+ b.Add("bits", strVal(o.bits))
+ b.Add("base", unify.NewValue(baseDomain))
if o.elemBits != o.bits {
- fields, vals = append(fields, "elemBits"), append(vals, strVal(o.elemBits))
+ b.Add("elemBits", strVal(o.elemBits))
}
- // otherwise it means the vector could be any shape.
- return
}
-func (o operandMask) toValue() (fields []string, vals []*unify.Value) {
- return []string{"class", "elemBits", "bits"}, []*unify.Value{strVal("mask"), strVal(o.elemBits), strVal(o.bits)}
+func (o operandMask) addToDef(b *unify.DefBuilder) {
+ b.Add("class", strVal("mask"))
+ if o.allMasks {
+ // If all operands are masks, omit sizes and let unification determine mask sizes.
+ return
+ }
+ b.Add("elemBits", strVal(o.elemBits))
+ b.Add("bits", strVal(o.bits))
}
-func (o operandMask) zeroMaskValue() (fields []string, vals []*unify.Value) {
- return []string{"class"}, []*unify.Value{strVal("mask")}
-}
-
-func (o operandImm) toValue() (fields []string, vals []*unify.Value) {
- return []string{"class", "bits"}, []*unify.Value{strVal("immediate"), strVal(o.bits)}
+func (o operandImm) addToDef(b *unify.DefBuilder) {
+ b.Add("class", strVal("immediate"))
+ b.Add("bits", strVal(o.bits))
}
var actionEncoding = map[string]operandAction{
@@ -400,24 +400,18 @@
func operandsToUVals(ops []operand) (in, out unify.Tuple) {
var inVals, outVals []*unify.Value
for asmPos, op := range ops {
- fields, values := op.toValue()
- if opm, ok := op.(operandMask); ok {
- if opm.allMasks {
- // If all operands are masks, leave the mask inferrence to the users.
- fields, values = opm.zeroMaskValue()
- }
- }
+ var db unify.DefBuilder
+ op.addToDef(&db)
- fields = append(fields, "asmPos")
- values = append(values, unify.NewValue(unify.NewStringExact(fmt.Sprint(asmPos))))
+ db.Add("asmPos", unify.NewValue(unify.NewStringExact(fmt.Sprint(asmPos))))
action := op.common().action
if action.r {
- inVal := unify.NewValue(unify.NewDef(fields, values))
+ inVal := unify.NewValue(db.Build())
inVals = append(inVals, inVal)
}
if action.w {
- outVal := unify.NewValue(unify.NewDef(fields, values))
+ outVal := unify.NewValue(db.Build())
outVals = append(outVals, outVal)
}
}
@@ -430,15 +424,14 @@
ins, outs := operandsToUVals(ops)
// TODO: "feature"
- fields := []string{"goarch", "asm", "in", "out", "extension", "isaset"}
- values := []*unify.Value{
- unify.NewValue(unify.NewStringExact("amd64")),
- unify.NewValue(unify.NewStringExact(inst.Opcode())),
- unify.NewValue(ins),
- unify.NewValue(outs),
- unify.NewValue(unify.NewStringExact(inst.Extension)),
- unify.NewValue(unify.NewStringExact(inst.ISASet)),
- }
+ var db unify.DefBuilder
+ db.Add("goarch", unify.NewValue(unify.NewStringExact("amd64")))
+ db.Add("asm", unify.NewValue(unify.NewStringExact(inst.Opcode())))
+ db.Add("in", unify.NewValue(ins))
+ db.Add("out", unify.NewValue(outs))
+ db.Add("extension", unify.NewValue(unify.NewStringExact(inst.Extension)))
+ db.Add("isaset", unify.NewValue(unify.NewStringExact(inst.ISASet)))
+
if strings.Contains(inst.Pattern, "ZEROING=0") {
// This is an EVEX instruction, but the ".Z" (zero-merging)
// instruction flag is NOT valid. EVEX.z must be zero.
@@ -455,11 +448,10 @@
// with a mem operand.
//
// There may be other reasons.
- fields = append(fields, "zeroing")
- values = append(values, unify.NewValue(unify.NewStringExact("false")))
+ db.Add("zeroing", unify.NewValue(unify.NewStringExact("false")))
}
pos := unify.Pos{Path: inst.Pos.Path, Line: inst.Pos.Line}
- return unify.NewValuePos(unify.NewDef(fields, values), pos)
+ return unify.NewValuePos(db.Build(), pos)
}
func singular[T comparable](xs []T) (T, bool) {
diff --git a/internal/unify/domain.go b/internal/unify/domain.go
index 7386ea2..1cd5af1 100644
--- a/internal/unify/domain.go
+++ b/internal/unify/domain.go
@@ -94,21 +94,25 @@
fields map[string]*Value
}
-// NewDef creates a new [Def].
-//
-// The fields and values slices must have the same length.
-func NewDef(fields []string, values []*Value) Def {
- if len(fields) != len(values) {
- panic("fields and values must have the same length")
+// A DefBuilder builds a [Def] one field at a time. The zero value is an empty
+// [Def].
+type DefBuilder struct {
+ fields map[string]*Value
+}
+
+func (b *DefBuilder) Add(name string, v *Value) {
+ if b.fields == nil {
+ b.fields = make(map[string]*Value)
}
- m := make(map[string]*Value, len(fields))
- for i := range fields {
- if _, ok := m[fields[i]]; ok {
- panic(fmt.Sprintf("duplicate field %q", fields[i]))
- }
- m[fields[i]] = values[i]
+ if _, ok := b.fields[name]; ok {
+ panic(fmt.Sprintf("duplicate field %q", name))
}
- return Def{m}
+ b.fields[name] = v
+}
+
+// Build constructs a [Def] from the fields added to this builder.
+func (b *DefBuilder) Build() Def {
+ return Def{maps.Clone(b.fields)}
}
// Exact returns true if all field Values are exact.
diff --git a/internal/unify/yaml.go b/internal/unify/yaml.go
index 08b060d..1b1c813 100644
--- a/internal/unify/yaml.go
+++ b/internal/unify/yaml.go
@@ -206,8 +206,7 @@
return mk2(NewStringRegex(vals...))
case is(yaml.MappingNode, "tag:yaml.org,2002:map"):
- var fields []string
- var vals []*Value
+ var db DefBuilder
for i := 0; i < len(node.Content); i += 2 {
key := node.Content[i]
if key.Kind != yaml.ScalarNode {
@@ -217,10 +216,9 @@
if err != nil {
return nil, err
}
- fields = append(fields, key.Value)
- vals = append(vals, val)
+ db.Add(key.Value, val)
}
- return mk(NewDef(fields, vals))
+ return mk(db.Build())
case is(yaml.SequenceNode, "tag:yaml.org,2002:seq"):
elts := node.Content